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_->view() && 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_->view()->cursor());
222 void LyXFunc::handleKeyFunc(FuncCode action)
224 char_type c = encoded_last_key;
229 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
230 lyx_view_->view()->getIntl().getTransManager().deadkey(
231 c, get_accent(action).accent, view()->cursor().innerText(), view()->cursor());
232 // Need to clear, in case the minibuffer calls these
235 // copied verbatim from do_accent_char
236 view()->cursor().resetAnchor();
237 view()->processUpdateFlags(Update::FitCursor);
240 //FIXME: bookmark handling is a frontend issue. This code should be transferred
241 // to GuiView and be GuiView and be window dependent.
242 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
244 LASSERT(lyx_view_, /**/);
245 if (!theSession().bookmarks().isValid(idx))
247 BookmarksSection::Bookmark const & bm = theSession().bookmarks().bookmark(idx);
248 LASSERT(!bm.filename.empty(), /**/);
249 string const file = bm.filename.absFilename();
250 // if the file is not opened, open it.
251 if (!theBufferList().exists(bm.filename)) {
253 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
257 // open may fail, so we need to test it again
258 if (!theBufferList().exists(bm.filename))
261 // bm can be changed when saving
262 BookmarksSection::Bookmark tmp = bm;
264 // Special case idx == 0 used for back-from-back jump navigation
266 dispatch(FuncRequest(LFUN_BOOKMARK_SAVE, "0"));
268 // if the current buffer is not that one, switch to it.
269 if (!lyx_view_->buffer() || lyx_view_->buffer()->fileName() != tmp.filename) {
272 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
275 // moveToPosition try paragraph id first and then paragraph (pit, pos).
276 if (!view()->moveToPosition(tmp.bottom_pit, tmp.bottom_pos,
277 tmp.top_id, tmp.top_pos))
284 // Cursor jump succeeded!
285 Cursor const & cur = view()->cursor();
286 pit_type new_pit = cur.pit();
287 pos_type new_pos = cur.pos();
288 int new_id = cur.paragraph().id();
290 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
291 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
292 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos
293 || bm.top_id != new_id) {
294 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(
295 new_pit, new_pos, new_id);
300 void LyXFunc::processKeySym(KeySymbol const & keysym, KeyModifier state)
302 LYXERR(Debug::KEY, "KeySym is " << keysym.getSymbolName());
304 // Do nothing if we have nothing (JMarc)
305 if (!keysym.isOK()) {
306 LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
307 lyx_view_->restartCursor();
311 if (keysym.isModifier()) {
312 LYXERR(Debug::KEY, "isModifier true");
314 lyx_view_->restartCursor();
318 //Encoding const * encoding = view()->cursor().getEncoding();
319 //encoded_last_key = keysym.getISOEncoded(encoding ? encoding->name() : "");
320 // FIXME: encoded_last_key shadows the member variable of the same
321 // name. Is that intended?
322 char_type encoded_last_key = keysym.getUCSEncoded();
324 // Do a one-deep top-level lookup for
325 // cancel and meta-fake keys. RVDK_PATCH_5
326 cancel_meta_seq.reset();
328 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
329 LYXERR(Debug::KEY, "action first set to [" << func.action << ']');
331 // When not cancel or meta-fake, do the normal lookup.
332 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
333 // Mostly, meta_fake_bit = NoModifier. RVDK_PATCH_5.
334 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
335 // remove Caps Lock and Mod2 as a modifiers
336 func = keyseq.addkey(keysym, (state | meta_fake_bit));
337 LYXERR(Debug::KEY, "action now set to [" << func.action << ']');
340 // Dont remove this unless you know what you are doing.
341 meta_fake_bit = NoModifier;
343 // Can this happen now ?
344 if (func.action == LFUN_NOACTION)
345 func = FuncRequest(LFUN_COMMAND_PREFIX);
347 LYXERR(Debug::KEY, " Key [action=" << func.action << "]["
348 << keyseq.print(KeySequence::Portable) << ']');
350 // already here we know if it any point in going further
351 // why not return already here if action == -1 and
352 // num_bytes == 0? (Lgb)
354 if (keyseq.length() > 1)
355 lyx_view_->message(keyseq.print(KeySequence::ForGui));
358 // Maybe user can only reach the key via holding down shift.
359 // Let's see. But only if shift is the only modifier
360 if (func.action == LFUN_UNKNOWN_ACTION && state == ShiftModifier) {
361 LYXERR(Debug::KEY, "Trying without shift");
362 func = keyseq.addkey(keysym, NoModifier);
363 LYXERR(Debug::KEY, "Action now " << func.action);
366 if (func.action == LFUN_UNKNOWN_ACTION) {
367 // Hmm, we didn't match any of the keysequences. See
368 // if it's normal insertable text not already covered
370 if (keysym.isText() && keyseq.length() == 1) {
371 LYXERR(Debug::KEY, "isText() is true, inserting.");
372 func = FuncRequest(LFUN_SELF_INSERT,
373 FuncRequest::KEYBOARD);
375 LYXERR(Debug::KEY, "Unknown, !isText() - giving up");
376 lyx_view_->message(_("Unknown function."));
377 lyx_view_->restartCursor();
382 if (func.action == LFUN_SELF_INSERT) {
383 if (encoded_last_key != 0) {
384 docstring const arg(1, encoded_last_key);
385 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
386 FuncRequest::KEYBOARD));
387 LYXERR(Debug::KEY, "SelfInsert arg[`" << to_utf8(arg) << "']");
397 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
399 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
402 /* In LyX/Mac, when a dialog is open, the menus of the
403 application can still be accessed without giving focus to
404 the main window. In this case, we want to disable the menu
405 entries that are buffer or view-related.
407 If this code is moved somewhere else (like in
408 GuiView::getStatus), then several functions will not be
411 frontend::LyXView * lv = 0;
414 && (cmd.origin != FuncRequest::MENU || lyx_view_->hasFocus())) {
416 buf = lyx_view_->buffer();
419 if (cmd.action == LFUN_NOACTION) {
420 flag.message(from_utf8(N_("Nothing to do")));
421 flag.setEnabled(false);
425 switch (cmd.action) {
426 case LFUN_UNKNOWN_ACTION:
428 flag.setEnabled(false);
435 if (flag.unknown()) {
436 flag.message(from_utf8(N_("Unknown action")));
440 if (!flag.enabled()) {
441 if (flag.message().empty())
442 flag.message(from_utf8(N_("Command disabled")));
446 // Check whether we need a buffer
447 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
449 flag.message(from_utf8(N_("Command not allowed with"
450 "out any document open")));
451 flag.setEnabled(false);
455 // I would really like to avoid having this switch and rather try to
456 // encode this in the function itself.
457 // -- And I'd rather let an inset decide which LFUNs it is willing
458 // to handle (Andre')
460 switch (cmd.action) {
462 case LFUN_BUFFER_TOGGLE_READ_ONLY:
463 flag.setOnOff(buf->isReadonly());
466 case LFUN_BUFFER_SWITCH:
467 // toggle on the current buffer, but do not toggle off
468 // the other ones (is that a good idea?)
469 if (buf && to_utf8(cmd.argument()) == buf->absFileName())
473 case LFUN_BUFFER_CHKTEX:
474 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
477 case LFUN_BUILD_PROGRAM:
478 enable = buf->isExportable("program");
481 case LFUN_VC_REGISTER:
482 enable = !buf->lyxvc().inUse();
484 case LFUN_VC_CHECK_IN:
485 enable = buf->lyxvc().checkInEnabled();
487 case LFUN_VC_CHECK_OUT:
488 enable = buf->lyxvc().checkOutEnabled();
490 case LFUN_VC_LOCKING_TOGGLE:
491 enable = !buf->isReadonly() && buf->lyxvc().lockingToggleEnabled();
492 flag.setOnOff(enable && !buf->lyxvc().locker().empty());
495 enable = buf->lyxvc().inUse();
497 case LFUN_VC_UNDO_LAST:
498 enable = buf->lyxvc().undoLastEnabled();
500 case LFUN_BUFFER_RELOAD:
501 enable = !buf->isUnnamed() && buf->fileName().exists()
502 && (!buf->isClean() || buf->isExternallyModified(Buffer::timestamp_method));
505 case LFUN_CITATION_INSERT: {
506 FuncRequest fr(LFUN_INSET_INSERT, "citation");
507 enable = getStatus(fr).enabled();
511 // This could be used for the no-GUI version. The GUI version is handled in
512 // LyXView::getStatus(). See above.
514 case LFUN_BUFFER_WRITE:
515 case LFUN_BUFFER_WRITE_AS: {
516 Buffer * b = theBufferList().getBuffer(FileName(cmd.getArg(0)));
517 enable = b && (b->isUnnamed() || !b->isClean());
522 case LFUN_BUFFER_WRITE_ALL: {
523 // We enable the command only if there are some modified buffers
524 Buffer * first = theBufferList().first();
529 // We cannot use a for loop as the buffer list is a cycle.
535 b = theBufferList().next(b);
536 } while (b != first);
540 case LFUN_BOOKMARK_GOTO: {
541 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
542 enable = theSession().bookmarks().isValid(num);
546 case LFUN_BOOKMARK_CLEAR:
547 enable = theSession().bookmarks().hasValid();
550 // this one is difficult to get right. As a half-baked
551 // solution, we consider only the first action of the sequence
552 case LFUN_COMMAND_SEQUENCE: {
553 // argument contains ';'-terminated commands
554 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
555 FuncRequest func(lyxaction.lookupFunc(firstcmd));
556 func.origin = cmd.origin;
557 flag = getStatus(func);
561 // we want to check if at least one of these is enabled
562 case LFUN_COMMAND_ALTERNATIVES: {
563 // argument contains ';'-terminated commands
564 string arg = to_utf8(cmd.argument());
565 while (!arg.empty()) {
567 arg = split(arg, first, ';');
568 FuncRequest func(lyxaction.lookupFunc(first));
569 func.origin = cmd.origin;
570 flag = getStatus(func);
571 // if this one is enabled, the whole thing is
580 string name = to_utf8(cmd.argument());
581 if (theTopLevelCmdDef().lock(name, func)) {
582 func.origin = cmd.origin;
583 flag = getStatus(func);
584 theTopLevelCmdDef().release(name);
586 // catch recursion or unknown command
587 // definition. all operations until the
588 // recursion or unknown command definition
589 // occurs are performed, so set the state to
596 case LFUN_VC_COMMAND: {
597 if (cmd.argument().empty())
600 if (!buf && contains(cmd.getArg(0), 'D'))
605 case LFUN_MASTER_BUFFER_UPDATE:
606 case LFUN_MASTER_BUFFER_VIEW:
607 if (!buf->parent()) {
611 case LFUN_BUFFER_UPDATE:
612 case LFUN_BUFFER_VIEW: {
613 string format = to_utf8(cmd.argument());
614 if (cmd.argument().empty())
615 format = buf->getDefaultOutputFormat();
616 typedef vector<Format const *> Formats;
618 formats = buf->exportableFormats(true);
619 Formats::const_iterator fit = formats.begin();
620 Formats::const_iterator end = formats.end();
622 for (; fit != end ; ++fit) {
623 if ((*fit)->name() == format)
629 case LFUN_WORD_FIND_FORWARD:
630 case LFUN_WORD_FIND_BACKWARD:
631 case LFUN_WORD_FINDADV:
632 case LFUN_COMMAND_PREFIX:
633 case LFUN_COMMAND_EXECUTE:
635 case LFUN_META_PREFIX:
636 case LFUN_BUFFER_CLOSE:
637 case LFUN_BUFFER_IMPORT:
638 case LFUN_BUFFER_AUTO_SAVE:
639 case LFUN_RECONFIGURE:
641 case LFUN_DROP_LAYOUTS_CHOICE:
643 case LFUN_SERVER_GET_FILENAME:
644 case LFUN_SERVER_NOTIFY:
645 case LFUN_SERVER_GOTO_FILE_ROW:
646 case LFUN_DIALOG_HIDE:
647 case LFUN_DIALOG_DISCONNECT_INSET:
648 case LFUN_BUFFER_CHILD_OPEN:
649 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
650 case LFUN_KEYMAP_OFF:
651 case LFUN_KEYMAP_PRIMARY:
652 case LFUN_KEYMAP_SECONDARY:
653 case LFUN_KEYMAP_TOGGLE:
655 case LFUN_BUFFER_EXPORT_CUSTOM:
656 case LFUN_PREFERENCES_SAVE:
658 case LFUN_INSET_EDIT:
659 case LFUN_BUFFER_LANGUAGE:
660 case LFUN_TEXTCLASS_APPLY:
661 case LFUN_TEXTCLASS_LOAD:
662 case LFUN_BUFFER_SAVE_AS_DEFAULT:
663 case LFUN_BUFFER_PARAMS_APPLY:
664 case LFUN_LAYOUT_MODULES_CLEAR:
665 case LFUN_LAYOUT_MODULE_ADD:
666 case LFUN_LAYOUT_RELOAD:
667 case LFUN_LYXRC_APPLY:
668 case LFUN_BUFFER_NEXT:
669 case LFUN_BUFFER_PREVIOUS:
670 // these are handled in our dispatch()
678 if (theApp()->getStatus(cmd, flag))
681 // Does the view know something?
686 if (lv->getStatus(cmd, flag))
689 // If we do not have a BufferView, then other functions are disabled
695 // Is this a function that acts on inset at point?
696 Inset * inset = view()->cursor().nextInset();
697 if (lyxaction.funcHasFlag(cmd.action, LyXAction::AtPoint)
698 && inset && inset->getStatus(view()->cursor(), cmd, flag))
701 bool decided = getLocalStatus(view()->cursor(), cmd, flag);
703 // try the BufferView
704 decided = view()->getStatus(cmd, flag);
707 view()->buffer().getStatus(cmd, flag);
711 flag.setEnabled(false);
713 // Can we use a readonly buffer?
714 if (buf && buf->isReadonly()
715 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
716 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
717 flag.message(from_utf8(N_("Document is read-only")));
718 flag.setEnabled(false);
721 // Are we in a DELETED change-tracking region?
723 && lookupChangeType(view()->cursor(), true) == Change::DELETED
724 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
725 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
726 flag.message(from_utf8(N_("This portion of the document is deleted.")));
727 flag.setEnabled(false);
730 // the default error message if we disable the command
731 if (!flag.enabled() && flag.message().empty())
732 flag.message(from_utf8(N_("Command disabled")));
738 bool LyXFunc::ensureBufferClean(BufferView * bv)
740 Buffer & buf = bv->buffer();
741 if (buf.isClean() && !buf.isUnnamed())
744 docstring const file = buf.fileName().displayName(30);
747 if (!buf.isUnnamed()) {
748 text = bformat(_("The document %1$s has unsaved "
749 "changes.\n\nDo you want to save "
750 "the document?"), file);
751 title = _("Save changed document?");
754 text = bformat(_("The document %1$s has not been "
755 "saved yet.\n\nDo you want to save "
756 "the document?"), file);
757 title = _("Save new document?");
759 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
762 lyx_view_->dispatch(FuncRequest(LFUN_BUFFER_WRITE));
764 return buf.isClean() && !buf.isUnnamed();
770 bool loadLayoutFile(string const & name, string const & buf_path)
772 if (!LayoutFileList::get().haveClass(name)) {
773 lyxerr << "Document class \"" << name
774 << "\" does not exist."
779 LayoutFile & tc = LayoutFileList::get()[name];
780 if (!tc.load(buf_path)) {
781 docstring s = bformat(_("The document class %1$s "
782 "could not be loaded."), from_utf8(name));
783 Alert::error(_("Could not load class"), s);
790 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
795 void LyXFunc::dispatch(FuncRequest const & cmd)
797 string const argument = to_utf8(cmd.argument());
798 FuncCode const action = cmd.action;
800 LYXERR(Debug::ACTION, "\nLyXFunc::dispatch: cmd: " << cmd);
801 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
803 // we have not done anything wrong yet.
805 dispatch_buffer.erase();
807 // redraw the screen at the end (first of the two drawing steps).
808 //This is done unless explicitely requested otherwise
809 Update::flags updateFlags = Update::FitCursor;
811 FuncStatus const flag = getStatus(cmd);
812 if (!flag.enabled()) {
813 // We cannot use this function here
814 LYXERR(Debug::ACTION, "LyXFunc::dispatch: "
815 << lyxaction.getActionName(action)
816 << " [" << action << "] is disabled at this location");
817 setErrorMessage(flag.message());
819 lyx_view_->restartCursor();
821 Buffer * buffer = lyx_view_ ? lyx_view_->buffer() : 0;
824 case LFUN_WORD_FIND_FORWARD:
825 case LFUN_WORD_FIND_BACKWARD: {
826 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
827 static docstring last_search;
828 docstring searched_string;
830 if (!cmd.argument().empty()) {
831 last_search = cmd.argument();
832 searched_string = cmd.argument();
834 searched_string = last_search;
837 if (searched_string.empty())
840 bool const fw = action == LFUN_WORD_FIND_FORWARD;
841 docstring const data =
842 find2string(searched_string, true, false, fw);
843 find(view(), FuncRequest(LFUN_WORD_FIND, data));
847 case LFUN_COMMAND_PREFIX:
848 LASSERT(lyx_view_, /**/);
849 lyx_view_->message(keyseq.printOptions(true));
853 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
855 meta_fake_bit = NoModifier;
857 // cancel any selection
858 dispatch(FuncRequest(LFUN_MARK_OFF));
859 setMessage(from_ascii(N_("Cancel")));
862 case LFUN_META_PREFIX:
863 meta_fake_bit = AltModifier;
864 setMessage(keyseq.print(KeySequence::ForGui));
867 case LFUN_BUFFER_TOGGLE_READ_ONLY: {
868 LASSERT(lyx_view_ && lyx_view_->view() && buffer, /**/);
869 if (buffer->lyxvc().inUse())
870 buffer->lyxvc().toggleReadOnly();
872 buffer->setReadonly(!buffer->isReadonly());
876 // --- Menus -----------------------------------------------
877 case LFUN_BUFFER_CLOSE:
878 lyx_view_->closeBuffer();
880 updateFlags = Update::None;
883 case LFUN_BUFFER_CLOSE_ALL:
884 lyx_view_->closeBufferAll();
886 updateFlags = Update::None;
889 case LFUN_BUFFER_RELOAD: {
890 LASSERT(lyx_view_ && buffer, /**/);
891 docstring const file = makeDisplayPath(buffer->absFileName(), 20);
892 docstring text = bformat(_("Any changes will be lost. Are you sure "
893 "you want to revert to the saved version of the document %1$s?"), file);
894 int const ret = Alert::prompt(_("Revert to saved document?"),
895 text, 1, 1, _("&Revert"), _("&Cancel"));
902 case LFUN_BUFFER_UPDATE: {
903 LASSERT(lyx_view_ && buffer, /**/);
904 string format = argument;
905 if (argument.empty())
906 format = buffer->getDefaultOutputFormat();
907 buffer->doExport(format, true);
911 case LFUN_BUFFER_VIEW: {
912 LASSERT(lyx_view_ && buffer, /**/);
913 string format = argument;
914 if (argument.empty())
915 format = buffer->getDefaultOutputFormat();
916 buffer->preview(format);
920 case LFUN_MASTER_BUFFER_UPDATE: {
921 LASSERT(lyx_view_ && buffer && buffer->masterBuffer(), /**/);
922 string format = argument;
923 if (argument.empty())
924 format = buffer->masterBuffer()->getDefaultOutputFormat();
925 buffer->masterBuffer()->doExport(format, true);
929 case LFUN_MASTER_BUFFER_VIEW: {
930 LASSERT(lyx_view_ && buffer && buffer->masterBuffer(), /**/);
931 string format = argument;
932 if (argument.empty())
933 format = buffer->masterBuffer()->getDefaultOutputFormat();
934 buffer->masterBuffer()->preview(format);
938 case LFUN_BUILD_PROGRAM:
939 LASSERT(lyx_view_ && buffer, /**/);
940 buffer->doExport("program", true);
943 case LFUN_BUFFER_CHKTEX:
944 LASSERT(lyx_view_ && buffer, /**/);
948 case LFUN_BUFFER_EXPORT:
949 LASSERT(lyx_view_ && buffer, /**/);
950 if (argument == "custom")
951 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"));
953 buffer->doExport(argument, false);
956 case LFUN_BUFFER_EXPORT_CUSTOM: {
957 LASSERT(lyx_view_ && buffer, /**/);
959 string command = split(argument, format_name, ' ');
960 Format const * format = formats.getFormat(format_name);
962 lyxerr << "Format \"" << format_name
963 << "\" not recognized!"
968 // The name of the file created by the conversion process
971 // Output to filename
972 if (format->name() == "lyx") {
973 string const latexname = buffer->latexName(false);
974 filename = changeExtension(latexname,
975 format->extension());
976 filename = addName(buffer->temppath(), filename);
978 if (!buffer->writeFile(FileName(filename)))
982 buffer->doExport(format_name, true, filename);
985 // Substitute $$FName for filename
986 if (!contains(command, "$$FName"))
987 command = "( " + command + " ) < $$FName";
988 command = subst(command, "$$FName", filename);
990 // Execute the command in the background
992 call.startscript(Systemcall::DontWait, command);
996 // FIXME: There is need for a command-line import.
998 case LFUN_BUFFER_IMPORT:
1003 case LFUN_BUFFER_AUTO_SAVE:
1007 case LFUN_RECONFIGURE:
1008 // argument is any additional parameter to the configure.py command
1009 reconfigure(lyx_view_, argument);
1012 case LFUN_HELP_OPEN: {
1014 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1015 string const arg = argument;
1017 setErrorMessage(from_utf8(N_("Missing argument")));
1020 FileName fname = i18nLibFileSearch("doc", arg, "lyx");
1022 fname = i18nLibFileSearch("examples", arg, "lyx");
1024 if (fname.empty()) {
1025 lyxerr << "LyX: unable to find documentation file `"
1026 << arg << "'. Bad installation?" << endl;
1029 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1030 makeDisplayPath(fname.absFilename())));
1031 Buffer * buf = lyx_view_->loadDocument(fname, false);
1033 buf->updateLabels();
1034 lyx_view_->setBuffer(buf);
1035 buf->errors("Parse");
1037 updateFlags = Update::None;
1041 // --- version control -------------------------------
1042 case LFUN_VC_REGISTER:
1043 LASSERT(lyx_view_ && buffer, /**/);
1044 if (!ensureBufferClean(view()))
1046 if (!buffer->lyxvc().inUse()) {
1047 if (buffer->lyxvc().registrer())
1050 updateFlags = Update::Force;
1053 case LFUN_VC_CHECK_IN:
1054 LASSERT(lyx_view_ && buffer, /**/);
1055 if (!ensureBufferClean(view()))
1057 if (buffer->lyxvc().inUse()
1058 && !buffer->isReadonly()) {
1059 setMessage(from_utf8(buffer->lyxvc().checkIn()));
1064 case LFUN_VC_CHECK_OUT:
1065 LASSERT(lyx_view_ && buffer, /**/);
1066 if (!ensureBufferClean(view()))
1068 if (buffer->lyxvc().inUse()) {
1069 setMessage(from_utf8(buffer->lyxvc().checkOut()));
1074 case LFUN_VC_LOCKING_TOGGLE:
1075 LASSERT(lyx_view_ && buffer, /**/);
1076 if (!ensureBufferClean(view()) || buffer->isReadonly())
1078 if (buffer->lyxvc().inUse()) {
1079 string res = buffer->lyxvc().lockingToggle();
1081 frontend::Alert::error(_("Revision control error."),
1082 _("Error when setting the locking property."));
1084 setMessage(from_utf8(res));
1090 case LFUN_VC_REVERT:
1091 LASSERT(lyx_view_ && buffer, /**/);
1092 buffer->lyxvc().revert();
1096 case LFUN_VC_UNDO_LAST:
1097 LASSERT(lyx_view_ && buffer, /**/);
1098 buffer->lyxvc().undoLast();
1102 // --- lyxserver commands ----------------------------
1103 case LFUN_SERVER_GET_FILENAME:
1104 LASSERT(lyx_view_ && buffer, /**/);
1105 setMessage(from_utf8(buffer->absFileName()));
1106 LYXERR(Debug::INFO, "FNAME["
1107 << buffer->absFileName() << ']');
1110 case LFUN_SERVER_NOTIFY:
1111 dispatch_buffer = keyseq.print(KeySequence::Portable);
1112 theServer().notifyClient(to_utf8(dispatch_buffer));
1115 case LFUN_SERVER_GOTO_FILE_ROW: {
1116 LASSERT(lyx_view_, /**/);
1119 istringstream is(argument);
1120 is >> file_name >> row;
1122 bool loaded = false;
1123 string const abstmp = package().temp_dir().absFilename();
1124 string const realtmp = package().temp_dir().realPath();
1125 // We have to use os::path_prefix_is() here, instead of
1126 // simply prefixIs(), because the file name comes from
1127 // an external application and may need case adjustment.
1128 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
1129 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
1130 // Needed by inverse dvi search. If it is a file
1131 // in tmpdir, call the apropriated function.
1132 // If tmpdir is a symlink, we may have the real
1133 // path passed back, so we correct for that.
1134 if (!prefixIs(file_name, abstmp))
1135 file_name = subst(file_name, realtmp, abstmp);
1136 buf = theBufferList().getBufferFromTmp(file_name);
1138 // Must replace extension of the file to be .lyx
1139 // and get full path
1140 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1141 // Either change buffer or load the file
1142 if (theBufferList().exists(s))
1143 buf = theBufferList().getBuffer(s);
1144 else if (s.exists()) {
1145 buf = lyx_view_->loadDocument(s);
1148 lyx_view_->message(bformat(
1149 _("File does not exist: %1$s"),
1150 makeDisplayPath(file_name)));
1154 updateFlags = Update::None;
1158 buf->updateLabels();
1159 lyx_view_->setBuffer(buf);
1160 view()->setCursorFromRow(row);
1162 buf->errors("Parse");
1163 updateFlags = Update::FitCursor;
1168 case LFUN_DIALOG_SHOW_NEW_INSET: {
1169 LASSERT(lyx_view_, /**/);
1170 string const name = cmd.getArg(0);
1171 InsetCode code = insetCode(name);
1172 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1173 bool insetCodeOK = true;
1180 case NOMENCL_PRINT_CODE:
1183 case HYPERLINK_CODE: {
1184 InsetCommandParams p(code);
1185 data = InsetCommand::params2string(name, p);
1188 case INCLUDE_CODE: {
1189 // data is the include type: one of "include",
1190 // "input", "verbatiminput" or "verbatiminput*"
1192 // default type is requested
1194 InsetCommandParams p(INCLUDE_CODE, data);
1195 data = InsetCommand::params2string("include", p);
1199 // \c data == "Boxed" || "Frameless" etc
1200 InsetBoxParams p(data);
1201 data = InsetBox::params2string(p);
1205 InsetBranchParams p;
1206 data = InsetBranch::params2string(p);
1210 InsetCommandParams p(CITE_CODE);
1211 data = InsetCommand::params2string(name, p);
1215 data = InsetERT::params2string(InsetCollapsable::Open);
1218 case EXTERNAL_CODE: {
1219 InsetExternalParams p;
1220 data = InsetExternal::params2string(p, *buffer);
1225 data = InsetFloat::params2string(p);
1228 case LISTINGS_CODE: {
1229 InsetListingsParams p;
1230 data = InsetListings::params2string(p);
1233 case GRAPHICS_CODE: {
1234 InsetGraphicsParams p;
1235 data = InsetGraphics::params2string(p, *buffer);
1240 data = InsetNote::params2string(p);
1243 case PHANTOM_CODE: {
1244 InsetPhantomParams p;
1245 data = InsetPhantom::params2string(p);
1250 data = InsetSpace::params2string(p);
1255 data = InsetVSpace::params2string(space);
1260 data = InsetWrap::params2string(p);
1264 lyxerr << "Inset type '" << name <<
1265 "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" << endl;
1266 insetCodeOK = false;
1268 } // end switch(code)
1270 dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data));
1274 case LFUN_CITATION_INSERT: {
1275 LASSERT(lyx_view_, /**/);
1276 if (!argument.empty()) {
1277 // we can have one optional argument, delimited by '|'
1278 // citation-insert <key>|<text_before>
1279 // this should be enhanced to also support text_after
1280 // and citation style
1281 string arg = argument;
1283 if (contains(argument, "|")) {
1284 arg = token(argument, '|', 0);
1285 opt1 = token(argument, '|', 1);
1287 InsetCommandParams icp(CITE_CODE);
1288 icp["key"] = from_utf8(arg);
1290 icp["before"] = from_utf8(opt1);
1291 string icstr = InsetCommand::params2string("citation", icp);
1292 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1295 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1299 case LFUN_BUFFER_CHILD_OPEN: {
1300 LASSERT(lyx_view_ && buffer, /**/);
1301 FileName filename = makeAbsPath(argument, buffer->filePath());
1302 view()->saveBookmark(false);
1304 bool parsed = false;
1305 if (theBufferList().exists(filename)) {
1306 child = theBufferList().getBuffer(filename);
1308 setMessage(bformat(_("Opening child document %1$s..."),
1309 makeDisplayPath(filename.absFilename())));
1310 child = lyx_view_->loadDocument(filename, false);
1314 // Set the parent name of the child document.
1315 // This makes insertion of citations and references in the child work,
1316 // when the target is in the parent or another child document.
1317 child->setParent(buffer);
1318 child->masterBuffer()->updateLabels();
1319 lyx_view_->setBuffer(child);
1321 child->errors("Parse");
1324 // If a screen update is required (in case where auto_open is false),
1325 // setBuffer() would have taken care of it already. Otherwise we shall
1326 // reset the update flag because it can cause a circular problem.
1328 updateFlags = Update::None;
1332 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
1333 LASSERT(lyx_view_, /**/);
1334 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1337 case LFUN_KEYMAP_OFF:
1338 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1339 lyx_view_->view()->getIntl().keyMapOn(false);
1342 case LFUN_KEYMAP_PRIMARY:
1343 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1344 lyx_view_->view()->getIntl().keyMapPrim();
1347 case LFUN_KEYMAP_SECONDARY:
1348 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1349 lyx_view_->view()->getIntl().keyMapSec();
1352 case LFUN_KEYMAP_TOGGLE:
1353 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1354 lyx_view_->view()->getIntl().toggleKeyMap();
1360 string rest = split(argument, countstr, ' ');
1361 istringstream is(countstr);
1364 //lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1365 for (int i = 0; i < count; ++i)
1366 dispatch(lyxaction.lookupFunc(rest));
1370 case LFUN_COMMAND_SEQUENCE: {
1371 // argument contains ';'-terminated commands
1372 string arg = argument;
1373 if (theBufferList().isLoaded(buffer))
1374 buffer->undo().beginUndoGroup();
1375 while (!arg.empty()) {
1377 arg = split(arg, first, ';');
1378 FuncRequest func(lyxaction.lookupFunc(first));
1379 func.origin = cmd.origin;
1382 if (theBufferList().isLoaded(buffer))
1383 buffer->undo().endUndoGroup();
1387 case LFUN_COMMAND_ALTERNATIVES: {
1388 // argument contains ';'-terminated commands
1389 string arg = argument;
1390 while (!arg.empty()) {
1392 arg = split(arg, first, ';');
1393 FuncRequest func(lyxaction.lookupFunc(first));
1394 func.origin = cmd.origin;
1395 FuncStatus stat = getStatus(func);
1396 if (stat.enabled()) {
1406 if (theTopLevelCmdDef().lock(argument, func)) {
1407 func.origin = cmd.origin;
1409 theTopLevelCmdDef().release(argument);
1411 if (func.action == LFUN_UNKNOWN_ACTION) {
1412 // unknown command definition
1413 lyxerr << "Warning: unknown command definition `"
1417 // recursion detected
1418 lyxerr << "Warning: Recursion in the command definition `"
1419 << argument << "' detected"
1426 case LFUN_PREFERENCES_SAVE: {
1427 lyxrc.write(makeAbsPath("preferences",
1428 package().user_support().absFilename()),
1434 LASSERT(lyx_view_, /**/);
1435 lyx_view_->message(from_utf8(argument));
1438 case LFUN_BUFFER_LANGUAGE: {
1439 LASSERT(lyx_view_, /**/);
1440 Language const * oldL = buffer->params().language;
1441 Language const * newL = languages.getLanguage(argument);
1442 if (!newL || oldL == newL)
1445 if (oldL->rightToLeft() == newL->rightToLeft()
1446 && !buffer->isMultiLingual())
1447 buffer->changeLanguage(oldL, newL);
1451 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1452 string const fname =
1453 addName(addPath(package().user_support().absFilename(), "templates/"),
1455 Buffer defaults(fname);
1457 istringstream ss(argument);
1460 int const unknown_tokens = defaults.readHeader(lex);
1462 if (unknown_tokens != 0) {
1463 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1464 << unknown_tokens << " unknown token"
1465 << (unknown_tokens == 1 ? "" : "s")
1469 if (defaults.writeFile(FileName(defaults.absFileName())))
1470 setMessage(bformat(_("Document defaults saved in %1$s"),
1471 makeDisplayPath(fname)));
1473 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1477 case LFUN_BUFFER_PARAMS_APPLY: {
1478 LASSERT(lyx_view_, /**/);
1480 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1481 Cursor & cur = view()->cursor();
1482 cur.recordUndoFullDocument();
1484 istringstream ss(argument);
1487 int const unknown_tokens = buffer->readHeader(lex);
1489 if (unknown_tokens != 0) {
1490 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1491 << unknown_tokens << " unknown token"
1492 << (unknown_tokens == 1 ? "" : "s")
1496 updateLayout(oldClass, buffer);
1498 updateFlags = Update::Force | Update::FitCursor;
1499 // We are most certainly here because of a change in the document
1500 // It is then better to make sure that all dialogs are in sync with
1501 // current document settings. LyXView::restartCursor() achieve this.
1502 lyx_view_->restartCursor();
1506 case LFUN_LAYOUT_MODULES_CLEAR: {
1507 LASSERT(lyx_view_, /**/);
1508 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1509 view()->cursor().recordUndoFullDocument();
1510 buffer->params().clearLayoutModules();
1511 buffer->params().makeDocumentClass();
1512 updateLayout(oldClass, buffer);
1513 updateFlags = Update::Force | Update::FitCursor;
1517 case LFUN_LAYOUT_MODULE_ADD: {
1518 LASSERT(lyx_view_, /**/);
1519 BufferParams const & params = buffer->params();
1520 if (!params.moduleCanBeAdded(argument)) {
1521 LYXERR0("Module `" << argument <<
1522 "' cannot be added due to failed requirements or "
1523 "conflicts with installed modules.");
1526 DocumentClass const * const oldClass = params.documentClassPtr();
1527 view()->cursor().recordUndoFullDocument();
1528 buffer->params().addLayoutModule(argument);
1529 buffer->params().makeDocumentClass();
1530 updateLayout(oldClass, buffer);
1531 updateFlags = Update::Force | Update::FitCursor;
1535 case LFUN_TEXTCLASS_APPLY: {
1536 LASSERT(lyx_view_, /**/);
1538 if (!loadLayoutFile(argument, buffer->temppath()) &&
1539 !loadLayoutFile(argument, buffer->filePath()))
1542 LayoutFile const * old_layout = buffer->params().baseClass();
1543 LayoutFile const * new_layout = &(LayoutFileList::get()[argument]);
1545 if (old_layout == new_layout)
1549 //Save the old, possibly modular, layout for use in conversion.
1550 DocumentClass const * const oldDocClass = buffer->params().documentClassPtr();
1551 view()->cursor().recordUndoFullDocument();
1552 buffer->params().setBaseClass(argument);
1553 buffer->params().makeDocumentClass();
1554 updateLayout(oldDocClass, buffer);
1555 updateFlags = Update::Force | Update::FitCursor;
1559 case LFUN_LAYOUT_RELOAD: {
1560 LASSERT(lyx_view_, /**/);
1561 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1562 LayoutFileIndex bc = buffer->params().baseClassID();
1563 LayoutFileList::get().reset(bc);
1564 buffer->params().setBaseClass(bc);
1565 buffer->params().makeDocumentClass();
1566 updateLayout(oldClass, buffer);
1567 updateFlags = Update::Force | Update::FitCursor;
1571 case LFUN_TEXTCLASS_LOAD:
1572 loadLayoutFile(argument, buffer->temppath()) ||
1573 loadLayoutFile(argument, buffer->filePath());
1576 case LFUN_LYXRC_APPLY: {
1577 // reset active key sequences, since the bindings
1578 // are updated (bug 6064)
1580 LyXRC const lyxrc_orig = lyxrc;
1582 istringstream ss(argument);
1583 bool const success = lyxrc.read(ss) == 0;
1586 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1587 << "Unable to read lyxrc data"
1592 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1596 theApp()->resetGui();
1598 /// We force the redraw in any case because there might be
1599 /// some screen font changes.
1600 /// FIXME: only the current view will be updated. the Gui
1601 /// class is able to furnish the list of views.
1602 updateFlags = Update::Force;
1606 case LFUN_BOOKMARK_GOTO:
1607 // go to bookmark, open unopened file and switch to buffer if necessary
1608 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1609 updateFlags = Update::FitCursor;
1612 case LFUN_BOOKMARK_CLEAR:
1613 theSession().bookmarks().clear();
1616 case LFUN_VC_COMMAND: {
1617 string flag = cmd.getArg(0);
1618 if (buffer && contains(flag, 'R') && !ensureBufferClean(view()))
1621 if (contains(flag, 'M'))
1622 if (!Alert::askForText(message, _("LyX VC: Log Message")))
1625 string path = cmd.getArg(1);
1626 if (contains(path, "$$p") && buffer)
1627 path = subst(path, "$$p", buffer->filePath());
1628 LYXERR(Debug::LYXVC, "Directory: " << path);
1630 if (!pp.isReadableDirectory()) {
1631 lyxerr << _("Directory is not accessible.") << endl;
1634 support::PathChanger p(pp);
1636 string command = cmd.getArg(2);
1637 if (command.empty())
1640 command = subst(command, "$$i", buffer->absFileName());
1641 command = subst(command, "$$p", buffer->filePath());
1643 command = subst(command, "$$m", to_utf8(message));
1644 LYXERR(Debug::LYXVC, "Command: " << command);
1646 one.startscript(Systemcall::Wait, command);
1650 if (contains(flag, 'I'))
1651 buffer->markDirty();
1652 if (contains(flag, 'R'))
1659 LASSERT(theApp(), /**/);
1660 // Let the frontend dispatch its own actions.
1661 if (theApp()->dispatch(cmd))
1662 // Nothing more to do.
1665 // Everything below is only for active lyx_view_
1669 // Start an undo group. This may be needed for
1670 // some stuff like inset-apply on labels.
1671 if (theBufferList().isLoaded(buffer))
1672 buffer->undo().beginUndoGroup();
1674 // Let the current LyXView dispatch its own actions.
1675 if (lyx_view_->dispatch(cmd)) {
1676 if (lyx_view_->view()) {
1677 updateFlags = lyx_view_->view()->cursor().result().update();
1678 if (theBufferList().isLoaded(buffer))
1679 buffer->undo().endUndoGroup();
1684 LASSERT(lyx_view_->view(), /**/);
1686 // Let the current BufferView dispatch its own actions.
1687 if (view()->dispatch(cmd)) {
1688 // The BufferView took care of its own updates if needed.
1689 updateFlags = Update::None;
1690 if (theBufferList().isLoaded(buffer))
1691 buffer->undo().endUndoGroup();
1695 // OK, so try the Buffer itself
1697 view()->buffer().dispatch(cmd, dr);
1698 if (dr.dispatched()) {
1699 updateFlags = dr.update();
1703 // Is this a function that acts on inset at point?
1704 Inset * inset = view()->cursor().nextInset();
1705 if (lyxaction.funcHasFlag(action, LyXAction::AtPoint)
1707 view()->cursor().result().dispatched(true);
1708 view()->cursor().result().update(Update::FitCursor | Update::Force);
1709 FuncRequest tmpcmd = cmd;
1710 inset->dispatch(view()->cursor(), tmpcmd);
1711 if (view()->cursor().result().dispatched()) {
1712 updateFlags = view()->cursor().result().update();
1717 // Let the current Cursor dispatch its own actions.
1718 Cursor old = view()->cursor();
1719 view()->cursor().getPos(cursorPosBeforeDispatchX_,
1720 cursorPosBeforeDispatchY_);
1721 view()->cursor().dispatch(cmd);
1723 // notify insets we just left
1724 if (view()->cursor() != old) {
1726 bool badcursor = notifyCursorLeavesOrEnters(old, view()->cursor());
1728 view()->cursor().fixIfBroken();
1731 if (theBufferList().isLoaded(buffer))
1732 buffer->undo().endUndoGroup();
1734 // update completion. We do it here and not in
1735 // processKeySym to avoid another redraw just for a
1736 // changed inline completion
1737 if (cmd.origin == FuncRequest::KEYBOARD) {
1738 if (cmd.action == LFUN_SELF_INSERT
1739 || (cmd.action == LFUN_ERT_INSERT && view()->cursor().inMathed()))
1740 lyx_view_->updateCompletion(view()->cursor(), true, true);
1741 else if (cmd.action == LFUN_CHAR_DELETE_BACKWARD)
1742 lyx_view_->updateCompletion(view()->cursor(), false, true);
1744 lyx_view_->updateCompletion(view()->cursor(), false, false);
1747 updateFlags = view()->cursor().result().update();
1750 // if we executed a mutating lfun, mark the buffer as dirty
1751 if (theBufferList().isLoaded(buffer) && flag.enabled()
1752 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1753 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1754 buffer->markDirty();
1756 if (lyx_view_ && lyx_view_->buffer()) {
1757 // BufferView::update() updates the ViewMetricsInfo and
1758 // also initializes the position cache for all insets in
1759 // (at least partially) visible top-level paragraphs.
1760 // We will redraw the screen only if needed.
1761 view()->processUpdateFlags(updateFlags);
1763 // Do we have a selection?
1764 theSelection().haveSelection(view()->cursor().selection());
1767 lyx_view_->restartCursor();
1771 // Some messages may already be translated, so we cannot use _()
1772 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1777 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1779 const bool verbose = (cmd.origin == FuncRequest::MENU
1780 || cmd.origin == FuncRequest::TOOLBAR
1781 || cmd.origin == FuncRequest::COMMANDBUFFER);
1783 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1784 LYXERR(Debug::ACTION, "dispatch msg is " << to_utf8(msg));
1786 lyx_view_->message(msg);
1790 docstring dispatch_msg = msg;
1791 if (!dispatch_msg.empty())
1792 dispatch_msg += ' ';
1794 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1796 bool argsadded = false;
1798 if (!cmd.argument().empty()) {
1799 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1800 comname += ' ' + cmd.argument();
1805 docstring const shortcuts = theTopLevelKeymap().printBindings(cmd, KeySequence::ForGui);
1807 if (!shortcuts.empty())
1808 comname += ": " + shortcuts;
1809 else if (!argsadded && !cmd.argument().empty())
1810 comname += ' ' + cmd.argument();
1812 if (!comname.empty()) {
1813 comname = rtrim(comname);
1814 dispatch_msg += '(' + rtrim(comname) + ')';
1817 LYXERR(Debug::ACTION, "verbose dispatch msg " << to_utf8(dispatch_msg));
1818 if (!dispatch_msg.empty())
1819 lyx_view_->message(dispatch_msg);
1823 void LyXFunc::reloadBuffer()
1825 FileName filename = lyx_view_->buffer()->fileName();
1826 // The user has already confirmed that the changes, if any, should
1827 // be discarded. So we just release the Buffer and don't call closeBuffer();
1828 theBufferList().release(lyx_view_->buffer());
1829 // if the lyx_view_ has been destroyed, create a new one
1831 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1832 Buffer * buf = lyx_view_->loadDocument(filename);
1833 docstring const disp_fn = makeDisplayPath(filename.absFilename());
1836 buf->updateLabels();
1837 lyx_view_->setBuffer(buf);
1838 buf->errors("Parse");
1839 str = bformat(_("Document %1$s reloaded."), disp_fn);
1841 str = bformat(_("Could not reload document %1$s"), disp_fn);
1843 lyx_view_->message(str);
1846 // Each "lyx_view_" should have it's own message method. lyxview and
1847 // the minibuffer would use the minibuffer, but lyxserver would
1848 // send an ERROR signal to its client. Alejandro 970603
1849 // This function is bit problematic when it comes to NLS, to make the
1850 // lyx servers client be language indepenent we must not translate
1851 // strings sent to this func.
1852 void LyXFunc::setErrorMessage(docstring const & m) const
1854 dispatch_buffer = m;
1859 void LyXFunc::setMessage(docstring const & m) const
1861 dispatch_buffer = m;
1865 docstring LyXFunc::viewStatusMessage()
1867 // When meta-fake key is pressed, show the key sequence so far + "M-".
1869 return keyseq.print(KeySequence::ForGui) + "M-";
1871 // Else, when a non-complete key sequence is pressed,
1872 // show the available options.
1873 if (keyseq.length() > 0 && !keyseq.deleted())
1874 return keyseq.printOptions(true);
1876 LASSERT(lyx_view_, /**/);
1877 if (!lyx_view_->buffer())
1878 return _("Welcome to LyX!");
1880 return view()->cursor().currentState();
1884 BufferView * LyXFunc::view() const
1886 LASSERT(lyx_view_, /**/);
1887 return lyx_view_->view();
1891 bool LyXFunc::wasMetaKey() const
1893 return (meta_fake_bit != NoModifier);
1897 void LyXFunc::updateLayout(DocumentClass const * const oldlayout, Buffer * buf)
1899 lyx_view_->message(_("Converting document to new document class..."));
1901 StableDocIterator backcur(view()->cursor());
1902 ErrorList & el = buf->errorList("Class Switch");
1903 cap::switchBetweenClasses(
1904 oldlayout, buf->params().documentClassPtr(),
1905 static_cast<InsetText &>(buf->inset()), el);
1907 view()->setCursor(backcur.asDocIterator(buf));
1909 buf->errors("Class Switch");
1910 buf->updateLabels();
1916 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1918 // Why the switch you might ask. It is a trick to ensure that all
1919 // the elements in the LyXRCTags enum is handled. As you can see
1920 // there are no breaks at all. So it is just a huge fall-through.
1921 // The nice thing is that we will get a warning from the compiler
1922 // if we forget an element.
1923 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1925 case LyXRC::RC_ACCEPT_COMPOUND:
1926 case LyXRC::RC_ALT_LANG:
1927 case LyXRC::RC_PLAINTEXT_LINELEN:
1928 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
1929 case LyXRC::RC_AUTOCORRECTION_MATH:
1930 case LyXRC::RC_AUTOREGIONDELETE:
1931 case LyXRC::RC_AUTORESET_OPTIONS:
1932 case LyXRC::RC_AUTOSAVE:
1933 case LyXRC::RC_AUTO_NUMBER:
1934 case LyXRC::RC_BACKUPDIR_PATH:
1935 case LyXRC::RC_BIBTEX_ALTERNATIVES:
1936 case LyXRC::RC_BIBTEX_COMMAND:
1937 case LyXRC::RC_BINDFILE:
1938 case LyXRC::RC_CHECKLASTFILES:
1939 case LyXRC::RC_COMPLETION_CURSOR_TEXT:
1940 case LyXRC::RC_COMPLETION_INLINE_DELAY:
1941 case LyXRC::RC_COMPLETION_INLINE_DOTS:
1942 case LyXRC::RC_COMPLETION_INLINE_MATH:
1943 case LyXRC::RC_COMPLETION_INLINE_TEXT:
1944 case LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE:
1945 case LyXRC::RC_COMPLETION_POPUP_DELAY:
1946 case LyXRC::RC_COMPLETION_POPUP_MATH:
1947 case LyXRC::RC_COMPLETION_POPUP_TEXT:
1948 case LyXRC::RC_USELASTFILEPOS:
1949 case LyXRC::RC_LOADSESSION:
1950 case LyXRC::RC_CHKTEX_COMMAND:
1951 case LyXRC::RC_CONVERTER:
1952 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
1953 case LyXRC::RC_COPIER:
1954 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1955 case LyXRC::RC_SCROLL_BELOW_DOCUMENT:
1956 case LyXRC::RC_DATE_INSERT_FORMAT:
1957 case LyXRC::RC_DEFAULT_LANGUAGE:
1958 case LyXRC::RC_GUI_LANGUAGE:
1959 case LyXRC::RC_DEFAULT_PAPERSIZE:
1960 case LyXRC::RC_DEFAULT_VIEW_FORMAT:
1961 case LyXRC::RC_DEFFILE:
1962 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1963 case LyXRC::RC_DISPLAY_GRAPHICS:
1964 case LyXRC::RC_DOCUMENTPATH:
1965 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1966 FileName path(lyxrc_new.document_path);
1967 if (path.exists() && path.isDirectory())
1968 package().document_dir() = FileName(lyxrc.document_path);
1970 case LyXRC::RC_ESC_CHARS:
1971 case LyXRC::RC_EXAMPLEPATH:
1972 case LyXRC::RC_FONT_ENCODING:
1973 case LyXRC::RC_FORMAT:
1974 case LyXRC::RC_GROUP_LAYOUTS:
1975 case LyXRC::RC_HUNSPELLDIR_PATH:
1976 case LyXRC::RC_INDEX_ALTERNATIVES:
1977 case LyXRC::RC_INDEX_COMMAND:
1978 case LyXRC::RC_JBIBTEX_COMMAND:
1979 case LyXRC::RC_JINDEX_COMMAND:
1980 case LyXRC::RC_NOMENCL_COMMAND:
1981 case LyXRC::RC_INPUT:
1982 case LyXRC::RC_KBMAP:
1983 case LyXRC::RC_KBMAP_PRIMARY:
1984 case LyXRC::RC_KBMAP_SECONDARY:
1985 case LyXRC::RC_LABEL_INIT_LENGTH:
1986 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
1987 case LyXRC::RC_LANGUAGE_AUTO_END:
1988 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
1989 case LyXRC::RC_LANGUAGE_COMMAND_END:
1990 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
1991 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
1992 case LyXRC::RC_LANGUAGE_PACKAGE:
1993 case LyXRC::RC_LANGUAGE_USE_BABEL:
1994 case LyXRC::RC_MAC_LIKE_WORD_MOVEMENT:
1995 case LyXRC::RC_MACRO_EDIT_STYLE:
1996 case LyXRC::RC_MAKE_BACKUP:
1997 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
1998 case LyXRC::RC_MOUSE_WHEEL_SPEED:
1999 case LyXRC::RC_NUMLASTFILES:
2000 case LyXRC::RC_PATH_PREFIX:
2001 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2002 prependEnvPath("PATH", lyxrc.path_prefix);
2004 case LyXRC::RC_PERS_DICT:
2005 case LyXRC::RC_PREVIEW:
2006 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2007 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2008 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2009 case LyXRC::RC_PRINTCOPIESFLAG:
2010 case LyXRC::RC_PRINTER:
2011 case LyXRC::RC_PRINTEVENPAGEFLAG:
2012 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2013 case LyXRC::RC_PRINTFILEEXTENSION:
2014 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2015 case LyXRC::RC_PRINTODDPAGEFLAG:
2016 case LyXRC::RC_PRINTPAGERANGEFLAG:
2017 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2018 case LyXRC::RC_PRINTPAPERFLAG:
2019 case LyXRC::RC_PRINTREVERSEFLAG:
2020 case LyXRC::RC_PRINTSPOOL_COMMAND:
2021 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2022 case LyXRC::RC_PRINTTOFILE:
2023 case LyXRC::RC_PRINTTOPRINTER:
2024 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2025 case LyXRC::RC_PRINT_COMMAND:
2026 case LyXRC::RC_RTL_SUPPORT:
2027 case LyXRC::RC_SCREEN_DPI:
2028 case LyXRC::RC_SCREEN_FONT_ROMAN:
2029 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2030 case LyXRC::RC_SCREEN_FONT_SANS:
2031 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2032 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2033 case LyXRC::RC_SCREEN_FONT_SIZES:
2034 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2035 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2036 case LyXRC::RC_GEOMETRY_SESSION:
2037 case LyXRC::RC_SCREEN_ZOOM:
2038 case LyXRC::RC_SERVERPIPE:
2039 case LyXRC::RC_SET_COLOR:
2040 case LyXRC::RC_SHOW_BANNER:
2041 case LyXRC::RC_OPEN_BUFFERS_IN_TABS:
2042 case LyXRC::RC_SPELL_COMMAND:
2043 case LyXRC::RC_SPELLCHECKER:
2044 case LyXRC::RC_SPELLCHECK_CONTINUOUSLY:
2045 case LyXRC::RC_SPLITINDEX_COMMAND:
2046 case LyXRC::RC_TEMPDIRPATH:
2047 case LyXRC::RC_TEMPLATEPATH:
2048 case LyXRC::RC_TEX_ALLOWS_SPACES:
2049 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2050 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2051 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2053 case LyXRC::RC_THESAURUSDIRPATH:
2054 case LyXRC::RC_UIFILE:
2055 case LyXRC::RC_USER_EMAIL:
2056 case LyXRC::RC_USER_NAME:
2057 case LyXRC::RC_USETEMPDIR:
2058 case LyXRC::RC_USE_ALT_LANG:
2059 case LyXRC::RC_USE_CONVERTER_CACHE:
2060 case LyXRC::RC_USE_ESC_CHARS:
2061 case LyXRC::RC_USE_INP_ENC:
2062 case LyXRC::RC_USE_PERS_DICT:
2063 case LyXRC::RC_USE_TOOLTIP:
2064 case LyXRC::RC_USE_PIXMAP_CACHE:
2065 case LyXRC::RC_USE_SPELL_LIB:
2066 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2067 case LyXRC::RC_SORT_LAYOUTS:
2068 case LyXRC::RC_FULL_SCREEN_LIMIT:
2069 case LyXRC::RC_FULL_SCREEN_SCROLLBAR:
2070 case LyXRC::RC_FULL_SCREEN_MENUBAR:
2071 case LyXRC::RC_FULL_SCREEN_TABBAR:
2072 case LyXRC::RC_FULL_SCREEN_TOOLBARS:
2073 case LyXRC::RC_FULL_SCREEN_WIDTH:
2074 case LyXRC::RC_VISUAL_CURSOR:
2075 case LyXRC::RC_VIEWER:
2076 case LyXRC::RC_LAST: