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_CHKTEX:
471 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
474 case LFUN_BUILD_PROGRAM:
475 enable = buf->isExportable("program");
478 case LFUN_VC_REGISTER:
479 enable = !buf->lyxvc().inUse();
481 case LFUN_VC_CHECK_IN:
482 enable = buf->lyxvc().checkInEnabled();
484 case LFUN_VC_CHECK_OUT:
485 enable = buf->lyxvc().checkOutEnabled();
487 case LFUN_VC_LOCKING_TOGGLE:
488 enable = !buf->isReadonly() && buf->lyxvc().lockingToggleEnabled();
489 flag.setOnOff(enable && !buf->lyxvc().locker().empty());
492 enable = buf->lyxvc().inUse();
494 case LFUN_VC_UNDO_LAST:
495 enable = buf->lyxvc().undoLastEnabled();
497 case LFUN_BUFFER_RELOAD:
498 enable = !buf->isUnnamed() && buf->fileName().exists()
499 && (!buf->isClean() || buf->isExternallyModified(Buffer::timestamp_method));
502 case LFUN_CITATION_INSERT: {
503 FuncRequest fr(LFUN_INSET_INSERT, "citation");
504 enable = getStatus(fr).enabled();
508 // This could be used for the no-GUI version. The GUI version is handled in
509 // LyXView::getStatus(). See above.
511 case LFUN_BUFFER_WRITE:
512 case LFUN_BUFFER_WRITE_AS: {
513 Buffer * b = theBufferList().getBuffer(FileName(cmd.getArg(0)));
514 enable = b && (b->isUnnamed() || !b->isClean());
519 case LFUN_BUFFER_WRITE_ALL: {
520 // We enable the command only if there are some modified buffers
521 Buffer * first = theBufferList().first();
526 // We cannot use a for loop as the buffer list is a cycle.
532 b = theBufferList().next(b);
533 } while (b != first);
537 case LFUN_BOOKMARK_GOTO: {
538 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
539 enable = theSession().bookmarks().isValid(num);
543 case LFUN_BOOKMARK_CLEAR:
544 enable = theSession().bookmarks().hasValid();
547 // this one is difficult to get right. As a half-baked
548 // solution, we consider only the first action of the sequence
549 case LFUN_COMMAND_SEQUENCE: {
550 // argument contains ';'-terminated commands
551 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
552 FuncRequest func(lyxaction.lookupFunc(firstcmd));
553 func.origin = cmd.origin;
554 flag = getStatus(func);
558 // we want to check if at least one of these is enabled
559 case LFUN_COMMAND_ALTERNATIVES: {
560 // argument contains ';'-terminated commands
561 string arg = to_utf8(cmd.argument());
562 while (!arg.empty()) {
564 arg = split(arg, first, ';');
565 FuncRequest func(lyxaction.lookupFunc(first));
566 func.origin = cmd.origin;
567 flag = getStatus(func);
568 // if this one is enabled, the whole thing is
577 string name = to_utf8(cmd.argument());
578 if (theTopLevelCmdDef().lock(name, func)) {
579 func.origin = cmd.origin;
580 flag = getStatus(func);
581 theTopLevelCmdDef().release(name);
583 // catch recursion or unknown command
584 // definition. all operations until the
585 // recursion or unknown command definition
586 // occurs are performed, so set the state to
593 case LFUN_VC_COMMAND: {
594 if (cmd.argument().empty())
597 if (!buf && contains(cmd.getArg(0), 'D'))
602 case LFUN_MASTER_BUFFER_UPDATE:
603 case LFUN_MASTER_BUFFER_VIEW:
604 if (!buf->parent()) {
608 case LFUN_BUFFER_UPDATE:
609 case LFUN_BUFFER_VIEW: {
610 string format = to_utf8(cmd.argument());
611 if (cmd.argument().empty())
612 format = buf->getDefaultOutputFormat();
613 typedef vector<Format const *> Formats;
615 formats = buf->exportableFormats(true);
616 Formats::const_iterator fit = formats.begin();
617 Formats::const_iterator end = formats.end();
619 for (; fit != end ; ++fit) {
620 if ((*fit)->name() == format)
626 case LFUN_WORD_FINDADV:
627 case LFUN_COMMAND_PREFIX:
628 case LFUN_COMMAND_EXECUTE:
630 case LFUN_META_PREFIX:
631 case LFUN_BUFFER_CLOSE:
632 case LFUN_BUFFER_IMPORT:
633 case LFUN_BUFFER_AUTO_SAVE:
634 case LFUN_RECONFIGURE:
636 case LFUN_DROP_LAYOUTS_CHOICE:
638 case LFUN_SERVER_GET_FILENAME:
639 case LFUN_SERVER_NOTIFY:
640 case LFUN_SERVER_GOTO_FILE_ROW:
641 case LFUN_DIALOG_HIDE:
642 case LFUN_DIALOG_DISCONNECT_INSET:
643 case LFUN_BUFFER_CHILD_OPEN:
644 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
645 case LFUN_KEYMAP_OFF:
646 case LFUN_KEYMAP_PRIMARY:
647 case LFUN_KEYMAP_SECONDARY:
648 case LFUN_KEYMAP_TOGGLE:
650 case LFUN_BUFFER_EXPORT_CUSTOM:
651 case LFUN_PREFERENCES_SAVE:
653 case LFUN_INSET_EDIT:
654 case LFUN_BUFFER_LANGUAGE:
655 case LFUN_TEXTCLASS_APPLY:
656 case LFUN_TEXTCLASS_LOAD:
657 case LFUN_BUFFER_SAVE_AS_DEFAULT:
658 case LFUN_BUFFER_PARAMS_APPLY:
659 case LFUN_LAYOUT_MODULES_CLEAR:
660 case LFUN_LAYOUT_MODULE_ADD:
661 case LFUN_LAYOUT_RELOAD:
662 case LFUN_LYXRC_APPLY:
663 case LFUN_BUFFER_NEXT:
664 case LFUN_BUFFER_PREVIOUS:
665 // these are handled in our dispatch()
673 if (theApp()->getStatus(cmd, flag))
676 // Does the view know something?
681 if (lv->getStatus(cmd, flag))
684 BufferView * bv = lv->currentBufferView();
685 // If we do not have a BufferView, then other functions are disabled
690 // Is this a function that acts on inset at point?
691 Inset * inset = bv->cursor().nextInset();
692 if (lyxaction.funcHasFlag(cmd.action, LyXAction::AtPoint)
693 && inset && inset->getStatus(bv->cursor(), cmd, flag))
696 bool decided = getLocalStatus(bv->cursor(), cmd, flag);
698 // try the BufferView
699 decided = bv->getStatus(cmd, flag);
702 bv->buffer().getStatus(cmd, flag);
706 flag.setEnabled(false);
708 // Can we use a readonly buffer?
709 if (buf && buf->isReadonly()
710 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
711 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
712 flag.message(from_utf8(N_("Document is read-only")));
713 flag.setEnabled(false);
716 // Are we in a DELETED change-tracking region?
717 if (lyx_view_ && lyx_view_->documentBufferView()
718 && (lookupChangeType(lyx_view_->documentBufferView()->cursor(), true)
720 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
721 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
722 flag.message(from_utf8(N_("This portion of the document is deleted.")));
723 flag.setEnabled(false);
726 // the default error message if we disable the command
727 if (!flag.enabled() && flag.message().empty())
728 flag.message(from_utf8(N_("Command disabled")));
734 bool LyXFunc::ensureBufferClean(BufferView * bv)
736 Buffer & buf = bv->buffer();
737 if (buf.isClean() && !buf.isUnnamed())
740 docstring const file = buf.fileName().displayName(30);
743 if (!buf.isUnnamed()) {
744 text = bformat(_("The document %1$s has unsaved "
745 "changes.\n\nDo you want to save "
746 "the document?"), file);
747 title = _("Save changed document?");
750 text = bformat(_("The document %1$s has not been "
751 "saved yet.\n\nDo you want to save "
752 "the document?"), file);
753 title = _("Save new document?");
755 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
758 lyx_view_->dispatch(FuncRequest(LFUN_BUFFER_WRITE));
760 return buf.isClean() && !buf.isUnnamed();
766 bool loadLayoutFile(string const & name, string const & buf_path)
768 if (!LayoutFileList::get().haveClass(name)) {
769 lyxerr << "Document class \"" << name
770 << "\" does not exist."
775 LayoutFile & tc = LayoutFileList::get()[name];
776 if (!tc.load(buf_path)) {
777 docstring s = bformat(_("The document class %1$s "
778 "could not be loaded."), from_utf8(name));
779 Alert::error(_("Could not load class"), s);
786 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
791 void LyXFunc::dispatch(FuncRequest const & cmd)
793 string const argument = to_utf8(cmd.argument());
794 FuncCode const action = cmd.action;
796 LYXERR(Debug::ACTION, "\nLyXFunc::dispatch: cmd: " << cmd);
797 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
799 // we have not done anything wrong yet.
801 dispatch_buffer.erase();
803 // redraw the screen at the end (first of the two drawing steps).
804 //This is done unless explicitely requested otherwise
805 Update::flags updateFlags = Update::FitCursor;
807 FuncStatus const flag = getStatus(cmd);
808 if (!flag.enabled()) {
809 // We cannot use this function here
810 LYXERR(Debug::ACTION, "LyXFunc::dispatch: "
811 << lyxaction.getActionName(action)
812 << " [" << action << "] is disabled at this location");
813 setErrorMessage(flag.message());
815 lyx_view_->restartCursor();
818 if (lyx_view_ && lyx_view_->currentBufferView())
819 buffer = &lyx_view_->currentBufferView()->buffer();
822 case LFUN_COMMAND_PREFIX:
823 LASSERT(lyx_view_, /**/);
824 lyx_view_->message(keyseq.printOptions(true));
828 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
830 meta_fake_bit = NoModifier;
832 // cancel any selection
833 dispatch(FuncRequest(LFUN_MARK_OFF));
834 setMessage(from_ascii(N_("Cancel")));
837 case LFUN_META_PREFIX:
838 meta_fake_bit = AltModifier;
839 setMessage(keyseq.print(KeySequence::ForGui));
842 case LFUN_BUFFER_TOGGLE_READ_ONLY: {
843 LASSERT(lyx_view_ && lyx_view_->currentBufferView() && buffer, /**/);
844 if (buffer->lyxvc().inUse())
845 buffer->lyxvc().toggleReadOnly();
847 buffer->setReadonly(!buffer->isReadonly());
851 // --- Menus -----------------------------------------------
852 case LFUN_BUFFER_CLOSE:
853 lyx_view_->closeBuffer();
855 updateFlags = Update::None;
858 case LFUN_BUFFER_CLOSE_ALL:
859 lyx_view_->closeBufferAll();
861 updateFlags = Update::None;
864 case LFUN_BUFFER_RELOAD: {
865 LASSERT(lyx_view_ && buffer, /**/);
866 docstring const file = makeDisplayPath(buffer->absFileName(), 20);
867 docstring text = bformat(_("Any changes will be lost. Are you sure "
868 "you want to revert to the saved version of the document %1$s?"), file);
869 int const ret = Alert::prompt(_("Revert to saved document?"),
870 text, 1, 1, _("&Revert"), _("&Cancel"));
877 case LFUN_BUFFER_UPDATE: {
878 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
879 Buffer & doc_buffer = lyx_view_->documentBufferView()->buffer();
880 string format = argument;
881 if (argument.empty())
882 format = doc_buffer.getDefaultOutputFormat();
883 doc_buffer.doExport(format, true);
887 case LFUN_BUFFER_VIEW: {
888 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
889 Buffer & doc_buffer = lyx_view_->documentBufferView()->buffer();
890 string format = argument;
891 if (argument.empty())
892 format = doc_buffer.getDefaultOutputFormat();
893 doc_buffer.preview(format);
897 case LFUN_MASTER_BUFFER_UPDATE: {
898 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
899 Buffer & doc_buffer = lyx_view_->documentBufferView()->buffer();
900 string format = argument;
901 if (argument.empty())
902 format = doc_buffer.masterBuffer()->getDefaultOutputFormat();
903 doc_buffer.masterBuffer()->doExport(format, true);
907 case LFUN_MASTER_BUFFER_VIEW: {
908 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
909 Buffer & doc_buffer = lyx_view_->documentBufferView()->buffer();
910 string format = argument;
911 if (argument.empty())
912 format = doc_buffer.masterBuffer()->getDefaultOutputFormat();
913 doc_buffer.masterBuffer()->preview(format);
917 case LFUN_BUILD_PROGRAM:
918 LASSERT(lyx_view_ && buffer, /**/);
919 buffer->doExport("program", true);
922 case LFUN_BUFFER_CHKTEX:
923 LASSERT(lyx_view_ && buffer, /**/);
927 case LFUN_BUFFER_EXPORT:
928 LASSERT(lyx_view_ && buffer, /**/);
929 if (argument == "custom")
930 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"));
932 buffer->doExport(argument, false);
935 case LFUN_BUFFER_EXPORT_CUSTOM: {
936 LASSERT(lyx_view_ && buffer, /**/);
938 string command = split(argument, format_name, ' ');
939 Format const * format = formats.getFormat(format_name);
941 lyxerr << "Format \"" << format_name
942 << "\" not recognized!"
947 // The name of the file created by the conversion process
950 // Output to filename
951 if (format->name() == "lyx") {
952 string const latexname = buffer->latexName(false);
953 filename = changeExtension(latexname,
954 format->extension());
955 filename = addName(buffer->temppath(), filename);
957 if (!buffer->writeFile(FileName(filename)))
961 buffer->doExport(format_name, true, filename);
964 // Substitute $$FName for filename
965 if (!contains(command, "$$FName"))
966 command = "( " + command + " ) < $$FName";
967 command = subst(command, "$$FName", filename);
969 // Execute the command in the background
971 call.startscript(Systemcall::DontWait, command);
975 // FIXME: There is need for a command-line import.
977 case LFUN_BUFFER_IMPORT:
982 case LFUN_BUFFER_AUTO_SAVE:
986 case LFUN_RECONFIGURE:
987 // argument is any additional parameter to the configure.py command
988 reconfigure(lyx_view_, argument);
991 case LFUN_HELP_OPEN: {
993 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
994 string const arg = argument;
996 setErrorMessage(from_utf8(N_("Missing argument")));
999 FileName fname = i18nLibFileSearch("doc", arg, "lyx");
1001 fname = i18nLibFileSearch("examples", arg, "lyx");
1003 if (fname.empty()) {
1004 lyxerr << "LyX: unable to find documentation file `"
1005 << arg << "'. Bad installation?" << endl;
1008 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1009 makeDisplayPath(fname.absFilename())));
1010 Buffer * buf = lyx_view_->loadDocument(fname, false);
1012 buf->updateLabels();
1013 lyx_view_->setBuffer(buf);
1014 buf->errors("Parse");
1016 updateFlags = Update::None;
1020 // --- version control -------------------------------
1021 case LFUN_VC_REGISTER:
1022 LASSERT(lyx_view_ && buffer, /**/);
1023 if (!ensureBufferClean(lyx_view_->documentBufferView()))
1025 if (!buffer->lyxvc().inUse()) {
1026 if (buffer->lyxvc().registrer())
1029 updateFlags = Update::Force;
1032 case LFUN_VC_CHECK_IN:
1033 LASSERT(lyx_view_ && buffer, /**/);
1034 if (!ensureBufferClean(lyx_view_->documentBufferView()))
1036 if (buffer->lyxvc().inUse()
1037 && !buffer->isReadonly()) {
1038 setMessage(from_utf8(buffer->lyxvc().checkIn()));
1043 case LFUN_VC_CHECK_OUT:
1044 LASSERT(lyx_view_ && buffer, /**/);
1045 if (!ensureBufferClean(lyx_view_->documentBufferView()))
1047 if (buffer->lyxvc().inUse()) {
1048 setMessage(from_utf8(buffer->lyxvc().checkOut()));
1053 case LFUN_VC_LOCKING_TOGGLE:
1054 LASSERT(lyx_view_ && buffer, /**/);
1055 if (!ensureBufferClean(lyx_view_->documentBufferView())
1056 || buffer->isReadonly())
1058 if (buffer->lyxvc().inUse()) {
1059 string res = buffer->lyxvc().lockingToggle();
1061 frontend::Alert::error(_("Revision control error."),
1062 _("Error when setting the locking property."));
1064 setMessage(from_utf8(res));
1070 case LFUN_VC_REVERT:
1071 LASSERT(lyx_view_ && buffer, /**/);
1072 buffer->lyxvc().revert();
1076 case LFUN_VC_UNDO_LAST:
1077 LASSERT(lyx_view_ && buffer, /**/);
1078 buffer->lyxvc().undoLast();
1082 // --- lyxserver commands ----------------------------
1083 case LFUN_SERVER_GET_FILENAME:
1084 LASSERT(lyx_view_ && buffer, /**/);
1085 setMessage(from_utf8(buffer->absFileName()));
1086 LYXERR(Debug::INFO, "FNAME["
1087 << buffer->absFileName() << ']');
1090 case LFUN_SERVER_NOTIFY:
1091 dispatch_buffer = keyseq.print(KeySequence::Portable);
1092 theServer().notifyClient(to_utf8(dispatch_buffer));
1095 case LFUN_SERVER_GOTO_FILE_ROW: {
1096 LASSERT(lyx_view_, /**/);
1099 istringstream is(argument);
1100 is >> file_name >> row;
1101 file_name = os::internal_path(file_name);
1103 bool loaded = false;
1104 string const abstmp = package().temp_dir().absFilename();
1105 string const realtmp = package().temp_dir().realPath();
1106 // We have to use os::path_prefix_is() here, instead of
1107 // simply prefixIs(), because the file name comes from
1108 // an external application and may need case adjustment.
1109 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
1110 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
1111 // Needed by inverse dvi search. If it is a file
1112 // in tmpdir, call the apropriated function.
1113 // If tmpdir is a symlink, we may have the real
1114 // path passed back, so we correct for that.
1115 if (!prefixIs(file_name, abstmp))
1116 file_name = subst(file_name, realtmp, abstmp);
1117 buf = theBufferList().getBufferFromTmp(file_name);
1119 // Must replace extension of the file to be .lyx
1120 // and get full path
1121 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1122 // Either change buffer or load the file
1123 if (theBufferList().exists(s))
1124 buf = theBufferList().getBuffer(s);
1125 else if (s.exists()) {
1126 buf = lyx_view_->loadDocument(s);
1129 lyx_view_->message(bformat(
1130 _("File does not exist: %1$s"),
1131 makeDisplayPath(file_name)));
1135 updateFlags = Update::None;
1139 buf->updateLabels();
1140 lyx_view_->setBuffer(buf);
1141 lyx_view_->documentBufferView()->setCursorFromRow(row);
1143 buf->errors("Parse");
1144 updateFlags = Update::FitCursor;
1149 case LFUN_DIALOG_SHOW_NEW_INSET: {
1150 LASSERT(lyx_view_, /**/);
1151 string const name = cmd.getArg(0);
1152 InsetCode code = insetCode(name);
1153 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1154 bool insetCodeOK = true;
1161 case NOMENCL_PRINT_CODE:
1164 case HYPERLINK_CODE: {
1165 InsetCommandParams p(code);
1166 data = InsetCommand::params2string(name, p);
1169 case INCLUDE_CODE: {
1170 // data is the include type: one of "include",
1171 // "input", "verbatiminput" or "verbatiminput*"
1173 // default type is requested
1175 InsetCommandParams p(INCLUDE_CODE, data);
1176 data = InsetCommand::params2string("include", p);
1180 // \c data == "Boxed" || "Frameless" etc
1181 InsetBoxParams p(data);
1182 data = InsetBox::params2string(p);
1186 InsetBranchParams p;
1187 data = InsetBranch::params2string(p);
1191 InsetCommandParams p(CITE_CODE);
1192 data = InsetCommand::params2string(name, p);
1196 data = InsetERT::params2string(InsetCollapsable::Open);
1199 case EXTERNAL_CODE: {
1200 InsetExternalParams p;
1201 data = InsetExternal::params2string(p, *buffer);
1206 data = InsetFloat::params2string(p);
1209 case LISTINGS_CODE: {
1210 InsetListingsParams p;
1211 data = InsetListings::params2string(p);
1214 case GRAPHICS_CODE: {
1215 InsetGraphicsParams p;
1216 data = InsetGraphics::params2string(p, *buffer);
1221 data = InsetNote::params2string(p);
1224 case PHANTOM_CODE: {
1225 InsetPhantomParams p;
1226 data = InsetPhantom::params2string(p);
1231 data = InsetSpace::params2string(p);
1236 data = InsetVSpace::params2string(space);
1241 data = InsetWrap::params2string(p);
1245 lyxerr << "Inset type '" << name <<
1246 "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" << endl;
1247 insetCodeOK = false;
1249 } // end switch(code)
1251 dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data));
1255 case LFUN_CITATION_INSERT: {
1256 LASSERT(lyx_view_, /**/);
1257 if (!argument.empty()) {
1258 // we can have one optional argument, delimited by '|'
1259 // citation-insert <key>|<text_before>
1260 // this should be enhanced to also support text_after
1261 // and citation style
1262 string arg = argument;
1264 if (contains(argument, "|")) {
1265 arg = token(argument, '|', 0);
1266 opt1 = token(argument, '|', 1);
1268 InsetCommandParams icp(CITE_CODE);
1269 icp["key"] = from_utf8(arg);
1271 icp["before"] = from_utf8(opt1);
1272 string icstr = InsetCommand::params2string("citation", icp);
1273 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1276 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1280 case LFUN_BUFFER_CHILD_OPEN: {
1281 LASSERT(lyx_view_ && buffer, /**/);
1282 FileName filename = makeAbsPath(argument, buffer->filePath());
1283 lyx_view_->documentBufferView()->saveBookmark(false);
1285 bool parsed = false;
1286 if (theBufferList().exists(filename)) {
1287 child = theBufferList().getBuffer(filename);
1289 setMessage(bformat(_("Opening child document %1$s..."),
1290 makeDisplayPath(filename.absFilename())));
1291 child = lyx_view_->loadDocument(filename, false);
1295 // Set the parent name of the child document.
1296 // This makes insertion of citations and references in the child work,
1297 // when the target is in the parent or another child document.
1298 child->setParent(buffer);
1299 child->masterBuffer()->updateLabels();
1300 lyx_view_->setBuffer(child);
1302 child->errors("Parse");
1305 // If a screen update is required (in case where auto_open is false),
1306 // setBuffer() would have taken care of it already. Otherwise we shall
1307 // reset the update flag because it can cause a circular problem.
1309 updateFlags = Update::None;
1313 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
1314 LASSERT(lyx_view_, /**/);
1315 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1318 case LFUN_KEYMAP_OFF:
1319 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1320 lyx_view_->currentBufferView()->getIntl().keyMapOn(false);
1323 case LFUN_KEYMAP_PRIMARY:
1324 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1325 lyx_view_->currentBufferView()->getIntl().keyMapPrim();
1328 case LFUN_KEYMAP_SECONDARY:
1329 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1330 lyx_view_->currentBufferView()->getIntl().keyMapSec();
1333 case LFUN_KEYMAP_TOGGLE:
1334 LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
1335 lyx_view_->currentBufferView()->getIntl().toggleKeyMap();
1341 string rest = split(argument, countstr, ' ');
1342 istringstream is(countstr);
1345 //lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1346 for (int i = 0; i < count; ++i)
1347 dispatch(lyxaction.lookupFunc(rest));
1351 case LFUN_COMMAND_SEQUENCE: {
1352 // argument contains ';'-terminated commands
1353 string arg = argument;
1354 if (theBufferList().isLoaded(buffer))
1355 buffer->undo().beginUndoGroup();
1356 while (!arg.empty()) {
1358 arg = split(arg, first, ';');
1359 FuncRequest func(lyxaction.lookupFunc(first));
1360 func.origin = cmd.origin;
1363 if (theBufferList().isLoaded(buffer))
1364 buffer->undo().endUndoGroup();
1368 case LFUN_COMMAND_ALTERNATIVES: {
1369 // argument contains ';'-terminated commands
1370 string arg = argument;
1371 while (!arg.empty()) {
1373 arg = split(arg, first, ';');
1374 FuncRequest func(lyxaction.lookupFunc(first));
1375 func.origin = cmd.origin;
1376 FuncStatus stat = getStatus(func);
1377 if (stat.enabled()) {
1387 if (theTopLevelCmdDef().lock(argument, func)) {
1388 func.origin = cmd.origin;
1390 theTopLevelCmdDef().release(argument);
1392 if (func.action == LFUN_UNKNOWN_ACTION) {
1393 // unknown command definition
1394 lyxerr << "Warning: unknown command definition `"
1398 // recursion detected
1399 lyxerr << "Warning: Recursion in the command definition `"
1400 << argument << "' detected"
1407 case LFUN_PREFERENCES_SAVE: {
1408 lyxrc.write(makeAbsPath("preferences",
1409 package().user_support().absFilename()),
1415 LASSERT(lyx_view_, /**/);
1416 lyx_view_->message(from_utf8(argument));
1419 case LFUN_BUFFER_LANGUAGE: {
1420 LASSERT(lyx_view_, /**/);
1421 Language const * oldL = buffer->params().language;
1422 Language const * newL = languages.getLanguage(argument);
1423 if (!newL || oldL == newL)
1426 if (oldL->rightToLeft() == newL->rightToLeft()
1427 && !buffer->isMultiLingual())
1428 buffer->changeLanguage(oldL, newL);
1432 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1433 string const fname =
1434 addName(addPath(package().user_support().absFilename(), "templates/"),
1436 Buffer defaults(fname);
1438 istringstream ss(argument);
1441 int const unknown_tokens = defaults.readHeader(lex);
1443 if (unknown_tokens != 0) {
1444 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1445 << unknown_tokens << " unknown token"
1446 << (unknown_tokens == 1 ? "" : "s")
1450 if (defaults.writeFile(FileName(defaults.absFileName())))
1451 setMessage(bformat(_("Document defaults saved in %1$s"),
1452 makeDisplayPath(fname)));
1454 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1458 case LFUN_BUFFER_PARAMS_APPLY: {
1459 LASSERT(lyx_view_, /**/);
1461 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1462 Cursor & cur = lyx_view_->documentBufferView()->cursor();
1463 cur.recordUndoFullDocument();
1465 istringstream ss(argument);
1468 int const unknown_tokens = buffer->readHeader(lex);
1470 if (unknown_tokens != 0) {
1471 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1472 << unknown_tokens << " unknown token"
1473 << (unknown_tokens == 1 ? "" : "s")
1477 updateLayout(oldClass, buffer);
1479 updateFlags = Update::Force | Update::FitCursor;
1480 // We are most certainly here because of a change in the document
1481 // It is then better to make sure that all dialogs are in sync with
1482 // current document settings. LyXView::restartCursor() achieve this.
1483 lyx_view_->restartCursor();
1487 case LFUN_LAYOUT_MODULES_CLEAR: {
1488 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
1489 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1490 lyx_view_->documentBufferView()->cursor().recordUndoFullDocument();
1491 buffer->params().clearLayoutModules();
1492 buffer->params().makeDocumentClass();
1493 updateLayout(oldClass, buffer);
1494 updateFlags = Update::Force | Update::FitCursor;
1498 case LFUN_LAYOUT_MODULE_ADD: {
1499 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
1500 BufferParams const & params = buffer->params();
1501 if (!params.moduleCanBeAdded(argument)) {
1502 LYXERR0("Module `" << argument <<
1503 "' cannot be added due to failed requirements or "
1504 "conflicts with installed modules.");
1507 DocumentClass const * const oldClass = params.documentClassPtr();
1508 lyx_view_->documentBufferView()->cursor().recordUndoFullDocument();
1509 buffer->params().addLayoutModule(argument);
1510 buffer->params().makeDocumentClass();
1511 updateLayout(oldClass, buffer);
1512 updateFlags = Update::Force | Update::FitCursor;
1516 case LFUN_TEXTCLASS_APPLY: {
1517 LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
1519 if (!loadLayoutFile(argument, buffer->temppath()) &&
1520 !loadLayoutFile(argument, buffer->filePath()))
1523 LayoutFile const * old_layout = buffer->params().baseClass();
1524 LayoutFile const * new_layout = &(LayoutFileList::get()[argument]);
1526 if (old_layout == new_layout)
1530 //Save the old, possibly modular, layout for use in conversion.
1531 DocumentClass const * const oldDocClass =
1532 buffer->params().documentClassPtr();
1533 lyx_view_->documentBufferView()->cursor().recordUndoFullDocument();
1534 buffer->params().setBaseClass(argument);
1535 buffer->params().makeDocumentClass();
1536 updateLayout(oldDocClass, buffer);
1537 updateFlags = Update::Force | Update::FitCursor;
1541 case LFUN_LAYOUT_RELOAD: {
1542 LASSERT(lyx_view_, /**/);
1543 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1544 LayoutFileIndex bc = buffer->params().baseClassID();
1545 LayoutFileList::get().reset(bc);
1546 buffer->params().setBaseClass(bc);
1547 buffer->params().makeDocumentClass();
1548 updateLayout(oldClass, buffer);
1549 updateFlags = Update::Force | Update::FitCursor;
1553 case LFUN_TEXTCLASS_LOAD:
1554 loadLayoutFile(argument, buffer->temppath()) ||
1555 loadLayoutFile(argument, buffer->filePath());
1558 case LFUN_LYXRC_APPLY: {
1559 // reset active key sequences, since the bindings
1560 // are updated (bug 6064)
1562 LyXRC const lyxrc_orig = lyxrc;
1564 istringstream ss(argument);
1565 bool const success = lyxrc.read(ss) == 0;
1568 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1569 << "Unable to read lyxrc data"
1574 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1578 theApp()->resetGui();
1580 /// We force the redraw in any case because there might be
1581 /// some screen font changes.
1582 /// FIXME: only the current view will be updated. the Gui
1583 /// class is able to furnish the list of views.
1584 updateFlags = Update::Force;
1588 case LFUN_BOOKMARK_GOTO:
1589 // go to bookmark, open unopened file and switch to buffer if necessary
1590 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1591 updateFlags = Update::FitCursor;
1594 case LFUN_BOOKMARK_CLEAR:
1595 theSession().bookmarks().clear();
1598 case LFUN_VC_COMMAND: {
1599 string flag = cmd.getArg(0);
1600 if (buffer && contains(flag, 'R')
1601 && !ensureBufferClean(lyx_view_->documentBufferView()))
1604 if (contains(flag, 'M'))
1605 if (!Alert::askForText(message, _("LyX VC: Log Message")))
1608 string path = cmd.getArg(1);
1609 if (contains(path, "$$p") && buffer)
1610 path = subst(path, "$$p", buffer->filePath());
1611 LYXERR(Debug::LYXVC, "Directory: " << path);
1613 if (!pp.isReadableDirectory()) {
1614 lyxerr << _("Directory is not accessible.") << endl;
1617 support::PathChanger p(pp);
1619 string command = cmd.getArg(2);
1620 if (command.empty())
1623 command = subst(command, "$$i", buffer->absFileName());
1624 command = subst(command, "$$p", buffer->filePath());
1626 command = subst(command, "$$m", to_utf8(message));
1627 LYXERR(Debug::LYXVC, "Command: " << command);
1629 one.startscript(Systemcall::Wait, command);
1633 if (contains(flag, 'I'))
1634 buffer->markDirty();
1635 if (contains(flag, 'R'))
1642 LASSERT(theApp(), /**/);
1643 // Let the frontend dispatch its own actions.
1644 if (theApp()->dispatch(cmd))
1645 // Nothing more to do.
1648 // Everything below is only for active lyx_view_
1652 // Start an undo group. This may be needed for
1653 // some stuff like inset-apply on labels.
1654 if (theBufferList().isLoaded(buffer))
1655 buffer->undo().beginUndoGroup();
1657 // Let the current LyXView dispatch its own actions.
1658 if (lyx_view_->dispatch(cmd)) {
1659 if (lyx_view_->currentBufferView()) {
1660 updateFlags = lyx_view_->currentBufferView()->cursor().result().update();
1661 if (theBufferList().isLoaded(buffer))
1662 buffer->undo().endUndoGroup();
1667 LASSERT(lyx_view_->currentBufferView(), /**/);
1669 // Let the current BufferView dispatch its own actions.
1670 if (lyx_view_->currentBufferView()->dispatch(cmd)) {
1671 // The BufferView took care of its own updates if needed.
1672 updateFlags = Update::None;
1673 if (theBufferList().isLoaded(buffer))
1674 buffer->undo().endUndoGroup();
1678 // OK, so try the Buffer itself
1680 BufferView * bv = lyx_view_->currentBufferView();
1681 bv->buffer().dispatch(cmd, dr);
1682 if (dr.dispatched()) {
1683 updateFlags = dr.update();
1687 // Is this a function that acts on inset at point?
1688 Inset * inset = bv->cursor().nextInset();
1689 if (lyxaction.funcHasFlag(action, LyXAction::AtPoint)
1691 bv->cursor().result().dispatched(true);
1692 bv->cursor().result().update(Update::FitCursor | Update::Force);
1693 FuncRequest tmpcmd = cmd;
1694 inset->dispatch(bv->cursor(), tmpcmd);
1695 if (bv->cursor().result().dispatched()) {
1696 updateFlags = bv->cursor().result().update();
1701 // Let the current Cursor dispatch its own actions.
1702 Cursor old = bv->cursor();
1703 bv->cursor().getPos(cursorPosBeforeDispatchX_,
1704 cursorPosBeforeDispatchY_);
1705 bv->cursor().dispatch(cmd);
1707 // notify insets we just left
1708 if (bv->cursor() != old) {
1710 bool badcursor = notifyCursorLeavesOrEnters(old, bv->cursor());
1712 bv->cursor().fixIfBroken();
1715 if (theBufferList().isLoaded(buffer))
1716 buffer->undo().endUndoGroup();
1718 // update completion. We do it here and not in
1719 // processKeySym to avoid another redraw just for a
1720 // changed inline completion
1721 if (cmd.origin == FuncRequest::KEYBOARD) {
1722 if (cmd.action == LFUN_SELF_INSERT
1723 || (cmd.action == LFUN_ERT_INSERT && bv->cursor().inMathed()))
1724 lyx_view_->updateCompletion(bv->cursor(), true, true);
1725 else if (cmd.action == LFUN_CHAR_DELETE_BACKWARD)
1726 lyx_view_->updateCompletion(bv->cursor(), false, true);
1728 lyx_view_->updateCompletion(bv->cursor(), false, false);
1731 updateFlags = bv->cursor().result().update();
1734 // if we executed a mutating lfun, mark the buffer as dirty
1735 if (theBufferList().isLoaded(buffer) && flag.enabled()
1736 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1737 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1738 buffer->markDirty();
1740 if (lyx_view_ && lyx_view_->currentBufferView()) {
1741 // BufferView::update() updates the ViewMetricsInfo and
1742 // also initializes the position cache for all insets in
1743 // (at least partially) visible top-level paragraphs.
1744 // We will redraw the screen only if needed.
1745 lyx_view_->currentBufferView()->processUpdateFlags(updateFlags);
1747 // Do we have a selection?
1748 theSelection().haveSelection(
1749 lyx_view_->currentBufferView()->cursor().selection());
1752 lyx_view_->restartCursor();
1756 // Some messages may already be translated, so we cannot use _()
1757 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1762 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1764 const bool verbose = (cmd.origin == FuncRequest::MENU
1765 || cmd.origin == FuncRequest::TOOLBAR
1766 || cmd.origin == FuncRequest::COMMANDBUFFER);
1768 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1769 LYXERR(Debug::ACTION, "dispatch msg is " << to_utf8(msg));
1771 lyx_view_->message(msg);
1775 docstring dispatch_msg = msg;
1776 if (!dispatch_msg.empty())
1777 dispatch_msg += ' ';
1779 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1781 bool argsadded = false;
1783 if (!cmd.argument().empty()) {
1784 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1785 comname += ' ' + cmd.argument();
1790 docstring const shortcuts = theTopLevelKeymap().printBindings(cmd, KeySequence::ForGui);
1792 if (!shortcuts.empty())
1793 comname += ": " + shortcuts;
1794 else if (!argsadded && !cmd.argument().empty())
1795 comname += ' ' + cmd.argument();
1797 if (!comname.empty()) {
1798 comname = rtrim(comname);
1799 dispatch_msg += '(' + rtrim(comname) + ')';
1802 LYXERR(Debug::ACTION, "verbose dispatch msg " << to_utf8(dispatch_msg));
1803 if (!dispatch_msg.empty())
1804 lyx_view_->message(dispatch_msg);
1808 void LyXFunc::reloadBuffer()
1810 Buffer * buf = &lyx_view_->documentBufferView()->buffer();
1811 FileName filename = buf->fileName();
1812 // The user has already confirmed that the changes, if any, should
1813 // be discarded. So we just release the Buffer and don't call closeBuffer();
1814 theBufferList().release(buf);
1815 // if the lyx_view_ has been destroyed, create a new one
1817 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1818 buf = lyx_view_->loadDocument(filename);
1819 docstring const disp_fn = makeDisplayPath(filename.absFilename());
1822 buf->updateLabels();
1823 lyx_view_->setBuffer(buf);
1824 buf->errors("Parse");
1825 str = bformat(_("Document %1$s reloaded."), disp_fn);
1827 str = bformat(_("Could not reload document %1$s"), disp_fn);
1829 lyx_view_->message(str);
1832 // Each "lyx_view_" should have it's own message method. lyxview and
1833 // the minibuffer would use the minibuffer, but lyxserver would
1834 // send an ERROR signal to its client. Alejandro 970603
1835 // This function is bit problematic when it comes to NLS, to make the
1836 // lyx servers client be language indepenent we must not translate
1837 // strings sent to this func.
1838 void LyXFunc::setErrorMessage(docstring const & m) const
1840 dispatch_buffer = m;
1845 void LyXFunc::setMessage(docstring const & m) const
1847 dispatch_buffer = m;
1851 docstring LyXFunc::viewStatusMessage()
1853 // When meta-fake key is pressed, show the key sequence so far + "M-".
1855 return keyseq.print(KeySequence::ForGui) + "M-";
1857 // Else, when a non-complete key sequence is pressed,
1858 // show the available options.
1859 if (keyseq.length() > 0 && !keyseq.deleted())
1860 return keyseq.printOptions(true);
1862 LASSERT(lyx_view_, /**/);
1863 if (!lyx_view_->currentBufferView())
1864 return _("Welcome to LyX!");
1866 return lyx_view_->currentBufferView()->cursor().currentState();
1870 bool LyXFunc::wasMetaKey() const
1872 return (meta_fake_bit != NoModifier);
1876 void LyXFunc::updateLayout(DocumentClass const * const oldlayout, Buffer * buf)
1878 lyx_view_->message(_("Converting document to new document class..."));
1880 StableDocIterator backcur(lyx_view_->currentBufferView()->cursor());
1881 ErrorList & el = buf->errorList("Class Switch");
1882 cap::switchBetweenClasses(
1883 oldlayout, buf->params().documentClassPtr(),
1884 static_cast<InsetText &>(buf->inset()), el);
1886 lyx_view_->currentBufferView()->setCursor(backcur.asDocIterator(buf));
1888 buf->errors("Class Switch");
1889 buf->updateLabels();
1895 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1897 // Why the switch you might ask. It is a trick to ensure that all
1898 // the elements in the LyXRCTags enum is handled. As you can see
1899 // there are no breaks at all. So it is just a huge fall-through.
1900 // The nice thing is that we will get a warning from the compiler
1901 // if we forget an element.
1902 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1904 case LyXRC::RC_ACCEPT_COMPOUND:
1905 case LyXRC::RC_ALT_LANG:
1906 case LyXRC::RC_PLAINTEXT_LINELEN:
1907 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
1908 case LyXRC::RC_AUTOCORRECTION_MATH:
1909 case LyXRC::RC_AUTOREGIONDELETE:
1910 case LyXRC::RC_AUTORESET_OPTIONS:
1911 case LyXRC::RC_AUTOSAVE:
1912 case LyXRC::RC_AUTO_NUMBER:
1913 case LyXRC::RC_BACKUPDIR_PATH:
1914 case LyXRC::RC_BIBTEX_ALTERNATIVES:
1915 case LyXRC::RC_BIBTEX_COMMAND:
1916 case LyXRC::RC_BINDFILE:
1917 case LyXRC::RC_CHECKLASTFILES:
1918 case LyXRC::RC_COMPLETION_CURSOR_TEXT:
1919 case LyXRC::RC_COMPLETION_INLINE_DELAY:
1920 case LyXRC::RC_COMPLETION_INLINE_DOTS:
1921 case LyXRC::RC_COMPLETION_INLINE_MATH:
1922 case LyXRC::RC_COMPLETION_INLINE_TEXT:
1923 case LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE:
1924 case LyXRC::RC_COMPLETION_POPUP_DELAY:
1925 case LyXRC::RC_COMPLETION_POPUP_MATH:
1926 case LyXRC::RC_COMPLETION_POPUP_TEXT:
1927 case LyXRC::RC_USELASTFILEPOS:
1928 case LyXRC::RC_LOADSESSION:
1929 case LyXRC::RC_CHKTEX_COMMAND:
1930 case LyXRC::RC_CONVERTER:
1931 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
1932 case LyXRC::RC_COPIER:
1933 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1934 case LyXRC::RC_SCROLL_BELOW_DOCUMENT:
1935 case LyXRC::RC_DATE_INSERT_FORMAT:
1936 case LyXRC::RC_DEFAULT_LANGUAGE:
1937 case LyXRC::RC_GUI_LANGUAGE:
1938 case LyXRC::RC_DEFAULT_PAPERSIZE:
1939 case LyXRC::RC_DEFAULT_VIEW_FORMAT:
1940 case LyXRC::RC_DEFFILE:
1941 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1942 case LyXRC::RC_DISPLAY_GRAPHICS:
1943 case LyXRC::RC_DOCUMENTPATH:
1944 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1945 FileName path(lyxrc_new.document_path);
1946 if (path.exists() && path.isDirectory())
1947 package().document_dir() = FileName(lyxrc.document_path);
1949 case LyXRC::RC_EDITOR_ALTERNATIVES:
1950 case LyXRC::RC_ESC_CHARS:
1951 case LyXRC::RC_EXAMPLEPATH:
1952 case LyXRC::RC_FONT_ENCODING:
1953 case LyXRC::RC_FORMAT:
1954 case LyXRC::RC_GROUP_LAYOUTS:
1955 case LyXRC::RC_HUNSPELLDIR_PATH:
1956 case LyXRC::RC_INDEX_ALTERNATIVES:
1957 case LyXRC::RC_INDEX_COMMAND:
1958 case LyXRC::RC_JBIBTEX_COMMAND:
1959 case LyXRC::RC_JINDEX_COMMAND:
1960 case LyXRC::RC_NOMENCL_COMMAND:
1961 case LyXRC::RC_INPUT:
1962 case LyXRC::RC_KBMAP:
1963 case LyXRC::RC_KBMAP_PRIMARY:
1964 case LyXRC::RC_KBMAP_SECONDARY:
1965 case LyXRC::RC_LABEL_INIT_LENGTH:
1966 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
1967 case LyXRC::RC_LANGUAGE_AUTO_END:
1968 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
1969 case LyXRC::RC_LANGUAGE_COMMAND_END:
1970 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
1971 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
1972 case LyXRC::RC_LANGUAGE_PACKAGE:
1973 case LyXRC::RC_LANGUAGE_USE_BABEL:
1974 case LyXRC::RC_MAC_LIKE_WORD_MOVEMENT:
1975 case LyXRC::RC_MACRO_EDIT_STYLE:
1976 case LyXRC::RC_MAKE_BACKUP:
1977 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
1978 case LyXRC::RC_MOUSE_WHEEL_SPEED:
1979 case LyXRC::RC_NUMLASTFILES:
1980 case LyXRC::RC_PARAGRAPH_MARKERS:
1981 case LyXRC::RC_PATH_PREFIX:
1982 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
1983 prependEnvPath("PATH", lyxrc.path_prefix);
1985 case LyXRC::RC_PERS_DICT:
1986 case LyXRC::RC_PREVIEW:
1987 case LyXRC::RC_PREVIEW_HASHED_LABELS:
1988 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
1989 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
1990 case LyXRC::RC_PRINTCOPIESFLAG:
1991 case LyXRC::RC_PRINTER:
1992 case LyXRC::RC_PRINTEVENPAGEFLAG:
1993 case LyXRC::RC_PRINTEXSTRAOPTIONS:
1994 case LyXRC::RC_PRINTFILEEXTENSION:
1995 case LyXRC::RC_PRINTLANDSCAPEFLAG:
1996 case LyXRC::RC_PRINTODDPAGEFLAG:
1997 case LyXRC::RC_PRINTPAGERANGEFLAG:
1998 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
1999 case LyXRC::RC_PRINTPAPERFLAG:
2000 case LyXRC::RC_PRINTREVERSEFLAG:
2001 case LyXRC::RC_PRINTSPOOL_COMMAND:
2002 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2003 case LyXRC::RC_PRINTTOFILE:
2004 case LyXRC::RC_PRINTTOPRINTER:
2005 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2006 case LyXRC::RC_PRINT_COMMAND:
2007 case LyXRC::RC_RTL_SUPPORT:
2008 case LyXRC::RC_SCREEN_DPI:
2009 case LyXRC::RC_SCREEN_FONT_ROMAN:
2010 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2011 case LyXRC::RC_SCREEN_FONT_SANS:
2012 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2013 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2014 case LyXRC::RC_SCREEN_FONT_SIZES:
2015 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2016 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2017 case LyXRC::RC_GEOMETRY_SESSION:
2018 case LyXRC::RC_SCREEN_ZOOM:
2019 case LyXRC::RC_SERVERPIPE:
2020 case LyXRC::RC_SET_COLOR:
2021 case LyXRC::RC_SHOW_BANNER:
2022 case LyXRC::RC_OPEN_BUFFERS_IN_TABS:
2023 case LyXRC::RC_SPELL_COMMAND:
2024 case LyXRC::RC_SPELLCHECKER:
2025 case LyXRC::RC_SPELLCHECK_CONTINUOUSLY:
2026 case LyXRC::RC_SPLITINDEX_COMMAND:
2027 case LyXRC::RC_TEMPDIRPATH:
2028 case LyXRC::RC_TEMPLATEPATH:
2029 case LyXRC::RC_TEX_ALLOWS_SPACES:
2030 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2031 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2032 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2034 case LyXRC::RC_THESAURUSDIRPATH:
2035 case LyXRC::RC_UIFILE:
2036 case LyXRC::RC_USER_EMAIL:
2037 case LyXRC::RC_USER_NAME:
2038 case LyXRC::RC_USETEMPDIR:
2039 case LyXRC::RC_USE_ALT_LANG:
2040 case LyXRC::RC_USE_CONVERTER_CACHE:
2041 case LyXRC::RC_USE_ESC_CHARS:
2042 case LyXRC::RC_USE_INP_ENC:
2043 case LyXRC::RC_USE_PERS_DICT:
2044 case LyXRC::RC_USE_TOOLTIP:
2045 case LyXRC::RC_USE_PIXMAP_CACHE:
2046 case LyXRC::RC_USE_SPELL_LIB:
2047 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2048 case LyXRC::RC_SORT_LAYOUTS:
2049 case LyXRC::RC_FULL_SCREEN_LIMIT:
2050 case LyXRC::RC_FULL_SCREEN_SCROLLBAR:
2051 case LyXRC::RC_FULL_SCREEN_MENUBAR:
2052 case LyXRC::RC_FULL_SCREEN_TABBAR:
2053 case LyXRC::RC_FULL_SCREEN_TOOLBARS:
2054 case LyXRC::RC_FULL_SCREEN_WIDTH:
2055 case LyXRC::RC_VISUAL_CURSOR:
2056 case LyXRC::RC_VIEWER:
2057 case LyXRC::RC_VIEWER_ALTERNATIVES:
2058 case LyXRC::RC_LAST: