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(),
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_->documentBufferView()
271 || lyx_view_->documentBufferView()->buffer().fileName() != tmp.filename) {
274 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
277 // moveToPosition try paragraph id first and then paragraph (pit, pos).
278 if (!view()->moveToPosition(tmp.bottom_pit, tmp.bottom_pos,
279 tmp.top_id, tmp.top_pos))
286 // Cursor jump succeeded!
287 Cursor const & cur = view()->cursor();
288 pit_type new_pit = cur.pit();
289 pos_type new_pos = cur.pos();
290 int new_id = cur.paragraph().id();
292 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
293 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
294 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos
295 || bm.top_id != new_id) {
296 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(
297 new_pit, new_pos, new_id);
302 void LyXFunc::processKeySym(KeySymbol const & keysym, KeyModifier state)
304 LYXERR(Debug::KEY, "KeySym is " << keysym.getSymbolName());
306 // Do nothing if we have nothing (JMarc)
307 if (!keysym.isOK()) {
308 LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
309 lyx_view_->restartCursor();
313 if (keysym.isModifier()) {
314 LYXERR(Debug::KEY, "isModifier true");
316 lyx_view_->restartCursor();
320 //Encoding const * encoding = view()->cursor().getEncoding();
321 //encoded_last_key = keysym.getISOEncoded(encoding ? encoding->name() : "");
322 // FIXME: encoded_last_key shadows the member variable of the same
323 // name. Is that intended?
324 char_type encoded_last_key = keysym.getUCSEncoded();
326 // Do a one-deep top-level lookup for
327 // cancel and meta-fake keys. RVDK_PATCH_5
328 cancel_meta_seq.reset();
330 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
331 LYXERR(Debug::KEY, "action first set to [" << func.action << ']');
333 // When not cancel or meta-fake, do the normal lookup.
334 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
335 // Mostly, meta_fake_bit = NoModifier. RVDK_PATCH_5.
336 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
337 // remove Caps Lock and Mod2 as a modifiers
338 func = keyseq.addkey(keysym, (state | meta_fake_bit));
339 LYXERR(Debug::KEY, "action now set to [" << func.action << ']');
342 // Dont remove this unless you know what you are doing.
343 meta_fake_bit = NoModifier;
345 // Can this happen now ?
346 if (func.action == LFUN_NOACTION)
347 func = FuncRequest(LFUN_COMMAND_PREFIX);
349 LYXERR(Debug::KEY, " Key [action=" << func.action << "]["
350 << keyseq.print(KeySequence::Portable) << ']');
352 // already here we know if it any point in going further
353 // why not return already here if action == -1 and
354 // num_bytes == 0? (Lgb)
356 if (keyseq.length() > 1)
357 lyx_view_->message(keyseq.print(KeySequence::ForGui));
360 // Maybe user can only reach the key via holding down shift.
361 // Let's see. But only if shift is the only modifier
362 if (func.action == LFUN_UNKNOWN_ACTION && state == ShiftModifier) {
363 LYXERR(Debug::KEY, "Trying without shift");
364 func = keyseq.addkey(keysym, NoModifier);
365 LYXERR(Debug::KEY, "Action now " << func.action);
368 if (func.action == LFUN_UNKNOWN_ACTION) {
369 // Hmm, we didn't match any of the keysequences. See
370 // if it's normal insertable text not already covered
372 if (keysym.isText() && keyseq.length() == 1) {
373 LYXERR(Debug::KEY, "isText() is true, inserting.");
374 func = FuncRequest(LFUN_SELF_INSERT,
375 FuncRequest::KEYBOARD);
377 LYXERR(Debug::KEY, "Unknown, !isText() - giving up");
378 lyx_view_->message(_("Unknown function."));
379 lyx_view_->restartCursor();
384 if (func.action == LFUN_SELF_INSERT) {
385 if (encoded_last_key != 0) {
386 docstring const arg(1, encoded_last_key);
387 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
388 FuncRequest::KEYBOARD));
389 LYXERR(Debug::KEY, "SelfInsert arg[`" << to_utf8(arg) << "']");
399 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
401 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
404 /* In LyX/Mac, when a dialog is open, the menus of the
405 application can still be accessed without giving focus to
406 the main window. In this case, we want to disable the menu
407 entries that are buffer or view-related.
409 If this code is moved somewhere else (like in
410 GuiView::getStatus), then several functions will not be
413 frontend::LyXView * lv = 0;
416 && (cmd.origin != FuncRequest::MENU || lyx_view_->hasFocus())) {
418 if (lyx_view_->documentBufferView())
419 buf = &lyx_view_->documentBufferView()->buffer();
422 if (cmd.action == LFUN_NOACTION) {
423 flag.message(from_utf8(N_("Nothing to do")));
424 flag.setEnabled(false);
428 switch (cmd.action) {
429 case LFUN_UNKNOWN_ACTION:
431 flag.setEnabled(false);
438 if (flag.unknown()) {
439 flag.message(from_utf8(N_("Unknown action")));
443 if (!flag.enabled()) {
444 if (flag.message().empty())
445 flag.message(from_utf8(N_("Command disabled")));
449 // Check whether we need a buffer
450 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
452 flag.message(from_utf8(N_("Command not allowed with"
453 "out any document open")));
454 flag.setEnabled(false);
458 // I would really like to avoid having this switch and rather try to
459 // encode this in the function itself.
460 // -- And I'd rather let an inset decide which LFUNs it is willing
461 // to handle (Andre')
463 switch (cmd.action) {
465 case LFUN_BUFFER_TOGGLE_READ_ONLY:
466 flag.setOnOff(buf->isReadonly());
469 case LFUN_BUFFER_SWITCH:
470 // toggle on the current buffer, but do not toggle off
471 // the other ones (is that a good idea?)
472 if (buf && to_utf8(cmd.argument()) == buf->absFileName())
476 case LFUN_BUFFER_CHKTEX:
477 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
480 case LFUN_BUILD_PROGRAM:
481 enable = buf->isExportable("program");
484 case LFUN_VC_REGISTER:
485 enable = !buf->lyxvc().inUse();
487 case LFUN_VC_CHECK_IN:
488 enable = buf->lyxvc().checkInEnabled();
490 case LFUN_VC_CHECK_OUT:
491 enable = buf->lyxvc().checkOutEnabled();
493 case LFUN_VC_LOCKING_TOGGLE:
494 enable = !buf->isReadonly() && buf->lyxvc().lockingToggleEnabled();
495 flag.setOnOff(enable && !buf->lyxvc().locker().empty());
498 enable = buf->lyxvc().inUse();
500 case LFUN_VC_UNDO_LAST:
501 enable = buf->lyxvc().undoLastEnabled();
503 case LFUN_BUFFER_RELOAD:
504 enable = !buf->isUnnamed() && buf->fileName().exists()
505 && (!buf->isClean() || buf->isExternallyModified(Buffer::timestamp_method));
508 case LFUN_CITATION_INSERT: {
509 FuncRequest fr(LFUN_INSET_INSERT, "citation");
510 enable = getStatus(fr).enabled();
514 // This could be used for the no-GUI version. The GUI version is handled in
515 // LyXView::getStatus(). See above.
517 case LFUN_BUFFER_WRITE:
518 case LFUN_BUFFER_WRITE_AS: {
519 Buffer * b = theBufferList().getBuffer(FileName(cmd.getArg(0)));
520 enable = b && (b->isUnnamed() || !b->isClean());
525 case LFUN_BUFFER_WRITE_ALL: {
526 // We enable the command only if there are some modified buffers
527 Buffer * first = theBufferList().first();
532 // We cannot use a for loop as the buffer list is a cycle.
538 b = theBufferList().next(b);
539 } while (b != first);
543 case LFUN_BOOKMARK_GOTO: {
544 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
545 enable = theSession().bookmarks().isValid(num);
549 case LFUN_BOOKMARK_CLEAR:
550 enable = theSession().bookmarks().hasValid();
553 // this one is difficult to get right. As a half-baked
554 // solution, we consider only the first action of the sequence
555 case LFUN_COMMAND_SEQUENCE: {
556 // argument contains ';'-terminated commands
557 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
558 FuncRequest func(lyxaction.lookupFunc(firstcmd));
559 func.origin = cmd.origin;
560 flag = getStatus(func);
564 // we want to check if at least one of these is enabled
565 case LFUN_COMMAND_ALTERNATIVES: {
566 // argument contains ';'-terminated commands
567 string arg = to_utf8(cmd.argument());
568 while (!arg.empty()) {
570 arg = split(arg, first, ';');
571 FuncRequest func(lyxaction.lookupFunc(first));
572 func.origin = cmd.origin;
573 flag = getStatus(func);
574 // if this one is enabled, the whole thing is
583 string name = to_utf8(cmd.argument());
584 if (theTopLevelCmdDef().lock(name, func)) {
585 func.origin = cmd.origin;
586 flag = getStatus(func);
587 theTopLevelCmdDef().release(name);
589 // catch recursion or unknown command
590 // definition. all operations until the
591 // recursion or unknown command definition
592 // occurs are performed, so set the state to
599 case LFUN_VC_COMMAND: {
600 if (cmd.argument().empty())
603 if (!buf && contains(cmd.getArg(0), 'D'))
608 case LFUN_MASTER_BUFFER_UPDATE:
609 case LFUN_MASTER_BUFFER_VIEW:
610 if (!buf->parent()) {
614 case LFUN_BUFFER_UPDATE:
615 case LFUN_BUFFER_VIEW: {
616 string format = to_utf8(cmd.argument());
617 if (cmd.argument().empty())
618 format = buf->getDefaultOutputFormat();
619 typedef vector<Format const *> Formats;
621 formats = buf->exportableFormats(true);
622 Formats::const_iterator fit = formats.begin();
623 Formats::const_iterator end = formats.end();
625 for (; fit != end ; ++fit) {
626 if ((*fit)->name() == format)
632 case LFUN_WORD_FIND_FORWARD:
633 case LFUN_WORD_FIND_BACKWARD:
634 case LFUN_WORD_FINDADV:
635 case LFUN_COMMAND_PREFIX:
636 case LFUN_COMMAND_EXECUTE:
638 case LFUN_META_PREFIX:
639 case LFUN_BUFFER_CLOSE:
640 case LFUN_BUFFER_IMPORT:
641 case LFUN_BUFFER_AUTO_SAVE:
642 case LFUN_RECONFIGURE:
644 case LFUN_DROP_LAYOUTS_CHOICE:
646 case LFUN_SERVER_GET_FILENAME:
647 case LFUN_SERVER_NOTIFY:
648 case LFUN_SERVER_GOTO_FILE_ROW:
649 case LFUN_DIALOG_HIDE:
650 case LFUN_DIALOG_DISCONNECT_INSET:
651 case LFUN_BUFFER_CHILD_OPEN:
652 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
653 case LFUN_KEYMAP_OFF:
654 case LFUN_KEYMAP_PRIMARY:
655 case LFUN_KEYMAP_SECONDARY:
656 case LFUN_KEYMAP_TOGGLE:
658 case LFUN_BUFFER_EXPORT_CUSTOM:
659 case LFUN_PREFERENCES_SAVE:
661 case LFUN_INSET_EDIT:
662 case LFUN_BUFFER_LANGUAGE:
663 case LFUN_TEXTCLASS_APPLY:
664 case LFUN_TEXTCLASS_LOAD:
665 case LFUN_BUFFER_SAVE_AS_DEFAULT:
666 case LFUN_BUFFER_PARAMS_APPLY:
667 case LFUN_LAYOUT_MODULES_CLEAR:
668 case LFUN_LAYOUT_MODULE_ADD:
669 case LFUN_LAYOUT_RELOAD:
670 case LFUN_LYXRC_APPLY:
671 case LFUN_BUFFER_NEXT:
672 case LFUN_BUFFER_PREVIOUS:
673 // these are handled in our dispatch()
681 if (theApp()->getStatus(cmd, flag))
684 // Does the view know something?
689 if (lv->getStatus(cmd, flag))
692 // If we do not have a BufferView, then other functions are disabled
698 // Is this a function that acts on inset at point?
699 Inset * inset = view()->cursor().nextInset();
700 if (lyxaction.funcHasFlag(cmd.action, LyXAction::AtPoint)
701 && inset && inset->getStatus(view()->cursor(), cmd, flag))
704 bool decided = getLocalStatus(view()->cursor(), cmd, flag);
706 // try the BufferView
707 decided = view()->getStatus(cmd, flag);
710 view()->buffer().getStatus(cmd, flag);
714 flag.setEnabled(false);
716 // Can we use a readonly buffer?
717 if (buf && buf->isReadonly()
718 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
719 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
720 flag.message(from_utf8(N_("Document is read-only")));
721 flag.setEnabled(false);
724 // Are we in a DELETED change-tracking region?
726 && lookupChangeType(view()->cursor(), true) == Change::DELETED
727 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
728 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
729 flag.message(from_utf8(N_("This portion of the document is deleted.")));
730 flag.setEnabled(false);
733 // the default error message if we disable the command
734 if (!flag.enabled() && flag.message().empty())
735 flag.message(from_utf8(N_("Command disabled")));
741 bool LyXFunc::ensureBufferClean(BufferView * bv)
743 Buffer & buf = bv->buffer();
744 if (buf.isClean() && !buf.isUnnamed())
747 docstring const file = buf.fileName().displayName(30);
750 if (!buf.isUnnamed()) {
751 text = bformat(_("The document %1$s has unsaved "
752 "changes.\n\nDo you want to save "
753 "the document?"), file);
754 title = _("Save changed document?");
757 text = bformat(_("The document %1$s has not been "
758 "saved yet.\n\nDo you want to save "
759 "the document?"), file);
760 title = _("Save new document?");
762 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
765 lyx_view_->dispatch(FuncRequest(LFUN_BUFFER_WRITE));
767 return buf.isClean() && !buf.isUnnamed();
773 bool loadLayoutFile(string const & name, string const & buf_path)
775 if (!LayoutFileList::get().haveClass(name)) {
776 lyxerr << "Document class \"" << name
777 << "\" does not exist."
782 LayoutFile & tc = LayoutFileList::get()[name];
783 if (!tc.load(buf_path)) {
784 docstring s = bformat(_("The document class %1$s "
785 "could not be loaded."), from_utf8(name));
786 Alert::error(_("Could not load class"), s);
793 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
798 void LyXFunc::dispatch(FuncRequest const & cmd)
800 string const argument = to_utf8(cmd.argument());
801 FuncCode const action = cmd.action;
803 LYXERR(Debug::ACTION, "\nLyXFunc::dispatch: cmd: " << cmd);
804 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
806 // we have not done anything wrong yet.
808 dispatch_buffer.erase();
810 // redraw the screen at the end (first of the two drawing steps).
811 //This is done unless explicitely requested otherwise
812 Update::flags updateFlags = Update::FitCursor;
814 FuncStatus const flag = getStatus(cmd);
815 if (!flag.enabled()) {
816 // We cannot use this function here
817 LYXERR(Debug::ACTION, "LyXFunc::dispatch: "
818 << lyxaction.getActionName(action)
819 << " [" << action << "] is disabled at this location");
820 setErrorMessage(flag.message());
822 lyx_view_->restartCursor();
825 if (lyx_view_ && lyx_view_->currentBufferView())
826 buffer = &lyx_view_->currentBufferView()->buffer();
829 case LFUN_WORD_FIND_FORWARD:
830 case LFUN_WORD_FIND_BACKWARD: {
831 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
832 static docstring last_search;
833 docstring searched_string;
835 if (!cmd.argument().empty()) {
836 last_search = cmd.argument();
837 searched_string = cmd.argument();
839 searched_string = last_search;
842 if (searched_string.empty())
845 bool const fw = action == LFUN_WORD_FIND_FORWARD;
846 docstring const data =
847 find2string(searched_string, true, false, fw);
848 find(view(), FuncRequest(LFUN_WORD_FIND, data));
852 case LFUN_COMMAND_PREFIX:
853 LASSERT(lyx_view_, /**/);
854 lyx_view_->message(keyseq.printOptions(true));
858 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
860 meta_fake_bit = NoModifier;
862 // cancel any selection
863 dispatch(FuncRequest(LFUN_MARK_OFF));
864 setMessage(from_ascii(N_("Cancel")));
867 case LFUN_META_PREFIX:
868 meta_fake_bit = AltModifier;
869 setMessage(keyseq.print(KeySequence::ForGui));
872 case LFUN_BUFFER_TOGGLE_READ_ONLY: {
873 LASSERT(lyx_view_ && lyx_view_->currentBufferView() && buffer, /**/);
874 if (buffer->lyxvc().inUse())
875 buffer->lyxvc().toggleReadOnly();
877 buffer->setReadonly(!buffer->isReadonly());
881 // --- Menus -----------------------------------------------
882 case LFUN_BUFFER_CLOSE:
883 lyx_view_->closeBuffer();
885 updateFlags = Update::None;
888 case LFUN_BUFFER_CLOSE_ALL:
889 lyx_view_->closeBufferAll();
891 updateFlags = Update::None;
894 case LFUN_BUFFER_RELOAD: {
895 LASSERT(lyx_view_ && buffer, /**/);
896 docstring const file = makeDisplayPath(buffer->absFileName(), 20);
897 docstring text = bformat(_("Any changes will be lost. Are you sure "
898 "you want to revert to the saved version of the document %1$s?"), file);
899 int const ret = Alert::prompt(_("Revert to saved document?"),
900 text, 1, 1, _("&Revert"), _("&Cancel"));
907 case LFUN_BUFFER_UPDATE: {
908 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
909 Buffer & doc_buffer = lyx_view_->documentBufferView()->buffer();
910 string format = argument;
911 if (argument.empty())
912 format = doc_buffer.getDefaultOutputFormat();
913 doc_buffer.doExport(format, true);
917 case LFUN_BUFFER_VIEW: {
918 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
919 Buffer & doc_buffer = lyx_view_->documentBufferView()->buffer();
920 string format = argument;
921 if (argument.empty())
922 format = doc_buffer.getDefaultOutputFormat();
923 doc_buffer.preview(format);
927 case LFUN_MASTER_BUFFER_UPDATE: {
928 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
929 Buffer & doc_buffer = lyx_view_->documentBufferView()->buffer();
930 string format = argument;
931 if (argument.empty())
932 format = doc_buffer.masterBuffer()->getDefaultOutputFormat();
933 doc_buffer.masterBuffer()->doExport(format, true);
937 case LFUN_MASTER_BUFFER_VIEW: {
938 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
939 Buffer & doc_buffer = lyx_view_->documentBufferView()->buffer();
940 string format = argument;
941 if (argument.empty())
942 format = doc_buffer.masterBuffer()->getDefaultOutputFormat();
943 doc_buffer.masterBuffer()->preview(format);
947 case LFUN_BUILD_PROGRAM:
948 LASSERT(lyx_view_ && buffer, /**/);
949 buffer->doExport("program", true);
952 case LFUN_BUFFER_CHKTEX:
953 LASSERT(lyx_view_ && buffer, /**/);
957 case LFUN_BUFFER_EXPORT:
958 LASSERT(lyx_view_ && buffer, /**/);
959 if (argument == "custom")
960 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"));
962 buffer->doExport(argument, false);
965 case LFUN_BUFFER_EXPORT_CUSTOM: {
966 LASSERT(lyx_view_ && buffer, /**/);
968 string command = split(argument, format_name, ' ');
969 Format const * format = formats.getFormat(format_name);
971 lyxerr << "Format \"" << format_name
972 << "\" not recognized!"
977 // The name of the file created by the conversion process
980 // Output to filename
981 if (format->name() == "lyx") {
982 string const latexname = buffer->latexName(false);
983 filename = changeExtension(latexname,
984 format->extension());
985 filename = addName(buffer->temppath(), filename);
987 if (!buffer->writeFile(FileName(filename)))
991 buffer->doExport(format_name, true, filename);
994 // Substitute $$FName for filename
995 if (!contains(command, "$$FName"))
996 command = "( " + command + " ) < $$FName";
997 command = subst(command, "$$FName", filename);
999 // Execute the command in the background
1001 call.startscript(Systemcall::DontWait, command);
1005 // FIXME: There is need for a command-line import.
1007 case LFUN_BUFFER_IMPORT:
1012 case LFUN_BUFFER_AUTO_SAVE:
1016 case LFUN_RECONFIGURE:
1017 // argument is any additional parameter to the configure.py command
1018 reconfigure(lyx_view_, argument);
1021 case LFUN_HELP_OPEN: {
1023 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1024 string const arg = argument;
1026 setErrorMessage(from_utf8(N_("Missing argument")));
1029 FileName fname = i18nLibFileSearch("doc", arg, "lyx");
1031 fname = i18nLibFileSearch("examples", arg, "lyx");
1033 if (fname.empty()) {
1034 lyxerr << "LyX: unable to find documentation file `"
1035 << arg << "'. Bad installation?" << endl;
1038 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1039 makeDisplayPath(fname.absFilename())));
1040 Buffer * buf = lyx_view_->loadDocument(fname, false);
1042 buf->updateLabels();
1043 lyx_view_->setBuffer(buf);
1044 buf->errors("Parse");
1046 updateFlags = Update::None;
1050 // --- version control -------------------------------
1051 case LFUN_VC_REGISTER:
1052 LASSERT(lyx_view_ && buffer, /**/);
1053 if (!ensureBufferClean(view()))
1055 if (!buffer->lyxvc().inUse()) {
1056 if (buffer->lyxvc().registrer())
1059 updateFlags = Update::Force;
1062 case LFUN_VC_CHECK_IN:
1063 LASSERT(lyx_view_ && buffer, /**/);
1064 if (!ensureBufferClean(view()))
1066 if (buffer->lyxvc().inUse()
1067 && !buffer->isReadonly()) {
1068 setMessage(from_utf8(buffer->lyxvc().checkIn()));
1073 case LFUN_VC_CHECK_OUT:
1074 LASSERT(lyx_view_ && buffer, /**/);
1075 if (!ensureBufferClean(view()))
1077 if (buffer->lyxvc().inUse()) {
1078 setMessage(from_utf8(buffer->lyxvc().checkOut()));
1083 case LFUN_VC_LOCKING_TOGGLE:
1084 LASSERT(lyx_view_ && buffer, /**/);
1085 if (!ensureBufferClean(view()) || buffer->isReadonly())
1087 if (buffer->lyxvc().inUse()) {
1088 string res = buffer->lyxvc().lockingToggle();
1090 frontend::Alert::error(_("Revision control error."),
1091 _("Error when setting the locking property."));
1093 setMessage(from_utf8(res));
1099 case LFUN_VC_REVERT:
1100 LASSERT(lyx_view_ && buffer, /**/);
1101 buffer->lyxvc().revert();
1105 case LFUN_VC_UNDO_LAST:
1106 LASSERT(lyx_view_ && buffer, /**/);
1107 buffer->lyxvc().undoLast();
1111 // --- lyxserver commands ----------------------------
1112 case LFUN_SERVER_GET_FILENAME:
1113 LASSERT(lyx_view_ && buffer, /**/);
1114 setMessage(from_utf8(buffer->absFileName()));
1115 LYXERR(Debug::INFO, "FNAME["
1116 << buffer->absFileName() << ']');
1119 case LFUN_SERVER_NOTIFY:
1120 dispatch_buffer = keyseq.print(KeySequence::Portable);
1121 theServer().notifyClient(to_utf8(dispatch_buffer));
1124 case LFUN_SERVER_GOTO_FILE_ROW: {
1125 LASSERT(lyx_view_, /**/);
1128 istringstream is(argument);
1129 is >> file_name >> row;
1130 file_name = os::internal_path(file_name);
1132 bool loaded = false;
1133 string const abstmp = package().temp_dir().absFilename();
1134 string const realtmp = package().temp_dir().realPath();
1135 // We have to use os::path_prefix_is() here, instead of
1136 // simply prefixIs(), because the file name comes from
1137 // an external application and may need case adjustment.
1138 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
1139 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
1140 // Needed by inverse dvi search. If it is a file
1141 // in tmpdir, call the apropriated function.
1142 // If tmpdir is a symlink, we may have the real
1143 // path passed back, so we correct for that.
1144 if (!prefixIs(file_name, abstmp))
1145 file_name = subst(file_name, realtmp, abstmp);
1146 buf = theBufferList().getBufferFromTmp(file_name);
1148 // Must replace extension of the file to be .lyx
1149 // and get full path
1150 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1151 // Either change buffer or load the file
1152 if (theBufferList().exists(s))
1153 buf = theBufferList().getBuffer(s);
1154 else if (s.exists()) {
1155 buf = lyx_view_->loadDocument(s);
1158 lyx_view_->message(bformat(
1159 _("File does not exist: %1$s"),
1160 makeDisplayPath(file_name)));
1164 updateFlags = Update::None;
1168 buf->updateLabels();
1169 lyx_view_->setBuffer(buf);
1170 view()->setCursorFromRow(row);
1172 buf->errors("Parse");
1173 updateFlags = Update::FitCursor;
1178 case LFUN_DIALOG_SHOW_NEW_INSET: {
1179 LASSERT(lyx_view_, /**/);
1180 string const name = cmd.getArg(0);
1181 InsetCode code = insetCode(name);
1182 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1183 bool insetCodeOK = true;
1190 case NOMENCL_PRINT_CODE:
1193 case HYPERLINK_CODE: {
1194 InsetCommandParams p(code);
1195 data = InsetCommand::params2string(name, p);
1198 case INCLUDE_CODE: {
1199 // data is the include type: one of "include",
1200 // "input", "verbatiminput" or "verbatiminput*"
1202 // default type is requested
1204 InsetCommandParams p(INCLUDE_CODE, data);
1205 data = InsetCommand::params2string("include", p);
1209 // \c data == "Boxed" || "Frameless" etc
1210 InsetBoxParams p(data);
1211 data = InsetBox::params2string(p);
1215 InsetBranchParams p;
1216 data = InsetBranch::params2string(p);
1220 InsetCommandParams p(CITE_CODE);
1221 data = InsetCommand::params2string(name, p);
1225 data = InsetERT::params2string(InsetCollapsable::Open);
1228 case EXTERNAL_CODE: {
1229 InsetExternalParams p;
1230 data = InsetExternal::params2string(p, *buffer);
1235 data = InsetFloat::params2string(p);
1238 case LISTINGS_CODE: {
1239 InsetListingsParams p;
1240 data = InsetListings::params2string(p);
1243 case GRAPHICS_CODE: {
1244 InsetGraphicsParams p;
1245 data = InsetGraphics::params2string(p, *buffer);
1250 data = InsetNote::params2string(p);
1253 case PHANTOM_CODE: {
1254 InsetPhantomParams p;
1255 data = InsetPhantom::params2string(p);
1260 data = InsetSpace::params2string(p);
1265 data = InsetVSpace::params2string(space);
1270 data = InsetWrap::params2string(p);
1274 lyxerr << "Inset type '" << name <<
1275 "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" << endl;
1276 insetCodeOK = false;
1278 } // end switch(code)
1280 dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data));
1284 case LFUN_CITATION_INSERT: {
1285 LASSERT(lyx_view_, /**/);
1286 if (!argument.empty()) {
1287 // we can have one optional argument, delimited by '|'
1288 // citation-insert <key>|<text_before>
1289 // this should be enhanced to also support text_after
1290 // and citation style
1291 string arg = argument;
1293 if (contains(argument, "|")) {
1294 arg = token(argument, '|', 0);
1295 opt1 = token(argument, '|', 1);
1297 InsetCommandParams icp(CITE_CODE);
1298 icp["key"] = from_utf8(arg);
1300 icp["before"] = from_utf8(opt1);
1301 string icstr = InsetCommand::params2string("citation", icp);
1302 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1305 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1309 case LFUN_BUFFER_CHILD_OPEN: {
1310 LASSERT(lyx_view_ && buffer, /**/);
1311 FileName filename = makeAbsPath(argument, buffer->filePath());
1312 view()->saveBookmark(false);
1314 bool parsed = false;
1315 if (theBufferList().exists(filename)) {
1316 child = theBufferList().getBuffer(filename);
1318 setMessage(bformat(_("Opening child document %1$s..."),
1319 makeDisplayPath(filename.absFilename())));
1320 child = lyx_view_->loadDocument(filename, false);
1324 // Set the parent name of the child document.
1325 // This makes insertion of citations and references in the child work,
1326 // when the target is in the parent or another child document.
1327 child->setParent(buffer);
1328 child->masterBuffer()->updateLabels();
1329 lyx_view_->setBuffer(child);
1331 child->errors("Parse");
1334 // If a screen update is required (in case where auto_open is false),
1335 // setBuffer() would have taken care of it already. Otherwise we shall
1336 // reset the update flag because it can cause a circular problem.
1338 updateFlags = Update::None;
1342 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
1343 LASSERT(lyx_view_, /**/);
1344 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1347 case LFUN_KEYMAP_OFF:
1348 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1349 lyx_view_->currentBufferView()->getIntl().keyMapOn(false);
1352 case LFUN_KEYMAP_PRIMARY:
1353 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1354 lyx_view_->currentBufferView()->getIntl().keyMapPrim();
1357 case LFUN_KEYMAP_SECONDARY:
1358 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1359 lyx_view_->currentBufferView()->getIntl().keyMapSec();
1362 case LFUN_KEYMAP_TOGGLE:
1363 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1364 lyx_view_->currentBufferView()->getIntl().toggleKeyMap();
1370 string rest = split(argument, countstr, ' ');
1371 istringstream is(countstr);
1374 //lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1375 for (int i = 0; i < count; ++i)
1376 dispatch(lyxaction.lookupFunc(rest));
1380 case LFUN_COMMAND_SEQUENCE: {
1381 // argument contains ';'-terminated commands
1382 string arg = argument;
1383 if (theBufferList().isLoaded(buffer))
1384 buffer->undo().beginUndoGroup();
1385 while (!arg.empty()) {
1387 arg = split(arg, first, ';');
1388 FuncRequest func(lyxaction.lookupFunc(first));
1389 func.origin = cmd.origin;
1392 if (theBufferList().isLoaded(buffer))
1393 buffer->undo().endUndoGroup();
1397 case LFUN_COMMAND_ALTERNATIVES: {
1398 // argument contains ';'-terminated commands
1399 string arg = argument;
1400 while (!arg.empty()) {
1402 arg = split(arg, first, ';');
1403 FuncRequest func(lyxaction.lookupFunc(first));
1404 func.origin = cmd.origin;
1405 FuncStatus stat = getStatus(func);
1406 if (stat.enabled()) {
1416 if (theTopLevelCmdDef().lock(argument, func)) {
1417 func.origin = cmd.origin;
1419 theTopLevelCmdDef().release(argument);
1421 if (func.action == LFUN_UNKNOWN_ACTION) {
1422 // unknown command definition
1423 lyxerr << "Warning: unknown command definition `"
1427 // recursion detected
1428 lyxerr << "Warning: Recursion in the command definition `"
1429 << argument << "' detected"
1436 case LFUN_PREFERENCES_SAVE: {
1437 lyxrc.write(makeAbsPath("preferences",
1438 package().user_support().absFilename()),
1444 LASSERT(lyx_view_, /**/);
1445 lyx_view_->message(from_utf8(argument));
1448 case LFUN_BUFFER_LANGUAGE: {
1449 LASSERT(lyx_view_, /**/);
1450 Language const * oldL = buffer->params().language;
1451 Language const * newL = languages.getLanguage(argument);
1452 if (!newL || oldL == newL)
1455 if (oldL->rightToLeft() == newL->rightToLeft()
1456 && !buffer->isMultiLingual())
1457 buffer->changeLanguage(oldL, newL);
1461 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1462 string const fname =
1463 addName(addPath(package().user_support().absFilename(), "templates/"),
1465 Buffer defaults(fname);
1467 istringstream ss(argument);
1470 int const unknown_tokens = defaults.readHeader(lex);
1472 if (unknown_tokens != 0) {
1473 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1474 << unknown_tokens << " unknown token"
1475 << (unknown_tokens == 1 ? "" : "s")
1479 if (defaults.writeFile(FileName(defaults.absFileName())))
1480 setMessage(bformat(_("Document defaults saved in %1$s"),
1481 makeDisplayPath(fname)));
1483 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1487 case LFUN_BUFFER_PARAMS_APPLY: {
1488 LASSERT(lyx_view_, /**/);
1490 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1491 Cursor & cur = view()->cursor();
1492 cur.recordUndoFullDocument();
1494 istringstream ss(argument);
1497 int const unknown_tokens = buffer->readHeader(lex);
1499 if (unknown_tokens != 0) {
1500 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1501 << unknown_tokens << " unknown token"
1502 << (unknown_tokens == 1 ? "" : "s")
1506 updateLayout(oldClass, buffer);
1508 updateFlags = Update::Force | Update::FitCursor;
1509 // We are most certainly here because of a change in the document
1510 // It is then better to make sure that all dialogs are in sync with
1511 // current document settings. LyXView::restartCursor() achieve this.
1512 lyx_view_->restartCursor();
1516 case LFUN_LAYOUT_MODULES_CLEAR: {
1517 LASSERT(lyx_view_, /**/);
1518 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1519 view()->cursor().recordUndoFullDocument();
1520 buffer->params().clearLayoutModules();
1521 buffer->params().makeDocumentClass();
1522 updateLayout(oldClass, buffer);
1523 updateFlags = Update::Force | Update::FitCursor;
1527 case LFUN_LAYOUT_MODULE_ADD: {
1528 LASSERT(lyx_view_, /**/);
1529 BufferParams const & params = buffer->params();
1530 if (!params.moduleCanBeAdded(argument)) {
1531 LYXERR0("Module `" << argument <<
1532 "' cannot be added due to failed requirements or "
1533 "conflicts with installed modules.");
1536 DocumentClass const * const oldClass = params.documentClassPtr();
1537 view()->cursor().recordUndoFullDocument();
1538 buffer->params().addLayoutModule(argument);
1539 buffer->params().makeDocumentClass();
1540 updateLayout(oldClass, buffer);
1541 updateFlags = Update::Force | Update::FitCursor;
1545 case LFUN_TEXTCLASS_APPLY: {
1546 LASSERT(lyx_view_, /**/);
1548 if (!loadLayoutFile(argument, buffer->temppath()) &&
1549 !loadLayoutFile(argument, buffer->filePath()))
1552 LayoutFile const * old_layout = buffer->params().baseClass();
1553 LayoutFile const * new_layout = &(LayoutFileList::get()[argument]);
1555 if (old_layout == new_layout)
1559 //Save the old, possibly modular, layout for use in conversion.
1560 DocumentClass const * const oldDocClass = buffer->params().documentClassPtr();
1561 view()->cursor().recordUndoFullDocument();
1562 buffer->params().setBaseClass(argument);
1563 buffer->params().makeDocumentClass();
1564 updateLayout(oldDocClass, buffer);
1565 updateFlags = Update::Force | Update::FitCursor;
1569 case LFUN_LAYOUT_RELOAD: {
1570 LASSERT(lyx_view_, /**/);
1571 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1572 LayoutFileIndex bc = buffer->params().baseClassID();
1573 LayoutFileList::get().reset(bc);
1574 buffer->params().setBaseClass(bc);
1575 buffer->params().makeDocumentClass();
1576 updateLayout(oldClass, buffer);
1577 updateFlags = Update::Force | Update::FitCursor;
1581 case LFUN_TEXTCLASS_LOAD:
1582 loadLayoutFile(argument, buffer->temppath()) ||
1583 loadLayoutFile(argument, buffer->filePath());
1586 case LFUN_LYXRC_APPLY: {
1587 // reset active key sequences, since the bindings
1588 // are updated (bug 6064)
1590 LyXRC const lyxrc_orig = lyxrc;
1592 istringstream ss(argument);
1593 bool const success = lyxrc.read(ss) == 0;
1596 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1597 << "Unable to read lyxrc data"
1602 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1606 theApp()->resetGui();
1608 /// We force the redraw in any case because there might be
1609 /// some screen font changes.
1610 /// FIXME: only the current view will be updated. the Gui
1611 /// class is able to furnish the list of views.
1612 updateFlags = Update::Force;
1616 case LFUN_BOOKMARK_GOTO:
1617 // go to bookmark, open unopened file and switch to buffer if necessary
1618 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1619 updateFlags = Update::FitCursor;
1622 case LFUN_BOOKMARK_CLEAR:
1623 theSession().bookmarks().clear();
1626 case LFUN_VC_COMMAND: {
1627 string flag = cmd.getArg(0);
1628 if (buffer && contains(flag, 'R') && !ensureBufferClean(view()))
1631 if (contains(flag, 'M'))
1632 if (!Alert::askForText(message, _("LyX VC: Log Message")))
1635 string path = cmd.getArg(1);
1636 if (contains(path, "$$p") && buffer)
1637 path = subst(path, "$$p", buffer->filePath());
1638 LYXERR(Debug::LYXVC, "Directory: " << path);
1640 if (!pp.isReadableDirectory()) {
1641 lyxerr << _("Directory is not accessible.") << endl;
1644 support::PathChanger p(pp);
1646 string command = cmd.getArg(2);
1647 if (command.empty())
1650 command = subst(command, "$$i", buffer->absFileName());
1651 command = subst(command, "$$p", buffer->filePath());
1653 command = subst(command, "$$m", to_utf8(message));
1654 LYXERR(Debug::LYXVC, "Command: " << command);
1656 one.startscript(Systemcall::Wait, command);
1660 if (contains(flag, 'I'))
1661 buffer->markDirty();
1662 if (contains(flag, 'R'))
1669 LASSERT(theApp(), /**/);
1670 // Let the frontend dispatch its own actions.
1671 if (theApp()->dispatch(cmd))
1672 // Nothing more to do.
1675 // Everything below is only for active lyx_view_
1679 // Start an undo group. This may be needed for
1680 // some stuff like inset-apply on labels.
1681 if (theBufferList().isLoaded(buffer))
1682 buffer->undo().beginUndoGroup();
1684 // Let the current LyXView dispatch its own actions.
1685 if (lyx_view_->dispatch(cmd)) {
1686 if (lyx_view_->currentBufferView()) {
1687 updateFlags = lyx_view_->currentBufferView()->cursor().result().update();
1688 if (theBufferList().isLoaded(buffer))
1689 buffer->undo().endUndoGroup();
1694 LASSERT(lyx_view_->currentBufferView(), /**/);
1696 // Let the current BufferView dispatch its own actions.
1697 if (view()->dispatch(cmd)) {
1698 // The BufferView took care of its own updates if needed.
1699 updateFlags = Update::None;
1700 if (theBufferList().isLoaded(buffer))
1701 buffer->undo().endUndoGroup();
1705 // OK, so try the Buffer itself
1707 view()->buffer().dispatch(cmd, dr);
1708 if (dr.dispatched()) {
1709 updateFlags = dr.update();
1713 // Is this a function that acts on inset at point?
1714 Inset * inset = view()->cursor().nextInset();
1715 if (lyxaction.funcHasFlag(action, LyXAction::AtPoint)
1717 view()->cursor().result().dispatched(true);
1718 view()->cursor().result().update(Update::FitCursor | Update::Force);
1719 FuncRequest tmpcmd = cmd;
1720 inset->dispatch(view()->cursor(), tmpcmd);
1721 if (view()->cursor().result().dispatched()) {
1722 updateFlags = view()->cursor().result().update();
1727 // Let the current Cursor dispatch its own actions.
1728 Cursor old = view()->cursor();
1729 view()->cursor().getPos(cursorPosBeforeDispatchX_,
1730 cursorPosBeforeDispatchY_);
1731 view()->cursor().dispatch(cmd);
1733 // notify insets we just left
1734 if (view()->cursor() != old) {
1736 bool badcursor = notifyCursorLeavesOrEnters(old, view()->cursor());
1738 view()->cursor().fixIfBroken();
1741 if (theBufferList().isLoaded(buffer))
1742 buffer->undo().endUndoGroup();
1744 // update completion. We do it here and not in
1745 // processKeySym to avoid another redraw just for a
1746 // changed inline completion
1747 if (cmd.origin == FuncRequest::KEYBOARD) {
1748 if (cmd.action == LFUN_SELF_INSERT
1749 || (cmd.action == LFUN_ERT_INSERT && view()->cursor().inMathed()))
1750 lyx_view_->updateCompletion(view()->cursor(), true, true);
1751 else if (cmd.action == LFUN_CHAR_DELETE_BACKWARD)
1752 lyx_view_->updateCompletion(view()->cursor(), false, true);
1754 lyx_view_->updateCompletion(view()->cursor(), false, false);
1757 updateFlags = view()->cursor().result().update();
1760 // if we executed a mutating lfun, mark the buffer as dirty
1761 if (theBufferList().isLoaded(buffer) && flag.enabled()
1762 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1763 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1764 buffer->markDirty();
1766 if (lyx_view_ && lyx_view_->currentBufferView()) {
1767 // BufferView::update() updates the ViewMetricsInfo and
1768 // also initializes the position cache for all insets in
1769 // (at least partially) visible top-level paragraphs.
1770 // We will redraw the screen only if needed.
1771 view()->processUpdateFlags(updateFlags);
1773 // Do we have a selection?
1774 theSelection().haveSelection(view()->cursor().selection());
1777 lyx_view_->restartCursor();
1781 // Some messages may already be translated, so we cannot use _()
1782 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1787 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1789 const bool verbose = (cmd.origin == FuncRequest::MENU
1790 || cmd.origin == FuncRequest::TOOLBAR
1791 || cmd.origin == FuncRequest::COMMANDBUFFER);
1793 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1794 LYXERR(Debug::ACTION, "dispatch msg is " << to_utf8(msg));
1796 lyx_view_->message(msg);
1800 docstring dispatch_msg = msg;
1801 if (!dispatch_msg.empty())
1802 dispatch_msg += ' ';
1804 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1806 bool argsadded = false;
1808 if (!cmd.argument().empty()) {
1809 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1810 comname += ' ' + cmd.argument();
1815 docstring const shortcuts = theTopLevelKeymap().printBindings(cmd, KeySequence::ForGui);
1817 if (!shortcuts.empty())
1818 comname += ": " + shortcuts;
1819 else if (!argsadded && !cmd.argument().empty())
1820 comname += ' ' + cmd.argument();
1822 if (!comname.empty()) {
1823 comname = rtrim(comname);
1824 dispatch_msg += '(' + rtrim(comname) + ')';
1827 LYXERR(Debug::ACTION, "verbose dispatch msg " << to_utf8(dispatch_msg));
1828 if (!dispatch_msg.empty())
1829 lyx_view_->message(dispatch_msg);
1833 void LyXFunc::reloadBuffer()
1835 Buffer * buf = &lyx_view_->documentBufferView()->buffer();
1836 FileName filename = buf->fileName();
1837 // The user has already confirmed that the changes, if any, should
1838 // be discarded. So we just release the Buffer and don't call closeBuffer();
1839 theBufferList().release(buf);
1840 // if the lyx_view_ has been destroyed, create a new one
1842 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1843 buf = lyx_view_->loadDocument(filename);
1844 docstring const disp_fn = makeDisplayPath(filename.absFilename());
1847 buf->updateLabels();
1848 lyx_view_->setBuffer(buf);
1849 buf->errors("Parse");
1850 str = bformat(_("Document %1$s reloaded."), disp_fn);
1852 str = bformat(_("Could not reload document %1$s"), disp_fn);
1854 lyx_view_->message(str);
1857 // Each "lyx_view_" should have it's own message method. lyxview and
1858 // the minibuffer would use the minibuffer, but lyxserver would
1859 // send an ERROR signal to its client. Alejandro 970603
1860 // This function is bit problematic when it comes to NLS, to make the
1861 // lyx servers client be language indepenent we must not translate
1862 // strings sent to this func.
1863 void LyXFunc::setErrorMessage(docstring const & m) const
1865 dispatch_buffer = m;
1870 void LyXFunc::setMessage(docstring const & m) const
1872 dispatch_buffer = m;
1876 docstring LyXFunc::viewStatusMessage()
1878 // When meta-fake key is pressed, show the key sequence so far + "M-".
1880 return keyseq.print(KeySequence::ForGui) + "M-";
1882 // Else, when a non-complete key sequence is pressed,
1883 // show the available options.
1884 if (keyseq.length() > 0 && !keyseq.deleted())
1885 return keyseq.printOptions(true);
1887 LASSERT(lyx_view_, /**/);
1888 if (!lyx_view_->currentBufferView())
1889 return _("Welcome to LyX!");
1891 return view()->cursor().currentState();
1895 BufferView * LyXFunc::view() const
1897 LASSERT(lyx_view_, /**/);
1898 return lyx_view_->currentBufferView();
1902 bool LyXFunc::wasMetaKey() const
1904 return (meta_fake_bit != NoModifier);
1908 void LyXFunc::updateLayout(DocumentClass const * const oldlayout, Buffer * buf)
1910 lyx_view_->message(_("Converting document to new document class..."));
1912 StableDocIterator backcur(view()->cursor());
1913 ErrorList & el = buf->errorList("Class Switch");
1914 cap::switchBetweenClasses(
1915 oldlayout, buf->params().documentClassPtr(),
1916 static_cast<InsetText &>(buf->inset()), el);
1918 view()->setCursor(backcur.asDocIterator(buf));
1920 buf->errors("Class Switch");
1921 buf->updateLabels();
1927 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1929 // Why the switch you might ask. It is a trick to ensure that all
1930 // the elements in the LyXRCTags enum is handled. As you can see
1931 // there are no breaks at all. So it is just a huge fall-through.
1932 // The nice thing is that we will get a warning from the compiler
1933 // if we forget an element.
1934 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1936 case LyXRC::RC_ACCEPT_COMPOUND:
1937 case LyXRC::RC_ALT_LANG:
1938 case LyXRC::RC_PLAINTEXT_LINELEN:
1939 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
1940 case LyXRC::RC_AUTOCORRECTION_MATH:
1941 case LyXRC::RC_AUTOREGIONDELETE:
1942 case LyXRC::RC_AUTORESET_OPTIONS:
1943 case LyXRC::RC_AUTOSAVE:
1944 case LyXRC::RC_AUTO_NUMBER:
1945 case LyXRC::RC_BACKUPDIR_PATH:
1946 case LyXRC::RC_BIBTEX_ALTERNATIVES:
1947 case LyXRC::RC_BIBTEX_COMMAND:
1948 case LyXRC::RC_BINDFILE:
1949 case LyXRC::RC_CHECKLASTFILES:
1950 case LyXRC::RC_COMPLETION_CURSOR_TEXT:
1951 case LyXRC::RC_COMPLETION_INLINE_DELAY:
1952 case LyXRC::RC_COMPLETION_INLINE_DOTS:
1953 case LyXRC::RC_COMPLETION_INLINE_MATH:
1954 case LyXRC::RC_COMPLETION_INLINE_TEXT:
1955 case LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE:
1956 case LyXRC::RC_COMPLETION_POPUP_DELAY:
1957 case LyXRC::RC_COMPLETION_POPUP_MATH:
1958 case LyXRC::RC_COMPLETION_POPUP_TEXT:
1959 case LyXRC::RC_USELASTFILEPOS:
1960 case LyXRC::RC_LOADSESSION:
1961 case LyXRC::RC_CHKTEX_COMMAND:
1962 case LyXRC::RC_CONVERTER:
1963 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
1964 case LyXRC::RC_COPIER:
1965 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1966 case LyXRC::RC_SCROLL_BELOW_DOCUMENT:
1967 case LyXRC::RC_DATE_INSERT_FORMAT:
1968 case LyXRC::RC_DEFAULT_LANGUAGE:
1969 case LyXRC::RC_GUI_LANGUAGE:
1970 case LyXRC::RC_DEFAULT_PAPERSIZE:
1971 case LyXRC::RC_DEFAULT_VIEW_FORMAT:
1972 case LyXRC::RC_DEFFILE:
1973 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1974 case LyXRC::RC_DISPLAY_GRAPHICS:
1975 case LyXRC::RC_DOCUMENTPATH:
1976 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1977 FileName path(lyxrc_new.document_path);
1978 if (path.exists() && path.isDirectory())
1979 package().document_dir() = FileName(lyxrc.document_path);
1981 case LyXRC::RC_ESC_CHARS:
1982 case LyXRC::RC_EXAMPLEPATH:
1983 case LyXRC::RC_FONT_ENCODING:
1984 case LyXRC::RC_FORMAT:
1985 case LyXRC::RC_GROUP_LAYOUTS:
1986 case LyXRC::RC_HUNSPELLDIR_PATH:
1987 case LyXRC::RC_INDEX_ALTERNATIVES:
1988 case LyXRC::RC_INDEX_COMMAND:
1989 case LyXRC::RC_JBIBTEX_COMMAND:
1990 case LyXRC::RC_JINDEX_COMMAND:
1991 case LyXRC::RC_NOMENCL_COMMAND:
1992 case LyXRC::RC_INPUT:
1993 case LyXRC::RC_KBMAP:
1994 case LyXRC::RC_KBMAP_PRIMARY:
1995 case LyXRC::RC_KBMAP_SECONDARY:
1996 case LyXRC::RC_LABEL_INIT_LENGTH:
1997 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
1998 case LyXRC::RC_LANGUAGE_AUTO_END:
1999 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2000 case LyXRC::RC_LANGUAGE_COMMAND_END:
2001 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2002 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2003 case LyXRC::RC_LANGUAGE_PACKAGE:
2004 case LyXRC::RC_LANGUAGE_USE_BABEL:
2005 case LyXRC::RC_MAC_LIKE_WORD_MOVEMENT:
2006 case LyXRC::RC_MACRO_EDIT_STYLE:
2007 case LyXRC::RC_MAKE_BACKUP:
2008 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2009 case LyXRC::RC_MOUSE_WHEEL_SPEED:
2010 case LyXRC::RC_NUMLASTFILES:
2011 case LyXRC::RC_PARAGRAPH_MARKERS:
2012 case LyXRC::RC_PATH_PREFIX:
2013 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2014 prependEnvPath("PATH", lyxrc.path_prefix);
2016 case LyXRC::RC_PERS_DICT:
2017 case LyXRC::RC_PREVIEW:
2018 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2019 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2020 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2021 case LyXRC::RC_PRINTCOPIESFLAG:
2022 case LyXRC::RC_PRINTER:
2023 case LyXRC::RC_PRINTEVENPAGEFLAG:
2024 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2025 case LyXRC::RC_PRINTFILEEXTENSION:
2026 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2027 case LyXRC::RC_PRINTODDPAGEFLAG:
2028 case LyXRC::RC_PRINTPAGERANGEFLAG:
2029 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2030 case LyXRC::RC_PRINTPAPERFLAG:
2031 case LyXRC::RC_PRINTREVERSEFLAG:
2032 case LyXRC::RC_PRINTSPOOL_COMMAND:
2033 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2034 case LyXRC::RC_PRINTTOFILE:
2035 case LyXRC::RC_PRINTTOPRINTER:
2036 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2037 case LyXRC::RC_PRINT_COMMAND:
2038 case LyXRC::RC_RTL_SUPPORT:
2039 case LyXRC::RC_SCREEN_DPI:
2040 case LyXRC::RC_SCREEN_FONT_ROMAN:
2041 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2042 case LyXRC::RC_SCREEN_FONT_SANS:
2043 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2044 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2045 case LyXRC::RC_SCREEN_FONT_SIZES:
2046 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2047 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2048 case LyXRC::RC_GEOMETRY_SESSION:
2049 case LyXRC::RC_SCREEN_ZOOM:
2050 case LyXRC::RC_SERVERPIPE:
2051 case LyXRC::RC_SET_COLOR:
2052 case LyXRC::RC_SHOW_BANNER:
2053 case LyXRC::RC_OPEN_BUFFERS_IN_TABS:
2054 case LyXRC::RC_SPELL_COMMAND:
2055 case LyXRC::RC_SPELLCHECKER:
2056 case LyXRC::RC_SPELLCHECK_CONTINUOUSLY:
2057 case LyXRC::RC_SPLITINDEX_COMMAND:
2058 case LyXRC::RC_TEMPDIRPATH:
2059 case LyXRC::RC_TEMPLATEPATH:
2060 case LyXRC::RC_TEX_ALLOWS_SPACES:
2061 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2062 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2063 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2065 case LyXRC::RC_THESAURUSDIRPATH:
2066 case LyXRC::RC_UIFILE:
2067 case LyXRC::RC_USER_EMAIL:
2068 case LyXRC::RC_USER_NAME:
2069 case LyXRC::RC_USETEMPDIR:
2070 case LyXRC::RC_USE_ALT_LANG:
2071 case LyXRC::RC_USE_CONVERTER_CACHE:
2072 case LyXRC::RC_USE_ESC_CHARS:
2073 case LyXRC::RC_USE_INP_ENC:
2074 case LyXRC::RC_USE_PERS_DICT:
2075 case LyXRC::RC_USE_TOOLTIP:
2076 case LyXRC::RC_USE_PIXMAP_CACHE:
2077 case LyXRC::RC_USE_SPELL_LIB:
2078 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2079 case LyXRC::RC_SORT_LAYOUTS:
2080 case LyXRC::RC_FULL_SCREEN_LIMIT:
2081 case LyXRC::RC_FULL_SCREEN_SCROLLBAR:
2082 case LyXRC::RC_FULL_SCREEN_MENUBAR:
2083 case LyXRC::RC_FULL_SCREEN_TABBAR:
2084 case LyXRC::RC_FULL_SCREEN_TOOLBARS:
2085 case LyXRC::RC_FULL_SCREEN_WIDTH:
2086 case LyXRC::RC_VISUAL_CURSOR:
2087 case LyXRC::RC_VIEWER:
2088 case LyXRC::RC_LAST: