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 : 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::handleKeyFunc(FuncCode action)
214 char_type c = encoded_last_key;
218 LyXView * lv = theApp()->currentWindow();
219 LASSERT(lv && lv->currentBufferView(), /**/);
220 BufferView * bv = lv->currentBufferView();
221 bv->getIntl().getTransManager().deadkey(
222 c, get_accent(action).accent, bv->cursor().innerText(),
224 // Need to clear, in case the minibuffer calls these
227 // copied verbatim from do_accent_char
228 bv->cursor().resetAnchor();
229 bv->processUpdateFlags(Update::FitCursor);
232 //FIXME: bookmark handling is a frontend issue. This code should be transferred
233 // to GuiView and be GuiView and be window dependent.
234 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
236 LyXView * lv = theApp()->currentWindow();
238 if (!theSession().bookmarks().isValid(idx))
240 BookmarksSection::Bookmark const & bm = theSession().bookmarks().bookmark(idx);
241 LASSERT(!bm.filename.empty(), /**/);
242 string const file = bm.filename.absFilename();
243 // if the file is not opened, open it.
244 if (!theBufferList().exists(bm.filename)) {
246 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
250 // open may fail, so we need to test it again
251 if (!theBufferList().exists(bm.filename))
254 // bm can be changed when saving
255 BookmarksSection::Bookmark tmp = bm;
257 // Special case idx == 0 used for back-from-back jump navigation
259 dispatch(FuncRequest(LFUN_BOOKMARK_SAVE, "0"));
261 // if the current buffer is not that one, switch to it.
262 if (!lv->documentBufferView()
263 || lv->documentBufferView()->buffer().fileName() != tmp.filename) {
266 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
269 // moveToPosition try paragraph id first and then paragraph (pit, pos).
270 if (!lv->documentBufferView()->moveToPosition(
271 tmp.bottom_pit, tmp.bottom_pos, tmp.top_id, tmp.top_pos))
278 // Cursor jump succeeded!
279 Cursor const & cur = lv->documentBufferView()->cursor();
280 pit_type new_pit = cur.pit();
281 pos_type new_pos = cur.pos();
282 int new_id = cur.paragraph().id();
284 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
285 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
286 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos
287 || bm.top_id != new_id) {
288 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(
289 new_pit, new_pos, new_id);
294 void LyXFunc::processKeySym(KeySymbol const & keysym, KeyModifier state)
296 LYXERR(Debug::KEY, "KeySym is " << keysym.getSymbolName());
298 LyXView * lv = theApp()->currentWindow();
300 // Do nothing if we have nothing (JMarc)
301 if (!keysym.isOK()) {
302 LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
307 if (keysym.isModifier()) {
308 LYXERR(Debug::KEY, "isModifier true");
314 //Encoding const * encoding = lv->documentBufferView()->cursor().getEncoding();
315 //encoded_last_key = keysym.getISOEncoded(encoding ? encoding->name() : "");
316 // FIXME: encoded_last_key shadows the member variable of the same
317 // name. Is that intended?
318 char_type encoded_last_key = keysym.getUCSEncoded();
320 // Do a one-deep top-level lookup for
321 // cancel and meta-fake keys. RVDK_PATCH_5
322 cancel_meta_seq.reset();
324 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
325 LYXERR(Debug::KEY, "action first set to [" << func.action << ']');
327 // When not cancel or meta-fake, do the normal lookup.
328 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
329 // Mostly, meta_fake_bit = NoModifier. RVDK_PATCH_5.
330 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
331 // remove Caps Lock and Mod2 as a modifiers
332 func = keyseq.addkey(keysym, (state | meta_fake_bit));
333 LYXERR(Debug::KEY, "action now set to [" << func.action << ']');
336 // Dont remove this unless you know what you are doing.
337 meta_fake_bit = NoModifier;
339 // Can this happen now ?
340 if (func.action == LFUN_NOACTION)
341 func = FuncRequest(LFUN_COMMAND_PREFIX);
343 LYXERR(Debug::KEY, " Key [action=" << func.action << "]["
344 << keyseq.print(KeySequence::Portable) << ']');
346 // already here we know if it any point in going further
347 // why not return already here if action == -1 and
348 // num_bytes == 0? (Lgb)
350 if (keyseq.length() > 1)
351 lv->message(keyseq.print(KeySequence::ForGui));
354 // Maybe user can only reach the key via holding down shift.
355 // Let's see. But only if shift is the only modifier
356 if (func.action == LFUN_UNKNOWN_ACTION && state == ShiftModifier) {
357 LYXERR(Debug::KEY, "Trying without shift");
358 func = keyseq.addkey(keysym, NoModifier);
359 LYXERR(Debug::KEY, "Action now " << func.action);
362 if (func.action == LFUN_UNKNOWN_ACTION) {
363 // Hmm, we didn't match any of the keysequences. See
364 // if it's normal insertable text not already covered
366 if (keysym.isText() && keyseq.length() == 1) {
367 LYXERR(Debug::KEY, "isText() is true, inserting.");
368 func = FuncRequest(LFUN_SELF_INSERT,
369 FuncRequest::KEYBOARD);
371 LYXERR(Debug::KEY, "Unknown, !isText() - giving up");
372 lv->message(_("Unknown function."));
378 if (func.action == LFUN_SELF_INSERT) {
379 if (encoded_last_key != 0) {
380 docstring const arg(1, encoded_last_key);
381 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
382 FuncRequest::KEYBOARD));
383 LYXERR(Debug::KEY, "SelfInsert arg[`" << to_utf8(arg) << "']");
393 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
395 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
398 /* In LyX/Mac, when a dialog is open, the menus of the
399 application can still be accessed without giving focus to
400 the main window. In this case, we want to disable the menu
401 entries that are buffer or view-related.
403 If this code is moved somewhere else (like in
404 GuiView::getStatus), then several functions will not be
407 frontend::LyXView * lv_current = theApp()->currentWindow();
408 frontend::LyXView * lv = 0;
411 && (cmd.origin != FuncRequest::MENU || lv_current->hasFocus())) {
413 if (lv_current->documentBufferView())
414 buf = &lv_current->documentBufferView()->buffer();
417 if (cmd.action == LFUN_NOACTION) {
418 flag.message(from_utf8(N_("Nothing to do")));
419 flag.setEnabled(false);
423 switch (cmd.action) {
424 case LFUN_UNKNOWN_ACTION:
426 flag.setEnabled(false);
433 if (flag.unknown()) {
434 flag.message(from_utf8(N_("Unknown action")));
438 if (!flag.enabled()) {
439 if (flag.message().empty())
440 flag.message(from_utf8(N_("Command disabled")));
444 // Check whether we need a buffer
445 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
447 flag.message(from_utf8(N_("Command not allowed with"
448 "out any document open")));
449 flag.setEnabled(false);
453 // I would really like to avoid having this switch and rather try to
454 // encode this in the function itself.
455 // -- And I'd rather let an inset decide which LFUNs it is willing
456 // to handle (Andre')
458 switch (cmd.action) {
460 case LFUN_CITATION_INSERT: {
461 FuncRequest fr(LFUN_INSET_INSERT, "citation");
462 enable = getStatus(fr).enabled();
466 // This could be used for the no-GUI version. The GUI version is handled in
467 // LyXView::getStatus(). See above.
469 case LFUN_BUFFER_WRITE:
470 case LFUN_BUFFER_WRITE_AS: {
471 Buffer * b = theBufferList().getBuffer(FileName(cmd.getArg(0)));
472 enable = b && (b->isUnnamed() || !b->isClean());
477 case LFUN_BOOKMARK_GOTO: {
478 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
479 enable = theSession().bookmarks().isValid(num);
483 case LFUN_BOOKMARK_CLEAR:
484 enable = theSession().bookmarks().hasValid();
487 // this one is difficult to get right. As a half-baked
488 // solution, we consider only the first action of the sequence
489 case LFUN_COMMAND_SEQUENCE: {
490 // argument contains ';'-terminated commands
491 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
492 FuncRequest func(lyxaction.lookupFunc(firstcmd));
493 func.origin = cmd.origin;
494 flag = getStatus(func);
498 // we want to check if at least one of these is enabled
499 case LFUN_COMMAND_ALTERNATIVES: {
500 // argument contains ';'-terminated commands
501 string arg = to_utf8(cmd.argument());
502 while (!arg.empty()) {
504 arg = split(arg, first, ';');
505 FuncRequest func(lyxaction.lookupFunc(first));
506 func.origin = cmd.origin;
507 flag = getStatus(func);
508 // if this one is enabled, the whole thing is
517 string name = to_utf8(cmd.argument());
518 if (theTopLevelCmdDef().lock(name, func)) {
519 func.origin = cmd.origin;
520 flag = getStatus(func);
521 theTopLevelCmdDef().release(name);
523 // catch recursion or unknown command
524 // definition. all operations until the
525 // recursion or unknown command definition
526 // occurs are performed, so set the state to
533 case LFUN_COMMAND_PREFIX:
535 case LFUN_META_PREFIX:
536 case LFUN_RECONFIGURE:
537 case LFUN_DROP_LAYOUTS_CHOICE:
538 case LFUN_SERVER_GET_FILENAME:
539 case LFUN_SERVER_NOTIFY:
540 case LFUN_SERVER_GOTO_FILE_ROW:
541 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
543 case LFUN_PREFERENCES_SAVE:
544 case LFUN_INSET_EDIT:
545 case LFUN_BUFFER_SAVE_AS_DEFAULT:
546 case LFUN_LYXRC_APPLY:
547 // these are handled in our dispatch()
555 if (theApp()->getStatus(cmd, flag))
558 // Does the view know something?
563 if (lv->getStatus(cmd, flag))
566 BufferView * bv = lv->currentBufferView();
567 BufferView * doc_bv = lv->documentBufferView();
568 // If we do not have a BufferView, then other functions are disabled
573 // Is this a function that acts on inset at point?
574 Inset * inset = bv->cursor().nextInset();
575 if (lyxaction.funcHasFlag(cmd.action, LyXAction::AtPoint)
576 && inset && inset->getStatus(bv->cursor(), cmd, flag))
579 bool decided = getLocalStatus(bv->cursor(), cmd, flag);
581 // try the BufferView
582 decided = bv->getStatus(cmd, flag);
585 decided = bv->buffer().getStatus(cmd, flag);
586 if (!decided && doc_bv)
587 // try the Document Buffer
588 decided = doc_bv->buffer().getStatus(cmd, flag);
592 flag.setEnabled(false);
594 // Can we use a readonly buffer?
595 if (buf && buf->isReadonly()
596 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
597 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
598 flag.message(from_utf8(N_("Document is read-only")));
599 flag.setEnabled(false);
602 // Are we in a DELETED change-tracking region?
603 if (lv && lv->documentBufferView()
604 && (lookupChangeType(lv->documentBufferView()->cursor(), true)
606 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
607 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
608 flag.message(from_utf8(N_("This portion of the document is deleted.")));
609 flag.setEnabled(false);
612 // the default error message if we disable the command
613 if (!flag.enabled() && flag.message().empty())
614 flag.message(from_utf8(N_("Command disabled")));
622 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
627 void LyXFunc::dispatch(FuncRequest const & cmd)
629 string const argument = to_utf8(cmd.argument());
630 FuncCode const action = cmd.action;
632 LYXERR(Debug::ACTION, "\nLyXFunc::dispatch: cmd: " << cmd);
633 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
635 // we have not done anything wrong yet.
637 dispatch_buffer.erase();
639 // redraw the screen at the end (first of the two drawing steps).
640 //This is done unless explicitely requested otherwise
641 Update::flags updateFlags = Update::FitCursor;
643 LyXView * lv = theApp()->currentWindow();
645 FuncStatus const flag = getStatus(cmd);
646 if (!flag.enabled()) {
647 // We cannot use this function here
648 LYXERR(Debug::ACTION, "LyXFunc::dispatch: "
649 << lyxaction.getActionName(action)
650 << " [" << action << "] is disabled at this location");
651 setErrorMessage(flag.message());
656 if (lv && lv->currentBufferView())
657 buffer = &lv->currentBufferView()->buffer();
660 case LFUN_COMMAND_PREFIX:
662 lv->message(keyseq.printOptions(true));
666 LASSERT(lv && lv->currentBufferView(), /**/);
668 meta_fake_bit = NoModifier;
670 // cancel any selection
671 dispatch(FuncRequest(LFUN_MARK_OFF));
672 setMessage(from_ascii(N_("Cancel")));
675 case LFUN_META_PREFIX:
676 meta_fake_bit = AltModifier;
677 setMessage(keyseq.print(KeySequence::ForGui));
680 // --- Menus -----------------------------------------------
681 case LFUN_RECONFIGURE:
682 // argument is any additional parameter to the configure.py command
683 reconfigure(lv, argument);
686 // --- lyxserver commands ----------------------------
687 case LFUN_SERVER_GET_FILENAME:
688 LASSERT(lv && buffer, /**/);
689 setMessage(from_utf8(buffer->absFileName()));
690 LYXERR(Debug::INFO, "FNAME["
691 << buffer->absFileName() << ']');
694 case LFUN_SERVER_NOTIFY:
695 dispatch_buffer = keyseq.print(KeySequence::Portable);
696 theServer().notifyClient(to_utf8(dispatch_buffer));
699 case LFUN_SERVER_GOTO_FILE_ROW: {
703 istringstream is(argument);
704 is >> file_name >> row;
705 file_name = os::internal_path(file_name);
708 string const abstmp = package().temp_dir().absFilename();
709 string const realtmp = package().temp_dir().realPath();
710 // We have to use os::path_prefix_is() here, instead of
711 // simply prefixIs(), because the file name comes from
712 // an external application and may need case adjustment.
713 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
714 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
715 // Needed by inverse dvi search. If it is a file
716 // in tmpdir, call the apropriated function.
717 // If tmpdir is a symlink, we may have the real
718 // path passed back, so we correct for that.
719 if (!prefixIs(file_name, abstmp))
720 file_name = subst(file_name, realtmp, abstmp);
721 buf = theBufferList().getBufferFromTmp(file_name);
723 // Must replace extension of the file to be .lyx
725 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
726 // Either change buffer or load the file
727 if (theBufferList().exists(s))
728 buf = theBufferList().getBuffer(s);
729 else if (s.exists()) {
730 buf = lv->loadDocument(s);
734 _("File does not exist: %1$s"),
735 makeDisplayPath(file_name)));
739 updateFlags = Update::None;
745 lv->documentBufferView()->setCursorFromRow(row);
747 buf->errors("Parse");
748 updateFlags = Update::FitCursor;
753 case LFUN_DIALOG_SHOW_NEW_INSET: {
755 string const name = cmd.getArg(0);
756 InsetCode code = insetCode(name);
757 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
758 bool insetCodeOK = true;
765 case NOMENCL_PRINT_CODE:
768 case HYPERLINK_CODE: {
769 InsetCommandParams p(code);
770 data = InsetCommand::params2string(name, p);
774 // data is the include type: one of "include",
775 // "input", "verbatiminput" or "verbatiminput*"
777 // default type is requested
779 InsetCommandParams p(INCLUDE_CODE, data);
780 data = InsetCommand::params2string("include", p);
784 // \c data == "Boxed" || "Frameless" etc
785 InsetBoxParams p(data);
786 data = InsetBox::params2string(p);
791 data = InsetBranch::params2string(p);
795 InsetCommandParams p(CITE_CODE);
796 data = InsetCommand::params2string(name, p);
800 data = InsetERT::params2string(InsetCollapsable::Open);
803 case EXTERNAL_CODE: {
804 InsetExternalParams p;
805 data = InsetExternal::params2string(p, *buffer);
810 data = InsetFloat::params2string(p);
813 case LISTINGS_CODE: {
814 InsetListingsParams p;
815 data = InsetListings::params2string(p);
818 case GRAPHICS_CODE: {
819 InsetGraphicsParams p;
820 data = InsetGraphics::params2string(p, *buffer);
825 data = InsetNote::params2string(p);
829 InsetPhantomParams p;
830 data = InsetPhantom::params2string(p);
835 data = InsetSpace::params2string(p);
840 data = InsetVSpace::params2string(space);
845 data = InsetWrap::params2string(p);
849 lyxerr << "Inset type '" << name <<
850 "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" << endl;
853 } // end switch(code)
855 dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data));
859 case LFUN_CITATION_INSERT: {
861 if (!argument.empty()) {
862 // we can have one optional argument, delimited by '|'
863 // citation-insert <key>|<text_before>
864 // this should be enhanced to also support text_after
865 // and citation style
866 string arg = argument;
868 if (contains(argument, "|")) {
869 arg = token(argument, '|', 0);
870 opt1 = token(argument, '|', 1);
872 InsetCommandParams icp(CITE_CODE);
873 icp["key"] = from_utf8(arg);
875 icp["before"] = from_utf8(opt1);
876 string icstr = InsetCommand::params2string("citation", icp);
877 FuncRequest fr(LFUN_INSET_INSERT, icstr);
880 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
884 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
886 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
892 string rest = split(argument, countstr, ' ');
893 istringstream is(countstr);
896 //lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
897 for (int i = 0; i < count; ++i)
898 dispatch(lyxaction.lookupFunc(rest));
902 case LFUN_COMMAND_SEQUENCE: {
903 // argument contains ';'-terminated commands
904 string arg = argument;
905 if (theBufferList().isLoaded(buffer))
906 buffer->undo().beginUndoGroup();
907 while (!arg.empty()) {
909 arg = split(arg, first, ';');
910 FuncRequest func(lyxaction.lookupFunc(first));
911 func.origin = cmd.origin;
914 if (theBufferList().isLoaded(buffer))
915 buffer->undo().endUndoGroup();
919 case LFUN_COMMAND_ALTERNATIVES: {
920 // argument contains ';'-terminated commands
921 string arg = argument;
922 while (!arg.empty()) {
924 arg = split(arg, first, ';');
925 FuncRequest func(lyxaction.lookupFunc(first));
926 func.origin = cmd.origin;
927 FuncStatus stat = getStatus(func);
928 if (stat.enabled()) {
938 if (theTopLevelCmdDef().lock(argument, func)) {
939 func.origin = cmd.origin;
941 theTopLevelCmdDef().release(argument);
943 if (func.action == LFUN_UNKNOWN_ACTION) {
944 // unknown command definition
945 lyxerr << "Warning: unknown command definition `"
949 // recursion detected
950 lyxerr << "Warning: Recursion in the command definition `"
951 << argument << "' detected"
958 case LFUN_PREFERENCES_SAVE: {
959 lyxrc.write(makeAbsPath("preferences",
960 package().user_support().absFilename()),
965 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
967 addName(addPath(package().user_support().absFilename(), "templates/"),
969 Buffer defaults(fname);
971 istringstream ss(argument);
974 int const unknown_tokens = defaults.readHeader(lex);
976 if (unknown_tokens != 0) {
977 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
978 << unknown_tokens << " unknown token"
979 << (unknown_tokens == 1 ? "" : "s")
983 if (defaults.writeFile(FileName(defaults.absFileName())))
984 setMessage(bformat(_("Document defaults saved in %1$s"),
985 makeDisplayPath(fname)));
987 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
991 case LFUN_LYXRC_APPLY: {
992 // reset active key sequences, since the bindings
993 // are updated (bug 6064)
995 LyXRC const lyxrc_orig = lyxrc;
997 istringstream ss(argument);
998 bool const success = lyxrc.read(ss) == 0;
1001 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1002 << "Unable to read lyxrc data"
1007 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1011 theApp()->resetGui();
1013 /// We force the redraw in any case because there might be
1014 /// some screen font changes.
1015 /// FIXME: only the current view will be updated. the Gui
1016 /// class is able to furnish the list of views.
1017 updateFlags = Update::Force;
1021 case LFUN_BOOKMARK_GOTO:
1022 // go to bookmark, open unopened file and switch to buffer if necessary
1023 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1024 updateFlags = Update::FitCursor;
1027 case LFUN_BOOKMARK_CLEAR:
1028 theSession().bookmarks().clear();
1032 LASSERT(theApp(), /**/);
1033 // Let the frontend dispatch its own actions.
1034 if (theApp()->dispatch(cmd))
1035 // Nothing more to do.
1038 // Everything below is only for active window
1042 // Start an undo group. This may be needed for
1043 // some stuff like inset-apply on labels.
1044 if (theBufferList().isLoaded(buffer))
1045 buffer->undo().beginUndoGroup();
1047 // Let the current LyXView dispatch its own actions.
1048 if (lv->dispatch(cmd)) {
1049 BufferView * bv = lv->currentBufferView();
1051 buffer = &(bv->buffer());
1052 updateFlags = bv->cursor().result().update();
1053 if (theBufferList().isLoaded(buffer))
1054 buffer->undo().endUndoGroup();
1059 BufferView * bv = lv->currentBufferView();
1062 // Let the current BufferView dispatch its own actions.
1063 if (bv->dispatch(cmd)) {
1064 // The BufferView took care of its own updates if needed.
1065 buffer = &(bv->buffer());
1066 updateFlags = Update::None;
1067 if (theBufferList().isLoaded(buffer))
1068 buffer->undo().endUndoGroup();
1072 BufferView * doc_bv = lv->documentBufferView();
1073 // Try with the document BufferView dispatch if any.
1074 if (doc_bv && doc_bv->dispatch(cmd)) {
1075 // The BufferView took care of its own updates if needed.
1076 buffer = &(doc_bv->buffer());
1077 updateFlags = Update::None;
1078 if (theBufferList().isLoaded(buffer))
1079 buffer->undo().endUndoGroup();
1083 // OK, so try the current Buffer itself...
1085 bv->buffer().dispatch(cmd, dr);
1086 if (dr.dispatched()) {
1087 updateFlags = dr.update();
1090 // and with the document Buffer.
1092 doc_bv->buffer().dispatch(cmd, dr);
1093 if (dr.dispatched()) {
1094 updateFlags = dr.update();
1099 // Is this a function that acts on inset at point?
1100 Inset * inset = bv->cursor().nextInset();
1101 if (lyxaction.funcHasFlag(action, LyXAction::AtPoint)
1103 bv->cursor().result().dispatched(true);
1104 bv->cursor().result().update(Update::FitCursor | Update::Force);
1105 FuncRequest tmpcmd = cmd;
1106 inset->dispatch(bv->cursor(), tmpcmd);
1107 if (bv->cursor().result().dispatched()) {
1108 updateFlags = bv->cursor().result().update();
1113 // Let the current Cursor dispatch its own actions.
1114 Cursor old = bv->cursor();
1115 bv->cursor().getPos(cursorPosBeforeDispatchX_,
1116 cursorPosBeforeDispatchY_);
1117 bv->cursor().dispatch(cmd);
1119 // notify insets we just left
1120 if (bv->cursor() != old) {
1122 bool badcursor = notifyCursorLeavesOrEnters(old, bv->cursor());
1124 bv->cursor().fixIfBroken();
1127 if (theBufferList().isLoaded(buffer))
1128 buffer->undo().endUndoGroup();
1130 // update completion. We do it here and not in
1131 // processKeySym to avoid another redraw just for a
1132 // changed inline completion
1133 if (cmd.origin == FuncRequest::KEYBOARD) {
1134 if (cmd.action == LFUN_SELF_INSERT
1135 || (cmd.action == LFUN_ERT_INSERT && bv->cursor().inMathed()))
1136 lv->updateCompletion(bv->cursor(), true, true);
1137 else if (cmd.action == LFUN_CHAR_DELETE_BACKWARD)
1138 lv->updateCompletion(bv->cursor(), false, true);
1140 lv->updateCompletion(bv->cursor(), false, false);
1143 updateFlags = bv->cursor().result().update();
1146 // if we executed a mutating lfun, mark the buffer as dirty
1147 if (theBufferList().isLoaded(buffer) && flag.enabled()
1148 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1149 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1150 buffer->markDirty();
1152 if (lv && lv->currentBufferView()) {
1153 // BufferView::update() updates the ViewMetricsInfo and
1154 // also initializes the position cache for all insets in
1155 // (at least partially) visible top-level paragraphs.
1156 // We will redraw the screen only if needed.
1157 lv->currentBufferView()->processUpdateFlags(updateFlags);
1159 // Do we have a selection?
1160 theSelection().haveSelection(
1161 lv->currentBufferView()->cursor().selection());
1164 lv->restartCursor();
1168 // Some messages may already be translated, so we cannot use _()
1169 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1174 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1176 const bool verbose = (cmd.origin == FuncRequest::MENU
1177 || cmd.origin == FuncRequest::TOOLBAR
1178 || cmd.origin == FuncRequest::COMMANDBUFFER);
1180 LyXView * lv = theApp()->currentWindow();
1181 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1182 LYXERR(Debug::ACTION, "dispatch msg is " << to_utf8(msg));
1188 docstring dispatch_msg = msg;
1189 if (!dispatch_msg.empty())
1190 dispatch_msg += ' ';
1192 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1194 bool argsadded = false;
1196 if (!cmd.argument().empty()) {
1197 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1198 comname += ' ' + cmd.argument();
1203 docstring const shortcuts = theTopLevelKeymap().printBindings(cmd, KeySequence::ForGui);
1205 if (!shortcuts.empty())
1206 comname += ": " + shortcuts;
1207 else if (!argsadded && !cmd.argument().empty())
1208 comname += ' ' + cmd.argument();
1210 if (!comname.empty()) {
1211 comname = rtrim(comname);
1212 dispatch_msg += '(' + rtrim(comname) + ')';
1215 LYXERR(Debug::ACTION, "verbose dispatch msg " << to_utf8(dispatch_msg));
1216 if (!dispatch_msg.empty())
1217 lv->message(dispatch_msg);
1221 // Each LyXView should have it's own message method. lyxview and
1222 // the minibuffer would use the minibuffer, but lyxserver would
1223 // send an ERROR signal to its client. Alejandro 970603
1224 // This function is bit problematic when it comes to NLS, to make the
1225 // lyx servers client be language indepenent we must not translate
1226 // strings sent to this func.
1227 void LyXFunc::setErrorMessage(docstring const & m) const
1229 dispatch_buffer = m;
1234 void LyXFunc::setMessage(docstring const & m) const
1236 dispatch_buffer = m;
1240 docstring LyXFunc::viewStatusMessage()
1242 // When meta-fake key is pressed, show the key sequence so far + "M-".
1244 return keyseq.print(KeySequence::ForGui) + "M-";
1246 // Else, when a non-complete key sequence is pressed,
1247 // show the available options.
1248 if (keyseq.length() > 0 && !keyseq.deleted())
1249 return keyseq.printOptions(true);
1251 LyXView * lv = theApp()->currentWindow();
1253 if (!lv->currentBufferView())
1254 return _("Welcome to LyX!");
1256 return lv->currentBufferView()->cursor().currentState();
1260 bool LyXFunc::wasMetaKey() const
1262 return (meta_fake_bit != NoModifier);
1268 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1270 // Why the switch you might ask. It is a trick to ensure that all
1271 // the elements in the LyXRCTags enum is handled. As you can see
1272 // there are no breaks at all. So it is just a huge fall-through.
1273 // The nice thing is that we will get a warning from the compiler
1274 // if we forget an element.
1275 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1277 case LyXRC::RC_ACCEPT_COMPOUND:
1278 case LyXRC::RC_ALT_LANG:
1279 case LyXRC::RC_PLAINTEXT_LINELEN:
1280 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
1281 case LyXRC::RC_AUTOCORRECTION_MATH:
1282 case LyXRC::RC_AUTOREGIONDELETE:
1283 case LyXRC::RC_AUTORESET_OPTIONS:
1284 case LyXRC::RC_AUTOSAVE:
1285 case LyXRC::RC_AUTO_NUMBER:
1286 case LyXRC::RC_BACKUPDIR_PATH:
1287 case LyXRC::RC_BIBTEX_ALTERNATIVES:
1288 case LyXRC::RC_BIBTEX_COMMAND:
1289 case LyXRC::RC_BINDFILE:
1290 case LyXRC::RC_CHECKLASTFILES:
1291 case LyXRC::RC_COMPLETION_CURSOR_TEXT:
1292 case LyXRC::RC_COMPLETION_INLINE_DELAY:
1293 case LyXRC::RC_COMPLETION_INLINE_DOTS:
1294 case LyXRC::RC_COMPLETION_INLINE_MATH:
1295 case LyXRC::RC_COMPLETION_INLINE_TEXT:
1296 case LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE:
1297 case LyXRC::RC_COMPLETION_POPUP_DELAY:
1298 case LyXRC::RC_COMPLETION_POPUP_MATH:
1299 case LyXRC::RC_COMPLETION_POPUP_TEXT:
1300 case LyXRC::RC_USELASTFILEPOS:
1301 case LyXRC::RC_LOADSESSION:
1302 case LyXRC::RC_CHKTEX_COMMAND:
1303 case LyXRC::RC_CONVERTER:
1304 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
1305 case LyXRC::RC_COPIER:
1306 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1307 case LyXRC::RC_SCROLL_BELOW_DOCUMENT:
1308 case LyXRC::RC_DATE_INSERT_FORMAT:
1309 case LyXRC::RC_DEFAULT_LANGUAGE:
1310 case LyXRC::RC_GUI_LANGUAGE:
1311 case LyXRC::RC_DEFAULT_PAPERSIZE:
1312 case LyXRC::RC_DEFAULT_VIEW_FORMAT:
1313 case LyXRC::RC_DEFFILE:
1314 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1315 case LyXRC::RC_DISPLAY_GRAPHICS:
1316 case LyXRC::RC_DOCUMENTPATH:
1317 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1318 FileName path(lyxrc_new.document_path);
1319 if (path.exists() && path.isDirectory())
1320 package().document_dir() = FileName(lyxrc.document_path);
1322 case LyXRC::RC_EDITOR_ALTERNATIVES:
1323 case LyXRC::RC_ESC_CHARS:
1324 case LyXRC::RC_EXAMPLEPATH:
1325 case LyXRC::RC_FONT_ENCODING:
1326 case LyXRC::RC_FORMAT:
1327 case LyXRC::RC_GROUP_LAYOUTS:
1328 case LyXRC::RC_HUNSPELLDIR_PATH:
1329 case LyXRC::RC_INDEX_ALTERNATIVES:
1330 case LyXRC::RC_INDEX_COMMAND:
1331 case LyXRC::RC_JBIBTEX_COMMAND:
1332 case LyXRC::RC_JINDEX_COMMAND:
1333 case LyXRC::RC_NOMENCL_COMMAND:
1334 case LyXRC::RC_INPUT:
1335 case LyXRC::RC_KBMAP:
1336 case LyXRC::RC_KBMAP_PRIMARY:
1337 case LyXRC::RC_KBMAP_SECONDARY:
1338 case LyXRC::RC_LABEL_INIT_LENGTH:
1339 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
1340 case LyXRC::RC_LANGUAGE_AUTO_END:
1341 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
1342 case LyXRC::RC_LANGUAGE_COMMAND_END:
1343 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
1344 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
1345 case LyXRC::RC_LANGUAGE_PACKAGE:
1346 case LyXRC::RC_LANGUAGE_USE_BABEL:
1347 case LyXRC::RC_MAC_LIKE_WORD_MOVEMENT:
1348 case LyXRC::RC_MACRO_EDIT_STYLE:
1349 case LyXRC::RC_MAKE_BACKUP:
1350 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
1351 case LyXRC::RC_MOUSE_WHEEL_SPEED:
1352 case LyXRC::RC_NUMLASTFILES:
1353 case LyXRC::RC_PARAGRAPH_MARKERS:
1354 case LyXRC::RC_PATH_PREFIX:
1355 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
1356 prependEnvPath("PATH", lyxrc.path_prefix);
1358 case LyXRC::RC_PERS_DICT:
1359 case LyXRC::RC_PREVIEW:
1360 case LyXRC::RC_PREVIEW_HASHED_LABELS:
1361 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
1362 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
1363 case LyXRC::RC_PRINTCOPIESFLAG:
1364 case LyXRC::RC_PRINTER:
1365 case LyXRC::RC_PRINTEVENPAGEFLAG:
1366 case LyXRC::RC_PRINTEXSTRAOPTIONS:
1367 case LyXRC::RC_PRINTFILEEXTENSION:
1368 case LyXRC::RC_PRINTLANDSCAPEFLAG:
1369 case LyXRC::RC_PRINTODDPAGEFLAG:
1370 case LyXRC::RC_PRINTPAGERANGEFLAG:
1371 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
1372 case LyXRC::RC_PRINTPAPERFLAG:
1373 case LyXRC::RC_PRINTREVERSEFLAG:
1374 case LyXRC::RC_PRINTSPOOL_COMMAND:
1375 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
1376 case LyXRC::RC_PRINTTOFILE:
1377 case LyXRC::RC_PRINTTOPRINTER:
1378 case LyXRC::RC_PRINT_ADAPTOUTPUT:
1379 case LyXRC::RC_PRINT_COMMAND:
1380 case LyXRC::RC_RTL_SUPPORT:
1381 case LyXRC::RC_SCREEN_DPI:
1382 case LyXRC::RC_SCREEN_FONT_ROMAN:
1383 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
1384 case LyXRC::RC_SCREEN_FONT_SANS:
1385 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
1386 case LyXRC::RC_SCREEN_FONT_SCALABLE:
1387 case LyXRC::RC_SCREEN_FONT_SIZES:
1388 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
1389 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
1390 case LyXRC::RC_GEOMETRY_SESSION:
1391 case LyXRC::RC_SCREEN_ZOOM:
1392 case LyXRC::RC_SERVERPIPE:
1393 case LyXRC::RC_SET_COLOR:
1394 case LyXRC::RC_SHOW_BANNER:
1395 case LyXRC::RC_OPEN_BUFFERS_IN_TABS:
1396 case LyXRC::RC_SPELL_COMMAND:
1397 case LyXRC::RC_SPELLCHECKER:
1398 case LyXRC::RC_SPELLCHECK_CONTINUOUSLY:
1399 case LyXRC::RC_SPLITINDEX_COMMAND:
1400 case LyXRC::RC_TEMPDIRPATH:
1401 case LyXRC::RC_TEMPLATEPATH:
1402 case LyXRC::RC_TEX_ALLOWS_SPACES:
1403 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
1404 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
1405 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
1407 case LyXRC::RC_THESAURUSDIRPATH:
1408 case LyXRC::RC_UIFILE:
1409 case LyXRC::RC_USER_EMAIL:
1410 case LyXRC::RC_USER_NAME:
1411 case LyXRC::RC_USETEMPDIR:
1412 case LyXRC::RC_USE_ALT_LANG:
1413 case LyXRC::RC_USE_CONVERTER_CACHE:
1414 case LyXRC::RC_USE_ESC_CHARS:
1415 case LyXRC::RC_USE_INP_ENC:
1416 case LyXRC::RC_USE_PERS_DICT:
1417 case LyXRC::RC_USE_TOOLTIP:
1418 case LyXRC::RC_USE_PIXMAP_CACHE:
1419 case LyXRC::RC_USE_SPELL_LIB:
1420 case LyXRC::RC_VIEWDVI_PAPEROPTION:
1421 case LyXRC::RC_SORT_LAYOUTS:
1422 case LyXRC::RC_FULL_SCREEN_LIMIT:
1423 case LyXRC::RC_FULL_SCREEN_SCROLLBAR:
1424 case LyXRC::RC_FULL_SCREEN_MENUBAR:
1425 case LyXRC::RC_FULL_SCREEN_TABBAR:
1426 case LyXRC::RC_FULL_SCREEN_TOOLBARS:
1427 case LyXRC::RC_FULL_SCREEN_WIDTH:
1428 case LyXRC::RC_VISUAL_CURSOR:
1429 case LyXRC::RC_VIEWER:
1430 case LyXRC::RC_VIEWER_ALTERNATIVES:
1431 case LyXRC::RC_LAST: