3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
9 * \author Angus Leeming
11 * \author André Pönitz
14 * \author Martin Vermeer
15 * \author Jürgen Vigna
17 * Full author contact details are available in file CREDITS.
24 #include "LayoutFile.h"
25 #include "BranchList.h"
26 #include "buffer_funcs.h"
28 #include "BufferList.h"
29 #include "BufferParams.h"
30 #include "BufferView.h"
33 #include "Converter.h"
35 #include "CutAndPaste.h"
36 #include "DispatchResult.h"
38 #include "ErrorList.h"
40 #include "FuncRequest.h"
41 #include "FuncStatus.h"
42 #include "InsetIterator.h"
47 #include "LyXAction.h"
52 #include "Paragraph.h"
53 #include "ParagraphParameters.h"
54 #include "ParIterator.h"
58 #include "SpellChecker.h"
60 #include "insets/InsetBox.h"
61 #include "insets/InsetBranch.h"
62 #include "insets/InsetCommand.h"
63 #include "insets/InsetERT.h"
64 #include "insets/InsetExternal.h"
65 #include "insets/InsetFloat.h"
66 #include "insets/InsetGraphics.h"
67 #include "insets/InsetInclude.h"
68 #include "insets/InsetListings.h"
69 #include "insets/InsetNote.h"
70 #include "insets/InsetPhantom.h"
71 #include "insets/InsetSpace.h"
72 #include "insets/InsetTabular.h"
73 #include "insets/InsetVSpace.h"
74 #include "insets/InsetWrap.h"
76 #include "frontends/alert.h"
77 #include "frontends/Application.h"
78 #include "frontends/KeySymbol.h"
79 #include "frontends/LyXView.h"
80 #include "frontends/Selection.h"
82 #include "support/debug.h"
83 #include "support/environment.h"
84 #include "support/FileName.h"
85 #include "support/filetools.h"
86 #include "support/gettext.h"
87 #include "support/lstrings.h"
88 #include "support/Path.h"
89 #include "support/Package.h"
90 #include "support/Systemcall.h"
91 #include "support/convert.h"
92 #include "support/os.h"
98 using namespace lyx::support;
102 using frontend::LyXView;
104 namespace Alert = frontend::Alert;
109 // This function runs "configure" and then rereads lyx.defaults to
110 // reconfigure the automatic settings.
111 void reconfigure(LyXView * lv, string const & option)
113 // emit message signal.
115 lv->message(_("Running configure..."));
117 // Run configure in user lyx directory
118 PathChanger p(package().user_support());
119 string configure_command = package().configure_command();
120 configure_command += option;
122 int ret = one.startscript(Systemcall::Wait, configure_command);
124 // emit message signal.
126 lv->message(_("Reloading configuration..."));
127 lyxrc.read(libFileSearch(string(), "lyxrc.defaults"));
128 // Re-read packages.lst
129 LaTeXFeatures::getAvailable();
132 Alert::information(_("System reconfiguration failed"),
133 _("The system reconfiguration has failed.\n"
134 "Default textclass is used but LyX may "
135 "not be able to work properly.\n"
136 "Please reconfigure again if needed."));
139 Alert::information(_("System reconfigured"),
140 _("The system has been reconfigured.\n"
141 "You need to restart LyX to make use of any\n"
142 "updated document class specifications."));
146 bool getLocalStatus(Cursor cursor, FuncRequest const & cmd, FuncStatus & status)
148 // Try to fix cursor in case it is broken.
149 cursor.fixIfBroken();
151 // This is, of course, a mess. Better create a new doc iterator and use
152 // this in Inset::getStatus. This might require an additional
153 // BufferView * arg, though (which should be avoided)
154 //Cursor safe = *this;
156 for ( ; cursor.depth(); cursor.pop()) {
157 //lyxerr << "\nCursor::getStatus: cmd: " << cmd << endl << *this << endl;
158 LASSERT(cursor.idx() <= cursor.lastidx(), /**/);
159 LASSERT(cursor.pit() <= cursor.lastpit(), /**/);
160 LASSERT(cursor.pos() <= cursor.lastpos(), /**/);
162 // The inset's getStatus() will return 'true' if it made
163 // a definitive decision on whether it want to handle the
164 // request or not. The result of this decision is put into
165 // the 'status' parameter.
166 if (cursor.inset().getStatus(cursor, cmd, status)) {
175 /** Return the change status at cursor position, taking in account the
176 * status at each level of the document iterator (a table in a deleted
177 * footnote is deleted).
178 * When \param outer is true, the top slice is not looked at.
180 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
182 size_t const depth = dit.depth() - (outer ? 1 : 0);
184 for (size_t i = 0 ; i < depth ; ++i) {
185 CursorSlice const & slice = dit[i];
186 if (!slice.inset().inMathed()
187 && slice.pos() < slice.paragraph().size()) {
188 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
189 if (ch != Change::UNCHANGED)
193 return Change::UNCHANGED;
200 : lyx_view_(0), encoded_last_key(0), meta_fake_bit(NoModifier)
205 void LyXFunc::initKeySequences(KeyMap * kb)
207 keyseq = KeySequence(kb, kb);
208 cancel_meta_seq = KeySequence(kb, kb);
212 void LyXFunc::setLyXView(LyXView * lv)
214 if (lyx_view_ && lyx_view_->currentBufferView() && lyx_view_ != lv)
215 // save current selection to the selection buffer to allow
216 // middle-button paste in another window
217 cap::saveSelection(lyx_view_->currentBufferView()->cursor());
222 void LyXFunc::handleKeyFunc(FuncCode action)
224 char_type c = encoded_last_key;
229 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
230 BufferView * bv = lyx_view_->currentBufferView();
231 bv->getIntl().getTransManager().deadkey(
232 c, get_accent(action).accent, bv->cursor().innerText(),
234 // Need to clear, in case the minibuffer calls these
237 // copied verbatim from do_accent_char
238 bv->cursor().resetAnchor();
239 bv->processUpdateFlags(Update::FitCursor);
242 //FIXME: bookmark handling is a frontend issue. This code should be transferred
243 // to GuiView and be GuiView and be window dependent.
244 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
246 LASSERT(lyx_view_, /**/);
247 if (!theSession().bookmarks().isValid(idx))
249 BookmarksSection::Bookmark const & bm = theSession().bookmarks().bookmark(idx);
250 LASSERT(!bm.filename.empty(), /**/);
251 string const file = bm.filename.absFilename();
252 // if the file is not opened, open it.
253 if (!theBufferList().exists(bm.filename)) {
255 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
259 // open may fail, so we need to test it again
260 if (!theBufferList().exists(bm.filename))
263 // bm can be changed when saving
264 BookmarksSection::Bookmark tmp = bm;
266 // Special case idx == 0 used for back-from-back jump navigation
268 dispatch(FuncRequest(LFUN_BOOKMARK_SAVE, "0"));
270 // if the current buffer is not that one, switch to it.
271 if (!lyx_view_->documentBufferView()
272 || lyx_view_->documentBufferView()->buffer().fileName() != tmp.filename) {
275 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
278 // moveToPosition try paragraph id first and then paragraph (pit, pos).
279 if (!lyx_view_->documentBufferView()->moveToPosition(
280 tmp.bottom_pit, tmp.bottom_pos, tmp.top_id, tmp.top_pos))
287 // Cursor jump succeeded!
288 Cursor const & cur = lyx_view_->documentBufferView()->cursor();
289 pit_type new_pit = cur.pit();
290 pos_type new_pos = cur.pos();
291 int new_id = cur.paragraph().id();
293 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
294 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
295 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos
296 || bm.top_id != new_id) {
297 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(
298 new_pit, new_pos, new_id);
303 void LyXFunc::processKeySym(KeySymbol const & keysym, KeyModifier state)
305 LYXERR(Debug::KEY, "KeySym is " << keysym.getSymbolName());
307 // Do nothing if we have nothing (JMarc)
308 if (!keysym.isOK()) {
309 LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
310 lyx_view_->restartCursor();
314 if (keysym.isModifier()) {
315 LYXERR(Debug::KEY, "isModifier true");
317 lyx_view_->restartCursor();
321 //Encoding const * encoding = lyx_view_->documentBufferView()->cursor().getEncoding();
322 //encoded_last_key = keysym.getISOEncoded(encoding ? encoding->name() : "");
323 // FIXME: encoded_last_key shadows the member variable of the same
324 // name. Is that intended?
325 char_type encoded_last_key = keysym.getUCSEncoded();
327 // Do a one-deep top-level lookup for
328 // cancel and meta-fake keys. RVDK_PATCH_5
329 cancel_meta_seq.reset();
331 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
332 LYXERR(Debug::KEY, "action first set to [" << func.action << ']');
334 // When not cancel or meta-fake, do the normal lookup.
335 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
336 // Mostly, meta_fake_bit = NoModifier. RVDK_PATCH_5.
337 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
338 // remove Caps Lock and Mod2 as a modifiers
339 func = keyseq.addkey(keysym, (state | meta_fake_bit));
340 LYXERR(Debug::KEY, "action now set to [" << func.action << ']');
343 // Dont remove this unless you know what you are doing.
344 meta_fake_bit = NoModifier;
346 // Can this happen now ?
347 if (func.action == LFUN_NOACTION)
348 func = FuncRequest(LFUN_COMMAND_PREFIX);
350 LYXERR(Debug::KEY, " Key [action=" << func.action << "]["
351 << keyseq.print(KeySequence::Portable) << ']');
353 // already here we know if it any point in going further
354 // why not return already here if action == -1 and
355 // num_bytes == 0? (Lgb)
357 if (keyseq.length() > 1)
358 lyx_view_->message(keyseq.print(KeySequence::ForGui));
361 // Maybe user can only reach the key via holding down shift.
362 // Let's see. But only if shift is the only modifier
363 if (func.action == LFUN_UNKNOWN_ACTION && state == ShiftModifier) {
364 LYXERR(Debug::KEY, "Trying without shift");
365 func = keyseq.addkey(keysym, NoModifier);
366 LYXERR(Debug::KEY, "Action now " << func.action);
369 if (func.action == LFUN_UNKNOWN_ACTION) {
370 // Hmm, we didn't match any of the keysequences. See
371 // if it's normal insertable text not already covered
373 if (keysym.isText() && keyseq.length() == 1) {
374 LYXERR(Debug::KEY, "isText() is true, inserting.");
375 func = FuncRequest(LFUN_SELF_INSERT,
376 FuncRequest::KEYBOARD);
378 LYXERR(Debug::KEY, "Unknown, !isText() - giving up");
379 lyx_view_->message(_("Unknown function."));
380 lyx_view_->restartCursor();
385 if (func.action == LFUN_SELF_INSERT) {
386 if (encoded_last_key != 0) {
387 docstring const arg(1, encoded_last_key);
388 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
389 FuncRequest::KEYBOARD));
390 LYXERR(Debug::KEY, "SelfInsert arg[`" << to_utf8(arg) << "']");
400 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
402 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
405 /* In LyX/Mac, when a dialog is open, the menus of the
406 application can still be accessed without giving focus to
407 the main window. In this case, we want to disable the menu
408 entries that are buffer or view-related.
410 If this code is moved somewhere else (like in
411 GuiView::getStatus), then several functions will not be
414 frontend::LyXView * lv = 0;
417 && (cmd.origin != FuncRequest::MENU || lyx_view_->hasFocus())) {
419 if (lyx_view_->documentBufferView())
420 buf = &lyx_view_->documentBufferView()->buffer();
423 if (cmd.action == LFUN_NOACTION) {
424 flag.message(from_utf8(N_("Nothing to do")));
425 flag.setEnabled(false);
429 switch (cmd.action) {
430 case LFUN_UNKNOWN_ACTION:
432 flag.setEnabled(false);
439 if (flag.unknown()) {
440 flag.message(from_utf8(N_("Unknown action")));
444 if (!flag.enabled()) {
445 if (flag.message().empty())
446 flag.message(from_utf8(N_("Command disabled")));
450 // Check whether we need a buffer
451 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
453 flag.message(from_utf8(N_("Command not allowed with"
454 "out any document open")));
455 flag.setEnabled(false);
459 // I would really like to avoid having this switch and rather try to
460 // encode this in the function itself.
461 // -- And I'd rather let an inset decide which LFUNs it is willing
462 // to handle (Andre')
464 switch (cmd.action) {
466 case LFUN_BUFFER_TOGGLE_READ_ONLY:
467 flag.setOnOff(buf->isReadonly());
470 case LFUN_BUFFER_SWITCH:
471 // toggle on the current buffer, but do not toggle off
472 // the other ones (is that a good idea?)
473 if (buf && to_utf8(cmd.argument()) == buf->absFileName())
477 case LFUN_BUFFER_CHKTEX:
478 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
481 case LFUN_BUILD_PROGRAM:
482 enable = buf->isExportable("program");
485 case LFUN_VC_REGISTER:
486 enable = !buf->lyxvc().inUse();
488 case LFUN_VC_CHECK_IN:
489 enable = buf->lyxvc().checkInEnabled();
491 case LFUN_VC_CHECK_OUT:
492 enable = buf->lyxvc().checkOutEnabled();
494 case LFUN_VC_LOCKING_TOGGLE:
495 enable = !buf->isReadonly() && buf->lyxvc().lockingToggleEnabled();
496 flag.setOnOff(enable && !buf->lyxvc().locker().empty());
499 enable = buf->lyxvc().inUse();
501 case LFUN_VC_UNDO_LAST:
502 enable = buf->lyxvc().undoLastEnabled();
504 case LFUN_BUFFER_RELOAD:
505 enable = !buf->isUnnamed() && buf->fileName().exists()
506 && (!buf->isClean() || buf->isExternallyModified(Buffer::timestamp_method));
509 case LFUN_CITATION_INSERT: {
510 FuncRequest fr(LFUN_INSET_INSERT, "citation");
511 enable = getStatus(fr).enabled();
515 // This could be used for the no-GUI version. The GUI version is handled in
516 // LyXView::getStatus(). See above.
518 case LFUN_BUFFER_WRITE:
519 case LFUN_BUFFER_WRITE_AS: {
520 Buffer * b = theBufferList().getBuffer(FileName(cmd.getArg(0)));
521 enable = b && (b->isUnnamed() || !b->isClean());
526 case LFUN_BUFFER_WRITE_ALL: {
527 // We enable the command only if there are some modified buffers
528 Buffer * first = theBufferList().first();
533 // We cannot use a for loop as the buffer list is a cycle.
539 b = theBufferList().next(b);
540 } while (b != first);
544 case LFUN_BOOKMARK_GOTO: {
545 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
546 enable = theSession().bookmarks().isValid(num);
550 case LFUN_BOOKMARK_CLEAR:
551 enable = theSession().bookmarks().hasValid();
554 // this one is difficult to get right. As a half-baked
555 // solution, we consider only the first action of the sequence
556 case LFUN_COMMAND_SEQUENCE: {
557 // argument contains ';'-terminated commands
558 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
559 FuncRequest func(lyxaction.lookupFunc(firstcmd));
560 func.origin = cmd.origin;
561 flag = getStatus(func);
565 // we want to check if at least one of these is enabled
566 case LFUN_COMMAND_ALTERNATIVES: {
567 // argument contains ';'-terminated commands
568 string arg = to_utf8(cmd.argument());
569 while (!arg.empty()) {
571 arg = split(arg, first, ';');
572 FuncRequest func(lyxaction.lookupFunc(first));
573 func.origin = cmd.origin;
574 flag = getStatus(func);
575 // if this one is enabled, the whole thing is
584 string name = to_utf8(cmd.argument());
585 if (theTopLevelCmdDef().lock(name, func)) {
586 func.origin = cmd.origin;
587 flag = getStatus(func);
588 theTopLevelCmdDef().release(name);
590 // catch recursion or unknown command
591 // definition. all operations until the
592 // recursion or unknown command definition
593 // occurs are performed, so set the state to
600 case LFUN_VC_COMMAND: {
601 if (cmd.argument().empty())
604 if (!buf && contains(cmd.getArg(0), 'D'))
609 case LFUN_MASTER_BUFFER_UPDATE:
610 case LFUN_MASTER_BUFFER_VIEW:
611 if (!buf->parent()) {
615 case LFUN_BUFFER_UPDATE:
616 case LFUN_BUFFER_VIEW: {
617 string format = to_utf8(cmd.argument());
618 if (cmd.argument().empty())
619 format = buf->getDefaultOutputFormat();
620 typedef vector<Format const *> Formats;
622 formats = buf->exportableFormats(true);
623 Formats::const_iterator fit = formats.begin();
624 Formats::const_iterator end = formats.end();
626 for (; fit != end ; ++fit) {
627 if ((*fit)->name() == format)
633 case LFUN_WORD_FIND_FORWARD:
634 case LFUN_WORD_FIND_BACKWARD:
635 case LFUN_WORD_FINDADV:
636 case LFUN_COMMAND_PREFIX:
637 case LFUN_COMMAND_EXECUTE:
639 case LFUN_META_PREFIX:
640 case LFUN_BUFFER_CLOSE:
641 case LFUN_BUFFER_IMPORT:
642 case LFUN_BUFFER_AUTO_SAVE:
643 case LFUN_RECONFIGURE:
645 case LFUN_DROP_LAYOUTS_CHOICE:
647 case LFUN_SERVER_GET_FILENAME:
648 case LFUN_SERVER_NOTIFY:
649 case LFUN_SERVER_GOTO_FILE_ROW:
650 case LFUN_DIALOG_HIDE:
651 case LFUN_DIALOG_DISCONNECT_INSET:
652 case LFUN_BUFFER_CHILD_OPEN:
653 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
654 case LFUN_KEYMAP_OFF:
655 case LFUN_KEYMAP_PRIMARY:
656 case LFUN_KEYMAP_SECONDARY:
657 case LFUN_KEYMAP_TOGGLE:
659 case LFUN_BUFFER_EXPORT_CUSTOM:
660 case LFUN_PREFERENCES_SAVE:
662 case LFUN_INSET_EDIT:
663 case LFUN_BUFFER_LANGUAGE:
664 case LFUN_TEXTCLASS_APPLY:
665 case LFUN_TEXTCLASS_LOAD:
666 case LFUN_BUFFER_SAVE_AS_DEFAULT:
667 case LFUN_BUFFER_PARAMS_APPLY:
668 case LFUN_LAYOUT_MODULES_CLEAR:
669 case LFUN_LAYOUT_MODULE_ADD:
670 case LFUN_LAYOUT_RELOAD:
671 case LFUN_LYXRC_APPLY:
672 case LFUN_BUFFER_NEXT:
673 case LFUN_BUFFER_PREVIOUS:
674 // these are handled in our dispatch()
682 if (theApp()->getStatus(cmd, flag))
685 // Does the view know something?
690 if (lv->getStatus(cmd, flag))
693 BufferView * bv = lv->currentBufferView();
694 // If we do not have a BufferView, then other functions are disabled
699 // Is this a function that acts on inset at point?
700 Inset * inset = bv->cursor().nextInset();
701 if (lyxaction.funcHasFlag(cmd.action, LyXAction::AtPoint)
702 && inset && inset->getStatus(bv->cursor(), cmd, flag))
705 bool decided = getLocalStatus(bv->cursor(), cmd, flag);
707 // try the BufferView
708 decided = bv->getStatus(cmd, flag);
711 bv->buffer().getStatus(cmd, flag);
715 flag.setEnabled(false);
717 // Can we use a readonly buffer?
718 if (buf && buf->isReadonly()
719 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
720 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
721 flag.message(from_utf8(N_("Document is read-only")));
722 flag.setEnabled(false);
725 // Are we in a DELETED change-tracking region?
726 if (lyx_view_ && lyx_view_->documentBufferView()
727 && (lookupChangeType(lyx_view_->documentBufferView()->cursor(), true)
729 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
730 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
731 flag.message(from_utf8(N_("This portion of the document is deleted.")));
732 flag.setEnabled(false);
735 // the default error message if we disable the command
736 if (!flag.enabled() && flag.message().empty())
737 flag.message(from_utf8(N_("Command disabled")));
743 bool LyXFunc::ensureBufferClean(BufferView * bv)
745 Buffer & buf = bv->buffer();
746 if (buf.isClean() && !buf.isUnnamed())
749 docstring const file = buf.fileName().displayName(30);
752 if (!buf.isUnnamed()) {
753 text = bformat(_("The document %1$s has unsaved "
754 "changes.\n\nDo you want to save "
755 "the document?"), file);
756 title = _("Save changed document?");
759 text = bformat(_("The document %1$s has not been "
760 "saved yet.\n\nDo you want to save "
761 "the document?"), file);
762 title = _("Save new document?");
764 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
767 lyx_view_->dispatch(FuncRequest(LFUN_BUFFER_WRITE));
769 return buf.isClean() && !buf.isUnnamed();
775 bool loadLayoutFile(string const & name, string const & buf_path)
777 if (!LayoutFileList::get().haveClass(name)) {
778 lyxerr << "Document class \"" << name
779 << "\" does not exist."
784 LayoutFile & tc = LayoutFileList::get()[name];
785 if (!tc.load(buf_path)) {
786 docstring s = bformat(_("The document class %1$s "
787 "could not be loaded."), from_utf8(name));
788 Alert::error(_("Could not load class"), s);
795 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
800 void LyXFunc::dispatch(FuncRequest const & cmd)
802 string const argument = to_utf8(cmd.argument());
803 FuncCode const action = cmd.action;
805 LYXERR(Debug::ACTION, "\nLyXFunc::dispatch: cmd: " << cmd);
806 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
808 // we have not done anything wrong yet.
810 dispatch_buffer.erase();
812 // redraw the screen at the end (first of the two drawing steps).
813 //This is done unless explicitely requested otherwise
814 Update::flags updateFlags = Update::FitCursor;
816 FuncStatus const flag = getStatus(cmd);
817 if (!flag.enabled()) {
818 // We cannot use this function here
819 LYXERR(Debug::ACTION, "LyXFunc::dispatch: "
820 << lyxaction.getActionName(action)
821 << " [" << action << "] is disabled at this location");
822 setErrorMessage(flag.message());
824 lyx_view_->restartCursor();
827 if (lyx_view_ && lyx_view_->currentBufferView())
828 buffer = &lyx_view_->currentBufferView()->buffer();
831 case LFUN_WORD_FIND_FORWARD:
832 case LFUN_WORD_FIND_BACKWARD: {
833 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
834 static docstring last_search;
835 docstring searched_string;
837 if (!cmd.argument().empty()) {
838 last_search = cmd.argument();
839 searched_string = cmd.argument();
841 searched_string = last_search;
844 if (searched_string.empty())
847 bool const fw = action == LFUN_WORD_FIND_FORWARD;
848 docstring const data =
849 find2string(searched_string, true, false, fw);
850 find(lyx_view_->documentBufferView(),
851 FuncRequest(LFUN_WORD_FIND, data));
855 case LFUN_COMMAND_PREFIX:
856 LASSERT(lyx_view_, /**/);
857 lyx_view_->message(keyseq.printOptions(true));
861 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
863 meta_fake_bit = NoModifier;
865 // cancel any selection
866 dispatch(FuncRequest(LFUN_MARK_OFF));
867 setMessage(from_ascii(N_("Cancel")));
870 case LFUN_META_PREFIX:
871 meta_fake_bit = AltModifier;
872 setMessage(keyseq.print(KeySequence::ForGui));
875 case LFUN_BUFFER_TOGGLE_READ_ONLY: {
876 LASSERT(lyx_view_ && lyx_view_->currentBufferView() && buffer, /**/);
877 if (buffer->lyxvc().inUse())
878 buffer->lyxvc().toggleReadOnly();
880 buffer->setReadonly(!buffer->isReadonly());
884 // --- Menus -----------------------------------------------
885 case LFUN_BUFFER_CLOSE:
886 lyx_view_->closeBuffer();
888 updateFlags = Update::None;
891 case LFUN_BUFFER_CLOSE_ALL:
892 lyx_view_->closeBufferAll();
894 updateFlags = Update::None;
897 case LFUN_BUFFER_RELOAD: {
898 LASSERT(lyx_view_ && buffer, /**/);
899 docstring const file = makeDisplayPath(buffer->absFileName(), 20);
900 docstring text = bformat(_("Any changes will be lost. Are you sure "
901 "you want to revert to the saved version of the document %1$s?"), file);
902 int const ret = Alert::prompt(_("Revert to saved document?"),
903 text, 1, 1, _("&Revert"), _("&Cancel"));
910 case LFUN_BUFFER_UPDATE: {
911 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
912 Buffer & doc_buffer = lyx_view_->documentBufferView()->buffer();
913 string format = argument;
914 if (argument.empty())
915 format = doc_buffer.getDefaultOutputFormat();
916 doc_buffer.doExport(format, true);
920 case LFUN_BUFFER_VIEW: {
921 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
922 Buffer & doc_buffer = lyx_view_->documentBufferView()->buffer();
923 string format = argument;
924 if (argument.empty())
925 format = doc_buffer.getDefaultOutputFormat();
926 doc_buffer.preview(format);
930 case LFUN_MASTER_BUFFER_UPDATE: {
931 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
932 Buffer & doc_buffer = lyx_view_->documentBufferView()->buffer();
933 string format = argument;
934 if (argument.empty())
935 format = doc_buffer.masterBuffer()->getDefaultOutputFormat();
936 doc_buffer.masterBuffer()->doExport(format, true);
940 case LFUN_MASTER_BUFFER_VIEW: {
941 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
942 Buffer & doc_buffer = lyx_view_->documentBufferView()->buffer();
943 string format = argument;
944 if (argument.empty())
945 format = doc_buffer.masterBuffer()->getDefaultOutputFormat();
946 doc_buffer.masterBuffer()->preview(format);
950 case LFUN_BUILD_PROGRAM:
951 LASSERT(lyx_view_ && buffer, /**/);
952 buffer->doExport("program", true);
955 case LFUN_BUFFER_CHKTEX:
956 LASSERT(lyx_view_ && buffer, /**/);
960 case LFUN_BUFFER_EXPORT:
961 LASSERT(lyx_view_ && buffer, /**/);
962 if (argument == "custom")
963 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"));
965 buffer->doExport(argument, false);
968 case LFUN_BUFFER_EXPORT_CUSTOM: {
969 LASSERT(lyx_view_ && buffer, /**/);
971 string command = split(argument, format_name, ' ');
972 Format const * format = formats.getFormat(format_name);
974 lyxerr << "Format \"" << format_name
975 << "\" not recognized!"
980 // The name of the file created by the conversion process
983 // Output to filename
984 if (format->name() == "lyx") {
985 string const latexname = buffer->latexName(false);
986 filename = changeExtension(latexname,
987 format->extension());
988 filename = addName(buffer->temppath(), filename);
990 if (!buffer->writeFile(FileName(filename)))
994 buffer->doExport(format_name, true, filename);
997 // Substitute $$FName for filename
998 if (!contains(command, "$$FName"))
999 command = "( " + command + " ) < $$FName";
1000 command = subst(command, "$$FName", filename);
1002 // Execute the command in the background
1004 call.startscript(Systemcall::DontWait, command);
1008 // FIXME: There is need for a command-line import.
1010 case LFUN_BUFFER_IMPORT:
1015 case LFUN_BUFFER_AUTO_SAVE:
1019 case LFUN_RECONFIGURE:
1020 // argument is any additional parameter to the configure.py command
1021 reconfigure(lyx_view_, argument);
1024 case LFUN_HELP_OPEN: {
1026 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1027 string const arg = argument;
1029 setErrorMessage(from_utf8(N_("Missing argument")));
1032 FileName fname = i18nLibFileSearch("doc", arg, "lyx");
1034 fname = i18nLibFileSearch("examples", arg, "lyx");
1036 if (fname.empty()) {
1037 lyxerr << "LyX: unable to find documentation file `"
1038 << arg << "'. Bad installation?" << endl;
1041 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1042 makeDisplayPath(fname.absFilename())));
1043 Buffer * buf = lyx_view_->loadDocument(fname, false);
1045 buf->updateLabels();
1046 lyx_view_->setBuffer(buf);
1047 buf->errors("Parse");
1049 updateFlags = Update::None;
1053 // --- version control -------------------------------
1054 case LFUN_VC_REGISTER:
1055 LASSERT(lyx_view_ && buffer, /**/);
1056 if (!ensureBufferClean(lyx_view_->documentBufferView()))
1058 if (!buffer->lyxvc().inUse()) {
1059 if (buffer->lyxvc().registrer())
1062 updateFlags = Update::Force;
1065 case LFUN_VC_CHECK_IN:
1066 LASSERT(lyx_view_ && buffer, /**/);
1067 if (!ensureBufferClean(lyx_view_->documentBufferView()))
1069 if (buffer->lyxvc().inUse()
1070 && !buffer->isReadonly()) {
1071 setMessage(from_utf8(buffer->lyxvc().checkIn()));
1076 case LFUN_VC_CHECK_OUT:
1077 LASSERT(lyx_view_ && buffer, /**/);
1078 if (!ensureBufferClean(lyx_view_->documentBufferView()))
1080 if (buffer->lyxvc().inUse()) {
1081 setMessage(from_utf8(buffer->lyxvc().checkOut()));
1086 case LFUN_VC_LOCKING_TOGGLE:
1087 LASSERT(lyx_view_ && buffer, /**/);
1088 if (!ensureBufferClean(lyx_view_->documentBufferView())
1089 || buffer->isReadonly())
1091 if (buffer->lyxvc().inUse()) {
1092 string res = buffer->lyxvc().lockingToggle();
1094 frontend::Alert::error(_("Revision control error."),
1095 _("Error when setting the locking property."));
1097 setMessage(from_utf8(res));
1103 case LFUN_VC_REVERT:
1104 LASSERT(lyx_view_ && buffer, /**/);
1105 buffer->lyxvc().revert();
1109 case LFUN_VC_UNDO_LAST:
1110 LASSERT(lyx_view_ && buffer, /**/);
1111 buffer->lyxvc().undoLast();
1115 // --- lyxserver commands ----------------------------
1116 case LFUN_SERVER_GET_FILENAME:
1117 LASSERT(lyx_view_ && buffer, /**/);
1118 setMessage(from_utf8(buffer->absFileName()));
1119 LYXERR(Debug::INFO, "FNAME["
1120 << buffer->absFileName() << ']');
1123 case LFUN_SERVER_NOTIFY:
1124 dispatch_buffer = keyseq.print(KeySequence::Portable);
1125 theServer().notifyClient(to_utf8(dispatch_buffer));
1128 case LFUN_SERVER_GOTO_FILE_ROW: {
1129 LASSERT(lyx_view_, /**/);
1132 istringstream is(argument);
1133 is >> file_name >> row;
1134 file_name = os::internal_path(file_name);
1136 bool loaded = false;
1137 string const abstmp = package().temp_dir().absFilename();
1138 string const realtmp = package().temp_dir().realPath();
1139 // We have to use os::path_prefix_is() here, instead of
1140 // simply prefixIs(), because the file name comes from
1141 // an external application and may need case adjustment.
1142 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
1143 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
1144 // Needed by inverse dvi search. If it is a file
1145 // in tmpdir, call the apropriated function.
1146 // If tmpdir is a symlink, we may have the real
1147 // path passed back, so we correct for that.
1148 if (!prefixIs(file_name, abstmp))
1149 file_name = subst(file_name, realtmp, abstmp);
1150 buf = theBufferList().getBufferFromTmp(file_name);
1152 // Must replace extension of the file to be .lyx
1153 // and get full path
1154 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1155 // Either change buffer or load the file
1156 if (theBufferList().exists(s))
1157 buf = theBufferList().getBuffer(s);
1158 else if (s.exists()) {
1159 buf = lyx_view_->loadDocument(s);
1162 lyx_view_->message(bformat(
1163 _("File does not exist: %1$s"),
1164 makeDisplayPath(file_name)));
1168 updateFlags = Update::None;
1172 buf->updateLabels();
1173 lyx_view_->setBuffer(buf);
1174 lyx_view_->documentBufferView()->setCursorFromRow(row);
1176 buf->errors("Parse");
1177 updateFlags = Update::FitCursor;
1182 case LFUN_DIALOG_SHOW_NEW_INSET: {
1183 LASSERT(lyx_view_, /**/);
1184 string const name = cmd.getArg(0);
1185 InsetCode code = insetCode(name);
1186 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1187 bool insetCodeOK = true;
1194 case NOMENCL_PRINT_CODE:
1197 case HYPERLINK_CODE: {
1198 InsetCommandParams p(code);
1199 data = InsetCommand::params2string(name, p);
1202 case INCLUDE_CODE: {
1203 // data is the include type: one of "include",
1204 // "input", "verbatiminput" or "verbatiminput*"
1206 // default type is requested
1208 InsetCommandParams p(INCLUDE_CODE, data);
1209 data = InsetCommand::params2string("include", p);
1213 // \c data == "Boxed" || "Frameless" etc
1214 InsetBoxParams p(data);
1215 data = InsetBox::params2string(p);
1219 InsetBranchParams p;
1220 data = InsetBranch::params2string(p);
1224 InsetCommandParams p(CITE_CODE);
1225 data = InsetCommand::params2string(name, p);
1229 data = InsetERT::params2string(InsetCollapsable::Open);
1232 case EXTERNAL_CODE: {
1233 InsetExternalParams p;
1234 data = InsetExternal::params2string(p, *buffer);
1239 data = InsetFloat::params2string(p);
1242 case LISTINGS_CODE: {
1243 InsetListingsParams p;
1244 data = InsetListings::params2string(p);
1247 case GRAPHICS_CODE: {
1248 InsetGraphicsParams p;
1249 data = InsetGraphics::params2string(p, *buffer);
1254 data = InsetNote::params2string(p);
1257 case PHANTOM_CODE: {
1258 InsetPhantomParams p;
1259 data = InsetPhantom::params2string(p);
1264 data = InsetSpace::params2string(p);
1269 data = InsetVSpace::params2string(space);
1274 data = InsetWrap::params2string(p);
1278 lyxerr << "Inset type '" << name <<
1279 "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" << endl;
1280 insetCodeOK = false;
1282 } // end switch(code)
1284 dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data));
1288 case LFUN_CITATION_INSERT: {
1289 LASSERT(lyx_view_, /**/);
1290 if (!argument.empty()) {
1291 // we can have one optional argument, delimited by '|'
1292 // citation-insert <key>|<text_before>
1293 // this should be enhanced to also support text_after
1294 // and citation style
1295 string arg = argument;
1297 if (contains(argument, "|")) {
1298 arg = token(argument, '|', 0);
1299 opt1 = token(argument, '|', 1);
1301 InsetCommandParams icp(CITE_CODE);
1302 icp["key"] = from_utf8(arg);
1304 icp["before"] = from_utf8(opt1);
1305 string icstr = InsetCommand::params2string("citation", icp);
1306 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1309 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1313 case LFUN_BUFFER_CHILD_OPEN: {
1314 LASSERT(lyx_view_ && buffer, /**/);
1315 FileName filename = makeAbsPath(argument, buffer->filePath());
1316 lyx_view_->documentBufferView()->saveBookmark(false);
1318 bool parsed = false;
1319 if (theBufferList().exists(filename)) {
1320 child = theBufferList().getBuffer(filename);
1322 setMessage(bformat(_("Opening child document %1$s..."),
1323 makeDisplayPath(filename.absFilename())));
1324 child = lyx_view_->loadDocument(filename, false);
1328 // Set the parent name of the child document.
1329 // This makes insertion of citations and references in the child work,
1330 // when the target is in the parent or another child document.
1331 child->setParent(buffer);
1332 child->masterBuffer()->updateLabels();
1333 lyx_view_->setBuffer(child);
1335 child->errors("Parse");
1338 // If a screen update is required (in case where auto_open is false),
1339 // setBuffer() would have taken care of it already. Otherwise we shall
1340 // reset the update flag because it can cause a circular problem.
1342 updateFlags = Update::None;
1346 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
1347 LASSERT(lyx_view_, /**/);
1348 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1351 case LFUN_KEYMAP_OFF:
1352 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1353 lyx_view_->currentBufferView()->getIntl().keyMapOn(false);
1356 case LFUN_KEYMAP_PRIMARY:
1357 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1358 lyx_view_->currentBufferView()->getIntl().keyMapPrim();
1361 case LFUN_KEYMAP_SECONDARY:
1362 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1363 lyx_view_->currentBufferView()->getIntl().keyMapSec();
1366 case LFUN_KEYMAP_TOGGLE:
1367 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1368 lyx_view_->currentBufferView()->getIntl().toggleKeyMap();
1374 string rest = split(argument, countstr, ' ');
1375 istringstream is(countstr);
1378 //lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1379 for (int i = 0; i < count; ++i)
1380 dispatch(lyxaction.lookupFunc(rest));
1384 case LFUN_COMMAND_SEQUENCE: {
1385 // argument contains ';'-terminated commands
1386 string arg = argument;
1387 if (theBufferList().isLoaded(buffer))
1388 buffer->undo().beginUndoGroup();
1389 while (!arg.empty()) {
1391 arg = split(arg, first, ';');
1392 FuncRequest func(lyxaction.lookupFunc(first));
1393 func.origin = cmd.origin;
1396 if (theBufferList().isLoaded(buffer))
1397 buffer->undo().endUndoGroup();
1401 case LFUN_COMMAND_ALTERNATIVES: {
1402 // argument contains ';'-terminated commands
1403 string arg = argument;
1404 while (!arg.empty()) {
1406 arg = split(arg, first, ';');
1407 FuncRequest func(lyxaction.lookupFunc(first));
1408 func.origin = cmd.origin;
1409 FuncStatus stat = getStatus(func);
1410 if (stat.enabled()) {
1420 if (theTopLevelCmdDef().lock(argument, func)) {
1421 func.origin = cmd.origin;
1423 theTopLevelCmdDef().release(argument);
1425 if (func.action == LFUN_UNKNOWN_ACTION) {
1426 // unknown command definition
1427 lyxerr << "Warning: unknown command definition `"
1431 // recursion detected
1432 lyxerr << "Warning: Recursion in the command definition `"
1433 << argument << "' detected"
1440 case LFUN_PREFERENCES_SAVE: {
1441 lyxrc.write(makeAbsPath("preferences",
1442 package().user_support().absFilename()),
1448 LASSERT(lyx_view_, /**/);
1449 lyx_view_->message(from_utf8(argument));
1452 case LFUN_BUFFER_LANGUAGE: {
1453 LASSERT(lyx_view_, /**/);
1454 Language const * oldL = buffer->params().language;
1455 Language const * newL = languages.getLanguage(argument);
1456 if (!newL || oldL == newL)
1459 if (oldL->rightToLeft() == newL->rightToLeft()
1460 && !buffer->isMultiLingual())
1461 buffer->changeLanguage(oldL, newL);
1465 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1466 string const fname =
1467 addName(addPath(package().user_support().absFilename(), "templates/"),
1469 Buffer defaults(fname);
1471 istringstream ss(argument);
1474 int const unknown_tokens = defaults.readHeader(lex);
1476 if (unknown_tokens != 0) {
1477 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1478 << unknown_tokens << " unknown token"
1479 << (unknown_tokens == 1 ? "" : "s")
1483 if (defaults.writeFile(FileName(defaults.absFileName())))
1484 setMessage(bformat(_("Document defaults saved in %1$s"),
1485 makeDisplayPath(fname)));
1487 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1491 case LFUN_BUFFER_PARAMS_APPLY: {
1492 LASSERT(lyx_view_, /**/);
1494 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1495 Cursor & cur = lyx_view_->documentBufferView()->cursor();
1496 cur.recordUndoFullDocument();
1498 istringstream ss(argument);
1501 int const unknown_tokens = buffer->readHeader(lex);
1503 if (unknown_tokens != 0) {
1504 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1505 << unknown_tokens << " unknown token"
1506 << (unknown_tokens == 1 ? "" : "s")
1510 updateLayout(oldClass, buffer);
1512 updateFlags = Update::Force | Update::FitCursor;
1513 // We are most certainly here because of a change in the document
1514 // It is then better to make sure that all dialogs are in sync with
1515 // current document settings. LyXView::restartCursor() achieve this.
1516 lyx_view_->restartCursor();
1520 case LFUN_LAYOUT_MODULES_CLEAR: {
1521 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
1522 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1523 lyx_view_->documentBufferView()->cursor().recordUndoFullDocument();
1524 buffer->params().clearLayoutModules();
1525 buffer->params().makeDocumentClass();
1526 updateLayout(oldClass, buffer);
1527 updateFlags = Update::Force | Update::FitCursor;
1531 case LFUN_LAYOUT_MODULE_ADD: {
1532 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
1533 BufferParams const & params = buffer->params();
1534 if (!params.moduleCanBeAdded(argument)) {
1535 LYXERR0("Module `" << argument <<
1536 "' cannot be added due to failed requirements or "
1537 "conflicts with installed modules.");
1540 DocumentClass const * const oldClass = params.documentClassPtr();
1541 lyx_view_->documentBufferView()->cursor().recordUndoFullDocument();
1542 buffer->params().addLayoutModule(argument);
1543 buffer->params().makeDocumentClass();
1544 updateLayout(oldClass, buffer);
1545 updateFlags = Update::Force | Update::FitCursor;
1549 case LFUN_TEXTCLASS_APPLY: {
1550 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
1552 if (!loadLayoutFile(argument, buffer->temppath()) &&
1553 !loadLayoutFile(argument, buffer->filePath()))
1556 LayoutFile const * old_layout = buffer->params().baseClass();
1557 LayoutFile const * new_layout = &(LayoutFileList::get()[argument]);
1559 if (old_layout == new_layout)
1563 //Save the old, possibly modular, layout for use in conversion.
1564 DocumentClass const * const oldDocClass =
1565 buffer->params().documentClassPtr();
1566 lyx_view_->documentBufferView()->cursor().recordUndoFullDocument();
1567 buffer->params().setBaseClass(argument);
1568 buffer->params().makeDocumentClass();
1569 updateLayout(oldDocClass, buffer);
1570 updateFlags = Update::Force | Update::FitCursor;
1574 case LFUN_LAYOUT_RELOAD: {
1575 LASSERT(lyx_view_, /**/);
1576 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1577 LayoutFileIndex bc = buffer->params().baseClassID();
1578 LayoutFileList::get().reset(bc);
1579 buffer->params().setBaseClass(bc);
1580 buffer->params().makeDocumentClass();
1581 updateLayout(oldClass, buffer);
1582 updateFlags = Update::Force | Update::FitCursor;
1586 case LFUN_TEXTCLASS_LOAD:
1587 loadLayoutFile(argument, buffer->temppath()) ||
1588 loadLayoutFile(argument, buffer->filePath());
1591 case LFUN_LYXRC_APPLY: {
1592 // reset active key sequences, since the bindings
1593 // are updated (bug 6064)
1595 LyXRC const lyxrc_orig = lyxrc;
1597 istringstream ss(argument);
1598 bool const success = lyxrc.read(ss) == 0;
1601 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1602 << "Unable to read lyxrc data"
1607 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1611 theApp()->resetGui();
1613 /// We force the redraw in any case because there might be
1614 /// some screen font changes.
1615 /// FIXME: only the current view will be updated. the Gui
1616 /// class is able to furnish the list of views.
1617 updateFlags = Update::Force;
1621 case LFUN_BOOKMARK_GOTO:
1622 // go to bookmark, open unopened file and switch to buffer if necessary
1623 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1624 updateFlags = Update::FitCursor;
1627 case LFUN_BOOKMARK_CLEAR:
1628 theSession().bookmarks().clear();
1631 case LFUN_VC_COMMAND: {
1632 string flag = cmd.getArg(0);
1633 if (buffer && contains(flag, 'R')
1634 && !ensureBufferClean(lyx_view_->documentBufferView()))
1637 if (contains(flag, 'M'))
1638 if (!Alert::askForText(message, _("LyX VC: Log Message")))
1641 string path = cmd.getArg(1);
1642 if (contains(path, "$$p") && buffer)
1643 path = subst(path, "$$p", buffer->filePath());
1644 LYXERR(Debug::LYXVC, "Directory: " << path);
1646 if (!pp.isReadableDirectory()) {
1647 lyxerr << _("Directory is not accessible.") << endl;
1650 support::PathChanger p(pp);
1652 string command = cmd.getArg(2);
1653 if (command.empty())
1656 command = subst(command, "$$i", buffer->absFileName());
1657 command = subst(command, "$$p", buffer->filePath());
1659 command = subst(command, "$$m", to_utf8(message));
1660 LYXERR(Debug::LYXVC, "Command: " << command);
1662 one.startscript(Systemcall::Wait, command);
1666 if (contains(flag, 'I'))
1667 buffer->markDirty();
1668 if (contains(flag, 'R'))
1675 LASSERT(theApp(), /**/);
1676 // Let the frontend dispatch its own actions.
1677 if (theApp()->dispatch(cmd))
1678 // Nothing more to do.
1681 // Everything below is only for active lyx_view_
1685 // Start an undo group. This may be needed for
1686 // some stuff like inset-apply on labels.
1687 if (theBufferList().isLoaded(buffer))
1688 buffer->undo().beginUndoGroup();
1690 // Let the current LyXView dispatch its own actions.
1691 if (lyx_view_->dispatch(cmd)) {
1692 if (lyx_view_->currentBufferView()) {
1693 updateFlags = lyx_view_->currentBufferView()->cursor().result().update();
1694 if (theBufferList().isLoaded(buffer))
1695 buffer->undo().endUndoGroup();
1700 LASSERT(lyx_view_->currentBufferView(), /**/);
1702 // Let the current BufferView dispatch its own actions.
1703 if (lyx_view_->currentBufferView()->dispatch(cmd)) {
1704 // The BufferView took care of its own updates if needed.
1705 updateFlags = Update::None;
1706 if (theBufferList().isLoaded(buffer))
1707 buffer->undo().endUndoGroup();
1711 // OK, so try the Buffer itself
1713 BufferView * bv = lyx_view_->currentBufferView();
1714 bv->buffer().dispatch(cmd, dr);
1715 if (dr.dispatched()) {
1716 updateFlags = dr.update();
1720 // Is this a function that acts on inset at point?
1721 Inset * inset = bv->cursor().nextInset();
1722 if (lyxaction.funcHasFlag(action, LyXAction::AtPoint)
1724 bv->cursor().result().dispatched(true);
1725 bv->cursor().result().update(Update::FitCursor | Update::Force);
1726 FuncRequest tmpcmd = cmd;
1727 inset->dispatch(bv->cursor(), tmpcmd);
1728 if (bv->cursor().result().dispatched()) {
1729 updateFlags = bv->cursor().result().update();
1734 // Let the current Cursor dispatch its own actions.
1735 Cursor old = bv->cursor();
1736 bv->cursor().getPos(cursorPosBeforeDispatchX_,
1737 cursorPosBeforeDispatchY_);
1738 bv->cursor().dispatch(cmd);
1740 // notify insets we just left
1741 if (bv->cursor() != old) {
1743 bool badcursor = notifyCursorLeavesOrEnters(old, bv->cursor());
1745 bv->cursor().fixIfBroken();
1748 if (theBufferList().isLoaded(buffer))
1749 buffer->undo().endUndoGroup();
1751 // update completion. We do it here and not in
1752 // processKeySym to avoid another redraw just for a
1753 // changed inline completion
1754 if (cmd.origin == FuncRequest::KEYBOARD) {
1755 if (cmd.action == LFUN_SELF_INSERT
1756 || (cmd.action == LFUN_ERT_INSERT && bv->cursor().inMathed()))
1757 lyx_view_->updateCompletion(bv->cursor(), true, true);
1758 else if (cmd.action == LFUN_CHAR_DELETE_BACKWARD)
1759 lyx_view_->updateCompletion(bv->cursor(), false, true);
1761 lyx_view_->updateCompletion(bv->cursor(), false, false);
1764 updateFlags = bv->cursor().result().update();
1767 // if we executed a mutating lfun, mark the buffer as dirty
1768 if (theBufferList().isLoaded(buffer) && flag.enabled()
1769 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1770 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1771 buffer->markDirty();
1773 if (lyx_view_ && lyx_view_->currentBufferView()) {
1774 // BufferView::update() updates the ViewMetricsInfo and
1775 // also initializes the position cache for all insets in
1776 // (at least partially) visible top-level paragraphs.
1777 // We will redraw the screen only if needed.
1778 lyx_view_->currentBufferView()->processUpdateFlags(updateFlags);
1780 // Do we have a selection?
1781 theSelection().haveSelection(
1782 lyx_view_->currentBufferView()->cursor().selection());
1785 lyx_view_->restartCursor();
1789 // Some messages may already be translated, so we cannot use _()
1790 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1795 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1797 const bool verbose = (cmd.origin == FuncRequest::MENU
1798 || cmd.origin == FuncRequest::TOOLBAR
1799 || cmd.origin == FuncRequest::COMMANDBUFFER);
1801 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1802 LYXERR(Debug::ACTION, "dispatch msg is " << to_utf8(msg));
1804 lyx_view_->message(msg);
1808 docstring dispatch_msg = msg;
1809 if (!dispatch_msg.empty())
1810 dispatch_msg += ' ';
1812 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1814 bool argsadded = false;
1816 if (!cmd.argument().empty()) {
1817 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1818 comname += ' ' + cmd.argument();
1823 docstring const shortcuts = theTopLevelKeymap().printBindings(cmd, KeySequence::ForGui);
1825 if (!shortcuts.empty())
1826 comname += ": " + shortcuts;
1827 else if (!argsadded && !cmd.argument().empty())
1828 comname += ' ' + cmd.argument();
1830 if (!comname.empty()) {
1831 comname = rtrim(comname);
1832 dispatch_msg += '(' + rtrim(comname) + ')';
1835 LYXERR(Debug::ACTION, "verbose dispatch msg " << to_utf8(dispatch_msg));
1836 if (!dispatch_msg.empty())
1837 lyx_view_->message(dispatch_msg);
1841 void LyXFunc::reloadBuffer()
1843 Buffer * buf = &lyx_view_->documentBufferView()->buffer();
1844 FileName filename = buf->fileName();
1845 // The user has already confirmed that the changes, if any, should
1846 // be discarded. So we just release the Buffer and don't call closeBuffer();
1847 theBufferList().release(buf);
1848 // if the lyx_view_ has been destroyed, create a new one
1850 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1851 buf = lyx_view_->loadDocument(filename);
1852 docstring const disp_fn = makeDisplayPath(filename.absFilename());
1855 buf->updateLabels();
1856 lyx_view_->setBuffer(buf);
1857 buf->errors("Parse");
1858 str = bformat(_("Document %1$s reloaded."), disp_fn);
1860 str = bformat(_("Could not reload document %1$s"), disp_fn);
1862 lyx_view_->message(str);
1865 // Each "lyx_view_" should have it's own message method. lyxview and
1866 // the minibuffer would use the minibuffer, but lyxserver would
1867 // send an ERROR signal to its client. Alejandro 970603
1868 // This function is bit problematic when it comes to NLS, to make the
1869 // lyx servers client be language indepenent we must not translate
1870 // strings sent to this func.
1871 void LyXFunc::setErrorMessage(docstring const & m) const
1873 dispatch_buffer = m;
1878 void LyXFunc::setMessage(docstring const & m) const
1880 dispatch_buffer = m;
1884 docstring LyXFunc::viewStatusMessage()
1886 // When meta-fake key is pressed, show the key sequence so far + "M-".
1888 return keyseq.print(KeySequence::ForGui) + "M-";
1890 // Else, when a non-complete key sequence is pressed,
1891 // show the available options.
1892 if (keyseq.length() > 0 && !keyseq.deleted())
1893 return keyseq.printOptions(true);
1895 LASSERT(lyx_view_, /**/);
1896 if (!lyx_view_->currentBufferView())
1897 return _("Welcome to LyX!");
1899 return lyx_view_->currentBufferView()->cursor().currentState();
1903 bool LyXFunc::wasMetaKey() const
1905 return (meta_fake_bit != NoModifier);
1909 void LyXFunc::updateLayout(DocumentClass const * const oldlayout, Buffer * buf)
1911 lyx_view_->message(_("Converting document to new document class..."));
1913 StableDocIterator backcur(lyx_view_->currentBufferView()->cursor());
1914 ErrorList & el = buf->errorList("Class Switch");
1915 cap::switchBetweenClasses(
1916 oldlayout, buf->params().documentClassPtr(),
1917 static_cast<InsetText &>(buf->inset()), el);
1919 lyx_view_->currentBufferView()->setCursor(backcur.asDocIterator(buf));
1921 buf->errors("Class Switch");
1922 buf->updateLabels();
1928 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1930 // Why the switch you might ask. It is a trick to ensure that all
1931 // the elements in the LyXRCTags enum is handled. As you can see
1932 // there are no breaks at all. So it is just a huge fall-through.
1933 // The nice thing is that we will get a warning from the compiler
1934 // if we forget an element.
1935 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1937 case LyXRC::RC_ACCEPT_COMPOUND:
1938 case LyXRC::RC_ALT_LANG:
1939 case LyXRC::RC_PLAINTEXT_LINELEN:
1940 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
1941 case LyXRC::RC_AUTOCORRECTION_MATH:
1942 case LyXRC::RC_AUTOREGIONDELETE:
1943 case LyXRC::RC_AUTORESET_OPTIONS:
1944 case LyXRC::RC_AUTOSAVE:
1945 case LyXRC::RC_AUTO_NUMBER:
1946 case LyXRC::RC_BACKUPDIR_PATH:
1947 case LyXRC::RC_BIBTEX_ALTERNATIVES:
1948 case LyXRC::RC_BIBTEX_COMMAND:
1949 case LyXRC::RC_BINDFILE:
1950 case LyXRC::RC_CHECKLASTFILES:
1951 case LyXRC::RC_COMPLETION_CURSOR_TEXT:
1952 case LyXRC::RC_COMPLETION_INLINE_DELAY:
1953 case LyXRC::RC_COMPLETION_INLINE_DOTS:
1954 case LyXRC::RC_COMPLETION_INLINE_MATH:
1955 case LyXRC::RC_COMPLETION_INLINE_TEXT:
1956 case LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE:
1957 case LyXRC::RC_COMPLETION_POPUP_DELAY:
1958 case LyXRC::RC_COMPLETION_POPUP_MATH:
1959 case LyXRC::RC_COMPLETION_POPUP_TEXT:
1960 case LyXRC::RC_USELASTFILEPOS:
1961 case LyXRC::RC_LOADSESSION:
1962 case LyXRC::RC_CHKTEX_COMMAND:
1963 case LyXRC::RC_CONVERTER:
1964 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
1965 case LyXRC::RC_COPIER:
1966 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1967 case LyXRC::RC_SCROLL_BELOW_DOCUMENT:
1968 case LyXRC::RC_DATE_INSERT_FORMAT:
1969 case LyXRC::RC_DEFAULT_LANGUAGE:
1970 case LyXRC::RC_GUI_LANGUAGE:
1971 case LyXRC::RC_DEFAULT_PAPERSIZE:
1972 case LyXRC::RC_DEFAULT_VIEW_FORMAT:
1973 case LyXRC::RC_DEFFILE:
1974 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1975 case LyXRC::RC_DISPLAY_GRAPHICS:
1976 case LyXRC::RC_DOCUMENTPATH:
1977 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1978 FileName path(lyxrc_new.document_path);
1979 if (path.exists() && path.isDirectory())
1980 package().document_dir() = FileName(lyxrc.document_path);
1982 case LyXRC::RC_EDITOR_ALTERNATIVES:
1983 case LyXRC::RC_ESC_CHARS:
1984 case LyXRC::RC_EXAMPLEPATH:
1985 case LyXRC::RC_FONT_ENCODING:
1986 case LyXRC::RC_FORMAT:
1987 case LyXRC::RC_GROUP_LAYOUTS:
1988 case LyXRC::RC_HUNSPELLDIR_PATH:
1989 case LyXRC::RC_INDEX_ALTERNATIVES:
1990 case LyXRC::RC_INDEX_COMMAND:
1991 case LyXRC::RC_JBIBTEX_COMMAND:
1992 case LyXRC::RC_JINDEX_COMMAND:
1993 case LyXRC::RC_NOMENCL_COMMAND:
1994 case LyXRC::RC_INPUT:
1995 case LyXRC::RC_KBMAP:
1996 case LyXRC::RC_KBMAP_PRIMARY:
1997 case LyXRC::RC_KBMAP_SECONDARY:
1998 case LyXRC::RC_LABEL_INIT_LENGTH:
1999 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2000 case LyXRC::RC_LANGUAGE_AUTO_END:
2001 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2002 case LyXRC::RC_LANGUAGE_COMMAND_END:
2003 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2004 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2005 case LyXRC::RC_LANGUAGE_PACKAGE:
2006 case LyXRC::RC_LANGUAGE_USE_BABEL:
2007 case LyXRC::RC_MAC_LIKE_WORD_MOVEMENT:
2008 case LyXRC::RC_MACRO_EDIT_STYLE:
2009 case LyXRC::RC_MAKE_BACKUP:
2010 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2011 case LyXRC::RC_MOUSE_WHEEL_SPEED:
2012 case LyXRC::RC_NUMLASTFILES:
2013 case LyXRC::RC_PARAGRAPH_MARKERS:
2014 case LyXRC::RC_PATH_PREFIX:
2015 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2016 prependEnvPath("PATH", lyxrc.path_prefix);
2018 case LyXRC::RC_PERS_DICT:
2019 case LyXRC::RC_PREVIEW:
2020 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2021 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2022 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2023 case LyXRC::RC_PRINTCOPIESFLAG:
2024 case LyXRC::RC_PRINTER:
2025 case LyXRC::RC_PRINTEVENPAGEFLAG:
2026 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2027 case LyXRC::RC_PRINTFILEEXTENSION:
2028 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2029 case LyXRC::RC_PRINTODDPAGEFLAG:
2030 case LyXRC::RC_PRINTPAGERANGEFLAG:
2031 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2032 case LyXRC::RC_PRINTPAPERFLAG:
2033 case LyXRC::RC_PRINTREVERSEFLAG:
2034 case LyXRC::RC_PRINTSPOOL_COMMAND:
2035 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2036 case LyXRC::RC_PRINTTOFILE:
2037 case LyXRC::RC_PRINTTOPRINTER:
2038 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2039 case LyXRC::RC_PRINT_COMMAND:
2040 case LyXRC::RC_RTL_SUPPORT:
2041 case LyXRC::RC_SCREEN_DPI:
2042 case LyXRC::RC_SCREEN_FONT_ROMAN:
2043 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2044 case LyXRC::RC_SCREEN_FONT_SANS:
2045 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2046 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2047 case LyXRC::RC_SCREEN_FONT_SIZES:
2048 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2049 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2050 case LyXRC::RC_GEOMETRY_SESSION:
2051 case LyXRC::RC_SCREEN_ZOOM:
2052 case LyXRC::RC_SERVERPIPE:
2053 case LyXRC::RC_SET_COLOR:
2054 case LyXRC::RC_SHOW_BANNER:
2055 case LyXRC::RC_OPEN_BUFFERS_IN_TABS:
2056 case LyXRC::RC_SPELL_COMMAND:
2057 case LyXRC::RC_SPELLCHECKER:
2058 case LyXRC::RC_SPELLCHECK_CONTINUOUSLY:
2059 case LyXRC::RC_SPLITINDEX_COMMAND:
2060 case LyXRC::RC_TEMPDIRPATH:
2061 case LyXRC::RC_TEMPLATEPATH:
2062 case LyXRC::RC_TEX_ALLOWS_SPACES:
2063 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2064 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2065 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2067 case LyXRC::RC_THESAURUSDIRPATH:
2068 case LyXRC::RC_UIFILE:
2069 case LyXRC::RC_USER_EMAIL:
2070 case LyXRC::RC_USER_NAME:
2071 case LyXRC::RC_USETEMPDIR:
2072 case LyXRC::RC_USE_ALT_LANG:
2073 case LyXRC::RC_USE_CONVERTER_CACHE:
2074 case LyXRC::RC_USE_ESC_CHARS:
2075 case LyXRC::RC_USE_INP_ENC:
2076 case LyXRC::RC_USE_PERS_DICT:
2077 case LyXRC::RC_USE_TOOLTIP:
2078 case LyXRC::RC_USE_PIXMAP_CACHE:
2079 case LyXRC::RC_USE_SPELL_LIB:
2080 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2081 case LyXRC::RC_SORT_LAYOUTS:
2082 case LyXRC::RC_FULL_SCREEN_LIMIT:
2083 case LyXRC::RC_FULL_SCREEN_SCROLLBAR:
2084 case LyXRC::RC_FULL_SCREEN_MENUBAR:
2085 case LyXRC::RC_FULL_SCREEN_TABBAR:
2086 case LyXRC::RC_FULL_SCREEN_TOOLBARS:
2087 case LyXRC::RC_FULL_SCREEN_WIDTH:
2088 case LyXRC::RC_VISUAL_CURSOR:
2089 case LyXRC::RC_VIEWER:
2090 case LyXRC::RC_VIEWER_ALTERNATIVES:
2091 case LyXRC::RC_LAST: