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_RELOAD: {
883 LASSERT(lyx_view_ && buffer, /**/);
884 docstring const file = makeDisplayPath(buffer->absFileName(), 20);
885 docstring text = bformat(_("Any changes will be lost. Are you sure "
886 "you want to revert to the saved version of the document %1$s?"), file);
887 int const ret = Alert::prompt(_("Revert to saved document?"),
888 text, 1, 1, _("&Revert"), _("&Cancel"));
895 case LFUN_BUFFER_UPDATE: {
896 LASSERT(lyx_view_ && buffer, /**/);
897 string format = argument;
898 if (argument.empty())
899 format = buffer->getDefaultOutputFormat();
900 buffer->doExport(format, true);
904 case LFUN_BUFFER_VIEW: {
905 LASSERT(lyx_view_ && buffer, /**/);
906 string format = argument;
907 if (argument.empty())
908 format = buffer->getDefaultOutputFormat();
909 buffer->preview(format);
913 case LFUN_MASTER_BUFFER_UPDATE: {
914 LASSERT(lyx_view_ && buffer && buffer->masterBuffer(), /**/);
915 string format = argument;
916 if (argument.empty())
917 format = buffer->masterBuffer()->getDefaultOutputFormat();
918 buffer->masterBuffer()->doExport(format, true);
922 case LFUN_MASTER_BUFFER_VIEW: {
923 LASSERT(lyx_view_ && buffer && buffer->masterBuffer(), /**/);
924 string format = argument;
925 if (argument.empty())
926 format = buffer->masterBuffer()->getDefaultOutputFormat();
927 buffer->masterBuffer()->preview(format);
931 case LFUN_BUILD_PROGRAM:
932 LASSERT(lyx_view_ && buffer, /**/);
933 buffer->doExport("program", true);
936 case LFUN_BUFFER_CHKTEX:
937 LASSERT(lyx_view_ && buffer, /**/);
941 case LFUN_BUFFER_EXPORT:
942 LASSERT(lyx_view_ && buffer, /**/);
943 if (argument == "custom")
944 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"));
946 buffer->doExport(argument, false);
949 case LFUN_BUFFER_EXPORT_CUSTOM: {
950 LASSERT(lyx_view_ && buffer, /**/);
952 string command = split(argument, format_name, ' ');
953 Format const * format = formats.getFormat(format_name);
955 lyxerr << "Format \"" << format_name
956 << "\" not recognized!"
961 // The name of the file created by the conversion process
964 // Output to filename
965 if (format->name() == "lyx") {
966 string const latexname = buffer->latexName(false);
967 filename = changeExtension(latexname,
968 format->extension());
969 filename = addName(buffer->temppath(), filename);
971 if (!buffer->writeFile(FileName(filename)))
975 buffer->doExport(format_name, true, filename);
978 // Substitute $$FName for filename
979 if (!contains(command, "$$FName"))
980 command = "( " + command + " ) < $$FName";
981 command = subst(command, "$$FName", filename);
983 // Execute the command in the background
985 call.startscript(Systemcall::DontWait, command);
989 // FIXME: There is need for a command-line import.
991 case LFUN_BUFFER_IMPORT:
996 case LFUN_BUFFER_AUTO_SAVE:
1000 case LFUN_RECONFIGURE:
1001 // argument is any additional parameter to the configure.py command
1002 reconfigure(lyx_view_, argument);
1005 case LFUN_HELP_OPEN: {
1007 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1008 string const arg = argument;
1010 setErrorMessage(from_utf8(N_("Missing argument")));
1013 FileName fname = i18nLibFileSearch("doc", arg, "lyx");
1015 fname = i18nLibFileSearch("examples", arg, "lyx");
1017 if (fname.empty()) {
1018 lyxerr << "LyX: unable to find documentation file `"
1019 << arg << "'. Bad installation?" << endl;
1022 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1023 makeDisplayPath(fname.absFilename())));
1024 Buffer * buf = lyx_view_->loadDocument(fname, false);
1026 buf->updateLabels();
1027 lyx_view_->setBuffer(buf);
1028 buf->errors("Parse");
1030 updateFlags = Update::None;
1034 // --- version control -------------------------------
1035 case LFUN_VC_REGISTER:
1036 LASSERT(lyx_view_ && buffer, /**/);
1037 if (!ensureBufferClean(view()))
1039 if (!buffer->lyxvc().inUse()) {
1040 if (buffer->lyxvc().registrer())
1043 updateFlags = Update::Force;
1046 case LFUN_VC_CHECK_IN:
1047 LASSERT(lyx_view_ && buffer, /**/);
1048 if (!ensureBufferClean(view()))
1050 if (buffer->lyxvc().inUse()
1051 && !buffer->isReadonly()) {
1052 setMessage(from_utf8(buffer->lyxvc().checkIn()));
1057 case LFUN_VC_CHECK_OUT:
1058 LASSERT(lyx_view_ && buffer, /**/);
1059 if (!ensureBufferClean(view()))
1061 if (buffer->lyxvc().inUse()) {
1062 setMessage(from_utf8(buffer->lyxvc().checkOut()));
1067 case LFUN_VC_LOCKING_TOGGLE:
1068 LASSERT(lyx_view_ && buffer, /**/);
1069 if (!ensureBufferClean(view()) || buffer->isReadonly())
1071 if (buffer->lyxvc().inUse()) {
1072 string res = buffer->lyxvc().lockingToggle();
1074 frontend::Alert::error(_("Revision control error."),
1075 _("Error when setting the locking property."));
1077 setMessage(from_utf8(res));
1083 case LFUN_VC_REVERT:
1084 LASSERT(lyx_view_ && buffer, /**/);
1085 buffer->lyxvc().revert();
1089 case LFUN_VC_UNDO_LAST:
1090 LASSERT(lyx_view_ && buffer, /**/);
1091 buffer->lyxvc().undoLast();
1095 // --- lyxserver commands ----------------------------
1096 case LFUN_SERVER_GET_FILENAME:
1097 LASSERT(lyx_view_ && buffer, /**/);
1098 setMessage(from_utf8(buffer->absFileName()));
1099 LYXERR(Debug::INFO, "FNAME["
1100 << buffer->absFileName() << ']');
1103 case LFUN_SERVER_NOTIFY:
1104 dispatch_buffer = keyseq.print(KeySequence::Portable);
1105 theServer().notifyClient(to_utf8(dispatch_buffer));
1108 case LFUN_SERVER_GOTO_FILE_ROW: {
1109 LASSERT(lyx_view_, /**/);
1112 istringstream is(argument);
1113 is >> file_name >> row;
1115 bool loaded = false;
1116 string const abstmp = package().temp_dir().absFilename();
1117 string const realtmp = package().temp_dir().realPath();
1118 // We have to use os::path_prefix_is() here, instead of
1119 // simply prefixIs(), because the file name comes from
1120 // an external application and may need case adjustment.
1121 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
1122 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
1123 // Needed by inverse dvi search. If it is a file
1124 // in tmpdir, call the apropriated function.
1125 // If tmpdir is a symlink, we may have the real
1126 // path passed back, so we correct for that.
1127 if (!prefixIs(file_name, abstmp))
1128 file_name = subst(file_name, realtmp, abstmp);
1129 buf = theBufferList().getBufferFromTmp(file_name);
1131 // Must replace extension of the file to be .lyx
1132 // and get full path
1133 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1134 // Either change buffer or load the file
1135 if (theBufferList().exists(s))
1136 buf = theBufferList().getBuffer(s);
1137 else if (s.exists()) {
1138 buf = lyx_view_->loadDocument(s);
1141 lyx_view_->message(bformat(
1142 _("File does not exist: %1$s"),
1143 makeDisplayPath(file_name)));
1147 updateFlags = Update::None;
1151 buf->updateLabels();
1152 lyx_view_->setBuffer(buf);
1153 view()->setCursorFromRow(row);
1155 buf->errors("Parse");
1156 updateFlags = Update::FitCursor;
1161 case LFUN_DIALOG_SHOW_NEW_INSET: {
1162 LASSERT(lyx_view_, /**/);
1163 string const name = cmd.getArg(0);
1164 InsetCode code = insetCode(name);
1165 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1166 bool insetCodeOK = true;
1173 case NOMENCL_PRINT_CODE:
1176 case HYPERLINK_CODE: {
1177 InsetCommandParams p(code);
1178 data = InsetCommand::params2string(name, p);
1181 case INCLUDE_CODE: {
1182 // data is the include type: one of "include",
1183 // "input", "verbatiminput" or "verbatiminput*"
1185 // default type is requested
1187 InsetCommandParams p(INCLUDE_CODE, data);
1188 data = InsetCommand::params2string("include", p);
1192 // \c data == "Boxed" || "Frameless" etc
1193 InsetBoxParams p(data);
1194 data = InsetBox::params2string(p);
1198 InsetBranchParams p;
1199 data = InsetBranch::params2string(p);
1203 InsetCommandParams p(CITE_CODE);
1204 data = InsetCommand::params2string(name, p);
1208 data = InsetERT::params2string(InsetCollapsable::Open);
1211 case EXTERNAL_CODE: {
1212 InsetExternalParams p;
1213 data = InsetExternal::params2string(p, *buffer);
1218 data = InsetFloat::params2string(p);
1221 case LISTINGS_CODE: {
1222 InsetListingsParams p;
1223 data = InsetListings::params2string(p);
1226 case GRAPHICS_CODE: {
1227 InsetGraphicsParams p;
1228 data = InsetGraphics::params2string(p, *buffer);
1233 data = InsetNote::params2string(p);
1236 case PHANTOM_CODE: {
1237 InsetPhantomParams p;
1238 data = InsetPhantom::params2string(p);
1243 data = InsetSpace::params2string(p);
1248 data = InsetVSpace::params2string(space);
1253 data = InsetWrap::params2string(p);
1257 lyxerr << "Inset type '" << name <<
1258 "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" << endl;
1259 insetCodeOK = false;
1261 } // end switch(code)
1263 dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data));
1267 case LFUN_CITATION_INSERT: {
1268 LASSERT(lyx_view_, /**/);
1269 if (!argument.empty()) {
1270 // we can have one optional argument, delimited by '|'
1271 // citation-insert <key>|<text_before>
1272 // this should be enhanced to also support text_after
1273 // and citation style
1274 string arg = argument;
1276 if (contains(argument, "|")) {
1277 arg = token(argument, '|', 0);
1278 opt1 = token(argument, '|', 1);
1280 InsetCommandParams icp(CITE_CODE);
1281 icp["key"] = from_utf8(arg);
1283 icp["before"] = from_utf8(opt1);
1284 string icstr = InsetCommand::params2string("citation", icp);
1285 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1288 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1292 case LFUN_BUFFER_CHILD_OPEN: {
1293 LASSERT(lyx_view_ && buffer, /**/);
1294 FileName filename = makeAbsPath(argument, buffer->filePath());
1295 view()->saveBookmark(false);
1297 bool parsed = false;
1298 if (theBufferList().exists(filename)) {
1299 child = theBufferList().getBuffer(filename);
1301 setMessage(bformat(_("Opening child document %1$s..."),
1302 makeDisplayPath(filename.absFilename())));
1303 child = lyx_view_->loadDocument(filename, false);
1307 // Set the parent name of the child document.
1308 // This makes insertion of citations and references in the child work,
1309 // when the target is in the parent or another child document.
1310 child->setParent(buffer);
1311 child->masterBuffer()->updateLabels();
1312 lyx_view_->setBuffer(child);
1314 child->errors("Parse");
1317 // If a screen update is required (in case where auto_open is false),
1318 // setBuffer() would have taken care of it already. Otherwise we shall
1319 // reset the update flag because it can cause a circular problem.
1321 updateFlags = Update::None;
1325 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
1326 LASSERT(lyx_view_, /**/);
1327 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1330 case LFUN_KEYMAP_OFF:
1331 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1332 lyx_view_->view()->getIntl().keyMapOn(false);
1335 case LFUN_KEYMAP_PRIMARY:
1336 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1337 lyx_view_->view()->getIntl().keyMapPrim();
1340 case LFUN_KEYMAP_SECONDARY:
1341 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1342 lyx_view_->view()->getIntl().keyMapSec();
1345 case LFUN_KEYMAP_TOGGLE:
1346 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1347 lyx_view_->view()->getIntl().toggleKeyMap();
1353 string rest = split(argument, countstr, ' ');
1354 istringstream is(countstr);
1357 //lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1358 for (int i = 0; i < count; ++i)
1359 dispatch(lyxaction.lookupFunc(rest));
1363 case LFUN_COMMAND_SEQUENCE: {
1364 // argument contains ';'-terminated commands
1365 string arg = argument;
1366 if (theBufferList().isLoaded(buffer))
1367 buffer->undo().beginUndoGroup();
1368 while (!arg.empty()) {
1370 arg = split(arg, first, ';');
1371 FuncRequest func(lyxaction.lookupFunc(first));
1372 func.origin = cmd.origin;
1375 if (theBufferList().isLoaded(buffer))
1376 buffer->undo().endUndoGroup();
1380 case LFUN_COMMAND_ALTERNATIVES: {
1381 // argument contains ';'-terminated commands
1382 string arg = argument;
1383 while (!arg.empty()) {
1385 arg = split(arg, first, ';');
1386 FuncRequest func(lyxaction.lookupFunc(first));
1387 func.origin = cmd.origin;
1388 FuncStatus stat = getStatus(func);
1389 if (stat.enabled()) {
1399 if (theTopLevelCmdDef().lock(argument, func)) {
1400 func.origin = cmd.origin;
1402 theTopLevelCmdDef().release(argument);
1404 if (func.action == LFUN_UNKNOWN_ACTION) {
1405 // unknown command definition
1406 lyxerr << "Warning: unknown command definition `"
1410 // recursion detected
1411 lyxerr << "Warning: Recursion in the command definition `"
1412 << argument << "' detected"
1419 case LFUN_PREFERENCES_SAVE: {
1420 lyxrc.write(makeAbsPath("preferences",
1421 package().user_support().absFilename()),
1427 LASSERT(lyx_view_, /**/);
1428 lyx_view_->message(from_utf8(argument));
1431 case LFUN_BUFFER_LANGUAGE: {
1432 LASSERT(lyx_view_, /**/);
1433 Language const * oldL = buffer->params().language;
1434 Language const * newL = languages.getLanguage(argument);
1435 if (!newL || oldL == newL)
1438 if (oldL->rightToLeft() == newL->rightToLeft()
1439 && !buffer->isMultiLingual())
1440 buffer->changeLanguage(oldL, newL);
1444 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1445 string const fname =
1446 addName(addPath(package().user_support().absFilename(), "templates/"),
1448 Buffer defaults(fname);
1450 istringstream ss(argument);
1453 int const unknown_tokens = defaults.readHeader(lex);
1455 if (unknown_tokens != 0) {
1456 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1457 << unknown_tokens << " unknown token"
1458 << (unknown_tokens == 1 ? "" : "s")
1462 if (defaults.writeFile(FileName(defaults.absFileName())))
1463 setMessage(bformat(_("Document defaults saved in %1$s"),
1464 makeDisplayPath(fname)));
1466 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1470 case LFUN_BUFFER_PARAMS_APPLY: {
1471 LASSERT(lyx_view_, /**/);
1473 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1474 Cursor & cur = view()->cursor();
1475 cur.recordUndoFullDocument();
1477 istringstream ss(argument);
1480 int const unknown_tokens = buffer->readHeader(lex);
1482 if (unknown_tokens != 0) {
1483 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1484 << unknown_tokens << " unknown token"
1485 << (unknown_tokens == 1 ? "" : "s")
1489 updateLayout(oldClass, buffer);
1491 updateFlags = Update::Force | Update::FitCursor;
1492 // We are most certainly here because of a change in the document
1493 // It is then better to make sure that all dialogs are in sync with
1494 // current document settings. LyXView::restartCursor() achieve this.
1495 lyx_view_->restartCursor();
1499 case LFUN_LAYOUT_MODULES_CLEAR: {
1500 LASSERT(lyx_view_, /**/);
1501 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1502 view()->cursor().recordUndoFullDocument();
1503 buffer->params().clearLayoutModules();
1504 buffer->params().makeDocumentClass();
1505 updateLayout(oldClass, buffer);
1506 updateFlags = Update::Force | Update::FitCursor;
1510 case LFUN_LAYOUT_MODULE_ADD: {
1511 LASSERT(lyx_view_, /**/);
1512 BufferParams const & params = buffer->params();
1513 if (!params.moduleCanBeAdded(argument)) {
1514 LYXERR0("Module `" << argument <<
1515 "' cannot be added due to failed requirements or "
1516 "conflicts with installed modules.");
1519 DocumentClass const * const oldClass = params.documentClassPtr();
1520 view()->cursor().recordUndoFullDocument();
1521 buffer->params().addLayoutModule(argument);
1522 buffer->params().makeDocumentClass();
1523 updateLayout(oldClass, buffer);
1524 updateFlags = Update::Force | Update::FitCursor;
1528 case LFUN_TEXTCLASS_APPLY: {
1529 LASSERT(lyx_view_, /**/);
1531 if (!loadLayoutFile(argument, buffer->temppath()) &&
1532 !loadLayoutFile(argument, buffer->filePath()))
1535 LayoutFile const * old_layout = buffer->params().baseClass();
1536 LayoutFile const * new_layout = &(LayoutFileList::get()[argument]);
1538 if (old_layout == new_layout)
1542 //Save the old, possibly modular, layout for use in conversion.
1543 DocumentClass const * const oldDocClass = buffer->params().documentClassPtr();
1544 view()->cursor().recordUndoFullDocument();
1545 buffer->params().setBaseClass(argument);
1546 buffer->params().makeDocumentClass();
1547 updateLayout(oldDocClass, buffer);
1548 updateFlags = Update::Force | Update::FitCursor;
1552 case LFUN_LAYOUT_RELOAD: {
1553 LASSERT(lyx_view_, /**/);
1554 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1555 LayoutFileIndex bc = buffer->params().baseClassID();
1556 LayoutFileList::get().reset(bc);
1557 buffer->params().setBaseClass(bc);
1558 buffer->params().makeDocumentClass();
1559 updateLayout(oldClass, buffer);
1560 updateFlags = Update::Force | Update::FitCursor;
1564 case LFUN_TEXTCLASS_LOAD:
1565 loadLayoutFile(argument, buffer->temppath()) ||
1566 loadLayoutFile(argument, buffer->filePath());
1569 case LFUN_LYXRC_APPLY: {
1570 LyXRC const lyxrc_orig = lyxrc;
1572 istringstream ss(argument);
1573 bool const success = lyxrc.read(ss) == 0;
1576 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1577 << "Unable to read lyxrc data"
1582 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1584 theApp()->resetGui();
1586 /// We force the redraw in any case because there might be
1587 /// some screen font changes.
1588 /// FIXME: only the current view will be updated. the Gui
1589 /// class is able to furnish the list of views.
1590 updateFlags = Update::Force;
1594 case LFUN_BOOKMARK_GOTO:
1595 // go to bookmark, open unopened file and switch to buffer if necessary
1596 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1597 updateFlags = Update::FitCursor;
1600 case LFUN_BOOKMARK_CLEAR:
1601 theSession().bookmarks().clear();
1604 case LFUN_VC_COMMAND: {
1605 string flag = cmd.getArg(0);
1606 if (buffer && contains(flag, 'R') && !ensureBufferClean(view()))
1609 if (contains(flag, 'M'))
1610 if (!Alert::askForText(message, _("LyX VC: Log Message")))
1613 string path = cmd.getArg(1);
1614 if (contains(path, "$$p") && buffer)
1615 path = subst(path, "$$p", buffer->filePath());
1616 LYXERR(Debug::LYXVC, "Directory: " << path);
1618 if (!pp.isReadableDirectory()) {
1619 lyxerr << _("Directory is not accessible.") << endl;
1622 support::PathChanger p(pp);
1624 string command = cmd.getArg(2);
1625 if (command.empty())
1628 command = subst(command, "$$i", buffer->absFileName());
1629 command = subst(command, "$$p", buffer->filePath());
1631 command = subst(command, "$$m", to_utf8(message));
1632 LYXERR(Debug::LYXVC, "Command: " << command);
1634 one.startscript(Systemcall::Wait, command);
1638 if (contains(flag, 'I'))
1639 buffer->markDirty();
1640 if (contains(flag, 'R'))
1647 LASSERT(theApp(), /**/);
1648 // Let the frontend dispatch its own actions.
1649 if (theApp()->dispatch(cmd))
1650 // Nothing more to do.
1653 // Everything below is only for active lyx_view_
1657 // Start an undo group. This may be needed for
1658 // some stuff like inset-apply on labels.
1659 if (theBufferList().isLoaded(buffer))
1660 buffer->undo().beginUndoGroup();
1662 // Let the current LyXView dispatch its own actions.
1663 if (lyx_view_->dispatch(cmd)) {
1664 if (lyx_view_->view()) {
1665 updateFlags = lyx_view_->view()->cursor().result().update();
1666 if (theBufferList().isLoaded(buffer))
1667 buffer->undo().endUndoGroup();
1672 LASSERT(lyx_view_->view(), /**/);
1674 // Let the current BufferView dispatch its own actions.
1675 if (view()->dispatch(cmd)) {
1676 // The BufferView took care of its own updates if needed.
1677 updateFlags = Update::None;
1678 if (theBufferList().isLoaded(buffer))
1679 buffer->undo().endUndoGroup();
1683 // OK, so try the Buffer itself
1685 view()->buffer().dispatch(cmd, dr);
1686 if (dr.dispatched()) {
1687 updateFlags = dr.update();
1691 // Is this a function that acts on inset at point?
1692 Inset * inset = view()->cursor().nextInset();
1693 if (lyxaction.funcHasFlag(action, LyXAction::AtPoint)
1695 view()->cursor().result().dispatched(true);
1696 view()->cursor().result().update(Update::FitCursor | Update::Force);
1697 FuncRequest tmpcmd = cmd;
1698 inset->dispatch(view()->cursor(), tmpcmd);
1699 if (view()->cursor().result().dispatched()) {
1700 updateFlags = view()->cursor().result().update();
1705 // Let the current Cursor dispatch its own actions.
1706 Cursor old = view()->cursor();
1707 view()->cursor().getPos(cursorPosBeforeDispatchX_,
1708 cursorPosBeforeDispatchY_);
1709 view()->cursor().dispatch(cmd);
1711 // notify insets we just left
1712 if (view()->cursor() != old) {
1714 bool badcursor = notifyCursorLeavesOrEnters(old, view()->cursor());
1716 view()->cursor().fixIfBroken();
1719 if (theBufferList().isLoaded(buffer))
1720 buffer->undo().endUndoGroup();
1722 // update completion. We do it here and not in
1723 // processKeySym to avoid another redraw just for a
1724 // changed inline completion
1725 if (cmd.origin == FuncRequest::KEYBOARD) {
1726 if (cmd.action == LFUN_SELF_INSERT
1727 || (cmd.action == LFUN_ERT_INSERT && view()->cursor().inMathed()))
1728 lyx_view_->updateCompletion(view()->cursor(), true, true);
1729 else if (cmd.action == LFUN_CHAR_DELETE_BACKWARD)
1730 lyx_view_->updateCompletion(view()->cursor(), false, true);
1732 lyx_view_->updateCompletion(view()->cursor(), false, false);
1735 updateFlags = view()->cursor().result().update();
1738 // if we executed a mutating lfun, mark the buffer as dirty
1739 if (theBufferList().isLoaded(buffer) && flag.enabled()
1740 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1741 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1742 buffer->markDirty();
1744 if (lyx_view_ && lyx_view_->buffer()) {
1745 // BufferView::update() updates the ViewMetricsInfo and
1746 // also initializes the position cache for all insets in
1747 // (at least partially) visible top-level paragraphs.
1748 // We will redraw the screen only if needed.
1749 view()->processUpdateFlags(updateFlags);
1751 // Do we have a selection?
1752 theSelection().haveSelection(view()->cursor().selection());
1755 lyx_view_->restartCursor();
1759 // Some messages may already be translated, so we cannot use _()
1760 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1765 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1767 const bool verbose = (cmd.origin == FuncRequest::MENU
1768 || cmd.origin == FuncRequest::TOOLBAR
1769 || cmd.origin == FuncRequest::COMMANDBUFFER);
1771 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1772 LYXERR(Debug::ACTION, "dispatch msg is " << to_utf8(msg));
1774 lyx_view_->message(msg);
1778 docstring dispatch_msg = msg;
1779 if (!dispatch_msg.empty())
1780 dispatch_msg += ' ';
1782 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1784 bool argsadded = false;
1786 if (!cmd.argument().empty()) {
1787 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1788 comname += ' ' + cmd.argument();
1793 docstring const shortcuts = theTopLevelKeymap().printBindings(cmd, KeySequence::ForGui);
1795 if (!shortcuts.empty())
1796 comname += ": " + shortcuts;
1797 else if (!argsadded && !cmd.argument().empty())
1798 comname += ' ' + cmd.argument();
1800 if (!comname.empty()) {
1801 comname = rtrim(comname);
1802 dispatch_msg += '(' + rtrim(comname) + ')';
1805 LYXERR(Debug::ACTION, "verbose dispatch msg " << to_utf8(dispatch_msg));
1806 if (!dispatch_msg.empty())
1807 lyx_view_->message(dispatch_msg);
1811 void LyXFunc::reloadBuffer()
1813 FileName filename = lyx_view_->buffer()->fileName();
1814 // The user has already confirmed that the changes, if any, should
1815 // be discarded. So we just release the Buffer and don't call closeBuffer();
1816 theBufferList().release(lyx_view_->buffer());
1817 // if the lyx_view_ has been destroyed, create a new one
1819 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1820 Buffer * buf = lyx_view_->loadDocument(filename);
1821 docstring const disp_fn = makeDisplayPath(filename.absFilename());
1824 buf->updateLabels();
1825 lyx_view_->setBuffer(buf);
1826 buf->errors("Parse");
1827 str = bformat(_("Document %1$s reloaded."), disp_fn);
1829 str = bformat(_("Could not reload document %1$s"), disp_fn);
1831 lyx_view_->message(str);
1834 // Each "lyx_view_" should have it's own message method. lyxview and
1835 // the minibuffer would use the minibuffer, but lyxserver would
1836 // send an ERROR signal to its client. Alejandro 970603
1837 // This function is bit problematic when it comes to NLS, to make the
1838 // lyx servers client be language indepenent we must not translate
1839 // strings sent to this func.
1840 void LyXFunc::setErrorMessage(docstring const & m) const
1842 dispatch_buffer = m;
1847 void LyXFunc::setMessage(docstring const & m) const
1849 dispatch_buffer = m;
1853 docstring LyXFunc::viewStatusMessage()
1855 // When meta-fake key is pressed, show the key sequence so far + "M-".
1857 return keyseq.print(KeySequence::ForGui) + "M-";
1859 // Else, when a non-complete key sequence is pressed,
1860 // show the available options.
1861 if (keyseq.length() > 0 && !keyseq.deleted())
1862 return keyseq.printOptions(true);
1864 LASSERT(lyx_view_, /**/);
1865 if (!lyx_view_->buffer())
1866 return _("Welcome to LyX!");
1868 return view()->cursor().currentState();
1872 BufferView * LyXFunc::view() const
1874 LASSERT(lyx_view_, /**/);
1875 return lyx_view_->view();
1879 bool LyXFunc::wasMetaKey() const
1881 return (meta_fake_bit != NoModifier);
1885 void LyXFunc::updateLayout(DocumentClass const * const oldlayout, Buffer * buf)
1887 lyx_view_->message(_("Converting document to new document class..."));
1889 StableDocIterator backcur(view()->cursor());
1890 ErrorList & el = buf->errorList("Class Switch");
1891 cap::switchBetweenClasses(
1892 oldlayout, buf->params().documentClassPtr(),
1893 static_cast<InsetText &>(buf->inset()), el);
1895 view()->setCursor(backcur.asDocIterator(buf));
1897 buf->errors("Class Switch");
1898 buf->updateLabels();
1904 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1906 // Why the switch you might ask. It is a trick to ensure that all
1907 // the elements in the LyXRCTags enum is handled. As you can see
1908 // there are no breaks at all. So it is just a huge fall-through.
1909 // The nice thing is that we will get a warning from the compiler
1910 // if we forget an element.
1911 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1913 case LyXRC::RC_ACCEPT_COMPOUND:
1914 case LyXRC::RC_ALT_LANG:
1915 case LyXRC::RC_PLAINTEXT_LINELEN:
1916 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
1917 case LyXRC::RC_AUTOCORRECTION_MATH:
1918 case LyXRC::RC_AUTOREGIONDELETE:
1919 case LyXRC::RC_AUTORESET_OPTIONS:
1920 case LyXRC::RC_AUTOSAVE:
1921 case LyXRC::RC_AUTO_NUMBER:
1922 case LyXRC::RC_BACKUPDIR_PATH:
1923 case LyXRC::RC_BIBTEX_ALTERNATIVES:
1924 case LyXRC::RC_BIBTEX_COMMAND:
1925 case LyXRC::RC_BINDFILE:
1926 case LyXRC::RC_CHECKLASTFILES:
1927 case LyXRC::RC_COMPLETION_CURSOR_TEXT:
1928 case LyXRC::RC_COMPLETION_INLINE_DELAY:
1929 case LyXRC::RC_COMPLETION_INLINE_DOTS:
1930 case LyXRC::RC_COMPLETION_INLINE_MATH:
1931 case LyXRC::RC_COMPLETION_INLINE_TEXT:
1932 case LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE:
1933 case LyXRC::RC_COMPLETION_POPUP_DELAY:
1934 case LyXRC::RC_COMPLETION_POPUP_MATH:
1935 case LyXRC::RC_COMPLETION_POPUP_TEXT:
1936 case LyXRC::RC_USELASTFILEPOS:
1937 case LyXRC::RC_LOADSESSION:
1938 case LyXRC::RC_CHKTEX_COMMAND:
1939 case LyXRC::RC_CONVERTER:
1940 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
1941 case LyXRC::RC_COPIER:
1942 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1943 case LyXRC::RC_SCROLL_BELOW_DOCUMENT:
1944 case LyXRC::RC_DATE_INSERT_FORMAT:
1945 case LyXRC::RC_DEFAULT_LANGUAGE:
1946 case LyXRC::RC_GUI_LANGUAGE:
1947 case LyXRC::RC_DEFAULT_PAPERSIZE:
1948 case LyXRC::RC_DEFAULT_VIEW_FORMAT:
1949 case LyXRC::RC_DEFFILE:
1950 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1951 case LyXRC::RC_DISPLAY_GRAPHICS:
1952 case LyXRC::RC_DOCUMENTPATH:
1953 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1954 FileName path(lyxrc_new.document_path);
1955 if (path.exists() && path.isDirectory())
1956 package().document_dir() = FileName(lyxrc.document_path);
1958 case LyXRC::RC_ESC_CHARS:
1959 case LyXRC::RC_EXAMPLEPATH:
1960 case LyXRC::RC_FONT_ENCODING:
1961 case LyXRC::RC_FORMAT:
1962 case LyXRC::RC_GROUP_LAYOUTS:
1963 case LyXRC::RC_INDEX_ALTERNATIVES:
1964 case LyXRC::RC_INDEX_COMMAND:
1965 case LyXRC::RC_JBIBTEX_COMMAND:
1966 case LyXRC::RC_JINDEX_COMMAND:
1967 case LyXRC::RC_NOMENCL_COMMAND:
1968 case LyXRC::RC_INPUT:
1969 case LyXRC::RC_KBMAP:
1970 case LyXRC::RC_KBMAP_PRIMARY:
1971 case LyXRC::RC_KBMAP_SECONDARY:
1972 case LyXRC::RC_LABEL_INIT_LENGTH:
1973 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
1974 case LyXRC::RC_LANGUAGE_AUTO_END:
1975 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
1976 case LyXRC::RC_LANGUAGE_COMMAND_END:
1977 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
1978 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
1979 case LyXRC::RC_LANGUAGE_PACKAGE:
1980 case LyXRC::RC_LANGUAGE_USE_BABEL:
1981 case LyXRC::RC_MAC_LIKE_WORD_MOVEMENT:
1982 case LyXRC::RC_MACRO_EDIT_STYLE:
1983 case LyXRC::RC_MAKE_BACKUP:
1984 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
1985 case LyXRC::RC_MOUSE_WHEEL_SPEED:
1986 case LyXRC::RC_NUMLASTFILES:
1987 case LyXRC::RC_PATH_PREFIX:
1988 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
1989 prependEnvPath("PATH", lyxrc.path_prefix);
1991 case LyXRC::RC_PERS_DICT:
1992 case LyXRC::RC_PREVIEW:
1993 case LyXRC::RC_PREVIEW_HASHED_LABELS:
1994 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
1995 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
1996 case LyXRC::RC_PRINTCOPIESFLAG:
1997 case LyXRC::RC_PRINTER:
1998 case LyXRC::RC_PRINTEVENPAGEFLAG:
1999 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2000 case LyXRC::RC_PRINTFILEEXTENSION:
2001 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2002 case LyXRC::RC_PRINTODDPAGEFLAG:
2003 case LyXRC::RC_PRINTPAGERANGEFLAG:
2004 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2005 case LyXRC::RC_PRINTPAPERFLAG:
2006 case LyXRC::RC_PRINTREVERSEFLAG:
2007 case LyXRC::RC_PRINTSPOOL_COMMAND:
2008 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2009 case LyXRC::RC_PRINTTOFILE:
2010 case LyXRC::RC_PRINTTOPRINTER:
2011 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2012 case LyXRC::RC_PRINT_COMMAND:
2013 case LyXRC::RC_RTL_SUPPORT:
2014 case LyXRC::RC_SCREEN_DPI:
2015 case LyXRC::RC_SCREEN_FONT_ROMAN:
2016 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2017 case LyXRC::RC_SCREEN_FONT_SANS:
2018 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2019 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2020 case LyXRC::RC_SCREEN_FONT_SIZES:
2021 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2022 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2023 case LyXRC::RC_GEOMETRY_SESSION:
2024 case LyXRC::RC_SCREEN_ZOOM:
2025 case LyXRC::RC_SERVERPIPE:
2026 case LyXRC::RC_SET_COLOR:
2027 case LyXRC::RC_SHOW_BANNER:
2028 case LyXRC::RC_OPEN_BUFFERS_IN_TABS:
2029 case LyXRC::RC_SPELL_COMMAND:
2030 case LyXRC::RC_SPELLCHECK_CONTINUOUSLY:
2031 case LyXRC::RC_SPLITINDEX_COMMAND:
2032 case LyXRC::RC_TEMPDIRPATH:
2033 case LyXRC::RC_TEMPLATEPATH:
2034 case LyXRC::RC_TEX_ALLOWS_SPACES:
2035 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2036 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2037 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2039 case LyXRC::RC_THESAURUSDIRPATH:
2040 case LyXRC::RC_UIFILE:
2041 case LyXRC::RC_USER_EMAIL:
2042 case LyXRC::RC_USER_NAME:
2043 case LyXRC::RC_USETEMPDIR:
2044 case LyXRC::RC_USE_ALT_LANG:
2045 case LyXRC::RC_USE_CONVERTER_CACHE:
2046 case LyXRC::RC_USE_ESC_CHARS:
2047 case LyXRC::RC_USE_INP_ENC:
2048 case LyXRC::RC_USE_PERS_DICT:
2049 case LyXRC::RC_USE_TOOLTIP:
2050 case LyXRC::RC_USE_PIXMAP_CACHE:
2051 case LyXRC::RC_USE_SPELL_LIB:
2052 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2053 case LyXRC::RC_SORT_LAYOUTS:
2054 case LyXRC::RC_FULL_SCREEN_LIMIT:
2055 case LyXRC::RC_FULL_SCREEN_SCROLLBAR:
2056 case LyXRC::RC_FULL_SCREEN_MENUBAR:
2057 case LyXRC::RC_FULL_SCREEN_TABBAR:
2058 case LyXRC::RC_FULL_SCREEN_TOOLBARS:
2059 case LyXRC::RC_FULL_SCREEN_WIDTH:
2060 case LyXRC::RC_VISUAL_CURSOR:
2061 case LyXRC::RC_VIEWER:
2062 case LyXRC::RC_LAST: