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_ && lyx_view_->documentBuffer(), /**/);
904 Buffer * doc_buffer = lyx_view_->documentBuffer();
905 string format = argument;
906 if (argument.empty())
907 format = doc_buffer->getDefaultOutputFormat();
908 doc_buffer->doExport(format, true);
912 case LFUN_BUFFER_VIEW: {
913 LASSERT(lyx_view_ && lyx_view_->documentBuffer(), /**/);
914 Buffer * doc_buffer = lyx_view_->documentBuffer();
915 string format = argument;
916 if (argument.empty())
917 format = doc_buffer->getDefaultOutputFormat();
918 doc_buffer->preview(format);
922 case LFUN_MASTER_BUFFER_UPDATE: {
923 LASSERT(lyx_view_ && lyx_view_->documentBuffer() && lyx_view_->documentBuffer()->masterBuffer(), /**/);
924 Buffer * doc_buffer = lyx_view_->documentBuffer();
925 string format = argument;
926 if (argument.empty())
927 format = doc_buffer->masterBuffer()->getDefaultOutputFormat();
928 doc_buffer->masterBuffer()->doExport(format, true);
932 case LFUN_MASTER_BUFFER_VIEW: {
933 LASSERT(lyx_view_ && lyx_view_->documentBuffer() && lyx_view_->documentBuffer()->masterBuffer(), /**/);
934 Buffer * doc_buffer = lyx_view_->documentBuffer();
935 string format = argument;
936 if (argument.empty())
937 format = doc_buffer->masterBuffer()->getDefaultOutputFormat();
938 doc_buffer->masterBuffer()->preview(format);
942 case LFUN_BUILD_PROGRAM:
943 LASSERT(lyx_view_ && buffer, /**/);
944 buffer->doExport("program", true);
947 case LFUN_BUFFER_CHKTEX:
948 LASSERT(lyx_view_ && buffer, /**/);
952 case LFUN_BUFFER_EXPORT:
953 LASSERT(lyx_view_ && buffer, /**/);
954 if (argument == "custom")
955 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"));
957 buffer->doExport(argument, false);
960 case LFUN_BUFFER_EXPORT_CUSTOM: {
961 LASSERT(lyx_view_ && buffer, /**/);
963 string command = split(argument, format_name, ' ');
964 Format const * format = formats.getFormat(format_name);
966 lyxerr << "Format \"" << format_name
967 << "\" not recognized!"
972 // The name of the file created by the conversion process
975 // Output to filename
976 if (format->name() == "lyx") {
977 string const latexname = buffer->latexName(false);
978 filename = changeExtension(latexname,
979 format->extension());
980 filename = addName(buffer->temppath(), filename);
982 if (!buffer->writeFile(FileName(filename)))
986 buffer->doExport(format_name, true, filename);
989 // Substitute $$FName for filename
990 if (!contains(command, "$$FName"))
991 command = "( " + command + " ) < $$FName";
992 command = subst(command, "$$FName", filename);
994 // Execute the command in the background
996 call.startscript(Systemcall::DontWait, command);
1000 // FIXME: There is need for a command-line import.
1002 case LFUN_BUFFER_IMPORT:
1007 case LFUN_BUFFER_AUTO_SAVE:
1011 case LFUN_RECONFIGURE:
1012 // argument is any additional parameter to the configure.py command
1013 reconfigure(lyx_view_, argument);
1016 case LFUN_HELP_OPEN: {
1018 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1019 string const arg = argument;
1021 setErrorMessage(from_utf8(N_("Missing argument")));
1024 FileName fname = i18nLibFileSearch("doc", arg, "lyx");
1026 fname = i18nLibFileSearch("examples", arg, "lyx");
1028 if (fname.empty()) {
1029 lyxerr << "LyX: unable to find documentation file `"
1030 << arg << "'. Bad installation?" << endl;
1033 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1034 makeDisplayPath(fname.absFilename())));
1035 Buffer * buf = lyx_view_->loadDocument(fname, false);
1037 buf->updateLabels();
1038 lyx_view_->setBuffer(buf);
1039 buf->errors("Parse");
1041 updateFlags = Update::None;
1045 // --- version control -------------------------------
1046 case LFUN_VC_REGISTER:
1047 LASSERT(lyx_view_ && buffer, /**/);
1048 if (!ensureBufferClean(view()))
1050 if (!buffer->lyxvc().inUse()) {
1051 if (buffer->lyxvc().registrer())
1054 updateFlags = Update::Force;
1057 case LFUN_VC_CHECK_IN:
1058 LASSERT(lyx_view_ && buffer, /**/);
1059 if (!ensureBufferClean(view()))
1061 if (buffer->lyxvc().inUse()
1062 && !buffer->isReadonly()) {
1063 setMessage(from_utf8(buffer->lyxvc().checkIn()));
1068 case LFUN_VC_CHECK_OUT:
1069 LASSERT(lyx_view_ && buffer, /**/);
1070 if (!ensureBufferClean(view()))
1072 if (buffer->lyxvc().inUse()) {
1073 setMessage(from_utf8(buffer->lyxvc().checkOut()));
1078 case LFUN_VC_LOCKING_TOGGLE:
1079 LASSERT(lyx_view_ && buffer, /**/);
1080 if (!ensureBufferClean(view()) || buffer->isReadonly())
1082 if (buffer->lyxvc().inUse()) {
1083 string res = buffer->lyxvc().lockingToggle();
1085 frontend::Alert::error(_("Revision control error."),
1086 _("Error when setting the locking property."));
1088 setMessage(from_utf8(res));
1094 case LFUN_VC_REVERT:
1095 LASSERT(lyx_view_ && buffer, /**/);
1096 buffer->lyxvc().revert();
1100 case LFUN_VC_UNDO_LAST:
1101 LASSERT(lyx_view_ && buffer, /**/);
1102 buffer->lyxvc().undoLast();
1106 // --- lyxserver commands ----------------------------
1107 case LFUN_SERVER_GET_FILENAME:
1108 LASSERT(lyx_view_ && buffer, /**/);
1109 setMessage(from_utf8(buffer->absFileName()));
1110 LYXERR(Debug::INFO, "FNAME["
1111 << buffer->absFileName() << ']');
1114 case LFUN_SERVER_NOTIFY:
1115 dispatch_buffer = keyseq.print(KeySequence::Portable);
1116 theServer().notifyClient(to_utf8(dispatch_buffer));
1119 case LFUN_SERVER_GOTO_FILE_ROW: {
1120 LASSERT(lyx_view_, /**/);
1123 istringstream is(argument);
1124 is >> file_name >> row;
1125 file_name = os::internal_path(file_name);
1127 bool loaded = false;
1128 string const abstmp = package().temp_dir().absFilename();
1129 string const realtmp = package().temp_dir().realPath();
1130 // We have to use os::path_prefix_is() here, instead of
1131 // simply prefixIs(), because the file name comes from
1132 // an external application and may need case adjustment.
1133 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
1134 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
1135 // Needed by inverse dvi search. If it is a file
1136 // in tmpdir, call the apropriated function.
1137 // If tmpdir is a symlink, we may have the real
1138 // path passed back, so we correct for that.
1139 if (!prefixIs(file_name, abstmp))
1140 file_name = subst(file_name, realtmp, abstmp);
1141 buf = theBufferList().getBufferFromTmp(file_name);
1143 // Must replace extension of the file to be .lyx
1144 // and get full path
1145 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1146 // Either change buffer or load the file
1147 if (theBufferList().exists(s))
1148 buf = theBufferList().getBuffer(s);
1149 else if (s.exists()) {
1150 buf = lyx_view_->loadDocument(s);
1153 lyx_view_->message(bformat(
1154 _("File does not exist: %1$s"),
1155 makeDisplayPath(file_name)));
1159 updateFlags = Update::None;
1163 buf->updateLabels();
1164 lyx_view_->setBuffer(buf);
1165 view()->setCursorFromRow(row);
1167 buf->errors("Parse");
1168 updateFlags = Update::FitCursor;
1173 case LFUN_DIALOG_SHOW_NEW_INSET: {
1174 LASSERT(lyx_view_, /**/);
1175 string const name = cmd.getArg(0);
1176 InsetCode code = insetCode(name);
1177 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1178 bool insetCodeOK = true;
1185 case NOMENCL_PRINT_CODE:
1188 case HYPERLINK_CODE: {
1189 InsetCommandParams p(code);
1190 data = InsetCommand::params2string(name, p);
1193 case INCLUDE_CODE: {
1194 // data is the include type: one of "include",
1195 // "input", "verbatiminput" or "verbatiminput*"
1197 // default type is requested
1199 InsetCommandParams p(INCLUDE_CODE, data);
1200 data = InsetCommand::params2string("include", p);
1204 // \c data == "Boxed" || "Frameless" etc
1205 InsetBoxParams p(data);
1206 data = InsetBox::params2string(p);
1210 InsetBranchParams p;
1211 data = InsetBranch::params2string(p);
1215 InsetCommandParams p(CITE_CODE);
1216 data = InsetCommand::params2string(name, p);
1220 data = InsetERT::params2string(InsetCollapsable::Open);
1223 case EXTERNAL_CODE: {
1224 InsetExternalParams p;
1225 data = InsetExternal::params2string(p, *buffer);
1230 data = InsetFloat::params2string(p);
1233 case LISTINGS_CODE: {
1234 InsetListingsParams p;
1235 data = InsetListings::params2string(p);
1238 case GRAPHICS_CODE: {
1239 InsetGraphicsParams p;
1240 data = InsetGraphics::params2string(p, *buffer);
1245 data = InsetNote::params2string(p);
1248 case PHANTOM_CODE: {
1249 InsetPhantomParams p;
1250 data = InsetPhantom::params2string(p);
1255 data = InsetSpace::params2string(p);
1260 data = InsetVSpace::params2string(space);
1265 data = InsetWrap::params2string(p);
1269 lyxerr << "Inset type '" << name <<
1270 "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" << endl;
1271 insetCodeOK = false;
1273 } // end switch(code)
1275 dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data));
1279 case LFUN_CITATION_INSERT: {
1280 LASSERT(lyx_view_, /**/);
1281 if (!argument.empty()) {
1282 // we can have one optional argument, delimited by '|'
1283 // citation-insert <key>|<text_before>
1284 // this should be enhanced to also support text_after
1285 // and citation style
1286 string arg = argument;
1288 if (contains(argument, "|")) {
1289 arg = token(argument, '|', 0);
1290 opt1 = token(argument, '|', 1);
1292 InsetCommandParams icp(CITE_CODE);
1293 icp["key"] = from_utf8(arg);
1295 icp["before"] = from_utf8(opt1);
1296 string icstr = InsetCommand::params2string("citation", icp);
1297 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1300 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1304 case LFUN_BUFFER_CHILD_OPEN: {
1305 LASSERT(lyx_view_ && buffer, /**/);
1306 FileName filename = makeAbsPath(argument, buffer->filePath());
1307 view()->saveBookmark(false);
1309 bool parsed = false;
1310 if (theBufferList().exists(filename)) {
1311 child = theBufferList().getBuffer(filename);
1313 setMessage(bformat(_("Opening child document %1$s..."),
1314 makeDisplayPath(filename.absFilename())));
1315 child = lyx_view_->loadDocument(filename, false);
1319 // Set the parent name of the child document.
1320 // This makes insertion of citations and references in the child work,
1321 // when the target is in the parent or another child document.
1322 child->setParent(buffer);
1323 child->masterBuffer()->updateLabels();
1324 lyx_view_->setBuffer(child);
1326 child->errors("Parse");
1329 // If a screen update is required (in case where auto_open is false),
1330 // setBuffer() would have taken care of it already. Otherwise we shall
1331 // reset the update flag because it can cause a circular problem.
1333 updateFlags = Update::None;
1337 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
1338 LASSERT(lyx_view_, /**/);
1339 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1342 case LFUN_KEYMAP_OFF:
1343 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1344 lyx_view_->view()->getIntl().keyMapOn(false);
1347 case LFUN_KEYMAP_PRIMARY:
1348 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1349 lyx_view_->view()->getIntl().keyMapPrim();
1352 case LFUN_KEYMAP_SECONDARY:
1353 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1354 lyx_view_->view()->getIntl().keyMapSec();
1357 case LFUN_KEYMAP_TOGGLE:
1358 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1359 lyx_view_->view()->getIntl().toggleKeyMap();
1365 string rest = split(argument, countstr, ' ');
1366 istringstream is(countstr);
1369 //lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1370 for (int i = 0; i < count; ++i)
1371 dispatch(lyxaction.lookupFunc(rest));
1375 case LFUN_COMMAND_SEQUENCE: {
1376 // argument contains ';'-terminated commands
1377 string arg = argument;
1378 if (theBufferList().isLoaded(buffer))
1379 buffer->undo().beginUndoGroup();
1380 while (!arg.empty()) {
1382 arg = split(arg, first, ';');
1383 FuncRequest func(lyxaction.lookupFunc(first));
1384 func.origin = cmd.origin;
1387 if (theBufferList().isLoaded(buffer))
1388 buffer->undo().endUndoGroup();
1392 case LFUN_COMMAND_ALTERNATIVES: {
1393 // argument contains ';'-terminated commands
1394 string arg = argument;
1395 while (!arg.empty()) {
1397 arg = split(arg, first, ';');
1398 FuncRequest func(lyxaction.lookupFunc(first));
1399 func.origin = cmd.origin;
1400 FuncStatus stat = getStatus(func);
1401 if (stat.enabled()) {
1411 if (theTopLevelCmdDef().lock(argument, func)) {
1412 func.origin = cmd.origin;
1414 theTopLevelCmdDef().release(argument);
1416 if (func.action == LFUN_UNKNOWN_ACTION) {
1417 // unknown command definition
1418 lyxerr << "Warning: unknown command definition `"
1422 // recursion detected
1423 lyxerr << "Warning: Recursion in the command definition `"
1424 << argument << "' detected"
1431 case LFUN_PREFERENCES_SAVE: {
1432 lyxrc.write(makeAbsPath("preferences",
1433 package().user_support().absFilename()),
1439 LASSERT(lyx_view_, /**/);
1440 lyx_view_->message(from_utf8(argument));
1443 case LFUN_BUFFER_LANGUAGE: {
1444 LASSERT(lyx_view_, /**/);
1445 Language const * oldL = buffer->params().language;
1446 Language const * newL = languages.getLanguage(argument);
1447 if (!newL || oldL == newL)
1450 if (oldL->rightToLeft() == newL->rightToLeft()
1451 && !buffer->isMultiLingual())
1452 buffer->changeLanguage(oldL, newL);
1456 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1457 string const fname =
1458 addName(addPath(package().user_support().absFilename(), "templates/"),
1460 Buffer defaults(fname);
1462 istringstream ss(argument);
1465 int const unknown_tokens = defaults.readHeader(lex);
1467 if (unknown_tokens != 0) {
1468 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1469 << unknown_tokens << " unknown token"
1470 << (unknown_tokens == 1 ? "" : "s")
1474 if (defaults.writeFile(FileName(defaults.absFileName())))
1475 setMessage(bformat(_("Document defaults saved in %1$s"),
1476 makeDisplayPath(fname)));
1478 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1482 case LFUN_BUFFER_PARAMS_APPLY: {
1483 LASSERT(lyx_view_, /**/);
1485 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1486 Cursor & cur = view()->cursor();
1487 cur.recordUndoFullDocument();
1489 istringstream ss(argument);
1492 int const unknown_tokens = buffer->readHeader(lex);
1494 if (unknown_tokens != 0) {
1495 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1496 << unknown_tokens << " unknown token"
1497 << (unknown_tokens == 1 ? "" : "s")
1501 updateLayout(oldClass, buffer);
1503 updateFlags = Update::Force | Update::FitCursor;
1504 // We are most certainly here because of a change in the document
1505 // It is then better to make sure that all dialogs are in sync with
1506 // current document settings. LyXView::restartCursor() achieve this.
1507 lyx_view_->restartCursor();
1511 case LFUN_LAYOUT_MODULES_CLEAR: {
1512 LASSERT(lyx_view_, /**/);
1513 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1514 view()->cursor().recordUndoFullDocument();
1515 buffer->params().clearLayoutModules();
1516 buffer->params().makeDocumentClass();
1517 updateLayout(oldClass, buffer);
1518 updateFlags = Update::Force | Update::FitCursor;
1522 case LFUN_LAYOUT_MODULE_ADD: {
1523 LASSERT(lyx_view_, /**/);
1524 BufferParams const & params = buffer->params();
1525 if (!params.moduleCanBeAdded(argument)) {
1526 LYXERR0("Module `" << argument <<
1527 "' cannot be added due to failed requirements or "
1528 "conflicts with installed modules.");
1531 DocumentClass const * const oldClass = params.documentClassPtr();
1532 view()->cursor().recordUndoFullDocument();
1533 buffer->params().addLayoutModule(argument);
1534 buffer->params().makeDocumentClass();
1535 updateLayout(oldClass, buffer);
1536 updateFlags = Update::Force | Update::FitCursor;
1540 case LFUN_TEXTCLASS_APPLY: {
1541 LASSERT(lyx_view_, /**/);
1543 if (!loadLayoutFile(argument, buffer->temppath()) &&
1544 !loadLayoutFile(argument, buffer->filePath()))
1547 LayoutFile const * old_layout = buffer->params().baseClass();
1548 LayoutFile const * new_layout = &(LayoutFileList::get()[argument]);
1550 if (old_layout == new_layout)
1554 //Save the old, possibly modular, layout for use in conversion.
1555 DocumentClass const * const oldDocClass = buffer->params().documentClassPtr();
1556 view()->cursor().recordUndoFullDocument();
1557 buffer->params().setBaseClass(argument);
1558 buffer->params().makeDocumentClass();
1559 updateLayout(oldDocClass, buffer);
1560 updateFlags = Update::Force | Update::FitCursor;
1564 case LFUN_LAYOUT_RELOAD: {
1565 LASSERT(lyx_view_, /**/);
1566 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1567 LayoutFileIndex bc = buffer->params().baseClassID();
1568 LayoutFileList::get().reset(bc);
1569 buffer->params().setBaseClass(bc);
1570 buffer->params().makeDocumentClass();
1571 updateLayout(oldClass, buffer);
1572 updateFlags = Update::Force | Update::FitCursor;
1576 case LFUN_TEXTCLASS_LOAD:
1577 loadLayoutFile(argument, buffer->temppath()) ||
1578 loadLayoutFile(argument, buffer->filePath());
1581 case LFUN_LYXRC_APPLY: {
1582 // reset active key sequences, since the bindings
1583 // are updated (bug 6064)
1585 LyXRC const lyxrc_orig = lyxrc;
1587 istringstream ss(argument);
1588 bool const success = lyxrc.read(ss) == 0;
1591 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1592 << "Unable to read lyxrc data"
1597 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1601 theApp()->resetGui();
1603 /// We force the redraw in any case because there might be
1604 /// some screen font changes.
1605 /// FIXME: only the current view will be updated. the Gui
1606 /// class is able to furnish the list of views.
1607 updateFlags = Update::Force;
1611 case LFUN_BOOKMARK_GOTO:
1612 // go to bookmark, open unopened file and switch to buffer if necessary
1613 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1614 updateFlags = Update::FitCursor;
1617 case LFUN_BOOKMARK_CLEAR:
1618 theSession().bookmarks().clear();
1621 case LFUN_VC_COMMAND: {
1622 string flag = cmd.getArg(0);
1623 if (buffer && contains(flag, 'R') && !ensureBufferClean(view()))
1626 if (contains(flag, 'M'))
1627 if (!Alert::askForText(message, _("LyX VC: Log Message")))
1630 string path = cmd.getArg(1);
1631 if (contains(path, "$$p") && buffer)
1632 path = subst(path, "$$p", buffer->filePath());
1633 LYXERR(Debug::LYXVC, "Directory: " << path);
1635 if (!pp.isReadableDirectory()) {
1636 lyxerr << _("Directory is not accessible.") << endl;
1639 support::PathChanger p(pp);
1641 string command = cmd.getArg(2);
1642 if (command.empty())
1645 command = subst(command, "$$i", buffer->absFileName());
1646 command = subst(command, "$$p", buffer->filePath());
1648 command = subst(command, "$$m", to_utf8(message));
1649 LYXERR(Debug::LYXVC, "Command: " << command);
1651 one.startscript(Systemcall::Wait, command);
1655 if (contains(flag, 'I'))
1656 buffer->markDirty();
1657 if (contains(flag, 'R'))
1664 LASSERT(theApp(), /**/);
1665 // Let the frontend dispatch its own actions.
1666 if (theApp()->dispatch(cmd))
1667 // Nothing more to do.
1670 // Everything below is only for active lyx_view_
1674 // Start an undo group. This may be needed for
1675 // some stuff like inset-apply on labels.
1676 if (theBufferList().isLoaded(buffer))
1677 buffer->undo().beginUndoGroup();
1679 // Let the current LyXView dispatch its own actions.
1680 if (lyx_view_->dispatch(cmd)) {
1681 if (lyx_view_->view()) {
1682 updateFlags = lyx_view_->view()->cursor().result().update();
1683 if (theBufferList().isLoaded(buffer))
1684 buffer->undo().endUndoGroup();
1689 LASSERT(lyx_view_->view(), /**/);
1691 // Let the current BufferView dispatch its own actions.
1692 if (view()->dispatch(cmd)) {
1693 // The BufferView took care of its own updates if needed.
1694 updateFlags = Update::None;
1695 if (theBufferList().isLoaded(buffer))
1696 buffer->undo().endUndoGroup();
1700 // OK, so try the Buffer itself
1702 view()->buffer().dispatch(cmd, dr);
1703 if (dr.dispatched()) {
1704 updateFlags = dr.update();
1708 // Is this a function that acts on inset at point?
1709 Inset * inset = view()->cursor().nextInset();
1710 if (lyxaction.funcHasFlag(action, LyXAction::AtPoint)
1712 view()->cursor().result().dispatched(true);
1713 view()->cursor().result().update(Update::FitCursor | Update::Force);
1714 FuncRequest tmpcmd = cmd;
1715 inset->dispatch(view()->cursor(), tmpcmd);
1716 if (view()->cursor().result().dispatched()) {
1717 updateFlags = view()->cursor().result().update();
1722 // Let the current Cursor dispatch its own actions.
1723 Cursor old = view()->cursor();
1724 view()->cursor().getPos(cursorPosBeforeDispatchX_,
1725 cursorPosBeforeDispatchY_);
1726 view()->cursor().dispatch(cmd);
1728 // notify insets we just left
1729 if (view()->cursor() != old) {
1731 bool badcursor = notifyCursorLeavesOrEnters(old, view()->cursor());
1733 view()->cursor().fixIfBroken();
1736 if (theBufferList().isLoaded(buffer))
1737 buffer->undo().endUndoGroup();
1739 // update completion. We do it here and not in
1740 // processKeySym to avoid another redraw just for a
1741 // changed inline completion
1742 if (cmd.origin == FuncRequest::KEYBOARD) {
1743 if (cmd.action == LFUN_SELF_INSERT
1744 || (cmd.action == LFUN_ERT_INSERT && view()->cursor().inMathed()))
1745 lyx_view_->updateCompletion(view()->cursor(), true, true);
1746 else if (cmd.action == LFUN_CHAR_DELETE_BACKWARD)
1747 lyx_view_->updateCompletion(view()->cursor(), false, true);
1749 lyx_view_->updateCompletion(view()->cursor(), false, false);
1752 updateFlags = view()->cursor().result().update();
1755 // if we executed a mutating lfun, mark the buffer as dirty
1756 if (theBufferList().isLoaded(buffer) && flag.enabled()
1757 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1758 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1759 buffer->markDirty();
1761 if (lyx_view_ && lyx_view_->buffer()) {
1762 // BufferView::update() updates the ViewMetricsInfo and
1763 // also initializes the position cache for all insets in
1764 // (at least partially) visible top-level paragraphs.
1765 // We will redraw the screen only if needed.
1766 view()->processUpdateFlags(updateFlags);
1768 // Do we have a selection?
1769 theSelection().haveSelection(view()->cursor().selection());
1772 lyx_view_->restartCursor();
1776 // Some messages may already be translated, so we cannot use _()
1777 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1782 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1784 const bool verbose = (cmd.origin == FuncRequest::MENU
1785 || cmd.origin == FuncRequest::TOOLBAR
1786 || cmd.origin == FuncRequest::COMMANDBUFFER);
1788 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1789 LYXERR(Debug::ACTION, "dispatch msg is " << to_utf8(msg));
1791 lyx_view_->message(msg);
1795 docstring dispatch_msg = msg;
1796 if (!dispatch_msg.empty())
1797 dispatch_msg += ' ';
1799 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1801 bool argsadded = false;
1803 if (!cmd.argument().empty()) {
1804 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1805 comname += ' ' + cmd.argument();
1810 docstring const shortcuts = theTopLevelKeymap().printBindings(cmd, KeySequence::ForGui);
1812 if (!shortcuts.empty())
1813 comname += ": " + shortcuts;
1814 else if (!argsadded && !cmd.argument().empty())
1815 comname += ' ' + cmd.argument();
1817 if (!comname.empty()) {
1818 comname = rtrim(comname);
1819 dispatch_msg += '(' + rtrim(comname) + ')';
1822 LYXERR(Debug::ACTION, "verbose dispatch msg " << to_utf8(dispatch_msg));
1823 if (!dispatch_msg.empty())
1824 lyx_view_->message(dispatch_msg);
1828 void LyXFunc::reloadBuffer()
1830 FileName filename = lyx_view_->buffer()->fileName();
1831 // The user has already confirmed that the changes, if any, should
1832 // be discarded. So we just release the Buffer and don't call closeBuffer();
1833 theBufferList().release(lyx_view_->buffer());
1834 // if the lyx_view_ has been destroyed, create a new one
1836 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1837 Buffer * buf = lyx_view_->loadDocument(filename);
1838 docstring const disp_fn = makeDisplayPath(filename.absFilename());
1841 buf->updateLabels();
1842 lyx_view_->setBuffer(buf);
1843 buf->errors("Parse");
1844 str = bformat(_("Document %1$s reloaded."), disp_fn);
1846 str = bformat(_("Could not reload document %1$s"), disp_fn);
1848 lyx_view_->message(str);
1851 // Each "lyx_view_" should have it's own message method. lyxview and
1852 // the minibuffer would use the minibuffer, but lyxserver would
1853 // send an ERROR signal to its client. Alejandro 970603
1854 // This function is bit problematic when it comes to NLS, to make the
1855 // lyx servers client be language indepenent we must not translate
1856 // strings sent to this func.
1857 void LyXFunc::setErrorMessage(docstring const & m) const
1859 dispatch_buffer = m;
1864 void LyXFunc::setMessage(docstring const & m) const
1866 dispatch_buffer = m;
1870 docstring LyXFunc::viewStatusMessage()
1872 // When meta-fake key is pressed, show the key sequence so far + "M-".
1874 return keyseq.print(KeySequence::ForGui) + "M-";
1876 // Else, when a non-complete key sequence is pressed,
1877 // show the available options.
1878 if (keyseq.length() > 0 && !keyseq.deleted())
1879 return keyseq.printOptions(true);
1881 LASSERT(lyx_view_, /**/);
1882 if (!lyx_view_->buffer())
1883 return _("Welcome to LyX!");
1885 return view()->cursor().currentState();
1889 BufferView * LyXFunc::view() const
1891 LASSERT(lyx_view_, /**/);
1892 return lyx_view_->view();
1896 bool LyXFunc::wasMetaKey() const
1898 return (meta_fake_bit != NoModifier);
1902 void LyXFunc::updateLayout(DocumentClass const * const oldlayout, Buffer * buf)
1904 lyx_view_->message(_("Converting document to new document class..."));
1906 StableDocIterator backcur(view()->cursor());
1907 ErrorList & el = buf->errorList("Class Switch");
1908 cap::switchBetweenClasses(
1909 oldlayout, buf->params().documentClassPtr(),
1910 static_cast<InsetText &>(buf->inset()), el);
1912 view()->setCursor(backcur.asDocIterator(buf));
1914 buf->errors("Class Switch");
1915 buf->updateLabels();
1921 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1923 // Why the switch you might ask. It is a trick to ensure that all
1924 // the elements in the LyXRCTags enum is handled. As you can see
1925 // there are no breaks at all. So it is just a huge fall-through.
1926 // The nice thing is that we will get a warning from the compiler
1927 // if we forget an element.
1928 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1930 case LyXRC::RC_ACCEPT_COMPOUND:
1931 case LyXRC::RC_ALT_LANG:
1932 case LyXRC::RC_PLAINTEXT_LINELEN:
1933 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
1934 case LyXRC::RC_AUTOCORRECTION_MATH:
1935 case LyXRC::RC_AUTOREGIONDELETE:
1936 case LyXRC::RC_AUTORESET_OPTIONS:
1937 case LyXRC::RC_AUTOSAVE:
1938 case LyXRC::RC_AUTO_NUMBER:
1939 case LyXRC::RC_BACKUPDIR_PATH:
1940 case LyXRC::RC_BIBTEX_ALTERNATIVES:
1941 case LyXRC::RC_BIBTEX_COMMAND:
1942 case LyXRC::RC_BINDFILE:
1943 case LyXRC::RC_CHECKLASTFILES:
1944 case LyXRC::RC_COMPLETION_CURSOR_TEXT:
1945 case LyXRC::RC_COMPLETION_INLINE_DELAY:
1946 case LyXRC::RC_COMPLETION_INLINE_DOTS:
1947 case LyXRC::RC_COMPLETION_INLINE_MATH:
1948 case LyXRC::RC_COMPLETION_INLINE_TEXT:
1949 case LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE:
1950 case LyXRC::RC_COMPLETION_POPUP_DELAY:
1951 case LyXRC::RC_COMPLETION_POPUP_MATH:
1952 case LyXRC::RC_COMPLETION_POPUP_TEXT:
1953 case LyXRC::RC_USELASTFILEPOS:
1954 case LyXRC::RC_LOADSESSION:
1955 case LyXRC::RC_CHKTEX_COMMAND:
1956 case LyXRC::RC_CONVERTER:
1957 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
1958 case LyXRC::RC_COPIER:
1959 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1960 case LyXRC::RC_SCROLL_BELOW_DOCUMENT:
1961 case LyXRC::RC_DATE_INSERT_FORMAT:
1962 case LyXRC::RC_DEFAULT_LANGUAGE:
1963 case LyXRC::RC_GUI_LANGUAGE:
1964 case LyXRC::RC_DEFAULT_PAPERSIZE:
1965 case LyXRC::RC_DEFAULT_VIEW_FORMAT:
1966 case LyXRC::RC_DEFFILE:
1967 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1968 case LyXRC::RC_DISPLAY_GRAPHICS:
1969 case LyXRC::RC_DOCUMENTPATH:
1970 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1971 FileName path(lyxrc_new.document_path);
1972 if (path.exists() && path.isDirectory())
1973 package().document_dir() = FileName(lyxrc.document_path);
1975 case LyXRC::RC_ESC_CHARS:
1976 case LyXRC::RC_EXAMPLEPATH:
1977 case LyXRC::RC_FONT_ENCODING:
1978 case LyXRC::RC_FORMAT:
1979 case LyXRC::RC_GROUP_LAYOUTS:
1980 case LyXRC::RC_HUNSPELLDIR_PATH:
1981 case LyXRC::RC_INDEX_ALTERNATIVES:
1982 case LyXRC::RC_INDEX_COMMAND:
1983 case LyXRC::RC_JBIBTEX_COMMAND:
1984 case LyXRC::RC_JINDEX_COMMAND:
1985 case LyXRC::RC_NOMENCL_COMMAND:
1986 case LyXRC::RC_INPUT:
1987 case LyXRC::RC_KBMAP:
1988 case LyXRC::RC_KBMAP_PRIMARY:
1989 case LyXRC::RC_KBMAP_SECONDARY:
1990 case LyXRC::RC_LABEL_INIT_LENGTH:
1991 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
1992 case LyXRC::RC_LANGUAGE_AUTO_END:
1993 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
1994 case LyXRC::RC_LANGUAGE_COMMAND_END:
1995 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
1996 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
1997 case LyXRC::RC_LANGUAGE_PACKAGE:
1998 case LyXRC::RC_LANGUAGE_USE_BABEL:
1999 case LyXRC::RC_MAC_LIKE_WORD_MOVEMENT:
2000 case LyXRC::RC_MACRO_EDIT_STYLE:
2001 case LyXRC::RC_MAKE_BACKUP:
2002 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2003 case LyXRC::RC_MOUSE_WHEEL_SPEED:
2004 case LyXRC::RC_NUMLASTFILES:
2005 case LyXRC::RC_PARAGRAPH_MARKERS:
2006 case LyXRC::RC_PATH_PREFIX:
2007 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2008 prependEnvPath("PATH", lyxrc.path_prefix);
2010 case LyXRC::RC_PERS_DICT:
2011 case LyXRC::RC_PREVIEW:
2012 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2013 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2014 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2015 case LyXRC::RC_PRINTCOPIESFLAG:
2016 case LyXRC::RC_PRINTER:
2017 case LyXRC::RC_PRINTEVENPAGEFLAG:
2018 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2019 case LyXRC::RC_PRINTFILEEXTENSION:
2020 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2021 case LyXRC::RC_PRINTODDPAGEFLAG:
2022 case LyXRC::RC_PRINTPAGERANGEFLAG:
2023 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2024 case LyXRC::RC_PRINTPAPERFLAG:
2025 case LyXRC::RC_PRINTREVERSEFLAG:
2026 case LyXRC::RC_PRINTSPOOL_COMMAND:
2027 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2028 case LyXRC::RC_PRINTTOFILE:
2029 case LyXRC::RC_PRINTTOPRINTER:
2030 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2031 case LyXRC::RC_PRINT_COMMAND:
2032 case LyXRC::RC_RTL_SUPPORT:
2033 case LyXRC::RC_SCREEN_DPI:
2034 case LyXRC::RC_SCREEN_FONT_ROMAN:
2035 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2036 case LyXRC::RC_SCREEN_FONT_SANS:
2037 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2038 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2039 case LyXRC::RC_SCREEN_FONT_SIZES:
2040 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2041 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2042 case LyXRC::RC_GEOMETRY_SESSION:
2043 case LyXRC::RC_SCREEN_ZOOM:
2044 case LyXRC::RC_SERVERPIPE:
2045 case LyXRC::RC_SET_COLOR:
2046 case LyXRC::RC_SHOW_BANNER:
2047 case LyXRC::RC_OPEN_BUFFERS_IN_TABS:
2048 case LyXRC::RC_SPELL_COMMAND:
2049 case LyXRC::RC_SPELLCHECKER:
2050 case LyXRC::RC_SPELLCHECK_CONTINUOUSLY:
2051 case LyXRC::RC_SPLITINDEX_COMMAND:
2052 case LyXRC::RC_TEMPDIRPATH:
2053 case LyXRC::RC_TEMPLATEPATH:
2054 case LyXRC::RC_TEX_ALLOWS_SPACES:
2055 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2056 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2057 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2059 case LyXRC::RC_THESAURUSDIRPATH:
2060 case LyXRC::RC_UIFILE:
2061 case LyXRC::RC_USER_EMAIL:
2062 case LyXRC::RC_USER_NAME:
2063 case LyXRC::RC_USETEMPDIR:
2064 case LyXRC::RC_USE_ALT_LANG:
2065 case LyXRC::RC_USE_CONVERTER_CACHE:
2066 case LyXRC::RC_USE_ESC_CHARS:
2067 case LyXRC::RC_USE_INP_ENC:
2068 case LyXRC::RC_USE_PERS_DICT:
2069 case LyXRC::RC_USE_TOOLTIP:
2070 case LyXRC::RC_USE_PIXMAP_CACHE:
2071 case LyXRC::RC_USE_SPELL_LIB:
2072 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2073 case LyXRC::RC_SORT_LAYOUTS:
2074 case LyXRC::RC_FULL_SCREEN_LIMIT:
2075 case LyXRC::RC_FULL_SCREEN_SCROLLBAR:
2076 case LyXRC::RC_FULL_SCREEN_MENUBAR:
2077 case LyXRC::RC_FULL_SCREEN_TABBAR:
2078 case LyXRC::RC_FULL_SCREEN_TOOLBARS:
2079 case LyXRC::RC_FULL_SCREEN_WIDTH:
2080 case LyXRC::RC_VISUAL_CURSOR:
2081 case LyXRC::RC_VIEWER:
2082 case LyXRC::RC_LAST: