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 BufferView * bv = lyx_view_->currentBufferView();
231 bv->getIntl().getTransManager().deadkey(
232 c, get_accent(action).accent, bv->cursor().innerText(),
234 // Need to clear, in case the minibuffer calls these
237 // copied verbatim from do_accent_char
238 bv->cursor().resetAnchor();
239 bv->processUpdateFlags(Update::FitCursor);
242 //FIXME: bookmark handling is a frontend issue. This code should be transferred
243 // to GuiView and be GuiView and be window dependent.
244 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
246 LASSERT(lyx_view_, /**/);
247 if (!theSession().bookmarks().isValid(idx))
249 BookmarksSection::Bookmark const & bm = theSession().bookmarks().bookmark(idx);
250 LASSERT(!bm.filename.empty(), /**/);
251 string const file = bm.filename.absFilename();
252 // if the file is not opened, open it.
253 if (!theBufferList().exists(bm.filename)) {
255 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
259 // open may fail, so we need to test it again
260 if (!theBufferList().exists(bm.filename))
263 // bm can be changed when saving
264 BookmarksSection::Bookmark tmp = bm;
266 // Special case idx == 0 used for back-from-back jump navigation
268 dispatch(FuncRequest(LFUN_BOOKMARK_SAVE, "0"));
270 // if the current buffer is not that one, switch to it.
271 if (!lyx_view_->documentBufferView()
272 || lyx_view_->documentBufferView()->buffer().fileName() != tmp.filename) {
275 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
278 // moveToPosition try paragraph id first and then paragraph (pit, pos).
279 if (!lyx_view_->documentBufferView()->moveToPosition(
280 tmp.bottom_pit, tmp.bottom_pos, tmp.top_id, tmp.top_pos))
287 // Cursor jump succeeded!
288 Cursor const & cur = lyx_view_->documentBufferView()->cursor();
289 pit_type new_pit = cur.pit();
290 pos_type new_pos = cur.pos();
291 int new_id = cur.paragraph().id();
293 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
294 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
295 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos
296 || bm.top_id != new_id) {
297 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(
298 new_pit, new_pos, new_id);
303 void LyXFunc::processKeySym(KeySymbol const & keysym, KeyModifier state)
305 LYXERR(Debug::KEY, "KeySym is " << keysym.getSymbolName());
307 // Do nothing if we have nothing (JMarc)
308 if (!keysym.isOK()) {
309 LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
310 lyx_view_->restartCursor();
314 if (keysym.isModifier()) {
315 LYXERR(Debug::KEY, "isModifier true");
317 lyx_view_->restartCursor();
321 //Encoding const * encoding = lyx_view_->documentBufferView()->cursor().getEncoding();
322 //encoded_last_key = keysym.getISOEncoded(encoding ? encoding->name() : "");
323 // FIXME: encoded_last_key shadows the member variable of the same
324 // name. Is that intended?
325 char_type encoded_last_key = keysym.getUCSEncoded();
327 // Do a one-deep top-level lookup for
328 // cancel and meta-fake keys. RVDK_PATCH_5
329 cancel_meta_seq.reset();
331 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
332 LYXERR(Debug::KEY, "action first set to [" << func.action << ']');
334 // When not cancel or meta-fake, do the normal lookup.
335 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
336 // Mostly, meta_fake_bit = NoModifier. RVDK_PATCH_5.
337 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
338 // remove Caps Lock and Mod2 as a modifiers
339 func = keyseq.addkey(keysym, (state | meta_fake_bit));
340 LYXERR(Debug::KEY, "action now set to [" << func.action << ']');
343 // Dont remove this unless you know what you are doing.
344 meta_fake_bit = NoModifier;
346 // Can this happen now ?
347 if (func.action == LFUN_NOACTION)
348 func = FuncRequest(LFUN_COMMAND_PREFIX);
350 LYXERR(Debug::KEY, " Key [action=" << func.action << "]["
351 << keyseq.print(KeySequence::Portable) << ']');
353 // already here we know if it any point in going further
354 // why not return already here if action == -1 and
355 // num_bytes == 0? (Lgb)
357 if (keyseq.length() > 1)
358 lyx_view_->message(keyseq.print(KeySequence::ForGui));
361 // Maybe user can only reach the key via holding down shift.
362 // Let's see. But only if shift is the only modifier
363 if (func.action == LFUN_UNKNOWN_ACTION && state == ShiftModifier) {
364 LYXERR(Debug::KEY, "Trying without shift");
365 func = keyseq.addkey(keysym, NoModifier);
366 LYXERR(Debug::KEY, "Action now " << func.action);
369 if (func.action == LFUN_UNKNOWN_ACTION) {
370 // Hmm, we didn't match any of the keysequences. See
371 // if it's normal insertable text not already covered
373 if (keysym.isText() && keyseq.length() == 1) {
374 LYXERR(Debug::KEY, "isText() is true, inserting.");
375 func = FuncRequest(LFUN_SELF_INSERT,
376 FuncRequest::KEYBOARD);
378 LYXERR(Debug::KEY, "Unknown, !isText() - giving up");
379 lyx_view_->message(_("Unknown function."));
380 lyx_view_->restartCursor();
385 if (func.action == LFUN_SELF_INSERT) {
386 if (encoded_last_key != 0) {
387 docstring const arg(1, encoded_last_key);
388 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
389 FuncRequest::KEYBOARD));
390 LYXERR(Debug::KEY, "SelfInsert arg[`" << to_utf8(arg) << "']");
400 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
402 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
405 /* In LyX/Mac, when a dialog is open, the menus of the
406 application can still be accessed without giving focus to
407 the main window. In this case, we want to disable the menu
408 entries that are buffer or view-related.
410 If this code is moved somewhere else (like in
411 GuiView::getStatus), then several functions will not be
414 frontend::LyXView * lv = 0;
417 && (cmd.origin != FuncRequest::MENU || lyx_view_->hasFocus())) {
419 if (lyx_view_->documentBufferView())
420 buf = &lyx_view_->documentBufferView()->buffer();
423 if (cmd.action == LFUN_NOACTION) {
424 flag.message(from_utf8(N_("Nothing to do")));
425 flag.setEnabled(false);
429 switch (cmd.action) {
430 case LFUN_UNKNOWN_ACTION:
432 flag.setEnabled(false);
439 if (flag.unknown()) {
440 flag.message(from_utf8(N_("Unknown action")));
444 if (!flag.enabled()) {
445 if (flag.message().empty())
446 flag.message(from_utf8(N_("Command disabled")));
450 // Check whether we need a buffer
451 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
453 flag.message(from_utf8(N_("Command not allowed with"
454 "out any document open")));
455 flag.setEnabled(false);
459 // I would really like to avoid having this switch and rather try to
460 // encode this in the function itself.
461 // -- And I'd rather let an inset decide which LFUNs it is willing
462 // to handle (Andre')
464 switch (cmd.action) {
466 case LFUN_BUFFER_TOGGLE_READ_ONLY:
467 flag.setOnOff(buf->isReadonly());
470 case LFUN_BUFFER_SWITCH:
471 // toggle on the current buffer, but do not toggle off
472 // the other ones (is that a good idea?)
473 if (buf && to_utf8(cmd.argument()) == buf->absFileName())
477 case LFUN_BUFFER_CHKTEX:
478 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
481 case LFUN_BUILD_PROGRAM:
482 enable = buf->isExportable("program");
485 case LFUN_VC_REGISTER:
486 enable = !buf->lyxvc().inUse();
488 case LFUN_VC_CHECK_IN:
489 enable = buf->lyxvc().checkInEnabled();
491 case LFUN_VC_CHECK_OUT:
492 enable = buf->lyxvc().checkOutEnabled();
494 case LFUN_VC_LOCKING_TOGGLE:
495 enable = !buf->isReadonly() && buf->lyxvc().lockingToggleEnabled();
496 flag.setOnOff(enable && !buf->lyxvc().locker().empty());
499 enable = buf->lyxvc().inUse();
501 case LFUN_VC_UNDO_LAST:
502 enable = buf->lyxvc().undoLastEnabled();
504 case LFUN_BUFFER_RELOAD:
505 enable = !buf->isUnnamed() && buf->fileName().exists()
506 && (!buf->isClean() || buf->isExternallyModified(Buffer::timestamp_method));
509 case LFUN_CITATION_INSERT: {
510 FuncRequest fr(LFUN_INSET_INSERT, "citation");
511 enable = getStatus(fr).enabled();
515 // This could be used for the no-GUI version. The GUI version is handled in
516 // LyXView::getStatus(). See above.
518 case LFUN_BUFFER_WRITE:
519 case LFUN_BUFFER_WRITE_AS: {
520 Buffer * b = theBufferList().getBuffer(FileName(cmd.getArg(0)));
521 enable = b && (b->isUnnamed() || !b->isClean());
526 case LFUN_BUFFER_WRITE_ALL: {
527 // We enable the command only if there are some modified buffers
528 Buffer * first = theBufferList().first();
533 // We cannot use a for loop as the buffer list is a cycle.
539 b = theBufferList().next(b);
540 } while (b != first);
544 case LFUN_BOOKMARK_GOTO: {
545 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
546 enable = theSession().bookmarks().isValid(num);
550 case LFUN_BOOKMARK_CLEAR:
551 enable = theSession().bookmarks().hasValid();
554 // this one is difficult to get right. As a half-baked
555 // solution, we consider only the first action of the sequence
556 case LFUN_COMMAND_SEQUENCE: {
557 // argument contains ';'-terminated commands
558 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
559 FuncRequest func(lyxaction.lookupFunc(firstcmd));
560 func.origin = cmd.origin;
561 flag = getStatus(func);
565 // we want to check if at least one of these is enabled
566 case LFUN_COMMAND_ALTERNATIVES: {
567 // argument contains ';'-terminated commands
568 string arg = to_utf8(cmd.argument());
569 while (!arg.empty()) {
571 arg = split(arg, first, ';');
572 FuncRequest func(lyxaction.lookupFunc(first));
573 func.origin = cmd.origin;
574 flag = getStatus(func);
575 // if this one is enabled, the whole thing is
584 string name = to_utf8(cmd.argument());
585 if (theTopLevelCmdDef().lock(name, func)) {
586 func.origin = cmd.origin;
587 flag = getStatus(func);
588 theTopLevelCmdDef().release(name);
590 // catch recursion or unknown command
591 // definition. all operations until the
592 // recursion or unknown command definition
593 // occurs are performed, so set the state to
600 case LFUN_VC_COMMAND: {
601 if (cmd.argument().empty())
604 if (!buf && contains(cmd.getArg(0), 'D'))
609 case LFUN_MASTER_BUFFER_UPDATE:
610 case LFUN_MASTER_BUFFER_VIEW:
611 if (!buf->parent()) {
615 case LFUN_BUFFER_UPDATE:
616 case LFUN_BUFFER_VIEW: {
617 string format = to_utf8(cmd.argument());
618 if (cmd.argument().empty())
619 format = buf->getDefaultOutputFormat();
620 typedef vector<Format const *> Formats;
622 formats = buf->exportableFormats(true);
623 Formats::const_iterator fit = formats.begin();
624 Formats::const_iterator end = formats.end();
626 for (; fit != end ; ++fit) {
627 if ((*fit)->name() == format)
633 case LFUN_WORD_FINDADV:
634 case LFUN_COMMAND_PREFIX:
635 case LFUN_COMMAND_EXECUTE:
637 case LFUN_META_PREFIX:
638 case LFUN_BUFFER_CLOSE:
639 case LFUN_BUFFER_IMPORT:
640 case LFUN_BUFFER_AUTO_SAVE:
641 case LFUN_RECONFIGURE:
643 case LFUN_DROP_LAYOUTS_CHOICE:
645 case LFUN_SERVER_GET_FILENAME:
646 case LFUN_SERVER_NOTIFY:
647 case LFUN_SERVER_GOTO_FILE_ROW:
648 case LFUN_DIALOG_HIDE:
649 case LFUN_DIALOG_DISCONNECT_INSET:
650 case LFUN_BUFFER_CHILD_OPEN:
651 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
652 case LFUN_KEYMAP_OFF:
653 case LFUN_KEYMAP_PRIMARY:
654 case LFUN_KEYMAP_SECONDARY:
655 case LFUN_KEYMAP_TOGGLE:
657 case LFUN_BUFFER_EXPORT_CUSTOM:
658 case LFUN_PREFERENCES_SAVE:
660 case LFUN_INSET_EDIT:
661 case LFUN_BUFFER_LANGUAGE:
662 case LFUN_TEXTCLASS_APPLY:
663 case LFUN_TEXTCLASS_LOAD:
664 case LFUN_BUFFER_SAVE_AS_DEFAULT:
665 case LFUN_BUFFER_PARAMS_APPLY:
666 case LFUN_LAYOUT_MODULES_CLEAR:
667 case LFUN_LAYOUT_MODULE_ADD:
668 case LFUN_LAYOUT_RELOAD:
669 case LFUN_LYXRC_APPLY:
670 case LFUN_BUFFER_NEXT:
671 case LFUN_BUFFER_PREVIOUS:
672 // these are handled in our dispatch()
680 if (theApp()->getStatus(cmd, flag))
683 // Does the view know something?
688 if (lv->getStatus(cmd, flag))
691 BufferView * bv = lv->currentBufferView();
692 // If we do not have a BufferView, then other functions are disabled
697 // Is this a function that acts on inset at point?
698 Inset * inset = bv->cursor().nextInset();
699 if (lyxaction.funcHasFlag(cmd.action, LyXAction::AtPoint)
700 && inset && inset->getStatus(bv->cursor(), cmd, flag))
703 bool decided = getLocalStatus(bv->cursor(), cmd, flag);
705 // try the BufferView
706 decided = bv->getStatus(cmd, flag);
709 bv->buffer().getStatus(cmd, flag);
713 flag.setEnabled(false);
715 // Can we use a readonly buffer?
716 if (buf && buf->isReadonly()
717 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
718 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
719 flag.message(from_utf8(N_("Document is read-only")));
720 flag.setEnabled(false);
723 // Are we in a DELETED change-tracking region?
724 if (lyx_view_ && lyx_view_->documentBufferView()
725 && (lookupChangeType(lyx_view_->documentBufferView()->cursor(), true)
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_COMMAND_PREFIX:
830 LASSERT(lyx_view_, /**/);
831 lyx_view_->message(keyseq.printOptions(true));
835 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
837 meta_fake_bit = NoModifier;
839 // cancel any selection
840 dispatch(FuncRequest(LFUN_MARK_OFF));
841 setMessage(from_ascii(N_("Cancel")));
844 case LFUN_META_PREFIX:
845 meta_fake_bit = AltModifier;
846 setMessage(keyseq.print(KeySequence::ForGui));
849 case LFUN_BUFFER_TOGGLE_READ_ONLY: {
850 LASSERT(lyx_view_ && lyx_view_->currentBufferView() && buffer, /**/);
851 if (buffer->lyxvc().inUse())
852 buffer->lyxvc().toggleReadOnly();
854 buffer->setReadonly(!buffer->isReadonly());
858 // --- Menus -----------------------------------------------
859 case LFUN_BUFFER_CLOSE:
860 lyx_view_->closeBuffer();
862 updateFlags = Update::None;
865 case LFUN_BUFFER_CLOSE_ALL:
866 lyx_view_->closeBufferAll();
868 updateFlags = Update::None;
871 case LFUN_BUFFER_RELOAD: {
872 LASSERT(lyx_view_ && buffer, /**/);
873 docstring const file = makeDisplayPath(buffer->absFileName(), 20);
874 docstring text = bformat(_("Any changes will be lost. Are you sure "
875 "you want to revert to the saved version of the document %1$s?"), file);
876 int const ret = Alert::prompt(_("Revert to saved document?"),
877 text, 1, 1, _("&Revert"), _("&Cancel"));
884 case LFUN_BUFFER_UPDATE: {
885 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
886 Buffer & doc_buffer = lyx_view_->documentBufferView()->buffer();
887 string format = argument;
888 if (argument.empty())
889 format = doc_buffer.getDefaultOutputFormat();
890 doc_buffer.doExport(format, true);
894 case LFUN_BUFFER_VIEW: {
895 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
896 Buffer & doc_buffer = lyx_view_->documentBufferView()->buffer();
897 string format = argument;
898 if (argument.empty())
899 format = doc_buffer.getDefaultOutputFormat();
900 doc_buffer.preview(format);
904 case LFUN_MASTER_BUFFER_UPDATE: {
905 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
906 Buffer & doc_buffer = lyx_view_->documentBufferView()->buffer();
907 string format = argument;
908 if (argument.empty())
909 format = doc_buffer.masterBuffer()->getDefaultOutputFormat();
910 doc_buffer.masterBuffer()->doExport(format, true);
914 case LFUN_MASTER_BUFFER_VIEW: {
915 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
916 Buffer & doc_buffer = lyx_view_->documentBufferView()->buffer();
917 string format = argument;
918 if (argument.empty())
919 format = doc_buffer.masterBuffer()->getDefaultOutputFormat();
920 doc_buffer.masterBuffer()->preview(format);
924 case LFUN_BUILD_PROGRAM:
925 LASSERT(lyx_view_ && buffer, /**/);
926 buffer->doExport("program", true);
929 case LFUN_BUFFER_CHKTEX:
930 LASSERT(lyx_view_ && buffer, /**/);
934 case LFUN_BUFFER_EXPORT:
935 LASSERT(lyx_view_ && buffer, /**/);
936 if (argument == "custom")
937 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"));
939 buffer->doExport(argument, false);
942 case LFUN_BUFFER_EXPORT_CUSTOM: {
943 LASSERT(lyx_view_ && buffer, /**/);
945 string command = split(argument, format_name, ' ');
946 Format const * format = formats.getFormat(format_name);
948 lyxerr << "Format \"" << format_name
949 << "\" not recognized!"
954 // The name of the file created by the conversion process
957 // Output to filename
958 if (format->name() == "lyx") {
959 string const latexname = buffer->latexName(false);
960 filename = changeExtension(latexname,
961 format->extension());
962 filename = addName(buffer->temppath(), filename);
964 if (!buffer->writeFile(FileName(filename)))
968 buffer->doExport(format_name, true, filename);
971 // Substitute $$FName for filename
972 if (!contains(command, "$$FName"))
973 command = "( " + command + " ) < $$FName";
974 command = subst(command, "$$FName", filename);
976 // Execute the command in the background
978 call.startscript(Systemcall::DontWait, command);
982 // FIXME: There is need for a command-line import.
984 case LFUN_BUFFER_IMPORT:
989 case LFUN_BUFFER_AUTO_SAVE:
993 case LFUN_RECONFIGURE:
994 // argument is any additional parameter to the configure.py command
995 reconfigure(lyx_view_, argument);
998 case LFUN_HELP_OPEN: {
1000 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1001 string const arg = argument;
1003 setErrorMessage(from_utf8(N_("Missing argument")));
1006 FileName fname = i18nLibFileSearch("doc", arg, "lyx");
1008 fname = i18nLibFileSearch("examples", arg, "lyx");
1010 if (fname.empty()) {
1011 lyxerr << "LyX: unable to find documentation file `"
1012 << arg << "'. Bad installation?" << endl;
1015 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1016 makeDisplayPath(fname.absFilename())));
1017 Buffer * buf = lyx_view_->loadDocument(fname, false);
1019 buf->updateLabels();
1020 lyx_view_->setBuffer(buf);
1021 buf->errors("Parse");
1023 updateFlags = Update::None;
1027 // --- version control -------------------------------
1028 case LFUN_VC_REGISTER:
1029 LASSERT(lyx_view_ && buffer, /**/);
1030 if (!ensureBufferClean(lyx_view_->documentBufferView()))
1032 if (!buffer->lyxvc().inUse()) {
1033 if (buffer->lyxvc().registrer())
1036 updateFlags = Update::Force;
1039 case LFUN_VC_CHECK_IN:
1040 LASSERT(lyx_view_ && buffer, /**/);
1041 if (!ensureBufferClean(lyx_view_->documentBufferView()))
1043 if (buffer->lyxvc().inUse()
1044 && !buffer->isReadonly()) {
1045 setMessage(from_utf8(buffer->lyxvc().checkIn()));
1050 case LFUN_VC_CHECK_OUT:
1051 LASSERT(lyx_view_ && buffer, /**/);
1052 if (!ensureBufferClean(lyx_view_->documentBufferView()))
1054 if (buffer->lyxvc().inUse()) {
1055 setMessage(from_utf8(buffer->lyxvc().checkOut()));
1060 case LFUN_VC_LOCKING_TOGGLE:
1061 LASSERT(lyx_view_ && buffer, /**/);
1062 if (!ensureBufferClean(lyx_view_->documentBufferView())
1063 || buffer->isReadonly())
1065 if (buffer->lyxvc().inUse()) {
1066 string res = buffer->lyxvc().lockingToggle();
1068 frontend::Alert::error(_("Revision control error."),
1069 _("Error when setting the locking property."));
1071 setMessage(from_utf8(res));
1077 case LFUN_VC_REVERT:
1078 LASSERT(lyx_view_ && buffer, /**/);
1079 buffer->lyxvc().revert();
1083 case LFUN_VC_UNDO_LAST:
1084 LASSERT(lyx_view_ && buffer, /**/);
1085 buffer->lyxvc().undoLast();
1089 // --- lyxserver commands ----------------------------
1090 case LFUN_SERVER_GET_FILENAME:
1091 LASSERT(lyx_view_ && buffer, /**/);
1092 setMessage(from_utf8(buffer->absFileName()));
1093 LYXERR(Debug::INFO, "FNAME["
1094 << buffer->absFileName() << ']');
1097 case LFUN_SERVER_NOTIFY:
1098 dispatch_buffer = keyseq.print(KeySequence::Portable);
1099 theServer().notifyClient(to_utf8(dispatch_buffer));
1102 case LFUN_SERVER_GOTO_FILE_ROW: {
1103 LASSERT(lyx_view_, /**/);
1106 istringstream is(argument);
1107 is >> file_name >> row;
1108 file_name = os::internal_path(file_name);
1110 bool loaded = false;
1111 string const abstmp = package().temp_dir().absFilename();
1112 string const realtmp = package().temp_dir().realPath();
1113 // We have to use os::path_prefix_is() here, instead of
1114 // simply prefixIs(), because the file name comes from
1115 // an external application and may need case adjustment.
1116 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
1117 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
1118 // Needed by inverse dvi search. If it is a file
1119 // in tmpdir, call the apropriated function.
1120 // If tmpdir is a symlink, we may have the real
1121 // path passed back, so we correct for that.
1122 if (!prefixIs(file_name, abstmp))
1123 file_name = subst(file_name, realtmp, abstmp);
1124 buf = theBufferList().getBufferFromTmp(file_name);
1126 // Must replace extension of the file to be .lyx
1127 // and get full path
1128 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1129 // Either change buffer or load the file
1130 if (theBufferList().exists(s))
1131 buf = theBufferList().getBuffer(s);
1132 else if (s.exists()) {
1133 buf = lyx_view_->loadDocument(s);
1136 lyx_view_->message(bformat(
1137 _("File does not exist: %1$s"),
1138 makeDisplayPath(file_name)));
1142 updateFlags = Update::None;
1146 buf->updateLabels();
1147 lyx_view_->setBuffer(buf);
1148 lyx_view_->documentBufferView()->setCursorFromRow(row);
1150 buf->errors("Parse");
1151 updateFlags = Update::FitCursor;
1156 case LFUN_DIALOG_SHOW_NEW_INSET: {
1157 LASSERT(lyx_view_, /**/);
1158 string const name = cmd.getArg(0);
1159 InsetCode code = insetCode(name);
1160 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1161 bool insetCodeOK = true;
1168 case NOMENCL_PRINT_CODE:
1171 case HYPERLINK_CODE: {
1172 InsetCommandParams p(code);
1173 data = InsetCommand::params2string(name, p);
1176 case INCLUDE_CODE: {
1177 // data is the include type: one of "include",
1178 // "input", "verbatiminput" or "verbatiminput*"
1180 // default type is requested
1182 InsetCommandParams p(INCLUDE_CODE, data);
1183 data = InsetCommand::params2string("include", p);
1187 // \c data == "Boxed" || "Frameless" etc
1188 InsetBoxParams p(data);
1189 data = InsetBox::params2string(p);
1193 InsetBranchParams p;
1194 data = InsetBranch::params2string(p);
1198 InsetCommandParams p(CITE_CODE);
1199 data = InsetCommand::params2string(name, p);
1203 data = InsetERT::params2string(InsetCollapsable::Open);
1206 case EXTERNAL_CODE: {
1207 InsetExternalParams p;
1208 data = InsetExternal::params2string(p, *buffer);
1213 data = InsetFloat::params2string(p);
1216 case LISTINGS_CODE: {
1217 InsetListingsParams p;
1218 data = InsetListings::params2string(p);
1221 case GRAPHICS_CODE: {
1222 InsetGraphicsParams p;
1223 data = InsetGraphics::params2string(p, *buffer);
1228 data = InsetNote::params2string(p);
1231 case PHANTOM_CODE: {
1232 InsetPhantomParams p;
1233 data = InsetPhantom::params2string(p);
1238 data = InsetSpace::params2string(p);
1243 data = InsetVSpace::params2string(space);
1248 data = InsetWrap::params2string(p);
1252 lyxerr << "Inset type '" << name <<
1253 "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" << endl;
1254 insetCodeOK = false;
1256 } // end switch(code)
1258 dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data));
1262 case LFUN_CITATION_INSERT: {
1263 LASSERT(lyx_view_, /**/);
1264 if (!argument.empty()) {
1265 // we can have one optional argument, delimited by '|'
1266 // citation-insert <key>|<text_before>
1267 // this should be enhanced to also support text_after
1268 // and citation style
1269 string arg = argument;
1271 if (contains(argument, "|")) {
1272 arg = token(argument, '|', 0);
1273 opt1 = token(argument, '|', 1);
1275 InsetCommandParams icp(CITE_CODE);
1276 icp["key"] = from_utf8(arg);
1278 icp["before"] = from_utf8(opt1);
1279 string icstr = InsetCommand::params2string("citation", icp);
1280 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1283 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1287 case LFUN_BUFFER_CHILD_OPEN: {
1288 LASSERT(lyx_view_ && buffer, /**/);
1289 FileName filename = makeAbsPath(argument, buffer->filePath());
1290 lyx_view_->documentBufferView()->saveBookmark(false);
1292 bool parsed = false;
1293 if (theBufferList().exists(filename)) {
1294 child = theBufferList().getBuffer(filename);
1296 setMessage(bformat(_("Opening child document %1$s..."),
1297 makeDisplayPath(filename.absFilename())));
1298 child = lyx_view_->loadDocument(filename, false);
1302 // Set the parent name of the child document.
1303 // This makes insertion of citations and references in the child work,
1304 // when the target is in the parent or another child document.
1305 child->setParent(buffer);
1306 child->masterBuffer()->updateLabels();
1307 lyx_view_->setBuffer(child);
1309 child->errors("Parse");
1312 // If a screen update is required (in case where auto_open is false),
1313 // setBuffer() would have taken care of it already. Otherwise we shall
1314 // reset the update flag because it can cause a circular problem.
1316 updateFlags = Update::None;
1320 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
1321 LASSERT(lyx_view_, /**/);
1322 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1325 case LFUN_KEYMAP_OFF:
1326 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1327 lyx_view_->currentBufferView()->getIntl().keyMapOn(false);
1330 case LFUN_KEYMAP_PRIMARY:
1331 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1332 lyx_view_->currentBufferView()->getIntl().keyMapPrim();
1335 case LFUN_KEYMAP_SECONDARY:
1336 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1337 lyx_view_->currentBufferView()->getIntl().keyMapSec();
1340 case LFUN_KEYMAP_TOGGLE:
1341 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1342 lyx_view_->currentBufferView()->getIntl().toggleKeyMap();
1348 string rest = split(argument, countstr, ' ');
1349 istringstream is(countstr);
1352 //lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1353 for (int i = 0; i < count; ++i)
1354 dispatch(lyxaction.lookupFunc(rest));
1358 case LFUN_COMMAND_SEQUENCE: {
1359 // argument contains ';'-terminated commands
1360 string arg = argument;
1361 if (theBufferList().isLoaded(buffer))
1362 buffer->undo().beginUndoGroup();
1363 while (!arg.empty()) {
1365 arg = split(arg, first, ';');
1366 FuncRequest func(lyxaction.lookupFunc(first));
1367 func.origin = cmd.origin;
1370 if (theBufferList().isLoaded(buffer))
1371 buffer->undo().endUndoGroup();
1375 case LFUN_COMMAND_ALTERNATIVES: {
1376 // argument contains ';'-terminated commands
1377 string arg = argument;
1378 while (!arg.empty()) {
1380 arg = split(arg, first, ';');
1381 FuncRequest func(lyxaction.lookupFunc(first));
1382 func.origin = cmd.origin;
1383 FuncStatus stat = getStatus(func);
1384 if (stat.enabled()) {
1394 if (theTopLevelCmdDef().lock(argument, func)) {
1395 func.origin = cmd.origin;
1397 theTopLevelCmdDef().release(argument);
1399 if (func.action == LFUN_UNKNOWN_ACTION) {
1400 // unknown command definition
1401 lyxerr << "Warning: unknown command definition `"
1405 // recursion detected
1406 lyxerr << "Warning: Recursion in the command definition `"
1407 << argument << "' detected"
1414 case LFUN_PREFERENCES_SAVE: {
1415 lyxrc.write(makeAbsPath("preferences",
1416 package().user_support().absFilename()),
1422 LASSERT(lyx_view_, /**/);
1423 lyx_view_->message(from_utf8(argument));
1426 case LFUN_BUFFER_LANGUAGE: {
1427 LASSERT(lyx_view_, /**/);
1428 Language const * oldL = buffer->params().language;
1429 Language const * newL = languages.getLanguage(argument);
1430 if (!newL || oldL == newL)
1433 if (oldL->rightToLeft() == newL->rightToLeft()
1434 && !buffer->isMultiLingual())
1435 buffer->changeLanguage(oldL, newL);
1439 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1440 string const fname =
1441 addName(addPath(package().user_support().absFilename(), "templates/"),
1443 Buffer defaults(fname);
1445 istringstream ss(argument);
1448 int const unknown_tokens = defaults.readHeader(lex);
1450 if (unknown_tokens != 0) {
1451 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1452 << unknown_tokens << " unknown token"
1453 << (unknown_tokens == 1 ? "" : "s")
1457 if (defaults.writeFile(FileName(defaults.absFileName())))
1458 setMessage(bformat(_("Document defaults saved in %1$s"),
1459 makeDisplayPath(fname)));
1461 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1465 case LFUN_BUFFER_PARAMS_APPLY: {
1466 LASSERT(lyx_view_, /**/);
1468 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1469 Cursor & cur = lyx_view_->documentBufferView()->cursor();
1470 cur.recordUndoFullDocument();
1472 istringstream ss(argument);
1475 int const unknown_tokens = buffer->readHeader(lex);
1477 if (unknown_tokens != 0) {
1478 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1479 << unknown_tokens << " unknown token"
1480 << (unknown_tokens == 1 ? "" : "s")
1484 updateLayout(oldClass, buffer);
1486 updateFlags = Update::Force | Update::FitCursor;
1487 // We are most certainly here because of a change in the document
1488 // It is then better to make sure that all dialogs are in sync with
1489 // current document settings. LyXView::restartCursor() achieve this.
1490 lyx_view_->restartCursor();
1494 case LFUN_LAYOUT_MODULES_CLEAR: {
1495 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
1496 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1497 lyx_view_->documentBufferView()->cursor().recordUndoFullDocument();
1498 buffer->params().clearLayoutModules();
1499 buffer->params().makeDocumentClass();
1500 updateLayout(oldClass, buffer);
1501 updateFlags = Update::Force | Update::FitCursor;
1505 case LFUN_LAYOUT_MODULE_ADD: {
1506 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
1507 BufferParams const & params = buffer->params();
1508 if (!params.moduleCanBeAdded(argument)) {
1509 LYXERR0("Module `" << argument <<
1510 "' cannot be added due to failed requirements or "
1511 "conflicts with installed modules.");
1514 DocumentClass const * const oldClass = params.documentClassPtr();
1515 lyx_view_->documentBufferView()->cursor().recordUndoFullDocument();
1516 buffer->params().addLayoutModule(argument);
1517 buffer->params().makeDocumentClass();
1518 updateLayout(oldClass, buffer);
1519 updateFlags = Update::Force | Update::FitCursor;
1523 case LFUN_TEXTCLASS_APPLY: {
1524 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
1526 if (!loadLayoutFile(argument, buffer->temppath()) &&
1527 !loadLayoutFile(argument, buffer->filePath()))
1530 LayoutFile const * old_layout = buffer->params().baseClass();
1531 LayoutFile const * new_layout = &(LayoutFileList::get()[argument]);
1533 if (old_layout == new_layout)
1537 //Save the old, possibly modular, layout for use in conversion.
1538 DocumentClass const * const oldDocClass =
1539 buffer->params().documentClassPtr();
1540 lyx_view_->documentBufferView()->cursor().recordUndoFullDocument();
1541 buffer->params().setBaseClass(argument);
1542 buffer->params().makeDocumentClass();
1543 updateLayout(oldDocClass, buffer);
1544 updateFlags = Update::Force | Update::FitCursor;
1548 case LFUN_LAYOUT_RELOAD: {
1549 LASSERT(lyx_view_, /**/);
1550 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1551 LayoutFileIndex bc = buffer->params().baseClassID();
1552 LayoutFileList::get().reset(bc);
1553 buffer->params().setBaseClass(bc);
1554 buffer->params().makeDocumentClass();
1555 updateLayout(oldClass, buffer);
1556 updateFlags = Update::Force | Update::FitCursor;
1560 case LFUN_TEXTCLASS_LOAD:
1561 loadLayoutFile(argument, buffer->temppath()) ||
1562 loadLayoutFile(argument, buffer->filePath());
1565 case LFUN_LYXRC_APPLY: {
1566 // reset active key sequences, since the bindings
1567 // are updated (bug 6064)
1569 LyXRC const lyxrc_orig = lyxrc;
1571 istringstream ss(argument);
1572 bool const success = lyxrc.read(ss) == 0;
1575 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1576 << "Unable to read lyxrc data"
1581 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1585 theApp()->resetGui();
1587 /// We force the redraw in any case because there might be
1588 /// some screen font changes.
1589 /// FIXME: only the current view will be updated. the Gui
1590 /// class is able to furnish the list of views.
1591 updateFlags = Update::Force;
1595 case LFUN_BOOKMARK_GOTO:
1596 // go to bookmark, open unopened file and switch to buffer if necessary
1597 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1598 updateFlags = Update::FitCursor;
1601 case LFUN_BOOKMARK_CLEAR:
1602 theSession().bookmarks().clear();
1605 case LFUN_VC_COMMAND: {
1606 string flag = cmd.getArg(0);
1607 if (buffer && contains(flag, 'R')
1608 && !ensureBufferClean(lyx_view_->documentBufferView()))
1611 if (contains(flag, 'M'))
1612 if (!Alert::askForText(message, _("LyX VC: Log Message")))
1615 string path = cmd.getArg(1);
1616 if (contains(path, "$$p") && buffer)
1617 path = subst(path, "$$p", buffer->filePath());
1618 LYXERR(Debug::LYXVC, "Directory: " << path);
1620 if (!pp.isReadableDirectory()) {
1621 lyxerr << _("Directory is not accessible.") << endl;
1624 support::PathChanger p(pp);
1626 string command = cmd.getArg(2);
1627 if (command.empty())
1630 command = subst(command, "$$i", buffer->absFileName());
1631 command = subst(command, "$$p", buffer->filePath());
1633 command = subst(command, "$$m", to_utf8(message));
1634 LYXERR(Debug::LYXVC, "Command: " << command);
1636 one.startscript(Systemcall::Wait, command);
1640 if (contains(flag, 'I'))
1641 buffer->markDirty();
1642 if (contains(flag, 'R'))
1649 LASSERT(theApp(), /**/);
1650 // Let the frontend dispatch its own actions.
1651 if (theApp()->dispatch(cmd))
1652 // Nothing more to do.
1655 // Everything below is only for active lyx_view_
1659 // Start an undo group. This may be needed for
1660 // some stuff like inset-apply on labels.
1661 if (theBufferList().isLoaded(buffer))
1662 buffer->undo().beginUndoGroup();
1664 // Let the current LyXView dispatch its own actions.
1665 if (lyx_view_->dispatch(cmd)) {
1666 if (lyx_view_->currentBufferView()) {
1667 updateFlags = lyx_view_->currentBufferView()->cursor().result().update();
1668 if (theBufferList().isLoaded(buffer))
1669 buffer->undo().endUndoGroup();
1674 LASSERT(lyx_view_->currentBufferView(), /**/);
1676 // Let the current BufferView dispatch its own actions.
1677 if (lyx_view_->currentBufferView()->dispatch(cmd)) {
1678 // The BufferView took care of its own updates if needed.
1679 updateFlags = Update::None;
1680 if (theBufferList().isLoaded(buffer))
1681 buffer->undo().endUndoGroup();
1685 // OK, so try the Buffer itself
1687 BufferView * bv = lyx_view_->currentBufferView();
1688 bv->buffer().dispatch(cmd, dr);
1689 if (dr.dispatched()) {
1690 updateFlags = dr.update();
1694 // Is this a function that acts on inset at point?
1695 Inset * inset = bv->cursor().nextInset();
1696 if (lyxaction.funcHasFlag(action, LyXAction::AtPoint)
1698 bv->cursor().result().dispatched(true);
1699 bv->cursor().result().update(Update::FitCursor | Update::Force);
1700 FuncRequest tmpcmd = cmd;
1701 inset->dispatch(bv->cursor(), tmpcmd);
1702 if (bv->cursor().result().dispatched()) {
1703 updateFlags = bv->cursor().result().update();
1708 // Let the current Cursor dispatch its own actions.
1709 Cursor old = bv->cursor();
1710 bv->cursor().getPos(cursorPosBeforeDispatchX_,
1711 cursorPosBeforeDispatchY_);
1712 bv->cursor().dispatch(cmd);
1714 // notify insets we just left
1715 if (bv->cursor() != old) {
1717 bool badcursor = notifyCursorLeavesOrEnters(old, bv->cursor());
1719 bv->cursor().fixIfBroken();
1722 if (theBufferList().isLoaded(buffer))
1723 buffer->undo().endUndoGroup();
1725 // update completion. We do it here and not in
1726 // processKeySym to avoid another redraw just for a
1727 // changed inline completion
1728 if (cmd.origin == FuncRequest::KEYBOARD) {
1729 if (cmd.action == LFUN_SELF_INSERT
1730 || (cmd.action == LFUN_ERT_INSERT && bv->cursor().inMathed()))
1731 lyx_view_->updateCompletion(bv->cursor(), true, true);
1732 else if (cmd.action == LFUN_CHAR_DELETE_BACKWARD)
1733 lyx_view_->updateCompletion(bv->cursor(), false, true);
1735 lyx_view_->updateCompletion(bv->cursor(), false, false);
1738 updateFlags = bv->cursor().result().update();
1741 // if we executed a mutating lfun, mark the buffer as dirty
1742 if (theBufferList().isLoaded(buffer) && flag.enabled()
1743 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1744 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1745 buffer->markDirty();
1747 if (lyx_view_ && lyx_view_->currentBufferView()) {
1748 // BufferView::update() updates the ViewMetricsInfo and
1749 // also initializes the position cache for all insets in
1750 // (at least partially) visible top-level paragraphs.
1751 // We will redraw the screen only if needed.
1752 lyx_view_->currentBufferView()->processUpdateFlags(updateFlags);
1754 // Do we have a selection?
1755 theSelection().haveSelection(
1756 lyx_view_->currentBufferView()->cursor().selection());
1759 lyx_view_->restartCursor();
1763 // Some messages may already be translated, so we cannot use _()
1764 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1769 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1771 const bool verbose = (cmd.origin == FuncRequest::MENU
1772 || cmd.origin == FuncRequest::TOOLBAR
1773 || cmd.origin == FuncRequest::COMMANDBUFFER);
1775 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1776 LYXERR(Debug::ACTION, "dispatch msg is " << to_utf8(msg));
1778 lyx_view_->message(msg);
1782 docstring dispatch_msg = msg;
1783 if (!dispatch_msg.empty())
1784 dispatch_msg += ' ';
1786 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1788 bool argsadded = false;
1790 if (!cmd.argument().empty()) {
1791 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1792 comname += ' ' + cmd.argument();
1797 docstring const shortcuts = theTopLevelKeymap().printBindings(cmd, KeySequence::ForGui);
1799 if (!shortcuts.empty())
1800 comname += ": " + shortcuts;
1801 else if (!argsadded && !cmd.argument().empty())
1802 comname += ' ' + cmd.argument();
1804 if (!comname.empty()) {
1805 comname = rtrim(comname);
1806 dispatch_msg += '(' + rtrim(comname) + ')';
1809 LYXERR(Debug::ACTION, "verbose dispatch msg " << to_utf8(dispatch_msg));
1810 if (!dispatch_msg.empty())
1811 lyx_view_->message(dispatch_msg);
1815 void LyXFunc::reloadBuffer()
1817 Buffer * buf = &lyx_view_->documentBufferView()->buffer();
1818 FileName filename = buf->fileName();
1819 // The user has already confirmed that the changes, if any, should
1820 // be discarded. So we just release the Buffer and don't call closeBuffer();
1821 theBufferList().release(buf);
1822 // if the lyx_view_ has been destroyed, create a new one
1824 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1825 buf = lyx_view_->loadDocument(filename);
1826 docstring const disp_fn = makeDisplayPath(filename.absFilename());
1829 buf->updateLabels();
1830 lyx_view_->setBuffer(buf);
1831 buf->errors("Parse");
1832 str = bformat(_("Document %1$s reloaded."), disp_fn);
1834 str = bformat(_("Could not reload document %1$s"), disp_fn);
1836 lyx_view_->message(str);
1839 // Each "lyx_view_" should have it's own message method. lyxview and
1840 // the minibuffer would use the minibuffer, but lyxserver would
1841 // send an ERROR signal to its client. Alejandro 970603
1842 // This function is bit problematic when it comes to NLS, to make the
1843 // lyx servers client be language indepenent we must not translate
1844 // strings sent to this func.
1845 void LyXFunc::setErrorMessage(docstring const & m) const
1847 dispatch_buffer = m;
1852 void LyXFunc::setMessage(docstring const & m) const
1854 dispatch_buffer = m;
1858 docstring LyXFunc::viewStatusMessage()
1860 // When meta-fake key is pressed, show the key sequence so far + "M-".
1862 return keyseq.print(KeySequence::ForGui) + "M-";
1864 // Else, when a non-complete key sequence is pressed,
1865 // show the available options.
1866 if (keyseq.length() > 0 && !keyseq.deleted())
1867 return keyseq.printOptions(true);
1869 LASSERT(lyx_view_, /**/);
1870 if (!lyx_view_->currentBufferView())
1871 return _("Welcome to LyX!");
1873 return lyx_view_->currentBufferView()->cursor().currentState();
1877 bool LyXFunc::wasMetaKey() const
1879 return (meta_fake_bit != NoModifier);
1883 void LyXFunc::updateLayout(DocumentClass const * const oldlayout, Buffer * buf)
1885 lyx_view_->message(_("Converting document to new document class..."));
1887 StableDocIterator backcur(lyx_view_->currentBufferView()->cursor());
1888 ErrorList & el = buf->errorList("Class Switch");
1889 cap::switchBetweenClasses(
1890 oldlayout, buf->params().documentClassPtr(),
1891 static_cast<InsetText &>(buf->inset()), el);
1893 lyx_view_->currentBufferView()->setCursor(backcur.asDocIterator(buf));
1895 buf->errors("Class Switch");
1896 buf->updateLabels();
1902 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1904 // Why the switch you might ask. It is a trick to ensure that all
1905 // the elements in the LyXRCTags enum is handled. As you can see
1906 // there are no breaks at all. So it is just a huge fall-through.
1907 // The nice thing is that we will get a warning from the compiler
1908 // if we forget an element.
1909 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1911 case LyXRC::RC_ACCEPT_COMPOUND:
1912 case LyXRC::RC_ALT_LANG:
1913 case LyXRC::RC_PLAINTEXT_LINELEN:
1914 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
1915 case LyXRC::RC_AUTOCORRECTION_MATH:
1916 case LyXRC::RC_AUTOREGIONDELETE:
1917 case LyXRC::RC_AUTORESET_OPTIONS:
1918 case LyXRC::RC_AUTOSAVE:
1919 case LyXRC::RC_AUTO_NUMBER:
1920 case LyXRC::RC_BACKUPDIR_PATH:
1921 case LyXRC::RC_BIBTEX_ALTERNATIVES:
1922 case LyXRC::RC_BIBTEX_COMMAND:
1923 case LyXRC::RC_BINDFILE:
1924 case LyXRC::RC_CHECKLASTFILES:
1925 case LyXRC::RC_COMPLETION_CURSOR_TEXT:
1926 case LyXRC::RC_COMPLETION_INLINE_DELAY:
1927 case LyXRC::RC_COMPLETION_INLINE_DOTS:
1928 case LyXRC::RC_COMPLETION_INLINE_MATH:
1929 case LyXRC::RC_COMPLETION_INLINE_TEXT:
1930 case LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE:
1931 case LyXRC::RC_COMPLETION_POPUP_DELAY:
1932 case LyXRC::RC_COMPLETION_POPUP_MATH:
1933 case LyXRC::RC_COMPLETION_POPUP_TEXT:
1934 case LyXRC::RC_USELASTFILEPOS:
1935 case LyXRC::RC_LOADSESSION:
1936 case LyXRC::RC_CHKTEX_COMMAND:
1937 case LyXRC::RC_CONVERTER:
1938 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
1939 case LyXRC::RC_COPIER:
1940 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1941 case LyXRC::RC_SCROLL_BELOW_DOCUMENT:
1942 case LyXRC::RC_DATE_INSERT_FORMAT:
1943 case LyXRC::RC_DEFAULT_LANGUAGE:
1944 case LyXRC::RC_GUI_LANGUAGE:
1945 case LyXRC::RC_DEFAULT_PAPERSIZE:
1946 case LyXRC::RC_DEFAULT_VIEW_FORMAT:
1947 case LyXRC::RC_DEFFILE:
1948 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1949 case LyXRC::RC_DISPLAY_GRAPHICS:
1950 case LyXRC::RC_DOCUMENTPATH:
1951 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1952 FileName path(lyxrc_new.document_path);
1953 if (path.exists() && path.isDirectory())
1954 package().document_dir() = FileName(lyxrc.document_path);
1956 case LyXRC::RC_EDITOR_ALTERNATIVES:
1957 case LyXRC::RC_ESC_CHARS:
1958 case LyXRC::RC_EXAMPLEPATH:
1959 case LyXRC::RC_FONT_ENCODING:
1960 case LyXRC::RC_FORMAT:
1961 case LyXRC::RC_GROUP_LAYOUTS:
1962 case LyXRC::RC_HUNSPELLDIR_PATH:
1963 case LyXRC::RC_INDEX_ALTERNATIVES:
1964 case LyXRC::RC_INDEX_COMMAND:
1965 case LyXRC::RC_JBIBTEX_COMMAND:
1966 case LyXRC::RC_JINDEX_COMMAND:
1967 case LyXRC::RC_NOMENCL_COMMAND:
1968 case LyXRC::RC_INPUT:
1969 case LyXRC::RC_KBMAP:
1970 case LyXRC::RC_KBMAP_PRIMARY:
1971 case LyXRC::RC_KBMAP_SECONDARY:
1972 case LyXRC::RC_LABEL_INIT_LENGTH:
1973 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
1974 case LyXRC::RC_LANGUAGE_AUTO_END:
1975 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
1976 case LyXRC::RC_LANGUAGE_COMMAND_END:
1977 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
1978 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
1979 case LyXRC::RC_LANGUAGE_PACKAGE:
1980 case LyXRC::RC_LANGUAGE_USE_BABEL:
1981 case LyXRC::RC_MAC_LIKE_WORD_MOVEMENT:
1982 case LyXRC::RC_MACRO_EDIT_STYLE:
1983 case LyXRC::RC_MAKE_BACKUP:
1984 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
1985 case LyXRC::RC_MOUSE_WHEEL_SPEED:
1986 case LyXRC::RC_NUMLASTFILES:
1987 case LyXRC::RC_PARAGRAPH_MARKERS:
1988 case LyXRC::RC_PATH_PREFIX:
1989 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
1990 prependEnvPath("PATH", lyxrc.path_prefix);
1992 case LyXRC::RC_PERS_DICT:
1993 case LyXRC::RC_PREVIEW:
1994 case LyXRC::RC_PREVIEW_HASHED_LABELS:
1995 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
1996 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
1997 case LyXRC::RC_PRINTCOPIESFLAG:
1998 case LyXRC::RC_PRINTER:
1999 case LyXRC::RC_PRINTEVENPAGEFLAG:
2000 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2001 case LyXRC::RC_PRINTFILEEXTENSION:
2002 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2003 case LyXRC::RC_PRINTODDPAGEFLAG:
2004 case LyXRC::RC_PRINTPAGERANGEFLAG:
2005 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2006 case LyXRC::RC_PRINTPAPERFLAG:
2007 case LyXRC::RC_PRINTREVERSEFLAG:
2008 case LyXRC::RC_PRINTSPOOL_COMMAND:
2009 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2010 case LyXRC::RC_PRINTTOFILE:
2011 case LyXRC::RC_PRINTTOPRINTER:
2012 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2013 case LyXRC::RC_PRINT_COMMAND:
2014 case LyXRC::RC_RTL_SUPPORT:
2015 case LyXRC::RC_SCREEN_DPI:
2016 case LyXRC::RC_SCREEN_FONT_ROMAN:
2017 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2018 case LyXRC::RC_SCREEN_FONT_SANS:
2019 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2020 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2021 case LyXRC::RC_SCREEN_FONT_SIZES:
2022 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2023 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2024 case LyXRC::RC_GEOMETRY_SESSION:
2025 case LyXRC::RC_SCREEN_ZOOM:
2026 case LyXRC::RC_SERVERPIPE:
2027 case LyXRC::RC_SET_COLOR:
2028 case LyXRC::RC_SHOW_BANNER:
2029 case LyXRC::RC_OPEN_BUFFERS_IN_TABS:
2030 case LyXRC::RC_SPELL_COMMAND:
2031 case LyXRC::RC_SPELLCHECKER:
2032 case LyXRC::RC_SPELLCHECK_CONTINUOUSLY:
2033 case LyXRC::RC_SPLITINDEX_COMMAND:
2034 case LyXRC::RC_TEMPDIRPATH:
2035 case LyXRC::RC_TEMPLATEPATH:
2036 case LyXRC::RC_TEX_ALLOWS_SPACES:
2037 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2038 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2039 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2041 case LyXRC::RC_THESAURUSDIRPATH:
2042 case LyXRC::RC_UIFILE:
2043 case LyXRC::RC_USER_EMAIL:
2044 case LyXRC::RC_USER_NAME:
2045 case LyXRC::RC_USETEMPDIR:
2046 case LyXRC::RC_USE_ALT_LANG:
2047 case LyXRC::RC_USE_CONVERTER_CACHE:
2048 case LyXRC::RC_USE_ESC_CHARS:
2049 case LyXRC::RC_USE_INP_ENC:
2050 case LyXRC::RC_USE_PERS_DICT:
2051 case LyXRC::RC_USE_TOOLTIP:
2052 case LyXRC::RC_USE_PIXMAP_CACHE:
2053 case LyXRC::RC_USE_SPELL_LIB:
2054 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2055 case LyXRC::RC_SORT_LAYOUTS:
2056 case LyXRC::RC_FULL_SCREEN_LIMIT:
2057 case LyXRC::RC_FULL_SCREEN_SCROLLBAR:
2058 case LyXRC::RC_FULL_SCREEN_MENUBAR:
2059 case LyXRC::RC_FULL_SCREEN_TABBAR:
2060 case LyXRC::RC_FULL_SCREEN_TOOLBARS:
2061 case LyXRC::RC_FULL_SCREEN_WIDTH:
2062 case LyXRC::RC_VISUAL_CURSOR:
2063 case LyXRC::RC_VIEWER:
2064 case LyXRC::RC_VIEWER_ALTERNATIVES:
2065 case LyXRC::RC_LAST: