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"
58 #include "SpellChecker.h"
60 #include "insets/InsetBox.h"
61 #include "insets/InsetBranch.h"
62 #include "insets/InsetCommand.h"
63 #include "insets/InsetERT.h"
64 #include "insets/InsetExternal.h"
65 #include "insets/InsetFloat.h"
66 #include "insets/InsetGraphics.h"
67 #include "insets/InsetInclude.h"
68 #include "insets/InsetListings.h"
69 #include "insets/InsetNote.h"
70 #include "insets/InsetPhantom.h"
71 #include "insets/InsetSpace.h"
72 #include "insets/InsetTabular.h"
73 #include "insets/InsetVSpace.h"
74 #include "insets/InsetWrap.h"
76 #include "frontends/alert.h"
77 #include "frontends/Application.h"
78 #include "frontends/KeySymbol.h"
79 #include "frontends/LyXView.h"
80 #include "frontends/Selection.h"
82 #include "support/debug.h"
83 #include "support/environment.h"
84 #include "support/FileName.h"
85 #include "support/filetools.h"
86 #include "support/gettext.h"
87 #include "support/lstrings.h"
88 #include "support/Path.h"
89 #include "support/Package.h"
90 #include "support/Systemcall.h"
91 #include "support/convert.h"
92 #include "support/os.h"
98 using namespace lyx::support;
102 using frontend::LyXView;
104 namespace Alert = frontend::Alert;
109 // This function runs "configure" and then rereads lyx.defaults to
110 // reconfigure the automatic settings.
111 void reconfigure(LyXView * lv, string const & option)
113 // emit message signal.
115 lv->message(_("Running configure..."));
117 // Run configure in user lyx directory
118 PathChanger p(package().user_support());
119 string configure_command = package().configure_command();
120 configure_command += option;
122 int ret = one.startscript(Systemcall::Wait, configure_command);
124 // emit message signal.
126 lv->message(_("Reloading configuration..."));
127 lyxrc.read(libFileSearch(string(), "lyxrc.defaults"));
128 // Re-read packages.lst
129 LaTeXFeatures::getAvailable();
132 Alert::information(_("System reconfiguration failed"),
133 _("The system reconfiguration has failed.\n"
134 "Default textclass is used but LyX may "
135 "not be able to work properly.\n"
136 "Please reconfigure again if needed."));
139 Alert::information(_("System reconfigured"),
140 _("The system has been reconfigured.\n"
141 "You need to restart LyX to make use of any\n"
142 "updated document class specifications."));
146 bool getLocalStatus(Cursor cursor, FuncRequest const & cmd, FuncStatus & status)
148 // Try to fix cursor in case it is broken.
149 cursor.fixIfBroken();
151 // This is, of course, a mess. Better create a new doc iterator and use
152 // this in Inset::getStatus. This might require an additional
153 // BufferView * arg, though (which should be avoided)
154 //Cursor safe = *this;
156 for ( ; cursor.depth(); cursor.pop()) {
157 //lyxerr << "\nCursor::getStatus: cmd: " << cmd << endl << *this << endl;
158 LASSERT(cursor.idx() <= cursor.lastidx(), /**/);
159 LASSERT(cursor.pit() <= cursor.lastpit(), /**/);
160 LASSERT(cursor.pos() <= cursor.lastpos(), /**/);
162 // The inset's getStatus() will return 'true' if it made
163 // a definitive decision on whether it want to handle the
164 // request or not. The result of this decision is put into
165 // the 'status' parameter.
166 if (cursor.inset().getStatus(cursor, cmd, status)) {
175 /** Return the change status at cursor position, taking in account the
176 * status at each level of the document iterator (a table in a deleted
177 * footnote is deleted).
178 * When \param outer is true, the top slice is not looked at.
180 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
182 size_t const depth = dit.depth() - (outer ? 1 : 0);
184 for (size_t i = 0 ; i < depth ; ++i) {
185 CursorSlice const & slice = dit[i];
186 if (!slice.inset().inMathed()
187 && slice.pos() < slice.paragraph().size()) {
188 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
189 if (ch != Change::UNCHANGED)
193 return Change::UNCHANGED;
200 : lyx_view_(0), encoded_last_key(0), meta_fake_bit(NoModifier)
205 void LyXFunc::initKeySequences(KeyMap * kb)
207 keyseq = KeySequence(kb, kb);
208 cancel_meta_seq = KeySequence(kb, kb);
212 void LyXFunc::setLyXView(LyXView * lv)
214 if (lyx_view_ && lyx_view_->view() && lyx_view_ != lv)
215 // save current selection to the selection buffer to allow
216 // middle-button paste in another window
217 cap::saveSelection(lyx_view_->view()->cursor());
222 void LyXFunc::handleKeyFunc(FuncCode action)
224 char_type c = encoded_last_key;
229 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
230 lyx_view_->view()->getIntl().getTransManager().deadkey(
231 c, get_accent(action).accent, view()->cursor().innerText(), view()->cursor());
232 // Need to clear, in case the minibuffer calls these
235 // copied verbatim from do_accent_char
236 view()->cursor().resetAnchor();
237 view()->processUpdateFlags(Update::FitCursor);
240 //FIXME: bookmark handling is a frontend issue. This code should be transferred
241 // to GuiView and be GuiView and be window dependent.
242 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
244 LASSERT(lyx_view_, /**/);
245 if (!theSession().bookmarks().isValid(idx))
247 BookmarksSection::Bookmark const & bm = theSession().bookmarks().bookmark(idx);
248 LASSERT(!bm.filename.empty(), /**/);
249 string const file = bm.filename.absFilename();
250 // if the file is not opened, open it.
251 if (!theBufferList().exists(bm.filename)) {
253 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
257 // open may fail, so we need to test it again
258 if (!theBufferList().exists(bm.filename))
261 // bm can be changed when saving
262 BookmarksSection::Bookmark tmp = bm;
264 // Special case idx == 0 used for back-from-back jump navigation
266 dispatch(FuncRequest(LFUN_BOOKMARK_SAVE, "0"));
268 // if the current buffer is not that one, switch to it.
269 if (!lyx_view_->buffer() || lyx_view_->buffer()->fileName() != tmp.filename) {
272 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
275 // moveToPosition try paragraph id first and then paragraph (pit, pos).
276 if (!view()->moveToPosition(tmp.bottom_pit, tmp.bottom_pos,
277 tmp.top_id, tmp.top_pos))
284 // Cursor jump succeeded!
285 Cursor const & cur = view()->cursor();
286 pit_type new_pit = cur.pit();
287 pos_type new_pos = cur.pos();
288 int new_id = cur.paragraph().id();
290 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
291 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
292 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos
293 || bm.top_id != new_id) {
294 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(
295 new_pit, new_pos, new_id);
300 void LyXFunc::processKeySym(KeySymbol const & keysym, KeyModifier state)
302 LYXERR(Debug::KEY, "KeySym is " << keysym.getSymbolName());
304 // Do nothing if we have nothing (JMarc)
305 if (!keysym.isOK()) {
306 LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
307 lyx_view_->restartCursor();
311 if (keysym.isModifier()) {
312 LYXERR(Debug::KEY, "isModifier true");
314 lyx_view_->restartCursor();
318 //Encoding const * encoding = view()->cursor().getEncoding();
319 //encoded_last_key = keysym.getISOEncoded(encoding ? encoding->name() : "");
320 // FIXME: encoded_last_key shadows the member variable of the same
321 // name. Is that intended?
322 char_type encoded_last_key = keysym.getUCSEncoded();
324 // Do a one-deep top-level lookup for
325 // cancel and meta-fake keys. RVDK_PATCH_5
326 cancel_meta_seq.reset();
328 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
329 LYXERR(Debug::KEY, "action first set to [" << func.action << ']');
331 // When not cancel or meta-fake, do the normal lookup.
332 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
333 // Mostly, meta_fake_bit = NoModifier. RVDK_PATCH_5.
334 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
335 // remove Caps Lock and Mod2 as a modifiers
336 func = keyseq.addkey(keysym, (state | meta_fake_bit));
337 LYXERR(Debug::KEY, "action now set to [" << func.action << ']');
340 // Dont remove this unless you know what you are doing.
341 meta_fake_bit = NoModifier;
343 // Can this happen now ?
344 if (func.action == LFUN_NOACTION)
345 func = FuncRequest(LFUN_COMMAND_PREFIX);
347 LYXERR(Debug::KEY, " Key [action=" << func.action << "]["
348 << keyseq.print(KeySequence::Portable) << ']');
350 // already here we know if it any point in going further
351 // why not return already here if action == -1 and
352 // num_bytes == 0? (Lgb)
354 if (keyseq.length() > 1)
355 lyx_view_->message(keyseq.print(KeySequence::ForGui));
358 // Maybe user can only reach the key via holding down shift.
359 // Let's see. But only if shift is the only modifier
360 if (func.action == LFUN_UNKNOWN_ACTION && state == ShiftModifier) {
361 LYXERR(Debug::KEY, "Trying without shift");
362 func = keyseq.addkey(keysym, NoModifier);
363 LYXERR(Debug::KEY, "Action now " << func.action);
366 if (func.action == LFUN_UNKNOWN_ACTION) {
367 // Hmm, we didn't match any of the keysequences. See
368 // if it's normal insertable text not already covered
370 if (keysym.isText() && keyseq.length() == 1) {
371 LYXERR(Debug::KEY, "isText() is true, inserting.");
372 func = FuncRequest(LFUN_SELF_INSERT,
373 FuncRequest::KEYBOARD);
375 LYXERR(Debug::KEY, "Unknown, !isText() - giving up");
376 lyx_view_->message(_("Unknown function."));
377 lyx_view_->restartCursor();
382 if (func.action == LFUN_SELF_INSERT) {
383 if (encoded_last_key != 0) {
384 docstring const arg(1, encoded_last_key);
385 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
386 FuncRequest::KEYBOARD));
387 LYXERR(Debug::KEY, "SelfInsert arg[`" << to_utf8(arg) << "']");
397 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
399 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
402 /* In LyX/Mac, when a dialog is open, the menus of the
403 application can still be accessed without giving focus to
404 the main window. In this case, we want to disable the menu
405 entries that are buffer or view-related.
407 If this code is moved somewhere else (like in
408 GuiView::getStatus), then several functions will not be
411 frontend::LyXView * lv = 0;
414 && (cmd.origin != FuncRequest::MENU || lyx_view_->hasFocus())) {
416 buf = lyx_view_->buffer();
419 if (cmd.action == LFUN_NOACTION) {
420 flag.message(from_utf8(N_("Nothing to do")));
421 flag.setEnabled(false);
425 switch (cmd.action) {
426 case LFUN_UNKNOWN_ACTION:
428 flag.setEnabled(false);
435 if (flag.unknown()) {
436 flag.message(from_utf8(N_("Unknown action")));
440 if (!flag.enabled()) {
441 if (flag.message().empty())
442 flag.message(from_utf8(N_("Command disabled")));
446 // Check whether we need a buffer
447 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
449 flag.message(from_utf8(N_("Command not allowed with"
450 "out any document open")));
451 flag.setEnabled(false);
455 // I would really like to avoid having this switch and rather try to
456 // encode this in the function itself.
457 // -- And I'd rather let an inset decide which LFUNs it is willing
458 // to handle (Andre')
460 switch (cmd.action) {
462 case LFUN_BUFFER_TOGGLE_READ_ONLY:
463 flag.setOnOff(buf->isReadonly());
466 case LFUN_BUFFER_SWITCH:
467 // toggle on the current buffer, but do not toggle off
468 // the other ones (is that a good idea?)
469 if (buf && to_utf8(cmd.argument()) == buf->absFileName())
473 case LFUN_BUFFER_CHKTEX:
474 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
477 case LFUN_BUILD_PROGRAM:
478 enable = buf->isExportable("program");
481 case LFUN_VC_REGISTER:
482 enable = !buf->lyxvc().inUse();
484 case LFUN_VC_CHECK_IN:
485 enable = buf->lyxvc().checkInEnabled();
487 case LFUN_VC_CHECK_OUT:
488 enable = buf->lyxvc().checkOutEnabled();
490 case LFUN_VC_LOCKING_TOGGLE:
491 enable = !buf->isReadonly() && buf->lyxvc().lockingToggleEnabled();
492 flag.setOnOff(enable && !buf->lyxvc().locker().empty());
495 enable = buf->lyxvc().inUse();
497 case LFUN_VC_UNDO_LAST:
498 enable = buf->lyxvc().undoLastEnabled();
500 case LFUN_BUFFER_RELOAD:
501 enable = !buf->isUnnamed() && buf->fileName().exists()
502 && (!buf->isClean() || buf->isExternallyModified(Buffer::timestamp_method));
505 case LFUN_CITATION_INSERT: {
506 FuncRequest fr(LFUN_INSET_INSERT, "citation");
507 enable = getStatus(fr).enabled();
511 // This could be used for the no-GUI version. The GUI version is handled in
512 // LyXView::getStatus(). See above.
514 case LFUN_BUFFER_WRITE:
515 case LFUN_BUFFER_WRITE_AS: {
516 Buffer * b = theBufferList().getBuffer(FileName(cmd.getArg(0)));
517 enable = b && (b->isUnnamed() || !b->isClean());
522 case LFUN_BUFFER_WRITE_ALL: {
523 // We enable the command only if there are some modified buffers
524 Buffer * first = theBufferList().first();
529 // We cannot use a for loop as the buffer list is a cycle.
535 b = theBufferList().next(b);
536 } while (b != first);
540 case LFUN_BOOKMARK_GOTO: {
541 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
542 enable = theSession().bookmarks().isValid(num);
546 case LFUN_BOOKMARK_CLEAR:
547 enable = theSession().bookmarks().hasValid();
550 // this one is difficult to get right. As a half-baked
551 // solution, we consider only the first action of the sequence
552 case LFUN_COMMAND_SEQUENCE: {
553 // argument contains ';'-terminated commands
554 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
555 FuncRequest func(lyxaction.lookupFunc(firstcmd));
556 func.origin = cmd.origin;
557 flag = getStatus(func);
561 // we want to check if at least one of these is enabled
562 case LFUN_COMMAND_ALTERNATIVES: {
563 // argument contains ';'-terminated commands
564 string arg = to_utf8(cmd.argument());
565 while (!arg.empty()) {
567 arg = split(arg, first, ';');
568 FuncRequest func(lyxaction.lookupFunc(first));
569 func.origin = cmd.origin;
570 flag = getStatus(func);
571 // if this one is enabled, the whole thing is
580 string name = to_utf8(cmd.argument());
581 if (theTopLevelCmdDef().lock(name, func)) {
582 func.origin = cmd.origin;
583 flag = getStatus(func);
584 theTopLevelCmdDef().release(name);
586 // catch recursion or unknown command
587 // definition. all operations until the
588 // recursion or unknown command definition
589 // occurs are performed, so set the state to
596 case LFUN_VC_COMMAND: {
597 if (cmd.argument().empty())
600 if (!buf && contains(cmd.getArg(0), 'D'))
605 case LFUN_MASTER_BUFFER_UPDATE:
606 case LFUN_MASTER_BUFFER_VIEW:
607 if (!buf->parent()) {
611 case LFUN_BUFFER_UPDATE:
612 case LFUN_BUFFER_VIEW: {
613 string format = to_utf8(cmd.argument());
614 if (cmd.argument().empty())
615 format = buf->getDefaultOutputFormat();
616 typedef vector<Format const *> Formats;
618 formats = buf->exportableFormats(true);
619 Formats::const_iterator fit = formats.begin();
620 Formats::const_iterator end = formats.end();
622 for (; fit != end ; ++fit) {
623 if ((*fit)->name() == format)
629 case LFUN_WORD_FIND_FORWARD:
630 case LFUN_WORD_FIND_BACKWARD:
631 case LFUN_WORD_FINDADV:
632 case LFUN_COMMAND_PREFIX:
633 case LFUN_COMMAND_EXECUTE:
635 case LFUN_META_PREFIX:
636 case LFUN_BUFFER_CLOSE:
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;
1121 file_name = os::internal_path(file_name);
1123 bool loaded = false;
1124 string const abstmp = package().temp_dir().absFilename();
1125 string const realtmp = package().temp_dir().realPath();
1126 // We have to use os::path_prefix_is() here, instead of
1127 // simply prefixIs(), because the file name comes from
1128 // an external application and may need case adjustment.
1129 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
1130 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
1131 // Needed by inverse dvi search. If it is a file
1132 // in tmpdir, call the apropriated function.
1133 // If tmpdir is a symlink, we may have the real
1134 // path passed back, so we correct for that.
1135 if (!prefixIs(file_name, abstmp))
1136 file_name = subst(file_name, realtmp, abstmp);
1137 buf = theBufferList().getBufferFromTmp(file_name);
1139 // Must replace extension of the file to be .lyx
1140 // and get full path
1141 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1142 // Either change buffer or load the file
1143 if (theBufferList().exists(s))
1144 buf = theBufferList().getBuffer(s);
1145 else if (s.exists()) {
1146 buf = lyx_view_->loadDocument(s);
1149 lyx_view_->message(bformat(
1150 _("File does not exist: %1$s"),
1151 makeDisplayPath(file_name)));
1155 updateFlags = Update::None;
1159 buf->updateLabels();
1160 lyx_view_->setBuffer(buf);
1161 view()->setCursorFromRow(row);
1163 buf->errors("Parse");
1164 updateFlags = Update::FitCursor;
1169 case LFUN_DIALOG_SHOW_NEW_INSET: {
1170 LASSERT(lyx_view_, /**/);
1171 string const name = cmd.getArg(0);
1172 InsetCode code = insetCode(name);
1173 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1174 bool insetCodeOK = true;
1181 case NOMENCL_PRINT_CODE:
1184 case HYPERLINK_CODE: {
1185 InsetCommandParams p(code);
1186 data = InsetCommand::params2string(name, p);
1189 case INCLUDE_CODE: {
1190 // data is the include type: one of "include",
1191 // "input", "verbatiminput" or "verbatiminput*"
1193 // default type is requested
1195 InsetCommandParams p(INCLUDE_CODE, data);
1196 data = InsetCommand::params2string("include", p);
1200 // \c data == "Boxed" || "Frameless" etc
1201 InsetBoxParams p(data);
1202 data = InsetBox::params2string(p);
1206 InsetBranchParams p;
1207 data = InsetBranch::params2string(p);
1211 InsetCommandParams p(CITE_CODE);
1212 data = InsetCommand::params2string(name, p);
1216 data = InsetERT::params2string(InsetCollapsable::Open);
1219 case EXTERNAL_CODE: {
1220 InsetExternalParams p;
1221 data = InsetExternal::params2string(p, *buffer);
1226 data = InsetFloat::params2string(p);
1229 case LISTINGS_CODE: {
1230 InsetListingsParams p;
1231 data = InsetListings::params2string(p);
1234 case GRAPHICS_CODE: {
1235 InsetGraphicsParams p;
1236 data = InsetGraphics::params2string(p, *buffer);
1241 data = InsetNote::params2string(p);
1244 case PHANTOM_CODE: {
1245 InsetPhantomParams p;
1246 data = InsetPhantom::params2string(p);
1251 data = InsetSpace::params2string(p);
1256 data = InsetVSpace::params2string(space);
1261 data = InsetWrap::params2string(p);
1265 lyxerr << "Inset type '" << name <<
1266 "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" << endl;
1267 insetCodeOK = false;
1269 } // end switch(code)
1271 dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data));
1275 case LFUN_CITATION_INSERT: {
1276 LASSERT(lyx_view_, /**/);
1277 if (!argument.empty()) {
1278 // we can have one optional argument, delimited by '|'
1279 // citation-insert <key>|<text_before>
1280 // this should be enhanced to also support text_after
1281 // and citation style
1282 string arg = argument;
1284 if (contains(argument, "|")) {
1285 arg = token(argument, '|', 0);
1286 opt1 = token(argument, '|', 1);
1288 InsetCommandParams icp(CITE_CODE);
1289 icp["key"] = from_utf8(arg);
1291 icp["before"] = from_utf8(opt1);
1292 string icstr = InsetCommand::params2string("citation", icp);
1293 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1296 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1300 case LFUN_BUFFER_CHILD_OPEN: {
1301 LASSERT(lyx_view_ && buffer, /**/);
1302 FileName filename = makeAbsPath(argument, buffer->filePath());
1303 view()->saveBookmark(false);
1305 bool parsed = false;
1306 if (theBufferList().exists(filename)) {
1307 child = theBufferList().getBuffer(filename);
1309 setMessage(bformat(_("Opening child document %1$s..."),
1310 makeDisplayPath(filename.absFilename())));
1311 child = lyx_view_->loadDocument(filename, false);
1315 // Set the parent name of the child document.
1316 // This makes insertion of citations and references in the child work,
1317 // when the target is in the parent or another child document.
1318 child->setParent(buffer);
1319 child->masterBuffer()->updateLabels();
1320 lyx_view_->setBuffer(child);
1322 child->errors("Parse");
1325 // If a screen update is required (in case where auto_open is false),
1326 // setBuffer() would have taken care of it already. Otherwise we shall
1327 // reset the update flag because it can cause a circular problem.
1329 updateFlags = Update::None;
1333 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
1334 LASSERT(lyx_view_, /**/);
1335 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1338 case LFUN_KEYMAP_OFF:
1339 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1340 lyx_view_->view()->getIntl().keyMapOn(false);
1343 case LFUN_KEYMAP_PRIMARY:
1344 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1345 lyx_view_->view()->getIntl().keyMapPrim();
1348 case LFUN_KEYMAP_SECONDARY:
1349 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1350 lyx_view_->view()->getIntl().keyMapSec();
1353 case LFUN_KEYMAP_TOGGLE:
1354 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1355 lyx_view_->view()->getIntl().toggleKeyMap();
1361 string rest = split(argument, countstr, ' ');
1362 istringstream is(countstr);
1365 //lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1366 for (int i = 0; i < count; ++i)
1367 dispatch(lyxaction.lookupFunc(rest));
1371 case LFUN_COMMAND_SEQUENCE: {
1372 // argument contains ';'-terminated commands
1373 string arg = argument;
1374 if (theBufferList().isLoaded(buffer))
1375 buffer->undo().beginUndoGroup();
1376 while (!arg.empty()) {
1378 arg = split(arg, first, ';');
1379 FuncRequest func(lyxaction.lookupFunc(first));
1380 func.origin = cmd.origin;
1383 if (theBufferList().isLoaded(buffer))
1384 buffer->undo().endUndoGroup();
1388 case LFUN_COMMAND_ALTERNATIVES: {
1389 // argument contains ';'-terminated commands
1390 string arg = argument;
1391 while (!arg.empty()) {
1393 arg = split(arg, first, ';');
1394 FuncRequest func(lyxaction.lookupFunc(first));
1395 func.origin = cmd.origin;
1396 FuncStatus stat = getStatus(func);
1397 if (stat.enabled()) {
1407 if (theTopLevelCmdDef().lock(argument, func)) {
1408 func.origin = cmd.origin;
1410 theTopLevelCmdDef().release(argument);
1412 if (func.action == LFUN_UNKNOWN_ACTION) {
1413 // unknown command definition
1414 lyxerr << "Warning: unknown command definition `"
1418 // recursion detected
1419 lyxerr << "Warning: Recursion in the command definition `"
1420 << argument << "' detected"
1427 case LFUN_PREFERENCES_SAVE: {
1428 lyxrc.write(makeAbsPath("preferences",
1429 package().user_support().absFilename()),
1435 LASSERT(lyx_view_, /**/);
1436 lyx_view_->message(from_utf8(argument));
1439 case LFUN_BUFFER_LANGUAGE: {
1440 LASSERT(lyx_view_, /**/);
1441 Language const * oldL = buffer->params().language;
1442 Language const * newL = languages.getLanguage(argument);
1443 if (!newL || oldL == newL)
1446 if (oldL->rightToLeft() == newL->rightToLeft()
1447 && !buffer->isMultiLingual())
1448 buffer->changeLanguage(oldL, newL);
1452 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1453 string const fname =
1454 addName(addPath(package().user_support().absFilename(), "templates/"),
1456 Buffer defaults(fname);
1458 istringstream ss(argument);
1461 int const unknown_tokens = defaults.readHeader(lex);
1463 if (unknown_tokens != 0) {
1464 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1465 << unknown_tokens << " unknown token"
1466 << (unknown_tokens == 1 ? "" : "s")
1470 if (defaults.writeFile(FileName(defaults.absFileName())))
1471 setMessage(bformat(_("Document defaults saved in %1$s"),
1472 makeDisplayPath(fname)));
1474 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1478 case LFUN_BUFFER_PARAMS_APPLY: {
1479 LASSERT(lyx_view_, /**/);
1481 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1482 Cursor & cur = view()->cursor();
1483 cur.recordUndoFullDocument();
1485 istringstream ss(argument);
1488 int const unknown_tokens = buffer->readHeader(lex);
1490 if (unknown_tokens != 0) {
1491 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1492 << unknown_tokens << " unknown token"
1493 << (unknown_tokens == 1 ? "" : "s")
1497 updateLayout(oldClass, buffer);
1499 updateFlags = Update::Force | Update::FitCursor;
1500 // We are most certainly here because of a change in the document
1501 // It is then better to make sure that all dialogs are in sync with
1502 // current document settings. LyXView::restartCursor() achieve this.
1503 lyx_view_->restartCursor();
1507 case LFUN_LAYOUT_MODULES_CLEAR: {
1508 LASSERT(lyx_view_, /**/);
1509 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1510 view()->cursor().recordUndoFullDocument();
1511 buffer->params().clearLayoutModules();
1512 buffer->params().makeDocumentClass();
1513 updateLayout(oldClass, buffer);
1514 updateFlags = Update::Force | Update::FitCursor;
1518 case LFUN_LAYOUT_MODULE_ADD: {
1519 LASSERT(lyx_view_, /**/);
1520 BufferParams const & params = buffer->params();
1521 if (!params.moduleCanBeAdded(argument)) {
1522 LYXERR0("Module `" << argument <<
1523 "' cannot be added due to failed requirements or "
1524 "conflicts with installed modules.");
1527 DocumentClass const * const oldClass = params.documentClassPtr();
1528 view()->cursor().recordUndoFullDocument();
1529 buffer->params().addLayoutModule(argument);
1530 buffer->params().makeDocumentClass();
1531 updateLayout(oldClass, buffer);
1532 updateFlags = Update::Force | Update::FitCursor;
1536 case LFUN_TEXTCLASS_APPLY: {
1537 LASSERT(lyx_view_, /**/);
1539 if (!loadLayoutFile(argument, buffer->temppath()) &&
1540 !loadLayoutFile(argument, buffer->filePath()))
1543 LayoutFile const * old_layout = buffer->params().baseClass();
1544 LayoutFile const * new_layout = &(LayoutFileList::get()[argument]);
1546 if (old_layout == new_layout)
1550 //Save the old, possibly modular, layout for use in conversion.
1551 DocumentClass const * const oldDocClass = buffer->params().documentClassPtr();
1552 view()->cursor().recordUndoFullDocument();
1553 buffer->params().setBaseClass(argument);
1554 buffer->params().makeDocumentClass();
1555 updateLayout(oldDocClass, buffer);
1556 updateFlags = Update::Force | Update::FitCursor;
1560 case LFUN_LAYOUT_RELOAD: {
1561 LASSERT(lyx_view_, /**/);
1562 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1563 LayoutFileIndex bc = buffer->params().baseClassID();
1564 LayoutFileList::get().reset(bc);
1565 buffer->params().setBaseClass(bc);
1566 buffer->params().makeDocumentClass();
1567 updateLayout(oldClass, buffer);
1568 updateFlags = Update::Force | Update::FitCursor;
1572 case LFUN_TEXTCLASS_LOAD:
1573 loadLayoutFile(argument, buffer->temppath()) ||
1574 loadLayoutFile(argument, buffer->filePath());
1577 case LFUN_LYXRC_APPLY: {
1578 // reset active key sequences, since the bindings
1579 // are updated (bug 6064)
1581 LyXRC const lyxrc_orig = lyxrc;
1583 istringstream ss(argument);
1584 bool const success = lyxrc.read(ss) == 0;
1587 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1588 << "Unable to read lyxrc data"
1593 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1597 theApp()->resetGui();
1599 /// We force the redraw in any case because there might be
1600 /// some screen font changes.
1601 /// FIXME: only the current view will be updated. the Gui
1602 /// class is able to furnish the list of views.
1603 updateFlags = Update::Force;
1607 case LFUN_BOOKMARK_GOTO:
1608 // go to bookmark, open unopened file and switch to buffer if necessary
1609 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1610 updateFlags = Update::FitCursor;
1613 case LFUN_BOOKMARK_CLEAR:
1614 theSession().bookmarks().clear();
1617 case LFUN_VC_COMMAND: {
1618 string flag = cmd.getArg(0);
1619 if (buffer && contains(flag, 'R') && !ensureBufferClean(view()))
1622 if (contains(flag, 'M'))
1623 if (!Alert::askForText(message, _("LyX VC: Log Message")))
1626 string path = cmd.getArg(1);
1627 if (contains(path, "$$p") && buffer)
1628 path = subst(path, "$$p", buffer->filePath());
1629 LYXERR(Debug::LYXVC, "Directory: " << path);
1631 if (!pp.isReadableDirectory()) {
1632 lyxerr << _("Directory is not accessible.") << endl;
1635 support::PathChanger p(pp);
1637 string command = cmd.getArg(2);
1638 if (command.empty())
1641 command = subst(command, "$$i", buffer->absFileName());
1642 command = subst(command, "$$p", buffer->filePath());
1644 command = subst(command, "$$m", to_utf8(message));
1645 LYXERR(Debug::LYXVC, "Command: " << command);
1647 one.startscript(Systemcall::Wait, command);
1651 if (contains(flag, 'I'))
1652 buffer->markDirty();
1653 if (contains(flag, 'R'))
1660 LASSERT(theApp(), /**/);
1661 // Let the frontend dispatch its own actions.
1662 if (theApp()->dispatch(cmd))
1663 // Nothing more to do.
1666 // Everything below is only for active lyx_view_
1670 // Start an undo group. This may be needed for
1671 // some stuff like inset-apply on labels.
1672 if (theBufferList().isLoaded(buffer))
1673 buffer->undo().beginUndoGroup();
1675 // Let the current LyXView dispatch its own actions.
1676 if (lyx_view_->dispatch(cmd)) {
1677 if (lyx_view_->view()) {
1678 updateFlags = lyx_view_->view()->cursor().result().update();
1679 if (theBufferList().isLoaded(buffer))
1680 buffer->undo().endUndoGroup();
1685 LASSERT(lyx_view_->view(), /**/);
1687 // Let the current BufferView dispatch its own actions.
1688 if (view()->dispatch(cmd)) {
1689 // The BufferView took care of its own updates if needed.
1690 updateFlags = Update::None;
1691 if (theBufferList().isLoaded(buffer))
1692 buffer->undo().endUndoGroup();
1696 // OK, so try the Buffer itself
1698 view()->buffer().dispatch(cmd, dr);
1699 if (dr.dispatched()) {
1700 updateFlags = dr.update();
1704 // Is this a function that acts on inset at point?
1705 Inset * inset = view()->cursor().nextInset();
1706 if (lyxaction.funcHasFlag(action, LyXAction::AtPoint)
1708 view()->cursor().result().dispatched(true);
1709 view()->cursor().result().update(Update::FitCursor | Update::Force);
1710 FuncRequest tmpcmd = cmd;
1711 inset->dispatch(view()->cursor(), tmpcmd);
1712 if (view()->cursor().result().dispatched()) {
1713 updateFlags = view()->cursor().result().update();
1718 // Let the current Cursor dispatch its own actions.
1719 Cursor old = view()->cursor();
1720 view()->cursor().getPos(cursorPosBeforeDispatchX_,
1721 cursorPosBeforeDispatchY_);
1722 view()->cursor().dispatch(cmd);
1724 // notify insets we just left
1725 if (view()->cursor() != old) {
1727 bool badcursor = notifyCursorLeavesOrEnters(old, view()->cursor());
1729 view()->cursor().fixIfBroken();
1732 if (theBufferList().isLoaded(buffer))
1733 buffer->undo().endUndoGroup();
1735 // update completion. We do it here and not in
1736 // processKeySym to avoid another redraw just for a
1737 // changed inline completion
1738 if (cmd.origin == FuncRequest::KEYBOARD) {
1739 if (cmd.action == LFUN_SELF_INSERT
1740 || (cmd.action == LFUN_ERT_INSERT && view()->cursor().inMathed()))
1741 lyx_view_->updateCompletion(view()->cursor(), true, true);
1742 else if (cmd.action == LFUN_CHAR_DELETE_BACKWARD)
1743 lyx_view_->updateCompletion(view()->cursor(), false, true);
1745 lyx_view_->updateCompletion(view()->cursor(), false, false);
1748 updateFlags = view()->cursor().result().update();
1751 // if we executed a mutating lfun, mark the buffer as dirty
1752 if (theBufferList().isLoaded(buffer) && flag.enabled()
1753 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1754 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1755 buffer->markDirty();
1757 if (lyx_view_ && lyx_view_->buffer()) {
1758 // BufferView::update() updates the ViewMetricsInfo and
1759 // also initializes the position cache for all insets in
1760 // (at least partially) visible top-level paragraphs.
1761 // We will redraw the screen only if needed.
1762 view()->processUpdateFlags(updateFlags);
1764 // Do we have a selection?
1765 theSelection().haveSelection(view()->cursor().selection());
1768 lyx_view_->restartCursor();
1772 // Some messages may already be translated, so we cannot use _()
1773 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1778 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1780 const bool verbose = (cmd.origin == FuncRequest::MENU
1781 || cmd.origin == FuncRequest::TOOLBAR
1782 || cmd.origin == FuncRequest::COMMANDBUFFER);
1784 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1785 LYXERR(Debug::ACTION, "dispatch msg is " << to_utf8(msg));
1787 lyx_view_->message(msg);
1791 docstring dispatch_msg = msg;
1792 if (!dispatch_msg.empty())
1793 dispatch_msg += ' ';
1795 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1797 bool argsadded = false;
1799 if (!cmd.argument().empty()) {
1800 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1801 comname += ' ' + cmd.argument();
1806 docstring const shortcuts = theTopLevelKeymap().printBindings(cmd, KeySequence::ForGui);
1808 if (!shortcuts.empty())
1809 comname += ": " + shortcuts;
1810 else if (!argsadded && !cmd.argument().empty())
1811 comname += ' ' + cmd.argument();
1813 if (!comname.empty()) {
1814 comname = rtrim(comname);
1815 dispatch_msg += '(' + rtrim(comname) + ')';
1818 LYXERR(Debug::ACTION, "verbose dispatch msg " << to_utf8(dispatch_msg));
1819 if (!dispatch_msg.empty())
1820 lyx_view_->message(dispatch_msg);
1824 void LyXFunc::reloadBuffer()
1826 FileName filename = lyx_view_->buffer()->fileName();
1827 // The user has already confirmed that the changes, if any, should
1828 // be discarded. So we just release the Buffer and don't call closeBuffer();
1829 theBufferList().release(lyx_view_->buffer());
1830 // if the lyx_view_ has been destroyed, create a new one
1832 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1833 Buffer * buf = lyx_view_->loadDocument(filename);
1834 docstring const disp_fn = makeDisplayPath(filename.absFilename());
1837 buf->updateLabels();
1838 lyx_view_->setBuffer(buf);
1839 buf->errors("Parse");
1840 str = bformat(_("Document %1$s reloaded."), disp_fn);
1842 str = bformat(_("Could not reload document %1$s"), disp_fn);
1844 lyx_view_->message(str);
1847 // Each "lyx_view_" should have it's own message method. lyxview and
1848 // the minibuffer would use the minibuffer, but lyxserver would
1849 // send an ERROR signal to its client. Alejandro 970603
1850 // This function is bit problematic when it comes to NLS, to make the
1851 // lyx servers client be language indepenent we must not translate
1852 // strings sent to this func.
1853 void LyXFunc::setErrorMessage(docstring const & m) const
1855 dispatch_buffer = m;
1860 void LyXFunc::setMessage(docstring const & m) const
1862 dispatch_buffer = m;
1866 docstring LyXFunc::viewStatusMessage()
1868 // When meta-fake key is pressed, show the key sequence so far + "M-".
1870 return keyseq.print(KeySequence::ForGui) + "M-";
1872 // Else, when a non-complete key sequence is pressed,
1873 // show the available options.
1874 if (keyseq.length() > 0 && !keyseq.deleted())
1875 return keyseq.printOptions(true);
1877 LASSERT(lyx_view_, /**/);
1878 if (!lyx_view_->buffer())
1879 return _("Welcome to LyX!");
1881 return view()->cursor().currentState();
1885 BufferView * LyXFunc::view() const
1887 LASSERT(lyx_view_, /**/);
1888 return lyx_view_->view();
1892 bool LyXFunc::wasMetaKey() const
1894 return (meta_fake_bit != NoModifier);
1898 void LyXFunc::updateLayout(DocumentClass const * const oldlayout, Buffer * buf)
1900 lyx_view_->message(_("Converting document to new document class..."));
1902 StableDocIterator backcur(view()->cursor());
1903 ErrorList & el = buf->errorList("Class Switch");
1904 cap::switchBetweenClasses(
1905 oldlayout, buf->params().documentClassPtr(),
1906 static_cast<InsetText &>(buf->inset()), el);
1908 view()->setCursor(backcur.asDocIterator(buf));
1910 buf->errors("Class Switch");
1911 buf->updateLabels();
1917 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1919 // Why the switch you might ask. It is a trick to ensure that all
1920 // the elements in the LyXRCTags enum is handled. As you can see
1921 // there are no breaks at all. So it is just a huge fall-through.
1922 // The nice thing is that we will get a warning from the compiler
1923 // if we forget an element.
1924 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1926 case LyXRC::RC_ACCEPT_COMPOUND:
1927 case LyXRC::RC_ALT_LANG:
1928 case LyXRC::RC_PLAINTEXT_LINELEN:
1929 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
1930 case LyXRC::RC_AUTOCORRECTION_MATH:
1931 case LyXRC::RC_AUTOREGIONDELETE:
1932 case LyXRC::RC_AUTORESET_OPTIONS:
1933 case LyXRC::RC_AUTOSAVE:
1934 case LyXRC::RC_AUTO_NUMBER:
1935 case LyXRC::RC_BACKUPDIR_PATH:
1936 case LyXRC::RC_BIBTEX_ALTERNATIVES:
1937 case LyXRC::RC_BIBTEX_COMMAND:
1938 case LyXRC::RC_BINDFILE:
1939 case LyXRC::RC_CHECKLASTFILES:
1940 case LyXRC::RC_COMPLETION_CURSOR_TEXT:
1941 case LyXRC::RC_COMPLETION_INLINE_DELAY:
1942 case LyXRC::RC_COMPLETION_INLINE_DOTS:
1943 case LyXRC::RC_COMPLETION_INLINE_MATH:
1944 case LyXRC::RC_COMPLETION_INLINE_TEXT:
1945 case LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE:
1946 case LyXRC::RC_COMPLETION_POPUP_DELAY:
1947 case LyXRC::RC_COMPLETION_POPUP_MATH:
1948 case LyXRC::RC_COMPLETION_POPUP_TEXT:
1949 case LyXRC::RC_USELASTFILEPOS:
1950 case LyXRC::RC_LOADSESSION:
1951 case LyXRC::RC_CHKTEX_COMMAND:
1952 case LyXRC::RC_CONVERTER:
1953 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
1954 case LyXRC::RC_COPIER:
1955 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1956 case LyXRC::RC_SCROLL_BELOW_DOCUMENT:
1957 case LyXRC::RC_DATE_INSERT_FORMAT:
1958 case LyXRC::RC_DEFAULT_LANGUAGE:
1959 case LyXRC::RC_GUI_LANGUAGE:
1960 case LyXRC::RC_DEFAULT_PAPERSIZE:
1961 case LyXRC::RC_DEFAULT_VIEW_FORMAT:
1962 case LyXRC::RC_DEFFILE:
1963 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1964 case LyXRC::RC_DISPLAY_GRAPHICS:
1965 case LyXRC::RC_DOCUMENTPATH:
1966 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1967 FileName path(lyxrc_new.document_path);
1968 if (path.exists() && path.isDirectory())
1969 package().document_dir() = FileName(lyxrc.document_path);
1971 case LyXRC::RC_ESC_CHARS:
1972 case LyXRC::RC_EXAMPLEPATH:
1973 case LyXRC::RC_FONT_ENCODING:
1974 case LyXRC::RC_FORMAT:
1975 case LyXRC::RC_GROUP_LAYOUTS:
1976 case LyXRC::RC_HUNSPELLDIR_PATH:
1977 case LyXRC::RC_INDEX_ALTERNATIVES:
1978 case LyXRC::RC_INDEX_COMMAND:
1979 case LyXRC::RC_JBIBTEX_COMMAND:
1980 case LyXRC::RC_JINDEX_COMMAND:
1981 case LyXRC::RC_NOMENCL_COMMAND:
1982 case LyXRC::RC_INPUT:
1983 case LyXRC::RC_KBMAP:
1984 case LyXRC::RC_KBMAP_PRIMARY:
1985 case LyXRC::RC_KBMAP_SECONDARY:
1986 case LyXRC::RC_LABEL_INIT_LENGTH:
1987 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
1988 case LyXRC::RC_LANGUAGE_AUTO_END:
1989 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
1990 case LyXRC::RC_LANGUAGE_COMMAND_END:
1991 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
1992 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
1993 case LyXRC::RC_LANGUAGE_PACKAGE:
1994 case LyXRC::RC_LANGUAGE_USE_BABEL:
1995 case LyXRC::RC_MAC_LIKE_WORD_MOVEMENT:
1996 case LyXRC::RC_MACRO_EDIT_STYLE:
1997 case LyXRC::RC_MAKE_BACKUP:
1998 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
1999 case LyXRC::RC_MOUSE_WHEEL_SPEED:
2000 case LyXRC::RC_NUMLASTFILES:
2001 case LyXRC::RC_PARAGRAPH_MARKERS:
2002 case LyXRC::RC_PATH_PREFIX:
2003 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2004 prependEnvPath("PATH", lyxrc.path_prefix);
2006 case LyXRC::RC_PERS_DICT:
2007 case LyXRC::RC_PREVIEW:
2008 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2009 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2010 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2011 case LyXRC::RC_PRINTCOPIESFLAG:
2012 case LyXRC::RC_PRINTER:
2013 case LyXRC::RC_PRINTEVENPAGEFLAG:
2014 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2015 case LyXRC::RC_PRINTFILEEXTENSION:
2016 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2017 case LyXRC::RC_PRINTODDPAGEFLAG:
2018 case LyXRC::RC_PRINTPAGERANGEFLAG:
2019 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2020 case LyXRC::RC_PRINTPAPERFLAG:
2021 case LyXRC::RC_PRINTREVERSEFLAG:
2022 case LyXRC::RC_PRINTSPOOL_COMMAND:
2023 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2024 case LyXRC::RC_PRINTTOFILE:
2025 case LyXRC::RC_PRINTTOPRINTER:
2026 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2027 case LyXRC::RC_PRINT_COMMAND:
2028 case LyXRC::RC_RTL_SUPPORT:
2029 case LyXRC::RC_SCREEN_DPI:
2030 case LyXRC::RC_SCREEN_FONT_ROMAN:
2031 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2032 case LyXRC::RC_SCREEN_FONT_SANS:
2033 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2034 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2035 case LyXRC::RC_SCREEN_FONT_SIZES:
2036 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2037 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2038 case LyXRC::RC_GEOMETRY_SESSION:
2039 case LyXRC::RC_SCREEN_ZOOM:
2040 case LyXRC::RC_SERVERPIPE:
2041 case LyXRC::RC_SET_COLOR:
2042 case LyXRC::RC_SHOW_BANNER:
2043 case LyXRC::RC_OPEN_BUFFERS_IN_TABS:
2044 case LyXRC::RC_SPELL_COMMAND:
2045 case LyXRC::RC_SPELLCHECKER:
2046 case LyXRC::RC_SPELLCHECK_CONTINUOUSLY:
2047 case LyXRC::RC_SPLITINDEX_COMMAND:
2048 case LyXRC::RC_TEMPDIRPATH:
2049 case LyXRC::RC_TEMPLATEPATH:
2050 case LyXRC::RC_TEX_ALLOWS_SPACES:
2051 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2052 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2053 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2055 case LyXRC::RC_THESAURUSDIRPATH:
2056 case LyXRC::RC_UIFILE:
2057 case LyXRC::RC_USER_EMAIL:
2058 case LyXRC::RC_USER_NAME:
2059 case LyXRC::RC_USETEMPDIR:
2060 case LyXRC::RC_USE_ALT_LANG:
2061 case LyXRC::RC_USE_CONVERTER_CACHE:
2062 case LyXRC::RC_USE_ESC_CHARS:
2063 case LyXRC::RC_USE_INP_ENC:
2064 case LyXRC::RC_USE_PERS_DICT:
2065 case LyXRC::RC_USE_TOOLTIP:
2066 case LyXRC::RC_USE_PIXMAP_CACHE:
2067 case LyXRC::RC_USE_SPELL_LIB:
2068 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2069 case LyXRC::RC_SORT_LAYOUTS:
2070 case LyXRC::RC_FULL_SCREEN_LIMIT:
2071 case LyXRC::RC_FULL_SCREEN_SCROLLBAR:
2072 case LyXRC::RC_FULL_SCREEN_MENUBAR:
2073 case LyXRC::RC_FULL_SCREEN_TABBAR:
2074 case LyXRC::RC_FULL_SCREEN_TOOLBARS:
2075 case LyXRC::RC_FULL_SCREEN_WIDTH:
2076 case LyXRC::RC_VISUAL_CURSOR:
2077 case LyXRC::RC_VIEWER:
2078 case LyXRC::RC_LAST: