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 "BranchList.h"
26 #include "buffer_funcs.h"
27 #include "BufferList.h"
28 #include "BufferParams.h"
29 #include "BufferView.h"
30 #include "bufferview_funcs.h"
32 #include "CutAndPaste.h"
34 #include "DispatchResult.h"
36 #include "ErrorList.h"
39 #include "FuncRequest.h"
40 #include "FuncStatus.h"
43 #include "InsetIterator.h"
51 #include "LyXAction.h"
57 #include "TextClassList.h"
59 #include "Paragraph.h"
60 #include "ParIterator.h"
61 #include "ParagraphParameters.h"
64 #include "insets/InsetBox.h"
65 #include "insets/InsetBranch.h"
66 #include "insets/InsetCommand.h"
67 #include "insets/InsetERT.h"
68 #include "insets/InsetExternal.h"
69 #include "insets/InsetFloat.h"
70 #include "insets/InsetGraphics.h"
71 #include "insets/InsetInclude.h"
72 #include "insets/InsetNote.h"
73 #include "insets/InsetTabular.h"
74 #include "insets/InsetVSpace.h"
75 #include "insets/InsetWrap.h"
77 #include "frontends/Application.h"
78 #include "frontends/alert.h"
79 #include "frontends/Dialogs.h"
80 #include "frontends/FileDialog.h"
81 #include "frontends/FontLoader.h"
82 #include "frontends/Gui.h"
83 #include "frontends/KeySymbol.h"
84 #include "frontends/LyXView.h"
85 #include "frontends/Menubar.h"
86 #include "frontends/Toolbars.h"
88 #include "support/environment.h"
89 #include "support/FileFilterList.h"
90 #include "support/filetools.h"
91 #include "support/ForkedcallsController.h"
92 #include "support/fs_extras.h"
93 #include "support/lstrings.h"
94 #include "support/Path.h"
95 #include "support/Package.h"
96 #include "support/Systemcall.h"
97 #include "support/convert.h"
98 #include "support/os.h"
100 #include <boost/current_function.hpp>
101 #include <boost/filesystem/operations.hpp>
108 using bv_funcs::freefont2string;
110 using support::absolutePath;
111 using support::addName;
112 using support::addPath;
113 using support::bformat;
114 using support::changeExtension;
115 using support::contains;
116 using support::FileFilterList;
117 using support::FileName;
118 using support::fileSearch;
119 using support::ForkedcallsController;
120 using support::i18nLibFileSearch;
121 using support::isDirWriteable;
122 using support::isFileReadable;
123 using support::isStrInt;
124 using support::makeAbsPath;
125 using support::makeDisplayPath;
126 using support::package;
127 using support::quoteName;
128 using support::rtrim;
129 using support::split;
130 using support::subst;
131 using support::Systemcall;
132 using support::token;
134 using support::prefixIs;
137 using std::make_pair;
140 using std::istringstream;
141 using std::ostringstream;
143 namespace Alert = frontend::Alert;
144 namespace fs = boost::filesystem;
149 bool getLocalStatus(Cursor cursor,
150 FuncRequest const & cmd, FuncStatus & status)
152 // Try to fix cursor in case it is broken.
153 cursor.fixIfBroken();
155 // This is, of course, a mess. Better create a new doc iterator and use
156 // this in Inset::getStatus. This might require an additional
157 // BufferView * arg, though (which should be avoided)
158 //Cursor safe = *this;
160 for ( ; cursor.depth(); cursor.pop()) {
161 //lyxerr << "\nCursor::getStatus: cmd: " << cmd << endl << *this << endl;
162 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
163 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
164 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
166 // The inset's getStatus() will return 'true' if it made
167 // a definitive decision on whether it want to handle the
168 // request or not. The result of this decision is put into
169 // the 'status' parameter.
170 if (cursor.inset().getStatus(cursor, cmd, status)) {
179 /** Return the change status at cursor position, taking in account the
180 * status at each level of the document iterator (a table in a deleted
181 * footnote is deleted).
182 * When \param outer is true, the top slice is not looked at.
184 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
186 size_t const depth = dit.depth() - (outer ? 1 : 0);
188 for (size_t i = 0 ; i < depth ; ++i) {
189 CursorSlice const & slice = dit[i];
190 if (!slice.inset().inMathed()
191 && slice.pos() < slice.paragraph().size()) {
192 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
193 if (ch != Change::UNCHANGED)
197 return Change::UNCHANGED;
205 meta_fake_bit(key_modifier::none)
210 void LyXFunc::initKeySequences(KeyMap * kb)
212 keyseq.reset(new KeySequence(kb, kb));
213 cancel_meta_seq.reset(new KeySequence(kb, kb));
217 void LyXFunc::setLyXView(LyXView * lv)
223 void LyXFunc::handleKeyFunc(kb_action action)
225 char_type c = encoded_last_key;
227 if (keyseq->length())
230 lyx_view_->view()->getIntl().getTransManager().deadkey(
231 c, get_accent(action).accent, view()->cursor().innerText(), view()->cursor());
232 // Need to clear, in case the minibuffer calls these
235 // copied verbatim from do_accent_char
236 view()->cursor().resetAnchor();
241 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
243 BOOST_ASSERT(lyx_view_);
244 if (!LyX::ref().session().bookmarks().isValid(idx))
246 BookmarksSection::Bookmark const & bm = LyX::ref().session().bookmarks().bookmark(idx);
247 BOOST_ASSERT(!bm.filename.empty());
248 string const file = bm.filename.absFilename();
249 // if the file is not opened, open it.
250 if (!theBufferList().exists(file)) {
252 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
256 // open may fail, so we need to test it again
257 if (theBufferList().exists(file)) {
258 // if the current buffer is not that one, switch to it.
259 if (lyx_view_->buffer()->fileName() != file) {
261 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
265 // moveToPosition use par_id, and par_pit and return new par_id.
269 boost::tie(new_pit, new_pos, new_id) = view()->moveToPosition(bm.bottom_pit, bm.bottom_pos, bm.top_id, bm.top_pos);
270 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
271 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
272 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos || bm.top_id != new_id )
273 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(new_pit, new_pos, new_id);
278 void LyXFunc::processKeySym(KeySymbolPtr keysym, key_modifier::state state)
280 LYXERR(Debug::KEY) << "KeySym is " << keysym->getSymbolName() << endl;
282 // Do nothing if we have nothing (JMarc)
283 if (!keysym->isOK()) {
284 LYXERR(Debug::KEY) << "Empty kbd action (probably composing)"
289 if (keysym->isModifier()) {
290 LYXERR(Debug::KEY) << "isModifier true" << endl;
294 //Encoding const * encoding = view()->cursor().getEncoding();
295 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
296 // FIXME: encoded_last_key shadows the member variable of the same
297 // name. Is that intended?
298 char_type encoded_last_key = keysym->getUCSEncoded();
300 // Do a one-deep top-level lookup for
301 // cancel and meta-fake keys. RVDK_PATCH_5
302 cancel_meta_seq->reset();
304 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
305 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
306 << " action first set to [" << func.action << ']'
309 // When not cancel or meta-fake, do the normal lookup.
310 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
311 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
312 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
313 // remove Caps Lock and Mod2 as a modifiers
314 func = keyseq->addkey(keysym, (state | meta_fake_bit));
315 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
316 << "action now set to ["
317 << func.action << ']' << endl;
320 // Dont remove this unless you know what you are doing.
321 meta_fake_bit = key_modifier::none;
323 // Can this happen now ?
324 if (func.action == LFUN_NOACTION) {
325 func = FuncRequest(LFUN_COMMAND_PREFIX);
328 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
330 << func.action << "]["
331 << to_utf8(keyseq->print(false)) << ']'
334 // already here we know if it any point in going further
335 // why not return already here if action == -1 and
336 // num_bytes == 0? (Lgb)
338 if (keyseq->length() > 1) {
339 lyx_view_->message(keyseq->print(true));
343 // Maybe user can only reach the key via holding down shift.
344 // Let's see. But only if shift is the only modifier
345 if (func.action == LFUN_UNKNOWN_ACTION &&
346 state == key_modifier::shift) {
347 LYXERR(Debug::KEY) << "Trying without shift" << endl;
348 func = keyseq->addkey(keysym, key_modifier::none);
349 LYXERR(Debug::KEY) << "Action now " << func.action << endl;
352 if (func.action == LFUN_UNKNOWN_ACTION) {
353 // Hmm, we didn't match any of the keysequences. See
354 // if it's normal insertable text not already covered
356 if (keysym->isText() && keyseq->length() == 1) {
357 LYXERR(Debug::KEY) << "isText() is true, inserting." << endl;
358 func = FuncRequest(LFUN_SELF_INSERT,
359 FuncRequest::KEYBOARD);
361 LYXERR(Debug::KEY) << "Unknown, !isText() - giving up" << endl;
362 lyx_view_->message(_("Unknown function."));
367 if (func.action == LFUN_SELF_INSERT) {
368 if (encoded_last_key != 0) {
369 docstring const arg(1, encoded_last_key);
370 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
371 FuncRequest::KEYBOARD));
373 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
381 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
383 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
386 Cursor & cur = view()->cursor();
388 /* In LyX/Mac, when a dialog is open, the menus of the
389 application can still be accessed without giving focus to
390 the main window. In this case, we want to disable the menu
391 entries that are buffer-related.
393 Note that this code is not perfect, as bug 1941 attests:
394 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
396 Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
397 if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
400 if (cmd.action == LFUN_NOACTION) {
401 flag.message(from_utf8(N_("Nothing to do")));
406 switch (cmd.action) {
407 case LFUN_UNKNOWN_ACTION:
408 #ifndef HAVE_LIBAIKSAURUS
409 case LFUN_THESAURUS_ENTRY:
419 if (flag.unknown()) {
420 flag.message(from_utf8(N_("Unknown action")));
424 if (!flag.enabled()) {
425 if (flag.message().empty())
426 flag.message(from_utf8(N_("Command disabled")));
430 // Check whether we need a buffer
431 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
433 flag.message(from_utf8(N_("Command not allowed with"
434 "out any document open")));
439 // I would really like to avoid having this switch and rather try to
440 // encode this in the function itself.
441 // -- And I'd rather let an inset decide which LFUNs it is willing
442 // to handle (Andre')
444 switch (cmd.action) {
445 case LFUN_BUFFER_TOGGLE_READ_ONLY:
446 flag.setOnOff(buf->isReadonly());
449 case LFUN_BUFFER_SWITCH:
450 // toggle on the current buffer, but do not toggle off
451 // the other ones (is that a good idea?)
452 if (to_utf8(cmd.argument()) == buf->fileName())
456 case LFUN_BUFFER_EXPORT:
457 enable = cmd.argument() == "custom"
458 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
461 case LFUN_BUFFER_CHKTEX:
462 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
465 case LFUN_BUILD_PROGRAM:
466 enable = Exporter::isExportable(*buf, "program");
469 case LFUN_LAYOUT_TABULAR:
470 enable = cur.innerInsetOfType(Inset::TABULAR_CODE);
474 case LFUN_LAYOUT_PARAGRAPH:
475 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
478 case LFUN_VC_REGISTER:
479 enable = !buf->lyxvc().inUse();
481 case LFUN_VC_CHECK_IN:
482 enable = buf->lyxvc().inUse() && !buf->isReadonly();
484 case LFUN_VC_CHECK_OUT:
485 enable = buf->lyxvc().inUse() && buf->isReadonly();
488 case LFUN_VC_UNDO_LAST:
489 enable = buf->lyxvc().inUse();
491 case LFUN_BUFFER_RELOAD:
492 enable = !buf->isUnnamed() && !buf->isClean();
495 case LFUN_INSET_SETTINGS: {
499 Inset::Code code = cur.inset().lyxCode();
501 case Inset::TABULAR_CODE:
502 enable = cmd.argument() == "tabular";
504 case Inset::ERT_CODE:
505 enable = cmd.argument() == "ert";
507 case Inset::FLOAT_CODE:
508 enable = cmd.argument() == "float";
510 case Inset::WRAP_CODE:
511 enable = cmd.argument() == "wrap";
513 case Inset::NOTE_CODE:
514 enable = cmd.argument() == "note";
516 case Inset::BRANCH_CODE:
517 enable = cmd.argument() == "branch";
519 case Inset::BOX_CODE:
520 enable = cmd.argument() == "box";
528 case LFUN_INSET_APPLY: {
529 string const name = cmd.getArg(0);
530 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
532 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
534 if (!inset->getStatus(cur, fr, fs)) {
535 // Every inset is supposed to handle this
540 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
541 flag |= getStatus(fr);
543 enable = flag.enabled();
547 case LFUN_DIALOG_TOGGLE:
548 flag.setOnOff(lyx_view_->getDialogs().visible(cmd.getArg(0)));
549 case LFUN_DIALOG_SHOW: {
550 string const name = cmd.getArg(0);
552 enable = name == "aboutlyx"
553 || name == "file" //FIXME: should be removed.
555 || name == "texinfo";
556 else if (name == "print")
557 enable = Exporter::isExportable(*buf, "dvi")
558 && lyxrc.print_command != "none";
559 else if (name == "character")
560 enable = cur.inset().lyxCode() != Inset::ERT_CODE;
561 else if (name == "latexlog")
562 enable = isFileReadable(FileName(buf->getLogName().second));
563 else if (name == "spellchecker")
564 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
565 enable = !buf->isReadonly();
569 else if (name == "vclog")
570 enable = buf->lyxvc().inUse();
574 case LFUN_DIALOG_SHOW_NEW_INSET:
575 enable = cur.inset().lyxCode() != Inset::ERT_CODE;
576 if (cur.inset().lyxCode() == Inset::CAPTION_CODE) {
578 if (cur.inset().getStatus(cur, cmd, flag))
583 case LFUN_DIALOG_UPDATE: {
584 string const name = cmd.getArg(0);
586 enable = name == "prefs";
590 case LFUN_CITATION_INSERT: {
591 FuncRequest fr(LFUN_INSET_INSERT, "citation");
592 enable = getStatus(fr).enabled();
596 case LFUN_BUFFER_WRITE: {
597 enable = view()->buffer()->isUnnamed()
598 || !view()->buffer()->isClean();
602 case LFUN_BOOKMARK_GOTO: {
603 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
604 enable = LyX::ref().session().bookmarks().isValid(num);
608 case LFUN_BOOKMARK_CLEAR:
609 enable = LyX::ref().session().bookmarks().size() > 0;
612 case LFUN_TOOLBAR_TOGGLE_STATE: {
613 ToolbarInfo::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
614 if (!(flags & ToolbarInfo::AUTO))
615 flag.setOnOff(flags & ToolbarInfo::ON);
619 // this one is difficult to get right. As a half-baked
620 // solution, we consider only the first action of the sequence
621 case LFUN_COMMAND_SEQUENCE: {
622 // argument contains ';'-terminated commands
623 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
624 FuncRequest func(lyxaction.lookupFunc(firstcmd));
625 func.origin = cmd.origin;
626 flag = getStatus(func);
629 case LFUN_BUFFER_NEW:
630 case LFUN_BUFFER_NEW_TEMPLATE:
631 case LFUN_WORD_FIND_FORWARD:
632 case LFUN_WORD_FIND_BACKWARD:
633 case LFUN_COMMAND_PREFIX:
634 case LFUN_COMMAND_EXECUTE:
636 case LFUN_META_PREFIX:
637 case LFUN_BUFFER_CLOSE:
638 case LFUN_BUFFER_WRITE_AS:
639 case LFUN_BUFFER_UPDATE:
640 case LFUN_BUFFER_VIEW:
641 case LFUN_BUFFER_IMPORT:
642 case LFUN_BUFFER_AUTO_SAVE:
643 case LFUN_RECONFIGURE:
647 case LFUN_DROP_LAYOUTS_CHOICE:
649 case LFUN_SERVER_GET_NAME:
650 case LFUN_SERVER_NOTIFY:
651 case LFUN_SERVER_GOTO_FILE_ROW:
652 case LFUN_DIALOG_HIDE:
653 case LFUN_DIALOG_DISCONNECT_INSET:
654 case LFUN_BUFFER_CHILD_OPEN:
655 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
656 case LFUN_KEYMAP_OFF:
657 case LFUN_KEYMAP_PRIMARY:
658 case LFUN_KEYMAP_SECONDARY:
659 case LFUN_KEYMAP_TOGGLE:
661 case LFUN_BUFFER_EXPORT_CUSTOM:
662 case LFUN_BUFFER_PRINT:
663 case LFUN_PREFERENCES_SAVE:
664 case LFUN_SCREEN_FONT_UPDATE:
667 case LFUN_EXTERNAL_EDIT:
668 case LFUN_GRAPHICS_EDIT:
669 case LFUN_ALL_INSETS_TOGGLE:
670 case LFUN_BUFFER_LANGUAGE:
671 case LFUN_TEXTCLASS_APPLY:
672 case LFUN_TEXTCLASS_LOAD:
673 case LFUN_BUFFER_SAVE_AS_DEFAULT:
674 case LFUN_BUFFER_PARAMS_APPLY:
675 case LFUN_LYXRC_APPLY:
676 case LFUN_BUFFER_NEXT:
677 case LFUN_BUFFER_PREVIOUS:
678 case LFUN_WINDOW_NEW:
679 case LFUN_WINDOW_CLOSE:
681 // these are handled in our dispatch()
685 if (!getLocalStatus(cur, cmd, flag))
686 flag = view()->getStatus(cmd);
692 // Can we use a readonly buffer?
693 if (buf && buf->isReadonly()
694 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
695 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
696 flag.message(from_utf8(N_("Document is read-only")));
700 // Are we in a DELETED change-tracking region?
701 if (buf && lookupChangeType(cur, true) == Change::DELETED
702 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
703 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
704 flag.message(from_utf8(N_("This portion of the document is deleted.")));
708 // the default error message if we disable the command
709 if (!flag.enabled() && flag.message().empty())
710 flag.message(from_utf8(N_("Command disabled")));
716 bool LyXFunc::ensureBufferClean(BufferView * bv)
718 Buffer & buf = *bv->buffer();
722 docstring const file = makeDisplayPath(buf.fileName(), 30);
723 docstring text = bformat(_("The document %1$s has unsaved "
724 "changes.\n\nDo you want to save "
725 "the document?"), file);
726 int const ret = Alert::prompt(_("Save changed document?"),
727 text, 0, 1, _("&Save"),
731 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
733 return buf.isClean();
739 void showPrintError(string const & name)
741 docstring str = bformat(_("Could not print the document %1$s.\n"
742 "Check that your printer is set up correctly."),
743 makeDisplayPath(name, 50));
744 Alert::error(_("Print document failed"), str);
748 void loadTextclass(string const & name)
750 std::pair<bool, textclass_type> const tc_pair =
751 textclasslist.numberOfClass(name);
753 if (!tc_pair.first) {
754 lyxerr << "Document class \"" << name
755 << "\" does not exist."
760 textclass_type const tc = tc_pair.second;
762 if (!textclasslist[tc].load()) {
763 docstring s = bformat(_("The document could not be converted\n"
764 "into the document class %1$s."),
765 from_utf8(textclasslist[tc].name()));
766 Alert::error(_("Could not change class"), s);
771 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
776 void LyXFunc::dispatch(FuncRequest const & cmd)
778 string const argument = to_utf8(cmd.argument());
779 kb_action const action = cmd.action;
781 LYXERR(Debug::ACTION) << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
782 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
784 // we have not done anything wrong yet.
786 dispatch_buffer.erase();
788 // redraw the screen at the end (first of the two drawing steps).
789 //This is done unless explicitely requested otherwise
790 Update::flags updateFlags = Update::FitCursor;
792 FuncStatus const flag = getStatus(cmd);
793 if (!flag.enabled()) {
794 // We cannot use this function here
795 LYXERR(Debug::ACTION) << "LyXFunc::dispatch: "
796 << lyxaction.getActionName(action)
797 << " [" << action << "] is disabled at this location"
799 setErrorMessage(flag.message());
803 case LFUN_WORD_FIND_FORWARD:
804 case LFUN_WORD_FIND_BACKWARD: {
805 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
806 static docstring last_search;
807 docstring searched_string;
809 if (!cmd.argument().empty()) {
810 last_search = cmd.argument();
811 searched_string = cmd.argument();
813 searched_string = last_search;
816 if (searched_string.empty())
819 bool const fw = action == LFUN_WORD_FIND_FORWARD;
820 docstring const data =
821 find2string(searched_string, true, false, fw);
822 find(view(), FuncRequest(LFUN_WORD_FIND, data));
826 case LFUN_COMMAND_PREFIX:
827 BOOST_ASSERT(lyx_view_);
828 lyx_view_->message(keyseq->printOptions(true));
831 case LFUN_COMMAND_EXECUTE:
832 BOOST_ASSERT(lyx_view_);
833 lyx_view_->getToolbars().display("minibuffer", true);
834 lyx_view_->focus_command_buffer();
838 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
840 meta_fake_bit = key_modifier::none;
841 if (view()->buffer())
842 // cancel any selection
843 dispatch(FuncRequest(LFUN_MARK_OFF));
844 setMessage(from_ascii(N_("Cancel")));
847 case LFUN_META_PREFIX:
848 meta_fake_bit = key_modifier::alt;
849 setMessage(keyseq->print(true));
852 case LFUN_BUFFER_TOGGLE_READ_ONLY:
853 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
854 if (lyx_view_->buffer()->lyxvc().inUse())
855 lyx_view_->buffer()->lyxvc().toggleReadOnly();
857 lyx_view_->buffer()->setReadonly(
858 !lyx_view_->buffer()->isReadonly());
861 // --- Menus -----------------------------------------------
862 case LFUN_BUFFER_NEW:
863 menuNew(argument, false);
866 case LFUN_BUFFER_NEW_TEMPLATE:
867 menuNew(argument, true);
870 case LFUN_BUFFER_CLOSE:
875 case LFUN_BUFFER_WRITE:
876 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
877 if (!lyx_view_->buffer()->isUnnamed()) {
878 docstring const str = bformat(_("Saving document %1$s..."),
879 makeDisplayPath(lyx_view_->buffer()->fileName()));
880 lyx_view_->message(str);
881 menuWrite(lyx_view_->buffer());
882 lyx_view_->message(str + _(" done."));
884 writeAs(lyx_view_->buffer());
886 updateFlags = Update::None;
889 case LFUN_BUFFER_WRITE_AS:
890 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
891 writeAs(lyx_view_->buffer(), argument);
892 updateFlags = Update::None;
895 case LFUN_BUFFER_RELOAD: {
896 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
897 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
898 docstring text = bformat(_("Any changes will be lost. Are you sure "
899 "you want to revert to the saved version of the document %1$s?"), file);
900 int const ret = Alert::prompt(_("Revert to saved document?"),
901 text, 0, 1, _("&Revert"), _("&Cancel"));
908 case LFUN_BUFFER_UPDATE:
909 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
910 Exporter::Export(lyx_view_->buffer(), argument, true);
913 case LFUN_BUFFER_VIEW:
914 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
915 Exporter::preview(lyx_view_->buffer(), argument);
918 case LFUN_BUILD_PROGRAM:
919 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
920 Exporter::Export(lyx_view_->buffer(), "program", true);
923 case LFUN_BUFFER_CHKTEX:
924 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
925 lyx_view_->buffer()->runChktex();
928 case LFUN_BUFFER_EXPORT:
929 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
930 if (argument == "custom")
931 lyx_view_->getDialogs().show("sendto");
933 Exporter::Export(lyx_view_->buffer(), argument, false);
937 case LFUN_BUFFER_EXPORT_CUSTOM: {
938 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
940 string command = split(argument, format_name, ' ');
941 Format const * format = formats.getFormat(format_name);
943 lyxerr << "Format \"" << format_name
944 << "\" not recognized!"
949 Buffer * buffer = lyx_view_->buffer();
951 // The name of the file created by the conversion process
954 // Output to filename
955 if (format->name() == "lyx") {
956 string const latexname =
957 buffer->getLatexName(false);
958 filename = changeExtension(latexname,
959 format->extension());
960 filename = addName(buffer->temppath(), filename);
962 if (!buffer->writeFile(FileName(filename)))
966 Exporter::Export(buffer, format_name, true, filename);
969 // Substitute $$FName for filename
970 if (!contains(command, "$$FName"))
971 command = "( " + command + " ) < $$FName";
972 command = subst(command, "$$FName", filename);
974 // Execute the command in the background
976 call.startscript(Systemcall::DontWait, command);
980 case LFUN_BUFFER_PRINT: {
981 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
984 string command = split(split(argument, target, ' '),
988 || target_name.empty()
989 || command.empty()) {
990 lyxerr << "Unable to parse \""
991 << argument << '"' << std::endl;
994 if (target != "printer" && target != "file") {
995 lyxerr << "Unrecognized target \""
996 << target << '"' << std::endl;
1000 Buffer * buffer = lyx_view_->buffer();
1002 if (!Exporter::Export(buffer, "dvi", true)) {
1003 showPrintError(buffer->fileName());
1007 // Push directory path.
1008 string const path(buffer->temppath());
1009 // Prevent the compiler from optimizing away p
1011 support::Path p(pp);
1013 // there are three cases here:
1014 // 1. we print to a file
1015 // 2. we print directly to a printer
1016 // 3. we print using a spool command (print to file first)
1019 string const dviname =
1020 changeExtension(buffer->getLatexName(true),
1023 if (target == "printer") {
1024 if (!lyxrc.print_spool_command.empty()) {
1025 // case 3: print using a spool
1026 string const psname =
1027 changeExtension(dviname,".ps");
1028 command += lyxrc.print_to_file
1031 + quoteName(dviname);
1034 lyxrc.print_spool_command +' ';
1035 if (target_name != "default") {
1036 command2 += lyxrc.print_spool_printerprefix
1040 command2 += quoteName(psname);
1042 // If successful, then spool command
1043 res = one.startscript(
1048 res = one.startscript(
1049 Systemcall::DontWait,
1052 // case 2: print directly to a printer
1053 res = one.startscript(
1054 Systemcall::DontWait,
1055 command + quoteName(dviname));
1059 // case 1: print to a file
1060 FileName const filename(makeAbsPath(target_name, path));
1061 if (fs::exists(filename.toFilesystemEncoding())) {
1062 docstring text = bformat(
1063 _("The file %1$s already exists.\n\n"
1064 "Do you want to over-write that file?"),
1065 makeDisplayPath(filename.absFilename()));
1066 if (Alert::prompt(_("Over-write file?"),
1067 text, 0, 1, _("&Over-write"), _("&Cancel")) != 0)
1070 command += lyxrc.print_to_file
1071 + quoteName(filename.toFilesystemEncoding())
1073 + quoteName(dviname);
1074 res = one.startscript(Systemcall::DontWait,
1079 showPrintError(buffer->fileName());
1083 case LFUN_BUFFER_IMPORT:
1088 // quitting is triggered by the gui code
1089 // (leaving the event loop).
1090 lyx_view_->message(from_utf8(N_("Exiting.")));
1091 if (theBufferList().quitWriteAll())
1092 theApp()->gui().closeAllViews();
1095 case LFUN_BUFFER_AUTO_SAVE:
1099 case LFUN_RECONFIGURE:
1100 BOOST_ASSERT(lyx_view_);
1101 reconfigure(*lyx_view_);
1104 case LFUN_HELP_OPEN: {
1105 BOOST_ASSERT(lyx_view_);
1106 string const arg = argument;
1108 setErrorMessage(from_ascii(N_("Missing argument")));
1111 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1112 if (fname.empty()) {
1113 lyxerr << "LyX: unable to find documentation file `"
1114 << arg << "'. Bad installation?" << endl;
1117 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1118 makeDisplayPath(fname.absFilename())));
1119 lyx_view_->loadLyXFile(fname, false);
1123 // --- version control -------------------------------
1124 case LFUN_VC_REGISTER:
1125 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1126 if (!ensureBufferClean(view()))
1128 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1129 lyx_view_->buffer()->lyxvc().registrer();
1132 updateFlags = Update::Force;
1135 case LFUN_VC_CHECK_IN:
1136 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1137 if (!ensureBufferClean(view()))
1139 if (lyx_view_->buffer()->lyxvc().inUse()
1140 && !lyx_view_->buffer()->isReadonly()) {
1141 lyx_view_->buffer()->lyxvc().checkIn();
1146 case LFUN_VC_CHECK_OUT:
1147 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1148 if (!ensureBufferClean(view()))
1150 if (lyx_view_->buffer()->lyxvc().inUse()
1151 && lyx_view_->buffer()->isReadonly()) {
1152 lyx_view_->buffer()->lyxvc().checkOut();
1157 case LFUN_VC_REVERT:
1158 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1159 lyx_view_->buffer()->lyxvc().revert();
1163 case LFUN_VC_UNDO_LAST:
1164 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1165 lyx_view_->buffer()->lyxvc().undoLast();
1169 // --- buffers ----------------------------------------
1170 case LFUN_BUFFER_SWITCH:
1171 BOOST_ASSERT(lyx_view_);
1172 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1175 case LFUN_BUFFER_NEXT:
1176 BOOST_ASSERT(lyx_view_);
1177 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1180 case LFUN_BUFFER_PREVIOUS:
1181 BOOST_ASSERT(lyx_view_);
1182 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1186 BOOST_ASSERT(lyx_view_);
1187 newFile(view(), argument);
1190 case LFUN_FILE_OPEN:
1191 BOOST_ASSERT(lyx_view_);
1195 case LFUN_DROP_LAYOUTS_CHOICE:
1196 BOOST_ASSERT(lyx_view_);
1197 lyx_view_->getToolbars().openLayoutList();
1200 case LFUN_MENU_OPEN:
1201 BOOST_ASSERT(lyx_view_);
1202 lyx_view_->getMenubar().openByName(from_utf8(argument));
1205 // --- lyxserver commands ----------------------------
1206 case LFUN_SERVER_GET_NAME:
1207 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1208 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1209 LYXERR(Debug::INFO) << "FNAME["
1210 << lyx_view_->buffer()->fileName()
1214 case LFUN_SERVER_NOTIFY:
1215 dispatch_buffer = keyseq->print(false);
1216 theServer().notifyClient(to_utf8(dispatch_buffer));
1219 case LFUN_SERVER_GOTO_FILE_ROW: {
1220 BOOST_ASSERT(lyx_view_);
1223 istringstream is(argument);
1224 is >> file_name >> row;
1225 if (prefixIs(file_name, package().temp_dir().absFilename())) {
1226 // Needed by inverse dvi search. If it is a file
1227 // in tmpdir, call the apropriated function
1228 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1230 // Must replace extension of the file to be .lyx
1231 // and get full path
1232 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1233 // Either change buffer or load the file
1234 if (theBufferList().exists(s.absFilename())) {
1235 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1237 lyx_view_->loadLyXFile(s);
1241 view()->setCursorFromRow(row);
1243 updateFlags = Update::FitCursor;
1247 case LFUN_DIALOG_SHOW: {
1248 BOOST_ASSERT(lyx_view_);
1249 string const name = cmd.getArg(0);
1250 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1252 if (name == "character") {
1253 data = freefont2string();
1255 lyx_view_->getDialogs().show("character", data);
1256 } else if (name == "latexlog") {
1257 pair<Buffer::LogType, string> const logfile =
1258 lyx_view_->buffer()->getLogName();
1259 switch (logfile.first) {
1260 case Buffer::latexlog:
1263 case Buffer::buildlog:
1267 data += Lexer::quoteString(logfile.second);
1268 lyx_view_->getDialogs().show("log", data);
1269 } else if (name == "vclog") {
1270 string const data = "vc " +
1271 Lexer::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1272 lyx_view_->getDialogs().show("log", data);
1274 lyx_view_->getDialogs().show(name, data);
1278 case LFUN_DIALOG_SHOW_NEW_INSET: {
1279 BOOST_ASSERT(lyx_view_);
1280 string const name = cmd.getArg(0);
1281 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1282 if (name == "bibitem" ||
1286 name == "nomenclature" ||
1290 InsetCommandParams p(name);
1291 data = InsetCommandMailer::params2string(name, p);
1292 } else if (name == "include") {
1293 // data is the include type: one of "include",
1294 // "input", "verbatiminput" or "verbatiminput*"
1296 // default type is requested
1298 InsetCommandParams p(data);
1299 data = InsetIncludeMailer::params2string(p);
1300 } else if (name == "box") {
1301 // \c data == "Boxed" || "Frameless" etc
1302 InsetBoxParams p(data);
1303 data = InsetBoxMailer::params2string(p);
1304 } else if (name == "branch") {
1305 InsetBranchParams p;
1306 data = InsetBranchMailer::params2string(p);
1307 } else if (name == "citation") {
1308 InsetCommandParams p("cite");
1309 data = InsetCommandMailer::params2string(name, p);
1310 } else if (name == "ert") {
1311 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1312 } else if (name == "external") {
1313 InsetExternalParams p;
1314 Buffer const & buffer = *lyx_view_->buffer();
1315 data = InsetExternalMailer::params2string(p, buffer);
1316 } else if (name == "float") {
1318 data = InsetFloatMailer::params2string(p);
1319 } else if (name == "graphics") {
1320 InsetGraphicsParams p;
1321 Buffer const & buffer = *lyx_view_->buffer();
1322 data = InsetGraphicsMailer::params2string(p, buffer);
1323 } else if (name == "note") {
1325 data = InsetNoteMailer::params2string(p);
1326 } else if (name == "vspace") {
1328 data = InsetVSpaceMailer::params2string(space);
1329 } else if (name == "wrap") {
1331 data = InsetWrapMailer::params2string(p);
1333 lyx_view_->getDialogs().show(name, data, 0);
1337 case LFUN_DIALOG_UPDATE: {
1338 BOOST_ASSERT(lyx_view_);
1339 string const & name = argument;
1340 // Can only update a dialog connected to an existing inset
1341 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1343 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1344 inset->dispatch(view()->cursor(), fr);
1345 } else if (name == "paragraph") {
1346 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1347 } else if (name == "prefs") {
1348 lyx_view_->getDialogs().update(name, string());
1353 case LFUN_DIALOG_HIDE:
1354 Dialogs::hide(argument, 0);
1357 case LFUN_DIALOG_TOGGLE: {
1358 BOOST_ASSERT(lyx_view_);
1359 if (lyx_view_->getDialogs().visible(cmd.getArg(0)))
1360 dispatch(FuncRequest(LFUN_DIALOG_HIDE, argument));
1362 dispatch(FuncRequest(LFUN_DIALOG_SHOW, argument));
1366 case LFUN_DIALOG_DISCONNECT_INSET:
1367 BOOST_ASSERT(lyx_view_);
1368 lyx_view_->getDialogs().disconnect(argument);
1372 case LFUN_CITATION_INSERT: {
1373 BOOST_ASSERT(lyx_view_);
1374 if (!argument.empty()) {
1375 // we can have one optional argument, delimited by '|'
1376 // citation-insert <key>|<text_before>
1377 // this should be enhanced to also support text_after
1378 // and citation style
1379 string arg = argument;
1381 if (contains(argument, "|")) {
1382 arg = token(argument, '|', 0);
1383 opt1 = token(argument, '|', 1);
1385 InsetCommandParams icp("cite");
1386 icp["key"] = from_utf8(arg);
1388 icp["before"] = from_utf8(opt1);
1389 string icstr = InsetCommandMailer::params2string("citation", icp);
1390 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1393 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1397 case LFUN_BUFFER_CHILD_OPEN: {
1398 BOOST_ASSERT(lyx_view_);
1399 FileName const filename =
1400 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1401 setMessage(bformat(_("Opening child document %1$s..."),
1402 makeDisplayPath(filename.absFilename())));
1403 view()->saveBookmark(false);
1404 string const parentfilename = lyx_view_->buffer()->fileName();
1405 if (theBufferList().exists(filename.absFilename()))
1406 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1408 lyx_view_->loadLyXFile(filename);
1409 // Set the parent name of the child document.
1410 // This makes insertion of citations and references in the child work,
1411 // when the target is in the parent or another child document.
1412 lyx_view_->buffer()->setParentName(parentfilename);
1416 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1417 BOOST_ASSERT(lyx_view_);
1418 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1421 case LFUN_KEYMAP_OFF:
1422 BOOST_ASSERT(lyx_view_);
1423 lyx_view_->view()->getIntl().keyMapOn(false);
1426 case LFUN_KEYMAP_PRIMARY:
1427 BOOST_ASSERT(lyx_view_);
1428 lyx_view_->view()->getIntl().keyMapPrim();
1431 case LFUN_KEYMAP_SECONDARY:
1432 BOOST_ASSERT(lyx_view_);
1433 lyx_view_->view()->getIntl().keyMapSec();
1436 case LFUN_KEYMAP_TOGGLE:
1437 BOOST_ASSERT(lyx_view_);
1438 lyx_view_->view()->getIntl().toggleKeyMap();
1444 string rest = split(argument, countstr, ' ');
1445 istringstream is(countstr);
1448 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1449 for (int i = 0; i < count; ++i)
1450 dispatch(lyxaction.lookupFunc(rest));
1454 case LFUN_COMMAND_SEQUENCE: {
1455 // argument contains ';'-terminated commands
1456 string arg = argument;
1457 while (!arg.empty()) {
1459 arg = split(arg, first, ';');
1460 FuncRequest func(lyxaction.lookupFunc(first));
1461 func.origin = cmd.origin;
1467 case LFUN_PREFERENCES_SAVE: {
1468 lyxrc.write(makeAbsPath("preferences",
1469 package().user_support().absFilename()),
1474 case LFUN_SCREEN_FONT_UPDATE:
1475 BOOST_ASSERT(lyx_view_);
1476 // handle the screen font changes.
1477 theFontLoader().update();
1478 /// FIXME: only the current view will be updated. the Gui
1479 /// class is able to furnish the list of views.
1480 updateFlags = Update::Force;
1483 case LFUN_SET_COLOR: {
1485 string const x11_name = split(argument, lyx_name, ' ');
1486 if (lyx_name.empty() || x11_name.empty()) {
1487 setErrorMessage(from_ascii(N_(
1488 "Syntax: set-color <lyx_name>"
1493 bool const graphicsbg_changed =
1494 (lyx_name == lcolor.getLyXName(Color::graphicsbg) &&
1495 x11_name != lcolor.getX11Name(Color::graphicsbg));
1497 if (!lcolor.setColor(lyx_name, x11_name)) {
1499 bformat(_("Set-color \"%1$s\" failed "
1500 "- color is undefined or "
1501 "may not be redefined"),
1502 from_utf8(lyx_name)));
1506 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1508 if (graphicsbg_changed) {
1509 #ifdef WITH_WARNINGS
1510 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1513 graphics::GCache::get().changeDisplay(true);
1520 BOOST_ASSERT(lyx_view_);
1521 lyx_view_->message(from_utf8(argument));
1524 case LFUN_EXTERNAL_EDIT: {
1525 BOOST_ASSERT(lyx_view_);
1526 FuncRequest fr(action, argument);
1527 InsetExternal().dispatch(view()->cursor(), fr);
1531 case LFUN_GRAPHICS_EDIT: {
1532 FuncRequest fr(action, argument);
1533 InsetGraphics().dispatch(view()->cursor(), fr);
1537 case LFUN_INSET_APPLY: {
1538 BOOST_ASSERT(lyx_view_);
1539 string const name = cmd.getArg(0);
1540 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1542 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1543 inset->dispatch(view()->cursor(), fr);
1545 FuncRequest fr(LFUN_INSET_INSERT, argument);
1548 // ideally, the update flag should be set by the insets,
1549 // but this is not possible currently
1550 updateFlags = Update::Force | Update::FitCursor;
1554 case LFUN_ALL_INSETS_TOGGLE: {
1555 BOOST_ASSERT(lyx_view_);
1557 string const name = split(argument, action, ' ');
1558 Inset::Code const inset_code =
1559 Inset::translate(name);
1561 Cursor & cur = view()->cursor();
1562 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1564 Inset & inset = lyx_view_->buffer()->inset();
1565 InsetIterator it = inset_iterator_begin(inset);
1566 InsetIterator const end = inset_iterator_end(inset);
1567 for (; it != end; ++it) {
1568 if (!it->asInsetMath()
1569 && (inset_code == Inset::NO_CODE
1570 || inset_code == it->lyxCode())) {
1571 Cursor tmpcur = cur;
1572 tmpcur.pushLeft(*it);
1573 it->dispatch(tmpcur, fr);
1576 updateFlags = Update::Force | Update::FitCursor;
1580 case LFUN_BUFFER_LANGUAGE: {
1581 BOOST_ASSERT(lyx_view_);
1582 Buffer & buffer = *lyx_view_->buffer();
1583 Language const * oldL = buffer.params().language;
1584 Language const * newL = languages.getLanguage(argument);
1585 if (!newL || oldL == newL)
1588 if (oldL->rightToLeft() == newL->rightToLeft()
1589 && !buffer.isMultiLingual())
1590 buffer.changeLanguage(oldL, newL);
1594 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1595 string const fname =
1596 addName(addPath(package().user_support().absFilename(), "templates/"),
1598 Buffer defaults(fname);
1600 istringstream ss(argument);
1603 int const unknown_tokens = defaults.readHeader(lex);
1605 if (unknown_tokens != 0) {
1606 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1607 << unknown_tokens << " unknown token"
1608 << (unknown_tokens == 1 ? "" : "s")
1612 if (defaults.writeFile(FileName(defaults.fileName())))
1613 setMessage(bformat(_("Document defaults saved in %1$s"),
1614 makeDisplayPath(fname)));
1616 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1620 case LFUN_BUFFER_PARAMS_APPLY: {
1621 BOOST_ASSERT(lyx_view_);
1622 biblio::CiteEngine const engine =
1623 lyx_view_->buffer()->params().getEngine();
1625 istringstream ss(argument);
1628 int const unknown_tokens =
1629 lyx_view_->buffer()->readHeader(lex);
1631 if (unknown_tokens != 0) {
1632 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1633 << unknown_tokens << " unknown token"
1634 << (unknown_tokens == 1 ? "" : "s")
1637 if (engine == lyx_view_->buffer()->params().getEngine())
1640 Cursor & cur = view()->cursor();
1641 FuncRequest fr(LFUN_INSET_REFRESH);
1643 Inset & inset = lyx_view_->buffer()->inset();
1644 InsetIterator it = inset_iterator_begin(inset);
1645 InsetIterator const end = inset_iterator_end(inset);
1646 for (; it != end; ++it)
1647 if (it->lyxCode() == Inset::CITE_CODE)
1648 it->dispatch(cur, fr);
1652 case LFUN_TEXTCLASS_APPLY: {
1653 BOOST_ASSERT(lyx_view_);
1654 Buffer * buffer = lyx_view_->buffer();
1656 textclass_type const old_class =
1657 buffer->params().textclass;
1659 loadTextclass(argument);
1661 std::pair<bool, textclass_type> const tc_pair =
1662 textclasslist.numberOfClass(argument);
1667 textclass_type const new_class = tc_pair.second;
1668 if (old_class == new_class)
1672 lyx_view_->message(_("Converting document to new document class..."));
1673 recordUndoFullDocument(view());
1674 buffer->params().textclass = new_class;
1675 StableDocIterator backcur(view()->cursor());
1676 ErrorList & el = buffer->errorList("Class Switch");
1677 cap::switchBetweenClasses(
1678 old_class, new_class,
1679 static_cast<InsetText &>(buffer->inset()), el);
1681 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1683 buffer->errors("Class Switch");
1684 updateLabels(*buffer);
1685 updateFlags = Update::Force | Update::FitCursor;
1689 case LFUN_TEXTCLASS_LOAD:
1690 loadTextclass(argument);
1693 case LFUN_LYXRC_APPLY: {
1694 LyXRC const lyxrc_orig = lyxrc;
1696 istringstream ss(argument);
1697 bool const success = lyxrc.read(ss) == 0;
1700 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1701 << "Unable to read lyxrc data"
1706 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1708 /// We force the redraw in any case because there might be
1709 /// some screen font changes.
1710 /// FIXME: only the current view will be updated. the Gui
1711 /// class is able to furnish the list of views.
1712 updateFlags = Update::Force;
1716 case LFUN_WINDOW_NEW:
1717 LyX::ref().newLyXView();
1720 case LFUN_WINDOW_CLOSE:
1721 BOOST_ASSERT(lyx_view_);
1722 BOOST_ASSERT(theApp());
1723 // update bookmark pit of the current buffer before window close
1724 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1725 gotoBookmark(i+1, false, false);
1726 // ask the user for saving changes or cancel quit
1727 if (!theBufferList().quitWriteAll())
1732 case LFUN_BOOKMARK_GOTO:
1733 // go to bookmark, open unopened file and switch to buffer if necessary
1734 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1737 case LFUN_BOOKMARK_CLEAR:
1738 LyX::ref().session().bookmarks().clear();
1741 case LFUN_TOOLBAR_TOGGLE_STATE:
1742 lyx_view_->toggleToolbarState(argument);
1746 BOOST_ASSERT(lyx_view_);
1747 view()->cursor().dispatch(cmd);
1748 updateFlags = view()->cursor().result().update();
1749 if (!view()->cursor().result().dispatched())
1750 updateFlags = view()->dispatch(cmd);
1755 if (lyx_view_ && view()->buffer()) {
1756 // BufferView::update() updates the ViewMetricsInfo and
1757 // also initializes the position cache for all insets in
1758 // (at least partially) visible top-level paragraphs.
1759 // We will redraw the screen only if needed.
1760 if (view()->update(updateFlags)) {
1761 // Buffer::changed() signals that a repaint is needed.
1762 // The frontend (WorkArea) knows which area to repaint
1763 // thanks to the ViewMetricsInfo updated above.
1764 view()->buffer()->changed();
1767 lyx_view_->updateStatusBar();
1769 // if we executed a mutating lfun, mark the buffer as dirty
1771 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1772 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1773 view()->buffer()->markDirty();
1775 if (view()->cursor().inTexted()) {
1776 lyx_view_->updateLayoutChoice();
1781 lyx_view_->updateMenubar();
1782 lyx_view_->updateToolbars();
1783 // Some messages may already be translated, so we cannot use _()
1784 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1789 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1791 const bool verbose = (cmd.origin == FuncRequest::MENU
1792 || cmd.origin == FuncRequest::TOOLBAR
1793 || cmd.origin == FuncRequest::COMMANDBUFFER);
1795 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1796 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1798 lyx_view_->message(msg);
1802 docstring dispatch_msg = msg;
1803 if (!dispatch_msg.empty())
1804 dispatch_msg += ' ';
1806 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1808 bool argsadded = false;
1810 if (!cmd.argument().empty()) {
1811 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1812 comname += ' ' + cmd.argument();
1817 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1819 if (!shortcuts.empty())
1820 comname += ": " + shortcuts;
1821 else if (!argsadded && !cmd.argument().empty())
1822 comname += ' ' + cmd.argument();
1824 if (!comname.empty()) {
1825 comname = rtrim(comname);
1826 dispatch_msg += '(' + rtrim(comname) + ')';
1829 LYXERR(Debug::ACTION) << "verbose dispatch msg "
1830 << to_utf8(dispatch_msg) << endl;
1831 if (!dispatch_msg.empty())
1832 lyx_view_->message(dispatch_msg);
1836 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1838 // FIXME: initpath is not used. What to do?
1839 string initpath = lyxrc.document_path;
1840 string filename(name);
1842 if (view()->buffer()) {
1843 string const trypath = lyx_view_->buffer()->filePath();
1844 // If directory is writeable, use this as default.
1845 if (isDirWriteable(FileName(trypath)))
1849 static int newfile_number;
1851 if (filename.empty()) {
1852 filename = addName(lyxrc.document_path,
1853 "newfile" + convert<string>(++newfile_number) + ".lyx");
1854 while (theBufferList().exists(filename) ||
1855 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1857 filename = addName(lyxrc.document_path,
1858 "newfile" + convert<string>(newfile_number) +
1863 // The template stuff
1866 FileDialog fileDlg(_("Select template file"),
1867 LFUN_SELECT_FILE_SYNC,
1868 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1869 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1871 FileDialog::Result result =
1872 fileDlg.open(from_utf8(lyxrc.template_path),
1873 FileFilterList(_("LyX Documents (*.lyx)")),
1876 if (result.first == FileDialog::Later)
1878 if (result.second.empty())
1880 templname = to_utf8(result.second);
1883 Buffer * const b = newFile(filename, templname, !name.empty());
1886 lyx_view_->setBuffer(b);
1891 void LyXFunc::open(string const & fname)
1893 string initpath = lyxrc.document_path;
1895 if (view()->buffer()) {
1896 string const trypath = lyx_view_->buffer()->filePath();
1897 // If directory is writeable, use this as default.
1898 if (isDirWriteable(FileName(trypath)))
1904 if (fname.empty()) {
1905 FileDialog fileDlg(_("Select document to open"),
1907 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1908 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1910 FileDialog::Result result =
1911 fileDlg.open(from_utf8(initpath),
1912 FileFilterList(_("LyX Documents (*.lyx)")),
1915 if (result.first == FileDialog::Later)
1918 filename = to_utf8(result.second);
1920 // check selected filename
1921 if (filename.empty()) {
1922 lyx_view_->message(_("Canceled."));
1928 // get absolute path of file and add ".lyx" to the filename if
1930 FileName const fullname = fileSearch(string(), filename, "lyx");
1931 if (!fullname.empty())
1932 filename = fullname.absFilename();
1934 // if the file doesn't exist, let the user create one
1935 if (!fs::exists(fullname.toFilesystemEncoding())) {
1936 // the user specifically chose this name. Believe him.
1937 Buffer * const b = newFile(filename, string(), true);
1939 lyx_view_->setBuffer(b);
1943 docstring const disp_fn = makeDisplayPath(filename);
1944 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1947 if (lyx_view_->loadLyXFile(fullname)) {
1948 str2 = bformat(_("Document %1$s opened."), disp_fn);
1950 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1952 lyx_view_->message(str2);
1956 void LyXFunc::doImport(string const & argument)
1959 string filename = split(argument, format, ' ');
1961 LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
1962 << " file: " << filename << endl;
1964 // need user interaction
1965 if (filename.empty()) {
1966 string initpath = lyxrc.document_path;
1968 if (view()->buffer()) {
1969 string const trypath = lyx_view_->buffer()->filePath();
1970 // If directory is writeable, use this as default.
1971 if (isDirWriteable(FileName(trypath)))
1975 docstring const text = bformat(_("Select %1$s file to import"),
1976 formats.prettyName(format));
1978 FileDialog fileDlg(text,
1980 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1981 make_pair(_("Examples|#E#e"),
1982 from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1984 docstring filter = formats.prettyName(format);
1987 filter += from_utf8(formats.extension(format));
1990 FileDialog::Result result =
1991 fileDlg.open(from_utf8(initpath),
1992 FileFilterList(filter),
1995 if (result.first == FileDialog::Later)
1998 filename = to_utf8(result.second);
2000 // check selected filename
2001 if (filename.empty())
2002 lyx_view_->message(_("Canceled."));
2005 if (filename.empty())
2008 // get absolute path of file
2009 FileName const fullname(makeAbsPath(filename));
2011 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2013 // Check if the document already is open
2014 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2015 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2016 lyx_view_->message(_("Canceled."));
2021 // if the file exists already, and we didn't do
2022 // -i lyx thefile.lyx, warn
2023 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2024 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2026 docstring text = bformat(_("The document %1$s already exists.\n\n"
2027 "Do you want to over-write that document?"), file);
2028 int const ret = Alert::prompt(_("Over-write document?"),
2029 text, 0, 1, _("&Over-write"), _("&Cancel"));
2032 lyx_view_->message(_("Canceled."));
2037 ErrorList errorList;
2038 Importer::Import(lyx_view_, fullname, format, errorList);
2039 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2043 void LyXFunc::closeBuffer()
2045 // goto bookmark to update bookmark pit.
2046 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2047 gotoBookmark(i+1, false, false);
2048 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2049 if (theBufferList().empty()) {
2050 // need this otherwise SEGV may occur while
2051 // trying to set variables that don't exist
2052 // since there's no current buffer
2053 lyx_view_->getDialogs().hideBufferDependent();
2055 lyx_view_->setBuffer(theBufferList().first());
2061 void LyXFunc::reloadBuffer()
2063 FileName filename(lyx_view_->buffer()->fileName());
2065 lyx_view_->loadLyXFile(filename);
2068 // Each "lyx_view_" should have it's own message method. lyxview and
2069 // the minibuffer would use the minibuffer, but lyxserver would
2070 // send an ERROR signal to its client. Alejandro 970603
2071 // This function is bit problematic when it comes to NLS, to make the
2072 // lyx servers client be language indepenent we must not translate
2073 // strings sent to this func.
2074 void LyXFunc::setErrorMessage(docstring const & m) const
2076 dispatch_buffer = m;
2081 void LyXFunc::setMessage(docstring const & m) const
2083 dispatch_buffer = m;
2087 docstring const LyXFunc::viewStatusMessage()
2089 // When meta-fake key is pressed, show the key sequence so far + "M-".
2091 return keyseq->print(true) + "M-";
2093 // Else, when a non-complete key sequence is pressed,
2094 // show the available options.
2095 if (keyseq->length() > 0 && !keyseq->deleted())
2096 return keyseq->printOptions(true);
2098 if (!view()->buffer())
2099 return _("Welcome to LyX!");
2101 return view()->cursor().currentState();
2105 BufferView * LyXFunc::view() const
2107 BOOST_ASSERT(lyx_view_);
2108 return lyx_view_->view();
2112 bool LyXFunc::wasMetaKey() const
2114 return (meta_fake_bit != key_modifier::none);
2120 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2122 // Why the switch you might ask. It is a trick to ensure that all
2123 // the elements in the LyXRCTags enum is handled. As you can see
2124 // there are no breaks at all. So it is just a huge fall-through.
2125 // The nice thing is that we will get a warning from the compiler
2126 // if we forget an element.
2127 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2129 case LyXRC::RC_ACCEPT_COMPOUND:
2130 case LyXRC::RC_ALT_LANG:
2131 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2132 case LyXRC::RC_PLAINTEXT_LINELEN:
2133 case LyXRC::RC_AUTOREGIONDELETE:
2134 case LyXRC::RC_AUTORESET_OPTIONS:
2135 case LyXRC::RC_AUTOSAVE:
2136 case LyXRC::RC_AUTO_NUMBER:
2137 case LyXRC::RC_BACKUPDIR_PATH:
2138 case LyXRC::RC_BIBTEX_COMMAND:
2139 case LyXRC::RC_BINDFILE:
2140 case LyXRC::RC_CHECKLASTFILES:
2141 case LyXRC::RC_USELASTFILEPOS:
2142 case LyXRC::RC_LOADSESSION:
2143 case LyXRC::RC_CHKTEX_COMMAND:
2144 case LyXRC::RC_CONVERTER:
2145 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2146 case LyXRC::RC_COPIER:
2147 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2148 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2149 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2150 case LyXRC::RC_DATE_INSERT_FORMAT:
2151 case LyXRC::RC_DEFAULT_LANGUAGE:
2152 case LyXRC::RC_DEFAULT_PAPERSIZE:
2153 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2154 case LyXRC::RC_DISPLAY_GRAPHICS:
2155 case LyXRC::RC_DOCUMENTPATH:
2156 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2157 string const encoded = FileName(
2158 lyxrc_new.document_path).toFilesystemEncoding();
2159 if (fs::exists(encoded) && fs::is_directory(encoded))
2160 support::package().document_dir() = FileName(lyxrc.document_path);
2162 case LyXRC::RC_ESC_CHARS:
2163 case LyXRC::RC_FONT_ENCODING:
2164 case LyXRC::RC_FORMAT:
2165 case LyXRC::RC_INDEX_COMMAND:
2166 case LyXRC::RC_INPUT:
2167 case LyXRC::RC_KBMAP:
2168 case LyXRC::RC_KBMAP_PRIMARY:
2169 case LyXRC::RC_KBMAP_SECONDARY:
2170 case LyXRC::RC_LABEL_INIT_LENGTH:
2171 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2172 case LyXRC::RC_LANGUAGE_AUTO_END:
2173 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2174 case LyXRC::RC_LANGUAGE_COMMAND_END:
2175 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2176 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2177 case LyXRC::RC_LANGUAGE_PACKAGE:
2178 case LyXRC::RC_LANGUAGE_USE_BABEL:
2179 case LyXRC::RC_MAKE_BACKUP:
2180 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2181 case LyXRC::RC_NUMLASTFILES:
2182 case LyXRC::RC_PATH_PREFIX:
2183 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2184 support::prependEnvPath("PATH", lyxrc.path_prefix);
2186 case LyXRC::RC_PERS_DICT:
2187 case LyXRC::RC_PREVIEW:
2188 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2189 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2190 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2191 case LyXRC::RC_PRINTCOPIESFLAG:
2192 case LyXRC::RC_PRINTER:
2193 case LyXRC::RC_PRINTEVENPAGEFLAG:
2194 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2195 case LyXRC::RC_PRINTFILEEXTENSION:
2196 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2197 case LyXRC::RC_PRINTODDPAGEFLAG:
2198 case LyXRC::RC_PRINTPAGERANGEFLAG:
2199 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2200 case LyXRC::RC_PRINTPAPERFLAG:
2201 case LyXRC::RC_PRINTREVERSEFLAG:
2202 case LyXRC::RC_PRINTSPOOL_COMMAND:
2203 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2204 case LyXRC::RC_PRINTTOFILE:
2205 case LyXRC::RC_PRINTTOPRINTER:
2206 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2207 case LyXRC::RC_PRINT_COMMAND:
2208 case LyXRC::RC_RTL_SUPPORT:
2209 case LyXRC::RC_SCREEN_DPI:
2210 case LyXRC::RC_SCREEN_FONT_ROMAN:
2211 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2212 case LyXRC::RC_SCREEN_FONT_SANS:
2213 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2214 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2215 case LyXRC::RC_SCREEN_FONT_SIZES:
2216 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2217 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2218 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2219 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2220 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2221 case LyXRC::RC_SCREEN_ZOOM:
2222 case LyXRC::RC_SERVERPIPE:
2223 case LyXRC::RC_SET_COLOR:
2224 case LyXRC::RC_SHOW_BANNER:
2225 case LyXRC::RC_SPELL_COMMAND:
2226 case LyXRC::RC_TEMPDIRPATH:
2227 case LyXRC::RC_TEMPLATEPATH:
2228 case LyXRC::RC_TEX_ALLOWS_SPACES:
2229 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2230 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2231 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2233 case LyXRC::RC_UIFILE:
2234 case LyXRC::RC_USER_EMAIL:
2235 case LyXRC::RC_USER_NAME:
2236 case LyXRC::RC_USETEMPDIR:
2237 case LyXRC::RC_USE_ALT_LANG:
2238 case LyXRC::RC_USE_CONVERTER_CACHE:
2239 case LyXRC::RC_USE_ESC_CHARS:
2240 case LyXRC::RC_USE_INP_ENC:
2241 case LyXRC::RC_USE_PERS_DICT:
2242 case LyXRC::RC_USE_SPELL_LIB:
2243 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2244 case LyXRC::RC_VIEWER:
2245 case LyXRC::RC_LAST: