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_->currentBufferView() && 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_->currentBufferView()->cursor());
222 void LyXFunc::handleKeyFunc(FuncCode action)
224 char_type c = encoded_last_key;
229 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
230 lyx_view_->currentBufferView()->getIntl().getTransManager().deadkey(
231 c, get_accent(action).accent, view()->cursor().innerText(),
232 currentBufferView()->cursor());
233 // Need to clear, in case the minibuffer calls these
236 // copied verbatim from do_accent_char
237 view()->cursor().resetAnchor();
238 view()->processUpdateFlags(Update::FitCursor);
241 //FIXME: bookmark handling is a frontend issue. This code should be transferred
242 // to GuiView and be GuiView and be window dependent.
243 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
245 LASSERT(lyx_view_, /**/);
246 if (!theSession().bookmarks().isValid(idx))
248 BookmarksSection::Bookmark const & bm = theSession().bookmarks().bookmark(idx);
249 LASSERT(!bm.filename.empty(), /**/);
250 string const file = bm.filename.absFilename();
251 // if the file is not opened, open it.
252 if (!theBufferList().exists(bm.filename)) {
254 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
258 // open may fail, so we need to test it again
259 if (!theBufferList().exists(bm.filename))
262 // bm can be changed when saving
263 BookmarksSection::Bookmark tmp = bm;
265 // Special case idx == 0 used for back-from-back jump navigation
267 dispatch(FuncRequest(LFUN_BOOKMARK_SAVE, "0"));
269 // if the current buffer is not that one, switch to it.
270 if (!lyx_view_->buffer() || lyx_view_->buffer()->fileName() != tmp.filename) {
273 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
276 // moveToPosition try paragraph id first and then paragraph (pit, pos).
277 if (!view()->moveToPosition(tmp.bottom_pit, tmp.bottom_pos,
278 tmp.top_id, tmp.top_pos))
285 // Cursor jump succeeded!
286 Cursor const & cur = view()->cursor();
287 pit_type new_pit = cur.pit();
288 pos_type new_pos = cur.pos();
289 int new_id = cur.paragraph().id();
291 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
292 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
293 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos
294 || bm.top_id != new_id) {
295 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(
296 new_pit, new_pos, new_id);
301 void LyXFunc::processKeySym(KeySymbol const & keysym, KeyModifier state)
303 LYXERR(Debug::KEY, "KeySym is " << keysym.getSymbolName());
305 // Do nothing if we have nothing (JMarc)
306 if (!keysym.isOK()) {
307 LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
308 lyx_view_->restartCursor();
312 if (keysym.isModifier()) {
313 LYXERR(Debug::KEY, "isModifier true");
315 lyx_view_->restartCursor();
319 //Encoding const * encoding = view()->cursor().getEncoding();
320 //encoded_last_key = keysym.getISOEncoded(encoding ? encoding->name() : "");
321 // FIXME: encoded_last_key shadows the member variable of the same
322 // name. Is that intended?
323 char_type encoded_last_key = keysym.getUCSEncoded();
325 // Do a one-deep top-level lookup for
326 // cancel and meta-fake keys. RVDK_PATCH_5
327 cancel_meta_seq.reset();
329 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
330 LYXERR(Debug::KEY, "action first set to [" << func.action << ']');
332 // When not cancel or meta-fake, do the normal lookup.
333 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
334 // Mostly, meta_fake_bit = NoModifier. RVDK_PATCH_5.
335 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
336 // remove Caps Lock and Mod2 as a modifiers
337 func = keyseq.addkey(keysym, (state | meta_fake_bit));
338 LYXERR(Debug::KEY, "action now set to [" << func.action << ']');
341 // Dont remove this unless you know what you are doing.
342 meta_fake_bit = NoModifier;
344 // Can this happen now ?
345 if (func.action == LFUN_NOACTION)
346 func = FuncRequest(LFUN_COMMAND_PREFIX);
348 LYXERR(Debug::KEY, " Key [action=" << func.action << "]["
349 << keyseq.print(KeySequence::Portable) << ']');
351 // already here we know if it any point in going further
352 // why not return already here if action == -1 and
353 // num_bytes == 0? (Lgb)
355 if (keyseq.length() > 1)
356 lyx_view_->message(keyseq.print(KeySequence::ForGui));
359 // Maybe user can only reach the key via holding down shift.
360 // Let's see. But only if shift is the only modifier
361 if (func.action == LFUN_UNKNOWN_ACTION && state == ShiftModifier) {
362 LYXERR(Debug::KEY, "Trying without shift");
363 func = keyseq.addkey(keysym, NoModifier);
364 LYXERR(Debug::KEY, "Action now " << func.action);
367 if (func.action == LFUN_UNKNOWN_ACTION) {
368 // Hmm, we didn't match any of the keysequences. See
369 // if it's normal insertable text not already covered
371 if (keysym.isText() && keyseq.length() == 1) {
372 LYXERR(Debug::KEY, "isText() is true, inserting.");
373 func = FuncRequest(LFUN_SELF_INSERT,
374 FuncRequest::KEYBOARD);
376 LYXERR(Debug::KEY, "Unknown, !isText() - giving up");
377 lyx_view_->message(_("Unknown function."));
378 lyx_view_->restartCursor();
383 if (func.action == LFUN_SELF_INSERT) {
384 if (encoded_last_key != 0) {
385 docstring const arg(1, encoded_last_key);
386 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
387 FuncRequest::KEYBOARD));
388 LYXERR(Debug::KEY, "SelfInsert arg[`" << to_utf8(arg) << "']");
398 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
400 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
403 /* In LyX/Mac, when a dialog is open, the menus of the
404 application can still be accessed without giving focus to
405 the main window. In this case, we want to disable the menu
406 entries that are buffer or view-related.
408 If this code is moved somewhere else (like in
409 GuiView::getStatus), then several functions will not be
412 frontend::LyXView * lv = 0;
415 && (cmd.origin != FuncRequest::MENU || lyx_view_->hasFocus())) {
417 buf = lyx_view_->buffer();
420 if (cmd.action == LFUN_NOACTION) {
421 flag.message(from_utf8(N_("Nothing to do")));
422 flag.setEnabled(false);
426 switch (cmd.action) {
427 case LFUN_UNKNOWN_ACTION:
429 flag.setEnabled(false);
436 if (flag.unknown()) {
437 flag.message(from_utf8(N_("Unknown action")));
441 if (!flag.enabled()) {
442 if (flag.message().empty())
443 flag.message(from_utf8(N_("Command disabled")));
447 // Check whether we need a buffer
448 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
450 flag.message(from_utf8(N_("Command not allowed with"
451 "out any document open")));
452 flag.setEnabled(false);
456 // I would really like to avoid having this switch and rather try to
457 // encode this in the function itself.
458 // -- And I'd rather let an inset decide which LFUNs it is willing
459 // to handle (Andre')
461 switch (cmd.action) {
463 case LFUN_BUFFER_TOGGLE_READ_ONLY:
464 flag.setOnOff(buf->isReadonly());
467 case LFUN_BUFFER_SWITCH:
468 // toggle on the current buffer, but do not toggle off
469 // the other ones (is that a good idea?)
470 if (buf && to_utf8(cmd.argument()) == buf->absFileName())
474 case LFUN_BUFFER_CHKTEX:
475 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
478 case LFUN_BUILD_PROGRAM:
479 enable = buf->isExportable("program");
482 case LFUN_VC_REGISTER:
483 enable = !buf->lyxvc().inUse();
485 case LFUN_VC_CHECK_IN:
486 enable = buf->lyxvc().checkInEnabled();
488 case LFUN_VC_CHECK_OUT:
489 enable = buf->lyxvc().checkOutEnabled();
491 case LFUN_VC_LOCKING_TOGGLE:
492 enable = !buf->isReadonly() && buf->lyxvc().lockingToggleEnabled();
493 flag.setOnOff(enable && !buf->lyxvc().locker().empty());
496 enable = buf->lyxvc().inUse();
498 case LFUN_VC_UNDO_LAST:
499 enable = buf->lyxvc().undoLastEnabled();
501 case LFUN_BUFFER_RELOAD:
502 enable = !buf->isUnnamed() && buf->fileName().exists()
503 && (!buf->isClean() || buf->isExternallyModified(Buffer::timestamp_method));
506 case LFUN_CITATION_INSERT: {
507 FuncRequest fr(LFUN_INSET_INSERT, "citation");
508 enable = getStatus(fr).enabled();
512 // This could be used for the no-GUI version. The GUI version is handled in
513 // LyXView::getStatus(). See above.
515 case LFUN_BUFFER_WRITE:
516 case LFUN_BUFFER_WRITE_AS: {
517 Buffer * b = theBufferList().getBuffer(FileName(cmd.getArg(0)));
518 enable = b && (b->isUnnamed() || !b->isClean());
523 case LFUN_BUFFER_WRITE_ALL: {
524 // We enable the command only if there are some modified buffers
525 Buffer * first = theBufferList().first();
530 // We cannot use a for loop as the buffer list is a cycle.
536 b = theBufferList().next(b);
537 } while (b != first);
541 case LFUN_BOOKMARK_GOTO: {
542 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
543 enable = theSession().bookmarks().isValid(num);
547 case LFUN_BOOKMARK_CLEAR:
548 enable = theSession().bookmarks().hasValid();
551 // this one is difficult to get right. As a half-baked
552 // solution, we consider only the first action of the sequence
553 case LFUN_COMMAND_SEQUENCE: {
554 // argument contains ';'-terminated commands
555 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
556 FuncRequest func(lyxaction.lookupFunc(firstcmd));
557 func.origin = cmd.origin;
558 flag = getStatus(func);
562 // we want to check if at least one of these is enabled
563 case LFUN_COMMAND_ALTERNATIVES: {
564 // argument contains ';'-terminated commands
565 string arg = to_utf8(cmd.argument());
566 while (!arg.empty()) {
568 arg = split(arg, first, ';');
569 FuncRequest func(lyxaction.lookupFunc(first));
570 func.origin = cmd.origin;
571 flag = getStatus(func);
572 // if this one is enabled, the whole thing is
581 string name = to_utf8(cmd.argument());
582 if (theTopLevelCmdDef().lock(name, func)) {
583 func.origin = cmd.origin;
584 flag = getStatus(func);
585 theTopLevelCmdDef().release(name);
587 // catch recursion or unknown command
588 // definition. all operations until the
589 // recursion or unknown command definition
590 // occurs are performed, so set the state to
597 case LFUN_VC_COMMAND: {
598 if (cmd.argument().empty())
601 if (!buf && contains(cmd.getArg(0), 'D'))
606 case LFUN_MASTER_BUFFER_UPDATE:
607 case LFUN_MASTER_BUFFER_VIEW:
608 if (!buf->parent()) {
612 case LFUN_BUFFER_UPDATE:
613 case LFUN_BUFFER_VIEW: {
614 string format = to_utf8(cmd.argument());
615 if (cmd.argument().empty())
616 format = buf->getDefaultOutputFormat();
617 typedef vector<Format const *> Formats;
619 formats = buf->exportableFormats(true);
620 Formats::const_iterator fit = formats.begin();
621 Formats::const_iterator end = formats.end();
623 for (; fit != end ; ++fit) {
624 if ((*fit)->name() == format)
630 case LFUN_WORD_FIND_FORWARD:
631 case LFUN_WORD_FIND_BACKWARD:
632 case LFUN_WORD_FINDADV:
633 case LFUN_COMMAND_PREFIX:
634 case LFUN_COMMAND_EXECUTE:
636 case LFUN_META_PREFIX:
637 case LFUN_BUFFER_CLOSE:
638 case LFUN_BUFFER_IMPORT:
639 case LFUN_BUFFER_AUTO_SAVE:
640 case LFUN_RECONFIGURE:
642 case LFUN_DROP_LAYOUTS_CHOICE:
644 case LFUN_SERVER_GET_FILENAME:
645 case LFUN_SERVER_NOTIFY:
646 case LFUN_SERVER_GOTO_FILE_ROW:
647 case LFUN_DIALOG_HIDE:
648 case LFUN_DIALOG_DISCONNECT_INSET:
649 case LFUN_BUFFER_CHILD_OPEN:
650 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
651 case LFUN_KEYMAP_OFF:
652 case LFUN_KEYMAP_PRIMARY:
653 case LFUN_KEYMAP_SECONDARY:
654 case LFUN_KEYMAP_TOGGLE:
656 case LFUN_BUFFER_EXPORT_CUSTOM:
657 case LFUN_PREFERENCES_SAVE:
659 case LFUN_INSET_EDIT:
660 case LFUN_BUFFER_LANGUAGE:
661 case LFUN_TEXTCLASS_APPLY:
662 case LFUN_TEXTCLASS_LOAD:
663 case LFUN_BUFFER_SAVE_AS_DEFAULT:
664 case LFUN_BUFFER_PARAMS_APPLY:
665 case LFUN_LAYOUT_MODULES_CLEAR:
666 case LFUN_LAYOUT_MODULE_ADD:
667 case LFUN_LAYOUT_RELOAD:
668 case LFUN_LYXRC_APPLY:
669 case LFUN_BUFFER_NEXT:
670 case LFUN_BUFFER_PREVIOUS:
671 // these are handled in our dispatch()
679 if (theApp()->getStatus(cmd, flag))
682 // Does the view know something?
687 if (lv->getStatus(cmd, flag))
690 // If we do not have a BufferView, then other functions are disabled
696 // Is this a function that acts on inset at point?
697 Inset * inset = view()->cursor().nextInset();
698 if (lyxaction.funcHasFlag(cmd.action, LyXAction::AtPoint)
699 && inset && inset->getStatus(view()->cursor(), cmd, flag))
702 bool decided = getLocalStatus(view()->cursor(), cmd, flag);
704 // try the BufferView
705 decided = view()->getStatus(cmd, flag);
708 view()->buffer().getStatus(cmd, flag);
712 flag.setEnabled(false);
714 // Can we use a readonly buffer?
715 if (buf && buf->isReadonly()
716 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
717 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
718 flag.message(from_utf8(N_("Document is read-only")));
719 flag.setEnabled(false);
722 // Are we in a DELETED change-tracking region?
724 && lookupChangeType(view()->cursor(), true) == Change::DELETED
725 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
726 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
727 flag.message(from_utf8(N_("This portion of the document is deleted.")));
728 flag.setEnabled(false);
731 // the default error message if we disable the command
732 if (!flag.enabled() && flag.message().empty())
733 flag.message(from_utf8(N_("Command disabled")));
739 bool LyXFunc::ensureBufferClean(BufferView * bv)
741 Buffer & buf = bv->buffer();
742 if (buf.isClean() && !buf.isUnnamed())
745 docstring const file = buf.fileName().displayName(30);
748 if (!buf.isUnnamed()) {
749 text = bformat(_("The document %1$s has unsaved "
750 "changes.\n\nDo you want to save "
751 "the document?"), file);
752 title = _("Save changed document?");
755 text = bformat(_("The document %1$s has not been "
756 "saved yet.\n\nDo you want to save "
757 "the document?"), file);
758 title = _("Save new document?");
760 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
763 lyx_view_->dispatch(FuncRequest(LFUN_BUFFER_WRITE));
765 return buf.isClean() && !buf.isUnnamed();
771 bool loadLayoutFile(string const & name, string const & buf_path)
773 if (!LayoutFileList::get().haveClass(name)) {
774 lyxerr << "Document class \"" << name
775 << "\" does not exist."
780 LayoutFile & tc = LayoutFileList::get()[name];
781 if (!tc.load(buf_path)) {
782 docstring s = bformat(_("The document class %1$s "
783 "could not be loaded."), from_utf8(name));
784 Alert::error(_("Could not load class"), s);
791 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
796 void LyXFunc::dispatch(FuncRequest const & cmd)
798 string const argument = to_utf8(cmd.argument());
799 FuncCode const action = cmd.action;
801 LYXERR(Debug::ACTION, "\nLyXFunc::dispatch: cmd: " << cmd);
802 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
804 // we have not done anything wrong yet.
806 dispatch_buffer.erase();
808 // redraw the screen at the end (first of the two drawing steps).
809 //This is done unless explicitely requested otherwise
810 Update::flags updateFlags = Update::FitCursor;
812 FuncStatus const flag = getStatus(cmd);
813 if (!flag.enabled()) {
814 // We cannot use this function here
815 LYXERR(Debug::ACTION, "LyXFunc::dispatch: "
816 << lyxaction.getActionName(action)
817 << " [" << action << "] is disabled at this location");
818 setErrorMessage(flag.message());
820 lyx_view_->restartCursor();
822 Buffer * buffer = lyx_view_ ? lyx_view_->buffer() : 0;
825 case LFUN_WORD_FIND_FORWARD:
826 case LFUN_WORD_FIND_BACKWARD: {
827 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
828 static docstring last_search;
829 docstring searched_string;
831 if (!cmd.argument().empty()) {
832 last_search = cmd.argument();
833 searched_string = cmd.argument();
835 searched_string = last_search;
838 if (searched_string.empty())
841 bool const fw = action == LFUN_WORD_FIND_FORWARD;
842 docstring const data =
843 find2string(searched_string, true, false, fw);
844 find(view(), FuncRequest(LFUN_WORD_FIND, data));
848 case LFUN_COMMAND_PREFIX:
849 LASSERT(lyx_view_, /**/);
850 lyx_view_->message(keyseq.printOptions(true));
854 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
856 meta_fake_bit = NoModifier;
858 // cancel any selection
859 dispatch(FuncRequest(LFUN_MARK_OFF));
860 setMessage(from_ascii(N_("Cancel")));
863 case LFUN_META_PREFIX:
864 meta_fake_bit = AltModifier;
865 setMessage(keyseq.print(KeySequence::ForGui));
868 case LFUN_BUFFER_TOGGLE_READ_ONLY: {
869 LASSERT(lyx_view_ && lyx_view_->currentBufferView() && buffer, /**/);
870 if (buffer->lyxvc().inUse())
871 buffer->lyxvc().toggleReadOnly();
873 buffer->setReadonly(!buffer->isReadonly());
877 // --- Menus -----------------------------------------------
878 case LFUN_BUFFER_CLOSE:
879 lyx_view_->closeBuffer();
881 updateFlags = Update::None;
884 case LFUN_BUFFER_CLOSE_ALL:
885 lyx_view_->closeBufferAll();
887 updateFlags = Update::None;
890 case LFUN_BUFFER_RELOAD: {
891 LASSERT(lyx_view_ && buffer, /**/);
892 docstring const file = makeDisplayPath(buffer->absFileName(), 20);
893 docstring text = bformat(_("Any changes will be lost. Are you sure "
894 "you want to revert to the saved version of the document %1$s?"), file);
895 int const ret = Alert::prompt(_("Revert to saved document?"),
896 text, 1, 1, _("&Revert"), _("&Cancel"));
903 case LFUN_BUFFER_UPDATE: {
904 LASSERT(lyx_view_ && lyx_view_->documentBuffer(), /**/);
905 Buffer * doc_buffer = lyx_view_->documentBuffer();
906 string format = argument;
907 if (argument.empty())
908 format = doc_buffer->getDefaultOutputFormat();
909 doc_buffer->doExport(format, true);
913 case LFUN_BUFFER_VIEW: {
914 LASSERT(lyx_view_ && lyx_view_->documentBuffer(), /**/);
915 Buffer * doc_buffer = lyx_view_->documentBuffer();
916 string format = argument;
917 if (argument.empty())
918 format = doc_buffer->getDefaultOutputFormat();
919 doc_buffer->preview(format);
923 case LFUN_MASTER_BUFFER_UPDATE: {
924 LASSERT(lyx_view_ && lyx_view_->documentBuffer() && lyx_view_->documentBuffer()->masterBuffer(), /**/);
925 Buffer * doc_buffer = lyx_view_->documentBuffer();
926 string format = argument;
927 if (argument.empty())
928 format = doc_buffer->masterBuffer()->getDefaultOutputFormat();
929 doc_buffer->masterBuffer()->doExport(format, true);
933 case LFUN_MASTER_BUFFER_VIEW: {
934 LASSERT(lyx_view_ && lyx_view_->documentBuffer() && lyx_view_->documentBuffer()->masterBuffer(), /**/);
935 Buffer * doc_buffer = lyx_view_->documentBuffer();
936 string format = argument;
937 if (argument.empty())
938 format = doc_buffer->masterBuffer()->getDefaultOutputFormat();
939 doc_buffer->masterBuffer()->preview(format);
943 case LFUN_BUILD_PROGRAM:
944 LASSERT(lyx_view_ && buffer, /**/);
945 buffer->doExport("program", true);
948 case LFUN_BUFFER_CHKTEX:
949 LASSERT(lyx_view_ && buffer, /**/);
953 case LFUN_BUFFER_EXPORT:
954 LASSERT(lyx_view_ && buffer, /**/);
955 if (argument == "custom")
956 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"));
958 buffer->doExport(argument, false);
961 case LFUN_BUFFER_EXPORT_CUSTOM: {
962 LASSERT(lyx_view_ && buffer, /**/);
964 string command = split(argument, format_name, ' ');
965 Format const * format = formats.getFormat(format_name);
967 lyxerr << "Format \"" << format_name
968 << "\" not recognized!"
973 // The name of the file created by the conversion process
976 // Output to filename
977 if (format->name() == "lyx") {
978 string const latexname = buffer->latexName(false);
979 filename = changeExtension(latexname,
980 format->extension());
981 filename = addName(buffer->temppath(), filename);
983 if (!buffer->writeFile(FileName(filename)))
987 buffer->doExport(format_name, true, filename);
990 // Substitute $$FName for filename
991 if (!contains(command, "$$FName"))
992 command = "( " + command + " ) < $$FName";
993 command = subst(command, "$$FName", filename);
995 // Execute the command in the background
997 call.startscript(Systemcall::DontWait, command);
1001 // FIXME: There is need for a command-line import.
1003 case LFUN_BUFFER_IMPORT:
1008 case LFUN_BUFFER_AUTO_SAVE:
1012 case LFUN_RECONFIGURE:
1013 // argument is any additional parameter to the configure.py command
1014 reconfigure(lyx_view_, argument);
1017 case LFUN_HELP_OPEN: {
1019 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1020 string const arg = argument;
1022 setErrorMessage(from_utf8(N_("Missing argument")));
1025 FileName fname = i18nLibFileSearch("doc", arg, "lyx");
1027 fname = i18nLibFileSearch("examples", arg, "lyx");
1029 if (fname.empty()) {
1030 lyxerr << "LyX: unable to find documentation file `"
1031 << arg << "'. Bad installation?" << endl;
1034 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1035 makeDisplayPath(fname.absFilename())));
1036 Buffer * buf = lyx_view_->loadDocument(fname, false);
1038 buf->updateLabels();
1039 lyx_view_->setBuffer(buf);
1040 buf->errors("Parse");
1042 updateFlags = Update::None;
1046 // --- version control -------------------------------
1047 case LFUN_VC_REGISTER:
1048 LASSERT(lyx_view_ && buffer, /**/);
1049 if (!ensureBufferClean(view()))
1051 if (!buffer->lyxvc().inUse()) {
1052 if (buffer->lyxvc().registrer())
1055 updateFlags = Update::Force;
1058 case LFUN_VC_CHECK_IN:
1059 LASSERT(lyx_view_ && buffer, /**/);
1060 if (!ensureBufferClean(view()))
1062 if (buffer->lyxvc().inUse()
1063 && !buffer->isReadonly()) {
1064 setMessage(from_utf8(buffer->lyxvc().checkIn()));
1069 case LFUN_VC_CHECK_OUT:
1070 LASSERT(lyx_view_ && buffer, /**/);
1071 if (!ensureBufferClean(view()))
1073 if (buffer->lyxvc().inUse()) {
1074 setMessage(from_utf8(buffer->lyxvc().checkOut()));
1079 case LFUN_VC_LOCKING_TOGGLE:
1080 LASSERT(lyx_view_ && buffer, /**/);
1081 if (!ensureBufferClean(view()) || buffer->isReadonly())
1083 if (buffer->lyxvc().inUse()) {
1084 string res = buffer->lyxvc().lockingToggle();
1086 frontend::Alert::error(_("Revision control error."),
1087 _("Error when setting the locking property."));
1089 setMessage(from_utf8(res));
1095 case LFUN_VC_REVERT:
1096 LASSERT(lyx_view_ && buffer, /**/);
1097 buffer->lyxvc().revert();
1101 case LFUN_VC_UNDO_LAST:
1102 LASSERT(lyx_view_ && buffer, /**/);
1103 buffer->lyxvc().undoLast();
1107 // --- lyxserver commands ----------------------------
1108 case LFUN_SERVER_GET_FILENAME:
1109 LASSERT(lyx_view_ && buffer, /**/);
1110 setMessage(from_utf8(buffer->absFileName()));
1111 LYXERR(Debug::INFO, "FNAME["
1112 << buffer->absFileName() << ']');
1115 case LFUN_SERVER_NOTIFY:
1116 dispatch_buffer = keyseq.print(KeySequence::Portable);
1117 theServer().notifyClient(to_utf8(dispatch_buffer));
1120 case LFUN_SERVER_GOTO_FILE_ROW: {
1121 LASSERT(lyx_view_, /**/);
1124 istringstream is(argument);
1125 is >> file_name >> row;
1126 file_name = os::internal_path(file_name);
1128 bool loaded = false;
1129 string const abstmp = package().temp_dir().absFilename();
1130 string const realtmp = package().temp_dir().realPath();
1131 // We have to use os::path_prefix_is() here, instead of
1132 // simply prefixIs(), because the file name comes from
1133 // an external application and may need case adjustment.
1134 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
1135 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
1136 // Needed by inverse dvi search. If it is a file
1137 // in tmpdir, call the apropriated function.
1138 // If tmpdir is a symlink, we may have the real
1139 // path passed back, so we correct for that.
1140 if (!prefixIs(file_name, abstmp))
1141 file_name = subst(file_name, realtmp, abstmp);
1142 buf = theBufferList().getBufferFromTmp(file_name);
1144 // Must replace extension of the file to be .lyx
1145 // and get full path
1146 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1147 // Either change buffer or load the file
1148 if (theBufferList().exists(s))
1149 buf = theBufferList().getBuffer(s);
1150 else if (s.exists()) {
1151 buf = lyx_view_->loadDocument(s);
1154 lyx_view_->message(bformat(
1155 _("File does not exist: %1$s"),
1156 makeDisplayPath(file_name)));
1160 updateFlags = Update::None;
1164 buf->updateLabels();
1165 lyx_view_->setBuffer(buf);
1166 view()->setCursorFromRow(row);
1168 buf->errors("Parse");
1169 updateFlags = Update::FitCursor;
1174 case LFUN_DIALOG_SHOW_NEW_INSET: {
1175 LASSERT(lyx_view_, /**/);
1176 string const name = cmd.getArg(0);
1177 InsetCode code = insetCode(name);
1178 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1179 bool insetCodeOK = true;
1186 case NOMENCL_PRINT_CODE:
1189 case HYPERLINK_CODE: {
1190 InsetCommandParams p(code);
1191 data = InsetCommand::params2string(name, p);
1194 case INCLUDE_CODE: {
1195 // data is the include type: one of "include",
1196 // "input", "verbatiminput" or "verbatiminput*"
1198 // default type is requested
1200 InsetCommandParams p(INCLUDE_CODE, data);
1201 data = InsetCommand::params2string("include", p);
1205 // \c data == "Boxed" || "Frameless" etc
1206 InsetBoxParams p(data);
1207 data = InsetBox::params2string(p);
1211 InsetBranchParams p;
1212 data = InsetBranch::params2string(p);
1216 InsetCommandParams p(CITE_CODE);
1217 data = InsetCommand::params2string(name, p);
1221 data = InsetERT::params2string(InsetCollapsable::Open);
1224 case EXTERNAL_CODE: {
1225 InsetExternalParams p;
1226 data = InsetExternal::params2string(p, *buffer);
1231 data = InsetFloat::params2string(p);
1234 case LISTINGS_CODE: {
1235 InsetListingsParams p;
1236 data = InsetListings::params2string(p);
1239 case GRAPHICS_CODE: {
1240 InsetGraphicsParams p;
1241 data = InsetGraphics::params2string(p, *buffer);
1246 data = InsetNote::params2string(p);
1249 case PHANTOM_CODE: {
1250 InsetPhantomParams p;
1251 data = InsetPhantom::params2string(p);
1256 data = InsetSpace::params2string(p);
1261 data = InsetVSpace::params2string(space);
1266 data = InsetWrap::params2string(p);
1270 lyxerr << "Inset type '" << name <<
1271 "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" << endl;
1272 insetCodeOK = false;
1274 } // end switch(code)
1276 dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data));
1280 case LFUN_CITATION_INSERT: {
1281 LASSERT(lyx_view_, /**/);
1282 if (!argument.empty()) {
1283 // we can have one optional argument, delimited by '|'
1284 // citation-insert <key>|<text_before>
1285 // this should be enhanced to also support text_after
1286 // and citation style
1287 string arg = argument;
1289 if (contains(argument, "|")) {
1290 arg = token(argument, '|', 0);
1291 opt1 = token(argument, '|', 1);
1293 InsetCommandParams icp(CITE_CODE);
1294 icp["key"] = from_utf8(arg);
1296 icp["before"] = from_utf8(opt1);
1297 string icstr = InsetCommand::params2string("citation", icp);
1298 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1301 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1305 case LFUN_BUFFER_CHILD_OPEN: {
1306 LASSERT(lyx_view_ && buffer, /**/);
1307 FileName filename = makeAbsPath(argument, buffer->filePath());
1308 view()->saveBookmark(false);
1310 bool parsed = false;
1311 if (theBufferList().exists(filename)) {
1312 child = theBufferList().getBuffer(filename);
1314 setMessage(bformat(_("Opening child document %1$s..."),
1315 makeDisplayPath(filename.absFilename())));
1316 child = lyx_view_->loadDocument(filename, false);
1320 // Set the parent name of the child document.
1321 // This makes insertion of citations and references in the child work,
1322 // when the target is in the parent or another child document.
1323 child->setParent(buffer);
1324 child->masterBuffer()->updateLabels();
1325 lyx_view_->setBuffer(child);
1327 child->errors("Parse");
1330 // If a screen update is required (in case where auto_open is false),
1331 // setBuffer() would have taken care of it already. Otherwise we shall
1332 // reset the update flag because it can cause a circular problem.
1334 updateFlags = Update::None;
1338 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
1339 LASSERT(lyx_view_, /**/);
1340 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1343 case LFUN_KEYMAP_OFF:
1344 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1345 lyx_view_->currentBufferView()->getIntl().keyMapOn(false);
1348 case LFUN_KEYMAP_PRIMARY:
1349 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1350 lyx_view_->currentBufferView()->getIntl().keyMapPrim();
1353 case LFUN_KEYMAP_SECONDARY:
1354 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1355 lyx_view_->currentBufferView()->getIntl().keyMapSec();
1358 case LFUN_KEYMAP_TOGGLE:
1359 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1360 lyx_view_->currentBufferView()->getIntl().toggleKeyMap();
1366 string rest = split(argument, countstr, ' ');
1367 istringstream is(countstr);
1370 //lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1371 for (int i = 0; i < count; ++i)
1372 dispatch(lyxaction.lookupFunc(rest));
1376 case LFUN_COMMAND_SEQUENCE: {
1377 // argument contains ';'-terminated commands
1378 string arg = argument;
1379 if (theBufferList().isLoaded(buffer))
1380 buffer->undo().beginUndoGroup();
1381 while (!arg.empty()) {
1383 arg = split(arg, first, ';');
1384 FuncRequest func(lyxaction.lookupFunc(first));
1385 func.origin = cmd.origin;
1388 if (theBufferList().isLoaded(buffer))
1389 buffer->undo().endUndoGroup();
1393 case LFUN_COMMAND_ALTERNATIVES: {
1394 // argument contains ';'-terminated commands
1395 string arg = argument;
1396 while (!arg.empty()) {
1398 arg = split(arg, first, ';');
1399 FuncRequest func(lyxaction.lookupFunc(first));
1400 func.origin = cmd.origin;
1401 FuncStatus stat = getStatus(func);
1402 if (stat.enabled()) {
1412 if (theTopLevelCmdDef().lock(argument, func)) {
1413 func.origin = cmd.origin;
1415 theTopLevelCmdDef().release(argument);
1417 if (func.action == LFUN_UNKNOWN_ACTION) {
1418 // unknown command definition
1419 lyxerr << "Warning: unknown command definition `"
1423 // recursion detected
1424 lyxerr << "Warning: Recursion in the command definition `"
1425 << argument << "' detected"
1432 case LFUN_PREFERENCES_SAVE: {
1433 lyxrc.write(makeAbsPath("preferences",
1434 package().user_support().absFilename()),
1440 LASSERT(lyx_view_, /**/);
1441 lyx_view_->message(from_utf8(argument));
1444 case LFUN_BUFFER_LANGUAGE: {
1445 LASSERT(lyx_view_, /**/);
1446 Language const * oldL = buffer->params().language;
1447 Language const * newL = languages.getLanguage(argument);
1448 if (!newL || oldL == newL)
1451 if (oldL->rightToLeft() == newL->rightToLeft()
1452 && !buffer->isMultiLingual())
1453 buffer->changeLanguage(oldL, newL);
1457 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1458 string const fname =
1459 addName(addPath(package().user_support().absFilename(), "templates/"),
1461 Buffer defaults(fname);
1463 istringstream ss(argument);
1466 int const unknown_tokens = defaults.readHeader(lex);
1468 if (unknown_tokens != 0) {
1469 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1470 << unknown_tokens << " unknown token"
1471 << (unknown_tokens == 1 ? "" : "s")
1475 if (defaults.writeFile(FileName(defaults.absFileName())))
1476 setMessage(bformat(_("Document defaults saved in %1$s"),
1477 makeDisplayPath(fname)));
1479 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1483 case LFUN_BUFFER_PARAMS_APPLY: {
1484 LASSERT(lyx_view_, /**/);
1486 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1487 Cursor & cur = view()->cursor();
1488 cur.recordUndoFullDocument();
1490 istringstream ss(argument);
1493 int const unknown_tokens = buffer->readHeader(lex);
1495 if (unknown_tokens != 0) {
1496 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1497 << unknown_tokens << " unknown token"
1498 << (unknown_tokens == 1 ? "" : "s")
1502 updateLayout(oldClass, buffer);
1504 updateFlags = Update::Force | Update::FitCursor;
1505 // We are most certainly here because of a change in the document
1506 // It is then better to make sure that all dialogs are in sync with
1507 // current document settings. LyXView::restartCursor() achieve this.
1508 lyx_view_->restartCursor();
1512 case LFUN_LAYOUT_MODULES_CLEAR: {
1513 LASSERT(lyx_view_, /**/);
1514 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1515 view()->cursor().recordUndoFullDocument();
1516 buffer->params().clearLayoutModules();
1517 buffer->params().makeDocumentClass();
1518 updateLayout(oldClass, buffer);
1519 updateFlags = Update::Force | Update::FitCursor;
1523 case LFUN_LAYOUT_MODULE_ADD: {
1524 LASSERT(lyx_view_, /**/);
1525 BufferParams const & params = buffer->params();
1526 if (!params.moduleCanBeAdded(argument)) {
1527 LYXERR0("Module `" << argument <<
1528 "' cannot be added due to failed requirements or "
1529 "conflicts with installed modules.");
1532 DocumentClass const * const oldClass = params.documentClassPtr();
1533 view()->cursor().recordUndoFullDocument();
1534 buffer->params().addLayoutModule(argument);
1535 buffer->params().makeDocumentClass();
1536 updateLayout(oldClass, buffer);
1537 updateFlags = Update::Force | Update::FitCursor;
1541 case LFUN_TEXTCLASS_APPLY: {
1542 LASSERT(lyx_view_, /**/);
1544 if (!loadLayoutFile(argument, buffer->temppath()) &&
1545 !loadLayoutFile(argument, buffer->filePath()))
1548 LayoutFile const * old_layout = buffer->params().baseClass();
1549 LayoutFile const * new_layout = &(LayoutFileList::get()[argument]);
1551 if (old_layout == new_layout)
1555 //Save the old, possibly modular, layout for use in conversion.
1556 DocumentClass const * const oldDocClass = buffer->params().documentClassPtr();
1557 view()->cursor().recordUndoFullDocument();
1558 buffer->params().setBaseClass(argument);
1559 buffer->params().makeDocumentClass();
1560 updateLayout(oldDocClass, buffer);
1561 updateFlags = Update::Force | Update::FitCursor;
1565 case LFUN_LAYOUT_RELOAD: {
1566 LASSERT(lyx_view_, /**/);
1567 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1568 LayoutFileIndex bc = buffer->params().baseClassID();
1569 LayoutFileList::get().reset(bc);
1570 buffer->params().setBaseClass(bc);
1571 buffer->params().makeDocumentClass();
1572 updateLayout(oldClass, buffer);
1573 updateFlags = Update::Force | Update::FitCursor;
1577 case LFUN_TEXTCLASS_LOAD:
1578 loadLayoutFile(argument, buffer->temppath()) ||
1579 loadLayoutFile(argument, buffer->filePath());
1582 case LFUN_LYXRC_APPLY: {
1583 // reset active key sequences, since the bindings
1584 // are updated (bug 6064)
1586 LyXRC const lyxrc_orig = lyxrc;
1588 istringstream ss(argument);
1589 bool const success = lyxrc.read(ss) == 0;
1592 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1593 << "Unable to read lyxrc data"
1598 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1602 theApp()->resetGui();
1604 /// We force the redraw in any case because there might be
1605 /// some screen font changes.
1606 /// FIXME: only the current view will be updated. the Gui
1607 /// class is able to furnish the list of views.
1608 updateFlags = Update::Force;
1612 case LFUN_BOOKMARK_GOTO:
1613 // go to bookmark, open unopened file and switch to buffer if necessary
1614 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1615 updateFlags = Update::FitCursor;
1618 case LFUN_BOOKMARK_CLEAR:
1619 theSession().bookmarks().clear();
1622 case LFUN_VC_COMMAND: {
1623 string flag = cmd.getArg(0);
1624 if (buffer && contains(flag, 'R') && !ensureBufferClean(view()))
1627 if (contains(flag, 'M'))
1628 if (!Alert::askForText(message, _("LyX VC: Log Message")))
1631 string path = cmd.getArg(1);
1632 if (contains(path, "$$p") && buffer)
1633 path = subst(path, "$$p", buffer->filePath());
1634 LYXERR(Debug::LYXVC, "Directory: " << path);
1636 if (!pp.isReadableDirectory()) {
1637 lyxerr << _("Directory is not accessible.") << endl;
1640 support::PathChanger p(pp);
1642 string command = cmd.getArg(2);
1643 if (command.empty())
1646 command = subst(command, "$$i", buffer->absFileName());
1647 command = subst(command, "$$p", buffer->filePath());
1649 command = subst(command, "$$m", to_utf8(message));
1650 LYXERR(Debug::LYXVC, "Command: " << command);
1652 one.startscript(Systemcall::Wait, command);
1656 if (contains(flag, 'I'))
1657 buffer->markDirty();
1658 if (contains(flag, 'R'))
1665 LASSERT(theApp(), /**/);
1666 // Let the frontend dispatch its own actions.
1667 if (theApp()->dispatch(cmd))
1668 // Nothing more to do.
1671 // Everything below is only for active lyx_view_
1675 // Start an undo group. This may be needed for
1676 // some stuff like inset-apply on labels.
1677 if (theBufferList().isLoaded(buffer))
1678 buffer->undo().beginUndoGroup();
1680 // Let the current LyXView dispatch its own actions.
1681 if (lyx_view_->dispatch(cmd)) {
1682 if (lyx_view_->currentBufferView()) {
1683 updateFlags = lyx_view_->currentBufferView()->cursor().result().update();
1684 if (theBufferList().isLoaded(buffer))
1685 buffer->undo().endUndoGroup();
1690 LASSERT(lyx_view_->currentBufferView(), /**/);
1692 // Let the current BufferView dispatch its own actions.
1693 if (view()->dispatch(cmd)) {
1694 // The BufferView took care of its own updates if needed.
1695 updateFlags = Update::None;
1696 if (theBufferList().isLoaded(buffer))
1697 buffer->undo().endUndoGroup();
1701 // OK, so try the Buffer itself
1703 view()->buffer().dispatch(cmd, dr);
1704 if (dr.dispatched()) {
1705 updateFlags = dr.update();
1709 // Is this a function that acts on inset at point?
1710 Inset * inset = view()->cursor().nextInset();
1711 if (lyxaction.funcHasFlag(action, LyXAction::AtPoint)
1713 view()->cursor().result().dispatched(true);
1714 view()->cursor().result().update(Update::FitCursor | Update::Force);
1715 FuncRequest tmpcmd = cmd;
1716 inset->dispatch(view()->cursor(), tmpcmd);
1717 if (view()->cursor().result().dispatched()) {
1718 updateFlags = view()->cursor().result().update();
1723 // Let the current Cursor dispatch its own actions.
1724 Cursor old = view()->cursor();
1725 view()->cursor().getPos(cursorPosBeforeDispatchX_,
1726 cursorPosBeforeDispatchY_);
1727 view()->cursor().dispatch(cmd);
1729 // notify insets we just left
1730 if (view()->cursor() != old) {
1732 bool badcursor = notifyCursorLeavesOrEnters(old, view()->cursor());
1734 view()->cursor().fixIfBroken();
1737 if (theBufferList().isLoaded(buffer))
1738 buffer->undo().endUndoGroup();
1740 // update completion. We do it here and not in
1741 // processKeySym to avoid another redraw just for a
1742 // changed inline completion
1743 if (cmd.origin == FuncRequest::KEYBOARD) {
1744 if (cmd.action == LFUN_SELF_INSERT
1745 || (cmd.action == LFUN_ERT_INSERT && view()->cursor().inMathed()))
1746 lyx_view_->updateCompletion(view()->cursor(), true, true);
1747 else if (cmd.action == LFUN_CHAR_DELETE_BACKWARD)
1748 lyx_view_->updateCompletion(view()->cursor(), false, true);
1750 lyx_view_->updateCompletion(view()->cursor(), false, false);
1753 updateFlags = view()->cursor().result().update();
1756 // if we executed a mutating lfun, mark the buffer as dirty
1757 if (theBufferList().isLoaded(buffer) && flag.enabled()
1758 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1759 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1760 buffer->markDirty();
1762 if (lyx_view_ && lyx_view_->buffer()) {
1763 // BufferView::update() updates the ViewMetricsInfo and
1764 // also initializes the position cache for all insets in
1765 // (at least partially) visible top-level paragraphs.
1766 // We will redraw the screen only if needed.
1767 view()->processUpdateFlags(updateFlags);
1769 // Do we have a selection?
1770 theSelection().haveSelection(view()->cursor().selection());
1773 lyx_view_->restartCursor();
1777 // Some messages may already be translated, so we cannot use _()
1778 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1783 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1785 const bool verbose = (cmd.origin == FuncRequest::MENU
1786 || cmd.origin == FuncRequest::TOOLBAR
1787 || cmd.origin == FuncRequest::COMMANDBUFFER);
1789 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1790 LYXERR(Debug::ACTION, "dispatch msg is " << to_utf8(msg));
1792 lyx_view_->message(msg);
1796 docstring dispatch_msg = msg;
1797 if (!dispatch_msg.empty())
1798 dispatch_msg += ' ';
1800 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1802 bool argsadded = false;
1804 if (!cmd.argument().empty()) {
1805 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1806 comname += ' ' + cmd.argument();
1811 docstring const shortcuts = theTopLevelKeymap().printBindings(cmd, KeySequence::ForGui);
1813 if (!shortcuts.empty())
1814 comname += ": " + shortcuts;
1815 else if (!argsadded && !cmd.argument().empty())
1816 comname += ' ' + cmd.argument();
1818 if (!comname.empty()) {
1819 comname = rtrim(comname);
1820 dispatch_msg += '(' + rtrim(comname) + ')';
1823 LYXERR(Debug::ACTION, "verbose dispatch msg " << to_utf8(dispatch_msg));
1824 if (!dispatch_msg.empty())
1825 lyx_view_->message(dispatch_msg);
1829 void LyXFunc::reloadBuffer()
1831 FileName filename = lyx_view_->buffer()->fileName();
1832 // The user has already confirmed that the changes, if any, should
1833 // be discarded. So we just release the Buffer and don't call closeBuffer();
1834 theBufferList().release(lyx_view_->buffer());
1835 // if the lyx_view_ has been destroyed, create a new one
1837 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1838 Buffer * buf = lyx_view_->loadDocument(filename);
1839 docstring const disp_fn = makeDisplayPath(filename.absFilename());
1842 buf->updateLabels();
1843 lyx_view_->setBuffer(buf);
1844 buf->errors("Parse");
1845 str = bformat(_("Document %1$s reloaded."), disp_fn);
1847 str = bformat(_("Could not reload document %1$s"), disp_fn);
1849 lyx_view_->message(str);
1852 // Each "lyx_view_" should have it's own message method. lyxview and
1853 // the minibuffer would use the minibuffer, but lyxserver would
1854 // send an ERROR signal to its client. Alejandro 970603
1855 // This function is bit problematic when it comes to NLS, to make the
1856 // lyx servers client be language indepenent we must not translate
1857 // strings sent to this func.
1858 void LyXFunc::setErrorMessage(docstring const & m) const
1860 dispatch_buffer = m;
1865 void LyXFunc::setMessage(docstring const & m) const
1867 dispatch_buffer = m;
1871 docstring LyXFunc::viewStatusMessage()
1873 // When meta-fake key is pressed, show the key sequence so far + "M-".
1875 return keyseq.print(KeySequence::ForGui) + "M-";
1877 // Else, when a non-complete key sequence is pressed,
1878 // show the available options.
1879 if (keyseq.length() > 0 && !keyseq.deleted())
1880 return keyseq.printOptions(true);
1882 LASSERT(lyx_view_, /**/);
1883 if (!lyx_view_->buffer())
1884 return _("Welcome to LyX!");
1886 return view()->cursor().currentState();
1890 BufferView * LyXFunc::view() const
1892 LASSERT(lyx_view_, /**/);
1893 return lyx_view_->currentBufferView();
1897 bool LyXFunc::wasMetaKey() const
1899 return (meta_fake_bit != NoModifier);
1903 void LyXFunc::updateLayout(DocumentClass const * const oldlayout, Buffer * buf)
1905 lyx_view_->message(_("Converting document to new document class..."));
1907 StableDocIterator backcur(view()->cursor());
1908 ErrorList & el = buf->errorList("Class Switch");
1909 cap::switchBetweenClasses(
1910 oldlayout, buf->params().documentClassPtr(),
1911 static_cast<InsetText &>(buf->inset()), el);
1913 view()->setCursor(backcur.asDocIterator(buf));
1915 buf->errors("Class Switch");
1916 buf->updateLabels();
1922 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1924 // Why the switch you might ask. It is a trick to ensure that all
1925 // the elements in the LyXRCTags enum is handled. As you can see
1926 // there are no breaks at all. So it is just a huge fall-through.
1927 // The nice thing is that we will get a warning from the compiler
1928 // if we forget an element.
1929 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1931 case LyXRC::RC_ACCEPT_COMPOUND:
1932 case LyXRC::RC_ALT_LANG:
1933 case LyXRC::RC_PLAINTEXT_LINELEN:
1934 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
1935 case LyXRC::RC_AUTOCORRECTION_MATH:
1936 case LyXRC::RC_AUTOREGIONDELETE:
1937 case LyXRC::RC_AUTORESET_OPTIONS:
1938 case LyXRC::RC_AUTOSAVE:
1939 case LyXRC::RC_AUTO_NUMBER:
1940 case LyXRC::RC_BACKUPDIR_PATH:
1941 case LyXRC::RC_BIBTEX_ALTERNATIVES:
1942 case LyXRC::RC_BIBTEX_COMMAND:
1943 case LyXRC::RC_BINDFILE:
1944 case LyXRC::RC_CHECKLASTFILES:
1945 case LyXRC::RC_COMPLETION_CURSOR_TEXT:
1946 case LyXRC::RC_COMPLETION_INLINE_DELAY:
1947 case LyXRC::RC_COMPLETION_INLINE_DOTS:
1948 case LyXRC::RC_COMPLETION_INLINE_MATH:
1949 case LyXRC::RC_COMPLETION_INLINE_TEXT:
1950 case LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE:
1951 case LyXRC::RC_COMPLETION_POPUP_DELAY:
1952 case LyXRC::RC_COMPLETION_POPUP_MATH:
1953 case LyXRC::RC_COMPLETION_POPUP_TEXT:
1954 case LyXRC::RC_USELASTFILEPOS:
1955 case LyXRC::RC_LOADSESSION:
1956 case LyXRC::RC_CHKTEX_COMMAND:
1957 case LyXRC::RC_CONVERTER:
1958 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
1959 case LyXRC::RC_COPIER:
1960 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1961 case LyXRC::RC_SCROLL_BELOW_DOCUMENT:
1962 case LyXRC::RC_DATE_INSERT_FORMAT:
1963 case LyXRC::RC_DEFAULT_LANGUAGE:
1964 case LyXRC::RC_GUI_LANGUAGE:
1965 case LyXRC::RC_DEFAULT_PAPERSIZE:
1966 case LyXRC::RC_DEFAULT_VIEW_FORMAT:
1967 case LyXRC::RC_DEFFILE:
1968 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1969 case LyXRC::RC_DISPLAY_GRAPHICS:
1970 case LyXRC::RC_DOCUMENTPATH:
1971 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1972 FileName path(lyxrc_new.document_path);
1973 if (path.exists() && path.isDirectory())
1974 package().document_dir() = FileName(lyxrc.document_path);
1976 case LyXRC::RC_ESC_CHARS:
1977 case LyXRC::RC_EXAMPLEPATH:
1978 case LyXRC::RC_FONT_ENCODING:
1979 case LyXRC::RC_FORMAT:
1980 case LyXRC::RC_GROUP_LAYOUTS:
1981 case LyXRC::RC_HUNSPELLDIR_PATH:
1982 case LyXRC::RC_INDEX_ALTERNATIVES:
1983 case LyXRC::RC_INDEX_COMMAND:
1984 case LyXRC::RC_JBIBTEX_COMMAND:
1985 case LyXRC::RC_JINDEX_COMMAND:
1986 case LyXRC::RC_NOMENCL_COMMAND:
1987 case LyXRC::RC_INPUT:
1988 case LyXRC::RC_KBMAP:
1989 case LyXRC::RC_KBMAP_PRIMARY:
1990 case LyXRC::RC_KBMAP_SECONDARY:
1991 case LyXRC::RC_LABEL_INIT_LENGTH:
1992 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
1993 case LyXRC::RC_LANGUAGE_AUTO_END:
1994 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
1995 case LyXRC::RC_LANGUAGE_COMMAND_END:
1996 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
1997 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
1998 case LyXRC::RC_LANGUAGE_PACKAGE:
1999 case LyXRC::RC_LANGUAGE_USE_BABEL:
2000 case LyXRC::RC_MAC_LIKE_WORD_MOVEMENT:
2001 case LyXRC::RC_MACRO_EDIT_STYLE:
2002 case LyXRC::RC_MAKE_BACKUP:
2003 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2004 case LyXRC::RC_MOUSE_WHEEL_SPEED:
2005 case LyXRC::RC_NUMLASTFILES:
2006 case LyXRC::RC_PARAGRAPH_MARKERS:
2007 case LyXRC::RC_PATH_PREFIX:
2008 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2009 prependEnvPath("PATH", lyxrc.path_prefix);
2011 case LyXRC::RC_PERS_DICT:
2012 case LyXRC::RC_PREVIEW:
2013 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2014 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2015 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2016 case LyXRC::RC_PRINTCOPIESFLAG:
2017 case LyXRC::RC_PRINTER:
2018 case LyXRC::RC_PRINTEVENPAGEFLAG:
2019 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2020 case LyXRC::RC_PRINTFILEEXTENSION:
2021 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2022 case LyXRC::RC_PRINTODDPAGEFLAG:
2023 case LyXRC::RC_PRINTPAGERANGEFLAG:
2024 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2025 case LyXRC::RC_PRINTPAPERFLAG:
2026 case LyXRC::RC_PRINTREVERSEFLAG:
2027 case LyXRC::RC_PRINTSPOOL_COMMAND:
2028 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2029 case LyXRC::RC_PRINTTOFILE:
2030 case LyXRC::RC_PRINTTOPRINTER:
2031 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2032 case LyXRC::RC_PRINT_COMMAND:
2033 case LyXRC::RC_RTL_SUPPORT:
2034 case LyXRC::RC_SCREEN_DPI:
2035 case LyXRC::RC_SCREEN_FONT_ROMAN:
2036 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2037 case LyXRC::RC_SCREEN_FONT_SANS:
2038 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2039 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2040 case LyXRC::RC_SCREEN_FONT_SIZES:
2041 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2042 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2043 case LyXRC::RC_GEOMETRY_SESSION:
2044 case LyXRC::RC_SCREEN_ZOOM:
2045 case LyXRC::RC_SERVERPIPE:
2046 case LyXRC::RC_SET_COLOR:
2047 case LyXRC::RC_SHOW_BANNER:
2048 case LyXRC::RC_OPEN_BUFFERS_IN_TABS:
2049 case LyXRC::RC_SPELL_COMMAND:
2050 case LyXRC::RC_SPELLCHECKER:
2051 case LyXRC::RC_SPELLCHECK_CONTINUOUSLY:
2052 case LyXRC::RC_SPLITINDEX_COMMAND:
2053 case LyXRC::RC_TEMPDIRPATH:
2054 case LyXRC::RC_TEMPLATEPATH:
2055 case LyXRC::RC_TEX_ALLOWS_SPACES:
2056 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2057 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2058 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2060 case LyXRC::RC_THESAURUSDIRPATH:
2061 case LyXRC::RC_UIFILE:
2062 case LyXRC::RC_USER_EMAIL:
2063 case LyXRC::RC_USER_NAME:
2064 case LyXRC::RC_USETEMPDIR:
2065 case LyXRC::RC_USE_ALT_LANG:
2066 case LyXRC::RC_USE_CONVERTER_CACHE:
2067 case LyXRC::RC_USE_ESC_CHARS:
2068 case LyXRC::RC_USE_INP_ENC:
2069 case LyXRC::RC_USE_PERS_DICT:
2070 case LyXRC::RC_USE_TOOLTIP:
2071 case LyXRC::RC_USE_PIXMAP_CACHE:
2072 case LyXRC::RC_USE_SPELL_LIB:
2073 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2074 case LyXRC::RC_SORT_LAYOUTS:
2075 case LyXRC::RC_FULL_SCREEN_LIMIT:
2076 case LyXRC::RC_FULL_SCREEN_SCROLLBAR:
2077 case LyXRC::RC_FULL_SCREEN_MENUBAR:
2078 case LyXRC::RC_FULL_SCREEN_TABBAR:
2079 case LyXRC::RC_FULL_SCREEN_TOOLBARS:
2080 case LyXRC::RC_FULL_SCREEN_WIDTH:
2081 case LyXRC::RC_VISUAL_CURSOR:
2082 case LyXRC::RC_VIEWER:
2083 case LyXRC::RC_LAST: