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_CLOSE_ALL:
637 case LFUN_BUFFER_IMPORT:
638 case LFUN_BUFFER_AUTO_SAVE:
639 case LFUN_RECONFIGURE:
641 case LFUN_DROP_LAYOUTS_CHOICE:
643 case LFUN_SERVER_GET_FILENAME:
644 case LFUN_SERVER_NOTIFY:
645 case LFUN_SERVER_GOTO_FILE_ROW:
646 case LFUN_DIALOG_HIDE:
647 case LFUN_DIALOG_DISCONNECT_INSET:
648 case LFUN_BUFFER_CHILD_OPEN:
649 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
650 case LFUN_KEYMAP_OFF:
651 case LFUN_KEYMAP_PRIMARY:
652 case LFUN_KEYMAP_SECONDARY:
653 case LFUN_KEYMAP_TOGGLE:
655 case LFUN_BUFFER_EXPORT_CUSTOM:
656 case LFUN_PREFERENCES_SAVE:
658 case LFUN_INSET_EDIT:
659 case LFUN_BUFFER_LANGUAGE:
660 case LFUN_TEXTCLASS_APPLY:
661 case LFUN_TEXTCLASS_LOAD:
662 case LFUN_BUFFER_SAVE_AS_DEFAULT:
663 case LFUN_BUFFER_PARAMS_APPLY:
664 case LFUN_LAYOUT_MODULES_CLEAR:
665 case LFUN_LAYOUT_MODULE_ADD:
666 case LFUN_LAYOUT_RELOAD:
667 case LFUN_LYXRC_APPLY:
668 case LFUN_BUFFER_NEXT:
669 case LFUN_BUFFER_PREVIOUS:
670 // these are handled in our dispatch()
678 if (theApp()->getStatus(cmd, flag))
681 // Does the view know something?
686 if (lv->getStatus(cmd, flag))
689 // If we do not have a BufferView, then other functions are disabled
695 // Is this a function that acts on inset at point?
696 Inset * inset = view()->cursor().nextInset();
697 if (lyxaction.funcHasFlag(cmd.action, LyXAction::AtPoint)
698 && inset && inset->getStatus(view()->cursor(), cmd, flag))
701 bool decided = getLocalStatus(view()->cursor(), cmd, flag);
703 // try the BufferView
704 decided = view()->getStatus(cmd, flag);
707 view()->buffer().getStatus(cmd, flag);
711 flag.setEnabled(false);
713 // Can we use a readonly buffer?
714 if (buf && buf->isReadonly()
715 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
716 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
717 flag.message(from_utf8(N_("Document is read-only")));
718 flag.setEnabled(false);
721 // Are we in a DELETED change-tracking region?
723 && lookupChangeType(view()->cursor(), true) == Change::DELETED
724 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
725 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
726 flag.message(from_utf8(N_("This portion of the document is deleted.")));
727 flag.setEnabled(false);
730 // the default error message if we disable the command
731 if (!flag.enabled() && flag.message().empty())
732 flag.message(from_utf8(N_("Command disabled")));
738 bool LyXFunc::ensureBufferClean(BufferView * bv)
740 Buffer & buf = bv->buffer();
741 if (buf.isClean() && !buf.isUnnamed())
744 docstring const file = buf.fileName().displayName(30);
747 if (!buf.isUnnamed()) {
748 text = bformat(_("The document %1$s has unsaved "
749 "changes.\n\nDo you want to save "
750 "the document?"), file);
751 title = _("Save changed document?");
754 text = bformat(_("The document %1$s has not been "
755 "saved yet.\n\nDo you want to save "
756 "the document?"), file);
757 title = _("Save new document?");
759 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
762 lyx_view_->dispatch(FuncRequest(LFUN_BUFFER_WRITE));
764 return buf.isClean() && !buf.isUnnamed();
770 bool loadLayoutFile(string const & name, string const & buf_path)
772 if (!LayoutFileList::get().haveClass(name)) {
773 lyxerr << "Document class \"" << name
774 << "\" does not exist."
779 LayoutFile & tc = LayoutFileList::get()[name];
780 if (!tc.load(buf_path)) {
781 docstring s = bformat(_("The document class %1$s "
782 "could not be loaded."), from_utf8(name));
783 Alert::error(_("Could not load class"), s);
790 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
795 void LyXFunc::dispatch(FuncRequest const & cmd)
797 string const argument = to_utf8(cmd.argument());
798 FuncCode const action = cmd.action;
800 LYXERR(Debug::ACTION, "\nLyXFunc::dispatch: cmd: " << cmd);
801 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
803 // we have not done anything wrong yet.
805 dispatch_buffer.erase();
807 // redraw the screen at the end (first of the two drawing steps).
808 //This is done unless explicitely requested otherwise
809 Update::flags updateFlags = Update::FitCursor;
811 FuncStatus const flag = getStatus(cmd);
812 if (!flag.enabled()) {
813 // We cannot use this function here
814 LYXERR(Debug::ACTION, "LyXFunc::dispatch: "
815 << lyxaction.getActionName(action)
816 << " [" << action << "] is disabled at this location");
817 setErrorMessage(flag.message());
819 lyx_view_->restartCursor();
821 Buffer * buffer = lyx_view_ ? lyx_view_->buffer() : 0;
824 case LFUN_WORD_FIND_FORWARD:
825 case LFUN_WORD_FIND_BACKWARD: {
826 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
827 static docstring last_search;
828 docstring searched_string;
830 if (!cmd.argument().empty()) {
831 last_search = cmd.argument();
832 searched_string = cmd.argument();
834 searched_string = last_search;
837 if (searched_string.empty())
840 bool const fw = action == LFUN_WORD_FIND_FORWARD;
841 docstring const data =
842 find2string(searched_string, true, false, fw);
843 find(view(), FuncRequest(LFUN_WORD_FIND, data));
847 case LFUN_COMMAND_PREFIX:
848 LASSERT(lyx_view_, /**/);
849 lyx_view_->message(keyseq.printOptions(true));
853 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
855 meta_fake_bit = NoModifier;
857 // cancel any selection
858 dispatch(FuncRequest(LFUN_MARK_OFF));
859 setMessage(from_ascii(N_("Cancel")));
862 case LFUN_META_PREFIX:
863 meta_fake_bit = AltModifier;
864 setMessage(keyseq.print(KeySequence::ForGui));
867 case LFUN_BUFFER_TOGGLE_READ_ONLY: {
868 LASSERT(lyx_view_ && lyx_view_->view() && buffer, /**/);
869 if (buffer->lyxvc().inUse())
870 buffer->lyxvc().toggleReadOnly();
872 buffer->setReadonly(!buffer->isReadonly());
876 // --- Menus -----------------------------------------------
877 case LFUN_BUFFER_CLOSE:
878 lyx_view_->closeBuffer();
880 updateFlags = Update::None;
883 case LFUN_BUFFER_CLOSE_ALL:
884 lyx_view_->closeBufferAll();
886 updateFlags = Update::None;
889 case LFUN_BUFFER_RELOAD: {
890 LASSERT(lyx_view_ && buffer, /**/);
891 docstring const file = makeDisplayPath(buffer->absFileName(), 20);
892 docstring text = bformat(_("Any changes will be lost. Are you sure "
893 "you want to revert to the saved version of the document %1$s?"), file);
894 int const ret = Alert::prompt(_("Revert to saved document?"),
895 text, 1, 1, _("&Revert"), _("&Cancel"));
902 case LFUN_BUFFER_UPDATE: {
903 LASSERT(lyx_view_ && buffer, /**/);
904 string format = argument;
905 if (argument.empty())
906 format = buffer->getDefaultOutputFormat();
907 buffer->doExport(format, true);
911 case LFUN_BUFFER_VIEW: {
912 LASSERT(lyx_view_ && buffer, /**/);
913 string format = argument;
914 if (argument.empty())
915 format = buffer->getDefaultOutputFormat();
916 buffer->preview(format);
920 case LFUN_MASTER_BUFFER_UPDATE: {
921 LASSERT(lyx_view_ && buffer && buffer->masterBuffer(), /**/);
922 string format = argument;
923 if (argument.empty())
924 format = buffer->masterBuffer()->getDefaultOutputFormat();
925 buffer->masterBuffer()->doExport(format, true);
929 case LFUN_MASTER_BUFFER_VIEW: {
930 LASSERT(lyx_view_ && buffer && buffer->masterBuffer(), /**/);
931 string format = argument;
932 if (argument.empty())
933 format = buffer->masterBuffer()->getDefaultOutputFormat();
934 buffer->masterBuffer()->preview(format);
938 case LFUN_BUILD_PROGRAM:
939 LASSERT(lyx_view_ && buffer, /**/);
940 buffer->doExport("program", true);
943 case LFUN_BUFFER_CHKTEX:
944 LASSERT(lyx_view_ && buffer, /**/);
948 case LFUN_BUFFER_EXPORT:
949 LASSERT(lyx_view_ && buffer, /**/);
950 if (argument == "custom")
951 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"));
953 buffer->doExport(argument, false);
956 case LFUN_BUFFER_EXPORT_CUSTOM: {
957 LASSERT(lyx_view_ && buffer, /**/);
959 string command = split(argument, format_name, ' ');
960 Format const * format = formats.getFormat(format_name);
962 lyxerr << "Format \"" << format_name
963 << "\" not recognized!"
968 // The name of the file created by the conversion process
971 // Output to filename
972 if (format->name() == "lyx") {
973 string const latexname = buffer->latexName(false);
974 filename = changeExtension(latexname,
975 format->extension());
976 filename = addName(buffer->temppath(), filename);
978 if (!buffer->writeFile(FileName(filename)))
982 buffer->doExport(format_name, true, filename);
985 // Substitute $$FName for filename
986 if (!contains(command, "$$FName"))
987 command = "( " + command + " ) < $$FName";
988 command = subst(command, "$$FName", filename);
990 // Execute the command in the background
992 call.startscript(Systemcall::DontWait, command);
996 // FIXME: There is need for a command-line import.
998 case LFUN_BUFFER_IMPORT:
1003 case LFUN_BUFFER_AUTO_SAVE:
1007 case LFUN_RECONFIGURE:
1008 // argument is any additional parameter to the configure.py command
1009 reconfigure(lyx_view_, argument);
1012 case LFUN_HELP_OPEN: {
1014 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1015 string const arg = argument;
1017 setErrorMessage(from_utf8(N_("Missing argument")));
1020 FileName fname = i18nLibFileSearch("doc", arg, "lyx");
1022 fname = i18nLibFileSearch("examples", arg, "lyx");
1024 if (fname.empty()) {
1025 lyxerr << "LyX: unable to find documentation file `"
1026 << arg << "'. Bad installation?" << endl;
1029 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1030 makeDisplayPath(fname.absFilename())));
1031 Buffer * buf = lyx_view_->loadDocument(fname, false);
1033 buf->updateLabels();
1034 lyx_view_->setBuffer(buf);
1035 buf->errors("Parse");
1037 updateFlags = Update::None;
1041 // --- version control -------------------------------
1042 case LFUN_VC_REGISTER:
1043 LASSERT(lyx_view_ && buffer, /**/);
1044 if (!ensureBufferClean(view()))
1046 if (!buffer->lyxvc().inUse()) {
1047 if (buffer->lyxvc().registrer())
1050 updateFlags = Update::Force;
1053 case LFUN_VC_CHECK_IN:
1054 LASSERT(lyx_view_ && buffer, /**/);
1055 if (!ensureBufferClean(view()))
1057 if (buffer->lyxvc().inUse()
1058 && !buffer->isReadonly()) {
1059 setMessage(from_utf8(buffer->lyxvc().checkIn()));
1064 case LFUN_VC_CHECK_OUT:
1065 LASSERT(lyx_view_ && buffer, /**/);
1066 if (!ensureBufferClean(view()))
1068 if (buffer->lyxvc().inUse()) {
1069 setMessage(from_utf8(buffer->lyxvc().checkOut()));
1074 case LFUN_VC_LOCKING_TOGGLE:
1075 LASSERT(lyx_view_ && buffer, /**/);
1076 if (!ensureBufferClean(view()) || buffer->isReadonly())
1078 if (buffer->lyxvc().inUse()) {
1079 string res = buffer->lyxvc().lockingToggle();
1081 frontend::Alert::error(_("Revision control error."),
1082 _("Error when setting the locking property."));
1084 setMessage(from_utf8(res));
1090 case LFUN_VC_REVERT:
1091 LASSERT(lyx_view_ && buffer, /**/);
1092 buffer->lyxvc().revert();
1096 case LFUN_VC_UNDO_LAST:
1097 LASSERT(lyx_view_ && buffer, /**/);
1098 buffer->lyxvc().undoLast();
1102 // --- lyxserver commands ----------------------------
1103 case LFUN_SERVER_GET_FILENAME:
1104 LASSERT(lyx_view_ && buffer, /**/);
1105 setMessage(from_utf8(buffer->absFileName()));
1106 LYXERR(Debug::INFO, "FNAME["
1107 << buffer->absFileName() << ']');
1110 case LFUN_SERVER_NOTIFY:
1111 dispatch_buffer = keyseq.print(KeySequence::Portable);
1112 theServer().notifyClient(to_utf8(dispatch_buffer));
1115 case LFUN_SERVER_GOTO_FILE_ROW: {
1116 LASSERT(lyx_view_, /**/);
1119 istringstream is(argument);
1120 is >> file_name >> row;
1122 bool loaded = false;
1123 string const abstmp = package().temp_dir().absFilename();
1124 string const realtmp = package().temp_dir().realPath();
1125 // We have to use os::path_prefix_is() here, instead of
1126 // simply prefixIs(), because the file name comes from
1127 // an external application and may need case adjustment.
1128 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
1129 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
1130 // Needed by inverse dvi search. If it is a file
1131 // in tmpdir, call the apropriated function.
1132 // If tmpdir is a symlink, we may have the real
1133 // path passed back, so we correct for that.
1134 if (!prefixIs(file_name, abstmp))
1135 file_name = subst(file_name, realtmp, abstmp);
1136 buf = theBufferList().getBufferFromTmp(file_name);
1138 // Must replace extension of the file to be .lyx
1139 // and get full path
1140 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1141 // Either change buffer or load the file
1142 if (theBufferList().exists(s))
1143 buf = theBufferList().getBuffer(s);
1144 else if (s.exists()) {
1145 buf = lyx_view_->loadDocument(s);
1148 lyx_view_->message(bformat(
1149 _("File does not exist: %1$s"),
1150 makeDisplayPath(file_name)));
1154 updateFlags = Update::None;
1158 buf->updateLabels();
1159 lyx_view_->setBuffer(buf);
1160 view()->setCursorFromRow(row);
1162 buf->errors("Parse");
1163 updateFlags = Update::FitCursor;
1168 case LFUN_DIALOG_SHOW_NEW_INSET: {
1169 LASSERT(lyx_view_, /**/);
1170 string const name = cmd.getArg(0);
1171 InsetCode code = insetCode(name);
1172 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1173 bool insetCodeOK = true;
1180 case NOMENCL_PRINT_CODE:
1183 case HYPERLINK_CODE: {
1184 InsetCommandParams p(code);
1185 data = InsetCommand::params2string(name, p);
1188 case INCLUDE_CODE: {
1189 // data is the include type: one of "include",
1190 // "input", "verbatiminput" or "verbatiminput*"
1192 // default type is requested
1194 InsetCommandParams p(INCLUDE_CODE, data);
1195 data = InsetCommand::params2string("include", p);
1199 // \c data == "Boxed" || "Frameless" etc
1200 InsetBoxParams p(data);
1201 data = InsetBox::params2string(p);
1205 InsetBranchParams p;
1206 data = InsetBranch::params2string(p);
1210 InsetCommandParams p(CITE_CODE);
1211 data = InsetCommand::params2string(name, p);
1215 data = InsetERT::params2string(InsetCollapsable::Open);
1218 case EXTERNAL_CODE: {
1219 InsetExternalParams p;
1220 data = InsetExternal::params2string(p, *buffer);
1225 data = InsetFloat::params2string(p);
1228 case LISTINGS_CODE: {
1229 InsetListingsParams p;
1230 data = InsetListings::params2string(p);
1233 case GRAPHICS_CODE: {
1234 InsetGraphicsParams p;
1235 data = InsetGraphics::params2string(p, *buffer);
1240 data = InsetNote::params2string(p);
1243 case PHANTOM_CODE: {
1244 InsetPhantomParams p;
1245 data = InsetPhantom::params2string(p);
1250 data = InsetSpace::params2string(p);
1255 data = InsetVSpace::params2string(space);
1260 data = InsetWrap::params2string(p);
1264 lyxerr << "Inset type '" << name <<
1265 "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" << endl;
1266 insetCodeOK = false;
1268 } // end switch(code)
1270 dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data));
1274 case LFUN_CITATION_INSERT: {
1275 LASSERT(lyx_view_, /**/);
1276 if (!argument.empty()) {
1277 // we can have one optional argument, delimited by '|'
1278 // citation-insert <key>|<text_before>
1279 // this should be enhanced to also support text_after
1280 // and citation style
1281 string arg = argument;
1283 if (contains(argument, "|")) {
1284 arg = token(argument, '|', 0);
1285 opt1 = token(argument, '|', 1);
1287 InsetCommandParams icp(CITE_CODE);
1288 icp["key"] = from_utf8(arg);
1290 icp["before"] = from_utf8(opt1);
1291 string icstr = InsetCommand::params2string("citation", icp);
1292 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1295 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1299 case LFUN_BUFFER_CHILD_OPEN: {
1300 LASSERT(lyx_view_ && buffer, /**/);
1301 FileName filename = makeAbsPath(argument, buffer->filePath());
1302 view()->saveBookmark(false);
1304 bool parsed = false;
1305 if (theBufferList().exists(filename)) {
1306 child = theBufferList().getBuffer(filename);
1308 setMessage(bformat(_("Opening child document %1$s..."),
1309 makeDisplayPath(filename.absFilename())));
1310 child = lyx_view_->loadDocument(filename, false);
1314 // Set the parent name of the child document.
1315 // This makes insertion of citations and references in the child work,
1316 // when the target is in the parent or another child document.
1317 child->setParent(buffer);
1318 child->masterBuffer()->updateLabels();
1319 lyx_view_->setBuffer(child);
1321 child->errors("Parse");
1324 // If a screen update is required (in case where auto_open is false),
1325 // setBuffer() would have taken care of it already. Otherwise we shall
1326 // reset the update flag because it can cause a circular problem.
1328 updateFlags = Update::None;
1332 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
1333 LASSERT(lyx_view_, /**/);
1334 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1337 case LFUN_KEYMAP_OFF:
1338 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1339 lyx_view_->view()->getIntl().keyMapOn(false);
1342 case LFUN_KEYMAP_PRIMARY:
1343 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1344 lyx_view_->view()->getIntl().keyMapPrim();
1347 case LFUN_KEYMAP_SECONDARY:
1348 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1349 lyx_view_->view()->getIntl().keyMapSec();
1352 case LFUN_KEYMAP_TOGGLE:
1353 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1354 lyx_view_->view()->getIntl().toggleKeyMap();
1360 string rest = split(argument, countstr, ' ');
1361 istringstream is(countstr);
1364 //lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1365 for (int i = 0; i < count; ++i)
1366 dispatch(lyxaction.lookupFunc(rest));
1370 case LFUN_COMMAND_SEQUENCE: {
1371 // argument contains ';'-terminated commands
1372 string arg = argument;
1373 if (theBufferList().isLoaded(buffer))
1374 buffer->undo().beginUndoGroup();
1375 while (!arg.empty()) {
1377 arg = split(arg, first, ';');
1378 FuncRequest func(lyxaction.lookupFunc(first));
1379 func.origin = cmd.origin;
1382 if (theBufferList().isLoaded(buffer))
1383 buffer->undo().endUndoGroup();
1387 case LFUN_COMMAND_ALTERNATIVES: {
1388 // argument contains ';'-terminated commands
1389 string arg = argument;
1390 while (!arg.empty()) {
1392 arg = split(arg, first, ';');
1393 FuncRequest func(lyxaction.lookupFunc(first));
1394 func.origin = cmd.origin;
1395 FuncStatus stat = getStatus(func);
1396 if (stat.enabled()) {
1406 if (theTopLevelCmdDef().lock(argument, func)) {
1407 func.origin = cmd.origin;
1409 theTopLevelCmdDef().release(argument);
1411 if (func.action == LFUN_UNKNOWN_ACTION) {
1412 // unknown command definition
1413 lyxerr << "Warning: unknown command definition `"
1417 // recursion detected
1418 lyxerr << "Warning: Recursion in the command definition `"
1419 << argument << "' detected"
1426 case LFUN_PREFERENCES_SAVE: {
1427 lyxrc.write(makeAbsPath("preferences",
1428 package().user_support().absFilename()),
1434 LASSERT(lyx_view_, /**/);
1435 lyx_view_->message(from_utf8(argument));
1438 case LFUN_BUFFER_LANGUAGE: {
1439 LASSERT(lyx_view_, /**/);
1440 Language const * oldL = buffer->params().language;
1441 Language const * newL = languages.getLanguage(argument);
1442 if (!newL || oldL == newL)
1445 if (oldL->rightToLeft() == newL->rightToLeft()
1446 && !buffer->isMultiLingual())
1447 buffer->changeLanguage(oldL, newL);
1451 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1452 string const fname =
1453 addName(addPath(package().user_support().absFilename(), "templates/"),
1455 Buffer defaults(fname);
1457 istringstream ss(argument);
1460 int const unknown_tokens = defaults.readHeader(lex);
1462 if (unknown_tokens != 0) {
1463 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1464 << unknown_tokens << " unknown token"
1465 << (unknown_tokens == 1 ? "" : "s")
1469 if (defaults.writeFile(FileName(defaults.absFileName())))
1470 setMessage(bformat(_("Document defaults saved in %1$s"),
1471 makeDisplayPath(fname)));
1473 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1477 case LFUN_BUFFER_PARAMS_APPLY: {
1478 LASSERT(lyx_view_, /**/);
1480 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1481 Cursor & cur = view()->cursor();
1482 cur.recordUndoFullDocument();
1484 istringstream ss(argument);
1487 int const unknown_tokens = buffer->readHeader(lex);
1489 if (unknown_tokens != 0) {
1490 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1491 << unknown_tokens << " unknown token"
1492 << (unknown_tokens == 1 ? "" : "s")
1496 updateLayout(oldClass, buffer);
1498 updateFlags = Update::Force | Update::FitCursor;
1499 // We are most certainly here because of a change in the document
1500 // It is then better to make sure that all dialogs are in sync with
1501 // current document settings. LyXView::restartCursor() achieve this.
1502 lyx_view_->restartCursor();
1506 case LFUN_LAYOUT_MODULES_CLEAR: {
1507 LASSERT(lyx_view_, /**/);
1508 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1509 view()->cursor().recordUndoFullDocument();
1510 buffer->params().clearLayoutModules();
1511 buffer->params().makeDocumentClass();
1512 updateLayout(oldClass, buffer);
1513 updateFlags = Update::Force | Update::FitCursor;
1517 case LFUN_LAYOUT_MODULE_ADD: {
1518 LASSERT(lyx_view_, /**/);
1519 BufferParams const & params = buffer->params();
1520 if (!params.moduleCanBeAdded(argument)) {
1521 LYXERR0("Module `" << argument <<
1522 "' cannot be added due to failed requirements or "
1523 "conflicts with installed modules.");
1526 DocumentClass const * const oldClass = params.documentClassPtr();
1527 view()->cursor().recordUndoFullDocument();
1528 buffer->params().addLayoutModule(argument);
1529 buffer->params().makeDocumentClass();
1530 updateLayout(oldClass, buffer);
1531 updateFlags = Update::Force | Update::FitCursor;
1535 case LFUN_TEXTCLASS_APPLY: {
1536 LASSERT(lyx_view_, /**/);
1538 if (!loadLayoutFile(argument, buffer->temppath()) &&
1539 !loadLayoutFile(argument, buffer->filePath()))
1542 LayoutFile const * old_layout = buffer->params().baseClass();
1543 LayoutFile const * new_layout = &(LayoutFileList::get()[argument]);
1545 if (old_layout == new_layout)
1549 //Save the old, possibly modular, layout for use in conversion.
1550 DocumentClass const * const oldDocClass = buffer->params().documentClassPtr();
1551 view()->cursor().recordUndoFullDocument();
1552 buffer->params().setBaseClass(argument);
1553 buffer->params().makeDocumentClass();
1554 updateLayout(oldDocClass, buffer);
1555 updateFlags = Update::Force | Update::FitCursor;
1559 case LFUN_LAYOUT_RELOAD: {
1560 LASSERT(lyx_view_, /**/);
1561 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1562 LayoutFileIndex bc = buffer->params().baseClassID();
1563 LayoutFileList::get().reset(bc);
1564 buffer->params().setBaseClass(bc);
1565 buffer->params().makeDocumentClass();
1566 updateLayout(oldClass, buffer);
1567 updateFlags = Update::Force | Update::FitCursor;
1571 case LFUN_TEXTCLASS_LOAD:
1572 loadLayoutFile(argument, buffer->temppath()) ||
1573 loadLayoutFile(argument, buffer->filePath());
1576 case LFUN_LYXRC_APPLY: {
1577 // reset active key sequences, since the bindings
1578 // are updated (bug 6064)
1580 LyXRC const lyxrc_orig = lyxrc;
1582 istringstream ss(argument);
1583 bool const success = lyxrc.read(ss) == 0;
1586 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1587 << "Unable to read lyxrc data"
1592 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1594 theApp()->resetGui();
1596 /// We force the redraw in any case because there might be
1597 /// some screen font changes.
1598 /// FIXME: only the current view will be updated. the Gui
1599 /// class is able to furnish the list of views.
1600 updateFlags = Update::Force;
1604 case LFUN_BOOKMARK_GOTO:
1605 // go to bookmark, open unopened file and switch to buffer if necessary
1606 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1607 updateFlags = Update::FitCursor;
1610 case LFUN_BOOKMARK_CLEAR:
1611 theSession().bookmarks().clear();
1614 case LFUN_VC_COMMAND: {
1615 string flag = cmd.getArg(0);
1616 if (buffer && contains(flag, 'R') && !ensureBufferClean(view()))
1619 if (contains(flag, 'M'))
1620 if (!Alert::askForText(message, _("LyX VC: Log Message")))
1623 string path = cmd.getArg(1);
1624 if (contains(path, "$$p") && buffer)
1625 path = subst(path, "$$p", buffer->filePath());
1626 LYXERR(Debug::LYXVC, "Directory: " << path);
1628 if (!pp.isReadableDirectory()) {
1629 lyxerr << _("Directory is not accessible.") << endl;
1632 support::PathChanger p(pp);
1634 string command = cmd.getArg(2);
1635 if (command.empty())
1638 command = subst(command, "$$i", buffer->absFileName());
1639 command = subst(command, "$$p", buffer->filePath());
1641 command = subst(command, "$$m", to_utf8(message));
1642 LYXERR(Debug::LYXVC, "Command: " << command);
1644 one.startscript(Systemcall::Wait, command);
1648 if (contains(flag, 'I'))
1649 buffer->markDirty();
1650 if (contains(flag, 'R'))
1657 LASSERT(theApp(), /**/);
1658 // Let the frontend dispatch its own actions.
1659 if (theApp()->dispatch(cmd))
1660 // Nothing more to do.
1663 // Everything below is only for active lyx_view_
1667 // Start an undo group. This may be needed for
1668 // some stuff like inset-apply on labels.
1669 if (theBufferList().isLoaded(buffer))
1670 buffer->undo().beginUndoGroup();
1672 // Let the current LyXView dispatch its own actions.
1673 if (lyx_view_->dispatch(cmd)) {
1674 if (lyx_view_->view()) {
1675 updateFlags = lyx_view_->view()->cursor().result().update();
1676 if (theBufferList().isLoaded(buffer))
1677 buffer->undo().endUndoGroup();
1682 LASSERT(lyx_view_->view(), /**/);
1684 // Let the current BufferView dispatch its own actions.
1685 if (view()->dispatch(cmd)) {
1686 // The BufferView took care of its own updates if needed.
1687 updateFlags = Update::None;
1688 if (theBufferList().isLoaded(buffer))
1689 buffer->undo().endUndoGroup();
1693 // OK, so try the Buffer itself
1695 view()->buffer().dispatch(cmd, dr);
1696 if (dr.dispatched()) {
1697 updateFlags = dr.update();
1701 // Is this a function that acts on inset at point?
1702 Inset * inset = view()->cursor().nextInset();
1703 if (lyxaction.funcHasFlag(action, LyXAction::AtPoint)
1705 view()->cursor().result().dispatched(true);
1706 view()->cursor().result().update(Update::FitCursor | Update::Force);
1707 FuncRequest tmpcmd = cmd;
1708 inset->dispatch(view()->cursor(), tmpcmd);
1709 if (view()->cursor().result().dispatched()) {
1710 updateFlags = view()->cursor().result().update();
1715 // Let the current Cursor dispatch its own actions.
1716 Cursor old = view()->cursor();
1717 view()->cursor().getPos(cursorPosBeforeDispatchX_,
1718 cursorPosBeforeDispatchY_);
1719 view()->cursor().dispatch(cmd);
1721 // notify insets we just left
1722 if (view()->cursor() != old) {
1724 bool badcursor = notifyCursorLeavesOrEnters(old, view()->cursor());
1726 view()->cursor().fixIfBroken();
1729 if (theBufferList().isLoaded(buffer))
1730 buffer->undo().endUndoGroup();
1732 // update completion. We do it here and not in
1733 // processKeySym to avoid another redraw just for a
1734 // changed inline completion
1735 if (cmd.origin == FuncRequest::KEYBOARD) {
1736 if (cmd.action == LFUN_SELF_INSERT
1737 || (cmd.action == LFUN_ERT_INSERT && view()->cursor().inMathed()))
1738 lyx_view_->updateCompletion(view()->cursor(), true, true);
1739 else if (cmd.action == LFUN_CHAR_DELETE_BACKWARD)
1740 lyx_view_->updateCompletion(view()->cursor(), false, true);
1742 lyx_view_->updateCompletion(view()->cursor(), false, false);
1745 updateFlags = view()->cursor().result().update();
1748 // if we executed a mutating lfun, mark the buffer as dirty
1749 if (theBufferList().isLoaded(buffer) && flag.enabled()
1750 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1751 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1752 buffer->markDirty();
1754 if (lyx_view_ && lyx_view_->buffer()) {
1755 // BufferView::update() updates the ViewMetricsInfo and
1756 // also initializes the position cache for all insets in
1757 // (at least partially) visible top-level paragraphs.
1758 // We will redraw the screen only if needed.
1759 view()->processUpdateFlags(updateFlags);
1761 // Do we have a selection?
1762 theSelection().haveSelection(view()->cursor().selection());
1765 lyx_view_->restartCursor();
1769 // Some messages may already be translated, so we cannot use _()
1770 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1775 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1777 const bool verbose = (cmd.origin == FuncRequest::MENU
1778 || cmd.origin == FuncRequest::TOOLBAR
1779 || cmd.origin == FuncRequest::COMMANDBUFFER);
1781 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1782 LYXERR(Debug::ACTION, "dispatch msg is " << to_utf8(msg));
1784 lyx_view_->message(msg);
1788 docstring dispatch_msg = msg;
1789 if (!dispatch_msg.empty())
1790 dispatch_msg += ' ';
1792 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1794 bool argsadded = false;
1796 if (!cmd.argument().empty()) {
1797 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1798 comname += ' ' + cmd.argument();
1803 docstring const shortcuts = theTopLevelKeymap().printBindings(cmd, KeySequence::ForGui);
1805 if (!shortcuts.empty())
1806 comname += ": " + shortcuts;
1807 else if (!argsadded && !cmd.argument().empty())
1808 comname += ' ' + cmd.argument();
1810 if (!comname.empty()) {
1811 comname = rtrim(comname);
1812 dispatch_msg += '(' + rtrim(comname) + ')';
1815 LYXERR(Debug::ACTION, "verbose dispatch msg " << to_utf8(dispatch_msg));
1816 if (!dispatch_msg.empty())
1817 lyx_view_->message(dispatch_msg);
1821 void LyXFunc::reloadBuffer()
1823 FileName filename = lyx_view_->buffer()->fileName();
1824 // The user has already confirmed that the changes, if any, should
1825 // be discarded. So we just release the Buffer and don't call closeBuffer();
1826 theBufferList().release(lyx_view_->buffer());
1827 // if the lyx_view_ has been destroyed, create a new one
1829 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1830 Buffer * buf = lyx_view_->loadDocument(filename);
1831 docstring const disp_fn = makeDisplayPath(filename.absFilename());
1834 buf->updateLabels();
1835 lyx_view_->setBuffer(buf);
1836 buf->errors("Parse");
1837 str = bformat(_("Document %1$s reloaded."), disp_fn);
1839 str = bformat(_("Could not reload document %1$s"), disp_fn);
1841 lyx_view_->message(str);
1844 // Each "lyx_view_" should have it's own message method. lyxview and
1845 // the minibuffer would use the minibuffer, but lyxserver would
1846 // send an ERROR signal to its client. Alejandro 970603
1847 // This function is bit problematic when it comes to NLS, to make the
1848 // lyx servers client be language indepenent we must not translate
1849 // strings sent to this func.
1850 void LyXFunc::setErrorMessage(docstring const & m) const
1852 dispatch_buffer = m;
1857 void LyXFunc::setMessage(docstring const & m) const
1859 dispatch_buffer = m;
1863 docstring LyXFunc::viewStatusMessage()
1865 // When meta-fake key is pressed, show the key sequence so far + "M-".
1867 return keyseq.print(KeySequence::ForGui) + "M-";
1869 // Else, when a non-complete key sequence is pressed,
1870 // show the available options.
1871 if (keyseq.length() > 0 && !keyseq.deleted())
1872 return keyseq.printOptions(true);
1874 LASSERT(lyx_view_, /**/);
1875 if (!lyx_view_->buffer())
1876 return _("Welcome to LyX!");
1878 return view()->cursor().currentState();
1882 BufferView * LyXFunc::view() const
1884 LASSERT(lyx_view_, /**/);
1885 return lyx_view_->view();
1889 bool LyXFunc::wasMetaKey() const
1891 return (meta_fake_bit != NoModifier);
1895 void LyXFunc::updateLayout(DocumentClass const * const oldlayout, Buffer * buf)
1897 lyx_view_->message(_("Converting document to new document class..."));
1899 StableDocIterator backcur(view()->cursor());
1900 ErrorList & el = buf->errorList("Class Switch");
1901 cap::switchBetweenClasses(
1902 oldlayout, buf->params().documentClassPtr(),
1903 static_cast<InsetText &>(buf->inset()), el);
1905 view()->setCursor(backcur.asDocIterator(buf));
1907 buf->errors("Class Switch");
1908 buf->updateLabels();
1914 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1916 // Why the switch you might ask. It is a trick to ensure that all
1917 // the elements in the LyXRCTags enum is handled. As you can see
1918 // there are no breaks at all. So it is just a huge fall-through.
1919 // The nice thing is that we will get a warning from the compiler
1920 // if we forget an element.
1921 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1923 case LyXRC::RC_ACCEPT_COMPOUND:
1924 case LyXRC::RC_ALT_LANG:
1925 case LyXRC::RC_PLAINTEXT_LINELEN:
1926 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
1927 case LyXRC::RC_AUTOCORRECTION_MATH:
1928 case LyXRC::RC_AUTOREGIONDELETE:
1929 case LyXRC::RC_AUTORESET_OPTIONS:
1930 case LyXRC::RC_AUTOSAVE:
1931 case LyXRC::RC_AUTO_NUMBER:
1932 case LyXRC::RC_BACKUPDIR_PATH:
1933 case LyXRC::RC_BIBTEX_ALTERNATIVES:
1934 case LyXRC::RC_BIBTEX_COMMAND:
1935 case LyXRC::RC_BINDFILE:
1936 case LyXRC::RC_CHECKLASTFILES:
1937 case LyXRC::RC_COMPLETION_CURSOR_TEXT:
1938 case LyXRC::RC_COMPLETION_INLINE_DELAY:
1939 case LyXRC::RC_COMPLETION_INLINE_DOTS:
1940 case LyXRC::RC_COMPLETION_INLINE_MATH:
1941 case LyXRC::RC_COMPLETION_INLINE_TEXT:
1942 case LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE:
1943 case LyXRC::RC_COMPLETION_POPUP_DELAY:
1944 case LyXRC::RC_COMPLETION_POPUP_MATH:
1945 case LyXRC::RC_COMPLETION_POPUP_TEXT:
1946 case LyXRC::RC_USELASTFILEPOS:
1947 case LyXRC::RC_LOADSESSION:
1948 case LyXRC::RC_CHKTEX_COMMAND:
1949 case LyXRC::RC_CONVERTER:
1950 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
1951 case LyXRC::RC_COPIER:
1952 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1953 case LyXRC::RC_SCROLL_BELOW_DOCUMENT:
1954 case LyXRC::RC_DATE_INSERT_FORMAT:
1955 case LyXRC::RC_DEFAULT_LANGUAGE:
1956 case LyXRC::RC_GUI_LANGUAGE:
1957 case LyXRC::RC_DEFAULT_PAPERSIZE:
1958 case LyXRC::RC_DEFAULT_VIEW_FORMAT:
1959 case LyXRC::RC_DEFFILE:
1960 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1961 case LyXRC::RC_DISPLAY_GRAPHICS:
1962 case LyXRC::RC_DOCUMENTPATH:
1963 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1964 FileName path(lyxrc_new.document_path);
1965 if (path.exists() && path.isDirectory())
1966 package().document_dir() = FileName(lyxrc.document_path);
1968 case LyXRC::RC_ESC_CHARS:
1969 case LyXRC::RC_EXAMPLEPATH:
1970 case LyXRC::RC_FONT_ENCODING:
1971 case LyXRC::RC_FORMAT:
1972 case LyXRC::RC_GROUP_LAYOUTS:
1973 case LyXRC::RC_INDEX_ALTERNATIVES:
1974 case LyXRC::RC_INDEX_COMMAND:
1975 case LyXRC::RC_JBIBTEX_COMMAND:
1976 case LyXRC::RC_JINDEX_COMMAND:
1977 case LyXRC::RC_NOMENCL_COMMAND:
1978 case LyXRC::RC_INPUT:
1979 case LyXRC::RC_KBMAP:
1980 case LyXRC::RC_KBMAP_PRIMARY:
1981 case LyXRC::RC_KBMAP_SECONDARY:
1982 case LyXRC::RC_LABEL_INIT_LENGTH:
1983 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
1984 case LyXRC::RC_LANGUAGE_AUTO_END:
1985 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
1986 case LyXRC::RC_LANGUAGE_COMMAND_END:
1987 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
1988 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
1989 case LyXRC::RC_LANGUAGE_PACKAGE:
1990 case LyXRC::RC_LANGUAGE_USE_BABEL:
1991 case LyXRC::RC_MAC_LIKE_WORD_MOVEMENT:
1992 case LyXRC::RC_MACRO_EDIT_STYLE:
1993 case LyXRC::RC_MAKE_BACKUP:
1994 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
1995 case LyXRC::RC_MOUSE_WHEEL_SPEED:
1996 case LyXRC::RC_NUMLASTFILES:
1997 case LyXRC::RC_PATH_PREFIX:
1998 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
1999 prependEnvPath("PATH", lyxrc.path_prefix);
2001 case LyXRC::RC_PERS_DICT:
2002 case LyXRC::RC_PREVIEW:
2003 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2004 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2005 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2006 case LyXRC::RC_PRINTCOPIESFLAG:
2007 case LyXRC::RC_PRINTER:
2008 case LyXRC::RC_PRINTEVENPAGEFLAG:
2009 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2010 case LyXRC::RC_PRINTFILEEXTENSION:
2011 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2012 case LyXRC::RC_PRINTODDPAGEFLAG:
2013 case LyXRC::RC_PRINTPAGERANGEFLAG:
2014 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2015 case LyXRC::RC_PRINTPAPERFLAG:
2016 case LyXRC::RC_PRINTREVERSEFLAG:
2017 case LyXRC::RC_PRINTSPOOL_COMMAND:
2018 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2019 case LyXRC::RC_PRINTTOFILE:
2020 case LyXRC::RC_PRINTTOPRINTER:
2021 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2022 case LyXRC::RC_PRINT_COMMAND:
2023 case LyXRC::RC_RTL_SUPPORT:
2024 case LyXRC::RC_SCREEN_DPI:
2025 case LyXRC::RC_SCREEN_FONT_ROMAN:
2026 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2027 case LyXRC::RC_SCREEN_FONT_SANS:
2028 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2029 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2030 case LyXRC::RC_SCREEN_FONT_SIZES:
2031 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2032 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2033 case LyXRC::RC_GEOMETRY_SESSION:
2034 case LyXRC::RC_SCREEN_ZOOM:
2035 case LyXRC::RC_SERVERPIPE:
2036 case LyXRC::RC_SET_COLOR:
2037 case LyXRC::RC_SHOW_BANNER:
2038 case LyXRC::RC_OPEN_BUFFERS_IN_TABS:
2039 case LyXRC::RC_SPELL_COMMAND:
2040 case LyXRC::RC_SPELLCHECK_CONTINUOUSLY:
2041 case LyXRC::RC_SPLITINDEX_COMMAND:
2042 case LyXRC::RC_TEMPDIRPATH:
2043 case LyXRC::RC_TEMPLATEPATH:
2044 case LyXRC::RC_TEX_ALLOWS_SPACES:
2045 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2046 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2047 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2049 case LyXRC::RC_THESAURUSDIRPATH:
2050 case LyXRC::RC_UIFILE:
2051 case LyXRC::RC_USER_EMAIL:
2052 case LyXRC::RC_USER_NAME:
2053 case LyXRC::RC_USETEMPDIR:
2054 case LyXRC::RC_USE_ALT_LANG:
2055 case LyXRC::RC_USE_CONVERTER_CACHE:
2056 case LyXRC::RC_USE_ESC_CHARS:
2057 case LyXRC::RC_USE_INP_ENC:
2058 case LyXRC::RC_USE_PERS_DICT:
2059 case LyXRC::RC_USE_TOOLTIP:
2060 case LyXRC::RC_USE_PIXMAP_CACHE:
2061 case LyXRC::RC_USE_SPELL_LIB:
2062 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2063 case LyXRC::RC_SORT_LAYOUTS:
2064 case LyXRC::RC_FULL_SCREEN_LIMIT:
2065 case LyXRC::RC_FULL_SCREEN_SCROLLBAR:
2066 case LyXRC::RC_FULL_SCREEN_MENUBAR:
2067 case LyXRC::RC_FULL_SCREEN_TABBAR:
2068 case LyXRC::RC_FULL_SCREEN_TOOLBARS:
2069 case LyXRC::RC_FULL_SCREEN_WIDTH:
2070 case LyXRC::RC_VISUAL_CURSOR:
2071 case LyXRC::RC_VIEWER:
2072 case LyXRC::RC_LAST: