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_CITATION_INSERT: {
467 FuncRequest fr(LFUN_INSET_INSERT, "citation");
468 enable = getStatus(fr).enabled();
472 // This could be used for the no-GUI version. The GUI version is handled in
473 // LyXView::getStatus(). See above.
475 case LFUN_BUFFER_WRITE:
476 case LFUN_BUFFER_WRITE_AS: {
477 Buffer * b = theBufferList().getBuffer(FileName(cmd.getArg(0)));
478 enable = b && (b->isUnnamed() || !b->isClean());
483 case LFUN_BUFFER_WRITE_ALL: {
484 // We enable the command only if there are some modified buffers
485 Buffer * first = theBufferList().first();
490 // We cannot use a for loop as the buffer list is a cycle.
496 b = theBufferList().next(b);
497 } while (b != first);
501 case LFUN_BOOKMARK_GOTO: {
502 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
503 enable = theSession().bookmarks().isValid(num);
507 case LFUN_BOOKMARK_CLEAR:
508 enable = theSession().bookmarks().hasValid();
511 // this one is difficult to get right. As a half-baked
512 // solution, we consider only the first action of the sequence
513 case LFUN_COMMAND_SEQUENCE: {
514 // argument contains ';'-terminated commands
515 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
516 FuncRequest func(lyxaction.lookupFunc(firstcmd));
517 func.origin = cmd.origin;
518 flag = getStatus(func);
522 // we want to check if at least one of these is enabled
523 case LFUN_COMMAND_ALTERNATIVES: {
524 // argument contains ';'-terminated commands
525 string arg = to_utf8(cmd.argument());
526 while (!arg.empty()) {
528 arg = split(arg, first, ';');
529 FuncRequest func(lyxaction.lookupFunc(first));
530 func.origin = cmd.origin;
531 flag = getStatus(func);
532 // if this one is enabled, the whole thing is
541 string name = to_utf8(cmd.argument());
542 if (theTopLevelCmdDef().lock(name, func)) {
543 func.origin = cmd.origin;
544 flag = getStatus(func);
545 theTopLevelCmdDef().release(name);
547 // catch recursion or unknown command
548 // definition. all operations until the
549 // recursion or unknown command definition
550 // occurs are performed, so set the state to
557 case LFUN_COMMAND_PREFIX:
559 case LFUN_META_PREFIX:
560 case LFUN_RECONFIGURE:
562 case LFUN_DROP_LAYOUTS_CHOICE:
563 case LFUN_SERVER_GET_FILENAME:
564 case LFUN_SERVER_NOTIFY:
565 case LFUN_SERVER_GOTO_FILE_ROW:
566 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
568 case LFUN_PREFERENCES_SAVE:
569 case LFUN_INSET_EDIT:
570 case LFUN_BUFFER_SAVE_AS_DEFAULT:
571 case LFUN_LYXRC_APPLY:
572 // these are handled in our dispatch()
580 if (theApp()->getStatus(cmd, flag))
583 // Does the view know something?
588 if (lv->getStatus(cmd, flag))
591 BufferView * bv = lv->currentBufferView();
592 BufferView * doc_bv = lv->documentBufferView();
593 // If we do not have a BufferView, then other functions are disabled
598 // Is this a function that acts on inset at point?
599 Inset * inset = bv->cursor().nextInset();
600 if (lyxaction.funcHasFlag(cmd.action, LyXAction::AtPoint)
601 && inset && inset->getStatus(bv->cursor(), cmd, flag))
604 bool decided = getLocalStatus(bv->cursor(), cmd, flag);
606 // try the BufferView
607 decided = bv->getStatus(cmd, flag);
610 decided = bv->buffer().getStatus(cmd, flag);
611 if (!decided && doc_bv)
612 // try the Document Buffer
613 decided = doc_bv->buffer().getStatus(cmd, flag);
617 flag.setEnabled(false);
619 // Can we use a readonly buffer?
620 if (buf && buf->isReadonly()
621 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
622 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
623 flag.message(from_utf8(N_("Document is read-only")));
624 flag.setEnabled(false);
627 // Are we in a DELETED change-tracking region?
628 if (lyx_view_ && lyx_view_->documentBufferView()
629 && (lookupChangeType(lyx_view_->documentBufferView()->cursor(), true)
631 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
632 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
633 flag.message(from_utf8(N_("This portion of the document is deleted.")));
634 flag.setEnabled(false);
637 // the default error message if we disable the command
638 if (!flag.enabled() && flag.message().empty())
639 flag.message(from_utf8(N_("Command disabled")));
647 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
652 void LyXFunc::dispatch(FuncRequest const & cmd)
654 string const argument = to_utf8(cmd.argument());
655 FuncCode const action = cmd.action;
657 LYXERR(Debug::ACTION, "\nLyXFunc::dispatch: cmd: " << cmd);
658 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
660 // we have not done anything wrong yet.
662 dispatch_buffer.erase();
664 // redraw the screen at the end (first of the two drawing steps).
665 //This is done unless explicitely requested otherwise
666 Update::flags updateFlags = Update::FitCursor;
668 FuncStatus const flag = getStatus(cmd);
669 if (!flag.enabled()) {
670 // We cannot use this function here
671 LYXERR(Debug::ACTION, "LyXFunc::dispatch: "
672 << lyxaction.getActionName(action)
673 << " [" << action << "] is disabled at this location");
674 setErrorMessage(flag.message());
676 lyx_view_->restartCursor();
679 if (lyx_view_ && lyx_view_->currentBufferView())
680 buffer = &lyx_view_->currentBufferView()->buffer();
683 case LFUN_COMMAND_PREFIX:
684 LASSERT(lyx_view_, /**/);
685 lyx_view_->message(keyseq.printOptions(true));
689 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
691 meta_fake_bit = NoModifier;
693 // cancel any selection
694 dispatch(FuncRequest(LFUN_MARK_OFF));
695 setMessage(from_ascii(N_("Cancel")));
698 case LFUN_META_PREFIX:
699 meta_fake_bit = AltModifier;
700 setMessage(keyseq.print(KeySequence::ForGui));
703 // --- Menus -----------------------------------------------
704 case LFUN_RECONFIGURE:
705 // argument is any additional parameter to the configure.py command
706 reconfigure(lyx_view_, argument);
709 case LFUN_HELP_OPEN: {
711 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
712 string const arg = argument;
714 setErrorMessage(from_utf8(N_("Missing argument")));
717 FileName fname = i18nLibFileSearch("doc", arg, "lyx");
719 fname = i18nLibFileSearch("examples", arg, "lyx");
722 lyxerr << "LyX: unable to find documentation file `"
723 << arg << "'. Bad installation?" << endl;
726 lyx_view_->message(bformat(_("Opening help file %1$s..."),
727 makeDisplayPath(fname.absFilename())));
728 Buffer * buf = lyx_view_->loadDocument(fname, false);
731 lyx_view_->setBuffer(buf);
732 buf->errors("Parse");
734 updateFlags = Update::None;
738 // --- lyxserver commands ----------------------------
739 case LFUN_SERVER_GET_FILENAME:
740 LASSERT(lyx_view_ && buffer, /**/);
741 setMessage(from_utf8(buffer->absFileName()));
742 LYXERR(Debug::INFO, "FNAME["
743 << buffer->absFileName() << ']');
746 case LFUN_SERVER_NOTIFY:
747 dispatch_buffer = keyseq.print(KeySequence::Portable);
748 theServer().notifyClient(to_utf8(dispatch_buffer));
751 case LFUN_SERVER_GOTO_FILE_ROW: {
752 LASSERT(lyx_view_, /**/);
755 istringstream is(argument);
756 is >> file_name >> row;
757 file_name = os::internal_path(file_name);
760 string const abstmp = package().temp_dir().absFilename();
761 string const realtmp = package().temp_dir().realPath();
762 // We have to use os::path_prefix_is() here, instead of
763 // simply prefixIs(), because the file name comes from
764 // an external application and may need case adjustment.
765 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
766 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
767 // Needed by inverse dvi search. If it is a file
768 // in tmpdir, call the apropriated function.
769 // If tmpdir is a symlink, we may have the real
770 // path passed back, so we correct for that.
771 if (!prefixIs(file_name, abstmp))
772 file_name = subst(file_name, realtmp, abstmp);
773 buf = theBufferList().getBufferFromTmp(file_name);
775 // Must replace extension of the file to be .lyx
777 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
778 // Either change buffer or load the file
779 if (theBufferList().exists(s))
780 buf = theBufferList().getBuffer(s);
781 else if (s.exists()) {
782 buf = lyx_view_->loadDocument(s);
785 lyx_view_->message(bformat(
786 _("File does not exist: %1$s"),
787 makeDisplayPath(file_name)));
791 updateFlags = Update::None;
796 lyx_view_->setBuffer(buf);
797 lyx_view_->documentBufferView()->setCursorFromRow(row);
799 buf->errors("Parse");
800 updateFlags = Update::FitCursor;
805 case LFUN_DIALOG_SHOW_NEW_INSET: {
806 LASSERT(lyx_view_, /**/);
807 string const name = cmd.getArg(0);
808 InsetCode code = insetCode(name);
809 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
810 bool insetCodeOK = true;
817 case NOMENCL_PRINT_CODE:
820 case HYPERLINK_CODE: {
821 InsetCommandParams p(code);
822 data = InsetCommand::params2string(name, p);
826 // data is the include type: one of "include",
827 // "input", "verbatiminput" or "verbatiminput*"
829 // default type is requested
831 InsetCommandParams p(INCLUDE_CODE, data);
832 data = InsetCommand::params2string("include", p);
836 // \c data == "Boxed" || "Frameless" etc
837 InsetBoxParams p(data);
838 data = InsetBox::params2string(p);
843 data = InsetBranch::params2string(p);
847 InsetCommandParams p(CITE_CODE);
848 data = InsetCommand::params2string(name, p);
852 data = InsetERT::params2string(InsetCollapsable::Open);
855 case EXTERNAL_CODE: {
856 InsetExternalParams p;
857 data = InsetExternal::params2string(p, *buffer);
862 data = InsetFloat::params2string(p);
865 case LISTINGS_CODE: {
866 InsetListingsParams p;
867 data = InsetListings::params2string(p);
870 case GRAPHICS_CODE: {
871 InsetGraphicsParams p;
872 data = InsetGraphics::params2string(p, *buffer);
877 data = InsetNote::params2string(p);
881 InsetPhantomParams p;
882 data = InsetPhantom::params2string(p);
887 data = InsetSpace::params2string(p);
892 data = InsetVSpace::params2string(space);
897 data = InsetWrap::params2string(p);
901 lyxerr << "Inset type '" << name <<
902 "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" << endl;
905 } // end switch(code)
907 dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data));
911 case LFUN_CITATION_INSERT: {
912 LASSERT(lyx_view_, /**/);
913 if (!argument.empty()) {
914 // we can have one optional argument, delimited by '|'
915 // citation-insert <key>|<text_before>
916 // this should be enhanced to also support text_after
917 // and citation style
918 string arg = argument;
920 if (contains(argument, "|")) {
921 arg = token(argument, '|', 0);
922 opt1 = token(argument, '|', 1);
924 InsetCommandParams icp(CITE_CODE);
925 icp["key"] = from_utf8(arg);
927 icp["before"] = from_utf8(opt1);
928 string icstr = InsetCommand::params2string("citation", icp);
929 FuncRequest fr(LFUN_INSET_INSERT, icstr);
932 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
936 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
937 LASSERT(lyx_view_, /**/);
938 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
944 string rest = split(argument, countstr, ' ');
945 istringstream is(countstr);
948 //lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
949 for (int i = 0; i < count; ++i)
950 dispatch(lyxaction.lookupFunc(rest));
954 case LFUN_COMMAND_SEQUENCE: {
955 // argument contains ';'-terminated commands
956 string arg = argument;
957 if (theBufferList().isLoaded(buffer))
958 buffer->undo().beginUndoGroup();
959 while (!arg.empty()) {
961 arg = split(arg, first, ';');
962 FuncRequest func(lyxaction.lookupFunc(first));
963 func.origin = cmd.origin;
966 if (theBufferList().isLoaded(buffer))
967 buffer->undo().endUndoGroup();
971 case LFUN_COMMAND_ALTERNATIVES: {
972 // argument contains ';'-terminated commands
973 string arg = argument;
974 while (!arg.empty()) {
976 arg = split(arg, first, ';');
977 FuncRequest func(lyxaction.lookupFunc(first));
978 func.origin = cmd.origin;
979 FuncStatus stat = getStatus(func);
980 if (stat.enabled()) {
990 if (theTopLevelCmdDef().lock(argument, func)) {
991 func.origin = cmd.origin;
993 theTopLevelCmdDef().release(argument);
995 if (func.action == LFUN_UNKNOWN_ACTION) {
996 // unknown command definition
997 lyxerr << "Warning: unknown command definition `"
1001 // recursion detected
1002 lyxerr << "Warning: Recursion in the command definition `"
1003 << argument << "' detected"
1010 case LFUN_PREFERENCES_SAVE: {
1011 lyxrc.write(makeAbsPath("preferences",
1012 package().user_support().absFilename()),
1017 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1018 string const fname =
1019 addName(addPath(package().user_support().absFilename(), "templates/"),
1021 Buffer defaults(fname);
1023 istringstream ss(argument);
1026 int const unknown_tokens = defaults.readHeader(lex);
1028 if (unknown_tokens != 0) {
1029 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1030 << unknown_tokens << " unknown token"
1031 << (unknown_tokens == 1 ? "" : "s")
1035 if (defaults.writeFile(FileName(defaults.absFileName())))
1036 setMessage(bformat(_("Document defaults saved in %1$s"),
1037 makeDisplayPath(fname)));
1039 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1043 case LFUN_LYXRC_APPLY: {
1044 // reset active key sequences, since the bindings
1045 // are updated (bug 6064)
1047 LyXRC const lyxrc_orig = lyxrc;
1049 istringstream ss(argument);
1050 bool const success = lyxrc.read(ss) == 0;
1053 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1054 << "Unable to read lyxrc data"
1059 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1063 theApp()->resetGui();
1065 /// We force the redraw in any case because there might be
1066 /// some screen font changes.
1067 /// FIXME: only the current view will be updated. the Gui
1068 /// class is able to furnish the list of views.
1069 updateFlags = Update::Force;
1073 case LFUN_BOOKMARK_GOTO:
1074 // go to bookmark, open unopened file and switch to buffer if necessary
1075 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1076 updateFlags = Update::FitCursor;
1079 case LFUN_BOOKMARK_CLEAR:
1080 theSession().bookmarks().clear();
1084 LASSERT(theApp(), /**/);
1085 // Let the frontend dispatch its own actions.
1086 if (theApp()->dispatch(cmd))
1087 // Nothing more to do.
1090 // Everything below is only for active lyx_view_
1094 BufferView * bv = lyx_view_->currentBufferView();
1095 BufferView * doc_bv = lyx_view_->documentBufferView();
1097 // Start an undo group. This may be needed for
1098 // some stuff like inset-apply on labels.
1099 if (theBufferList().isLoaded(buffer))
1100 buffer->undo().beginUndoGroup();
1102 // Let the current LyXView dispatch its own actions.
1103 if (lyx_view_->dispatch(cmd)) {
1105 updateFlags = bv->cursor().result().update();
1106 if (theBufferList().isLoaded(buffer))
1107 buffer->undo().endUndoGroup();
1114 // Let the current BufferView dispatch its own actions.
1115 if (bv->dispatch(cmd)) {
1116 // The BufferView took care of its own updates if needed.
1117 updateFlags = Update::None;
1118 if (theBufferList().isLoaded(buffer))
1119 buffer->undo().endUndoGroup();
1122 // Try with the document BufferView dispatch if any.
1123 if (doc_bv && doc_bv->dispatch(cmd)) {
1124 // The BufferView took care of its own updates if needed.
1125 buffer = &(doc_bv->buffer());
1126 updateFlags = Update::None;
1127 if (theBufferList().isLoaded(buffer))
1128 buffer->undo().endUndoGroup();
1132 // OK, so try the current Buffer itself...
1134 bv->buffer().dispatch(cmd, dr);
1135 if (dr.dispatched()) {
1136 updateFlags = dr.update();
1139 // and with the document Buffer.
1141 doc_bv->buffer().dispatch(cmd, dr);
1142 if (dr.dispatched()) {
1143 updateFlags = dr.update();
1148 // Is this a function that acts on inset at point?
1149 Inset * inset = bv->cursor().nextInset();
1150 if (lyxaction.funcHasFlag(action, LyXAction::AtPoint)
1152 bv->cursor().result().dispatched(true);
1153 bv->cursor().result().update(Update::FitCursor | Update::Force);
1154 FuncRequest tmpcmd = cmd;
1155 inset->dispatch(bv->cursor(), tmpcmd);
1156 if (bv->cursor().result().dispatched()) {
1157 updateFlags = bv->cursor().result().update();
1162 // Let the current Cursor dispatch its own actions.
1163 Cursor old = bv->cursor();
1164 bv->cursor().getPos(cursorPosBeforeDispatchX_,
1165 cursorPosBeforeDispatchY_);
1166 bv->cursor().dispatch(cmd);
1168 // notify insets we just left
1169 if (bv->cursor() != old) {
1171 bool badcursor = notifyCursorLeavesOrEnters(old, bv->cursor());
1173 bv->cursor().fixIfBroken();
1176 if (theBufferList().isLoaded(buffer))
1177 buffer->undo().endUndoGroup();
1179 // update completion. We do it here and not in
1180 // processKeySym to avoid another redraw just for a
1181 // changed inline completion
1182 if (cmd.origin == FuncRequest::KEYBOARD) {
1183 if (cmd.action == LFUN_SELF_INSERT
1184 || (cmd.action == LFUN_ERT_INSERT && bv->cursor().inMathed()))
1185 lyx_view_->updateCompletion(bv->cursor(), true, true);
1186 else if (cmd.action == LFUN_CHAR_DELETE_BACKWARD)
1187 lyx_view_->updateCompletion(bv->cursor(), false, true);
1189 lyx_view_->updateCompletion(bv->cursor(), false, false);
1192 updateFlags = bv->cursor().result().update();
1195 // if we executed a mutating lfun, mark the buffer as dirty
1196 if (theBufferList().isLoaded(buffer) && flag.enabled()
1197 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1198 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1199 buffer->markDirty();
1201 if (lyx_view_ && lyx_view_->currentBufferView()) {
1202 // BufferView::update() updates the ViewMetricsInfo and
1203 // also initializes the position cache for all insets in
1204 // (at least partially) visible top-level paragraphs.
1205 // We will redraw the screen only if needed.
1206 lyx_view_->currentBufferView()->processUpdateFlags(updateFlags);
1208 // Do we have a selection?
1209 theSelection().haveSelection(
1210 lyx_view_->currentBufferView()->cursor().selection());
1213 lyx_view_->restartCursor();
1217 // Some messages may already be translated, so we cannot use _()
1218 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1223 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1225 const bool verbose = (cmd.origin == FuncRequest::MENU
1226 || cmd.origin == FuncRequest::TOOLBAR
1227 || cmd.origin == FuncRequest::COMMANDBUFFER);
1229 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1230 LYXERR(Debug::ACTION, "dispatch msg is " << to_utf8(msg));
1232 lyx_view_->message(msg);
1236 docstring dispatch_msg = msg;
1237 if (!dispatch_msg.empty())
1238 dispatch_msg += ' ';
1240 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1242 bool argsadded = false;
1244 if (!cmd.argument().empty()) {
1245 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1246 comname += ' ' + cmd.argument();
1251 docstring const shortcuts = theTopLevelKeymap().printBindings(cmd, KeySequence::ForGui);
1253 if (!shortcuts.empty())
1254 comname += ": " + shortcuts;
1255 else if (!argsadded && !cmd.argument().empty())
1256 comname += ' ' + cmd.argument();
1258 if (!comname.empty()) {
1259 comname = rtrim(comname);
1260 dispatch_msg += '(' + rtrim(comname) + ')';
1263 LYXERR(Debug::ACTION, "verbose dispatch msg " << to_utf8(dispatch_msg));
1264 if (!dispatch_msg.empty())
1265 lyx_view_->message(dispatch_msg);
1269 // Each "lyx_view_" should have it's own message method. lyxview and
1270 // the minibuffer would use the minibuffer, but lyxserver would
1271 // send an ERROR signal to its client. Alejandro 970603
1272 // This function is bit problematic when it comes to NLS, to make the
1273 // lyx servers client be language indepenent we must not translate
1274 // strings sent to this func.
1275 void LyXFunc::setErrorMessage(docstring const & m) const
1277 dispatch_buffer = m;
1282 void LyXFunc::setMessage(docstring const & m) const
1284 dispatch_buffer = m;
1288 docstring LyXFunc::viewStatusMessage()
1290 // When meta-fake key is pressed, show the key sequence so far + "M-".
1292 return keyseq.print(KeySequence::ForGui) + "M-";
1294 // Else, when a non-complete key sequence is pressed,
1295 // show the available options.
1296 if (keyseq.length() > 0 && !keyseq.deleted())
1297 return keyseq.printOptions(true);
1299 LASSERT(lyx_view_, /**/);
1300 if (!lyx_view_->currentBufferView())
1301 return _("Welcome to LyX!");
1303 return lyx_view_->currentBufferView()->cursor().currentState();
1307 bool LyXFunc::wasMetaKey() const
1309 return (meta_fake_bit != NoModifier);
1315 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1317 // Why the switch you might ask. It is a trick to ensure that all
1318 // the elements in the LyXRCTags enum is handled. As you can see
1319 // there are no breaks at all. So it is just a huge fall-through.
1320 // The nice thing is that we will get a warning from the compiler
1321 // if we forget an element.
1322 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1324 case LyXRC::RC_ACCEPT_COMPOUND:
1325 case LyXRC::RC_ALT_LANG:
1326 case LyXRC::RC_PLAINTEXT_LINELEN:
1327 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
1328 case LyXRC::RC_AUTOCORRECTION_MATH:
1329 case LyXRC::RC_AUTOREGIONDELETE:
1330 case LyXRC::RC_AUTORESET_OPTIONS:
1331 case LyXRC::RC_AUTOSAVE:
1332 case LyXRC::RC_AUTO_NUMBER:
1333 case LyXRC::RC_BACKUPDIR_PATH:
1334 case LyXRC::RC_BIBTEX_ALTERNATIVES:
1335 case LyXRC::RC_BIBTEX_COMMAND:
1336 case LyXRC::RC_BINDFILE:
1337 case LyXRC::RC_CHECKLASTFILES:
1338 case LyXRC::RC_COMPLETION_CURSOR_TEXT:
1339 case LyXRC::RC_COMPLETION_INLINE_DELAY:
1340 case LyXRC::RC_COMPLETION_INLINE_DOTS:
1341 case LyXRC::RC_COMPLETION_INLINE_MATH:
1342 case LyXRC::RC_COMPLETION_INLINE_TEXT:
1343 case LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE:
1344 case LyXRC::RC_COMPLETION_POPUP_DELAY:
1345 case LyXRC::RC_COMPLETION_POPUP_MATH:
1346 case LyXRC::RC_COMPLETION_POPUP_TEXT:
1347 case LyXRC::RC_USELASTFILEPOS:
1348 case LyXRC::RC_LOADSESSION:
1349 case LyXRC::RC_CHKTEX_COMMAND:
1350 case LyXRC::RC_CONVERTER:
1351 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
1352 case LyXRC::RC_COPIER:
1353 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1354 case LyXRC::RC_SCROLL_BELOW_DOCUMENT:
1355 case LyXRC::RC_DATE_INSERT_FORMAT:
1356 case LyXRC::RC_DEFAULT_LANGUAGE:
1357 case LyXRC::RC_GUI_LANGUAGE:
1358 case LyXRC::RC_DEFAULT_PAPERSIZE:
1359 case LyXRC::RC_DEFAULT_VIEW_FORMAT:
1360 case LyXRC::RC_DEFFILE:
1361 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1362 case LyXRC::RC_DISPLAY_GRAPHICS:
1363 case LyXRC::RC_DOCUMENTPATH:
1364 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1365 FileName path(lyxrc_new.document_path);
1366 if (path.exists() && path.isDirectory())
1367 package().document_dir() = FileName(lyxrc.document_path);
1369 case LyXRC::RC_EDITOR_ALTERNATIVES:
1370 case LyXRC::RC_ESC_CHARS:
1371 case LyXRC::RC_EXAMPLEPATH:
1372 case LyXRC::RC_FONT_ENCODING:
1373 case LyXRC::RC_FORMAT:
1374 case LyXRC::RC_GROUP_LAYOUTS:
1375 case LyXRC::RC_HUNSPELLDIR_PATH:
1376 case LyXRC::RC_INDEX_ALTERNATIVES:
1377 case LyXRC::RC_INDEX_COMMAND:
1378 case LyXRC::RC_JBIBTEX_COMMAND:
1379 case LyXRC::RC_JINDEX_COMMAND:
1380 case LyXRC::RC_NOMENCL_COMMAND:
1381 case LyXRC::RC_INPUT:
1382 case LyXRC::RC_KBMAP:
1383 case LyXRC::RC_KBMAP_PRIMARY:
1384 case LyXRC::RC_KBMAP_SECONDARY:
1385 case LyXRC::RC_LABEL_INIT_LENGTH:
1386 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
1387 case LyXRC::RC_LANGUAGE_AUTO_END:
1388 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
1389 case LyXRC::RC_LANGUAGE_COMMAND_END:
1390 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
1391 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
1392 case LyXRC::RC_LANGUAGE_PACKAGE:
1393 case LyXRC::RC_LANGUAGE_USE_BABEL:
1394 case LyXRC::RC_MAC_LIKE_WORD_MOVEMENT:
1395 case LyXRC::RC_MACRO_EDIT_STYLE:
1396 case LyXRC::RC_MAKE_BACKUP:
1397 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
1398 case LyXRC::RC_MOUSE_WHEEL_SPEED:
1399 case LyXRC::RC_NUMLASTFILES:
1400 case LyXRC::RC_PARAGRAPH_MARKERS:
1401 case LyXRC::RC_PATH_PREFIX:
1402 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
1403 prependEnvPath("PATH", lyxrc.path_prefix);
1405 case LyXRC::RC_PERS_DICT:
1406 case LyXRC::RC_PREVIEW:
1407 case LyXRC::RC_PREVIEW_HASHED_LABELS:
1408 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
1409 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
1410 case LyXRC::RC_PRINTCOPIESFLAG:
1411 case LyXRC::RC_PRINTER:
1412 case LyXRC::RC_PRINTEVENPAGEFLAG:
1413 case LyXRC::RC_PRINTEXSTRAOPTIONS:
1414 case LyXRC::RC_PRINTFILEEXTENSION:
1415 case LyXRC::RC_PRINTLANDSCAPEFLAG:
1416 case LyXRC::RC_PRINTODDPAGEFLAG:
1417 case LyXRC::RC_PRINTPAGERANGEFLAG:
1418 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
1419 case LyXRC::RC_PRINTPAPERFLAG:
1420 case LyXRC::RC_PRINTREVERSEFLAG:
1421 case LyXRC::RC_PRINTSPOOL_COMMAND:
1422 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
1423 case LyXRC::RC_PRINTTOFILE:
1424 case LyXRC::RC_PRINTTOPRINTER:
1425 case LyXRC::RC_PRINT_ADAPTOUTPUT:
1426 case LyXRC::RC_PRINT_COMMAND:
1427 case LyXRC::RC_RTL_SUPPORT:
1428 case LyXRC::RC_SCREEN_DPI:
1429 case LyXRC::RC_SCREEN_FONT_ROMAN:
1430 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
1431 case LyXRC::RC_SCREEN_FONT_SANS:
1432 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
1433 case LyXRC::RC_SCREEN_FONT_SCALABLE:
1434 case LyXRC::RC_SCREEN_FONT_SIZES:
1435 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
1436 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
1437 case LyXRC::RC_GEOMETRY_SESSION:
1438 case LyXRC::RC_SCREEN_ZOOM:
1439 case LyXRC::RC_SERVERPIPE:
1440 case LyXRC::RC_SET_COLOR:
1441 case LyXRC::RC_SHOW_BANNER:
1442 case LyXRC::RC_OPEN_BUFFERS_IN_TABS:
1443 case LyXRC::RC_SPELL_COMMAND:
1444 case LyXRC::RC_SPELLCHECKER:
1445 case LyXRC::RC_SPELLCHECK_CONTINUOUSLY:
1446 case LyXRC::RC_SPLITINDEX_COMMAND:
1447 case LyXRC::RC_TEMPDIRPATH:
1448 case LyXRC::RC_TEMPLATEPATH:
1449 case LyXRC::RC_TEX_ALLOWS_SPACES:
1450 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
1451 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
1452 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
1454 case LyXRC::RC_THESAURUSDIRPATH:
1455 case LyXRC::RC_UIFILE:
1456 case LyXRC::RC_USER_EMAIL:
1457 case LyXRC::RC_USER_NAME:
1458 case LyXRC::RC_USETEMPDIR:
1459 case LyXRC::RC_USE_ALT_LANG:
1460 case LyXRC::RC_USE_CONVERTER_CACHE:
1461 case LyXRC::RC_USE_ESC_CHARS:
1462 case LyXRC::RC_USE_INP_ENC:
1463 case LyXRC::RC_USE_PERS_DICT:
1464 case LyXRC::RC_USE_TOOLTIP:
1465 case LyXRC::RC_USE_PIXMAP_CACHE:
1466 case LyXRC::RC_USE_SPELL_LIB:
1467 case LyXRC::RC_VIEWDVI_PAPEROPTION:
1468 case LyXRC::RC_SORT_LAYOUTS:
1469 case LyXRC::RC_FULL_SCREEN_LIMIT:
1470 case LyXRC::RC_FULL_SCREEN_SCROLLBAR:
1471 case LyXRC::RC_FULL_SCREEN_MENUBAR:
1472 case LyXRC::RC_FULL_SCREEN_TABBAR:
1473 case LyXRC::RC_FULL_SCREEN_TOOLBARS:
1474 case LyXRC::RC_FULL_SCREEN_WIDTH:
1475 case LyXRC::RC_VISUAL_CURSOR:
1476 case LyXRC::RC_VIEWER:
1477 case LyXRC::RC_VIEWER_ALTERNATIVES:
1478 case LyXRC::RC_LAST: