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"
31 #include "CutAndPaste.h"
33 #include "dispatchresult.h"
35 #include "errorlist.h"
38 #include "funcrequest.h"
39 #include "FuncStatus.h"
42 #include "insetiterator.h"
50 #include "LyXAction.h"
55 #include "lyxserver.h"
56 #include "lyxtextclasslist.h"
58 #include "paragraph.h"
59 #include "pariterator.h"
60 #include "ParagraphParameters.h"
63 #include "insets/insetbox.h"
64 #include "insets/insetbranch.h"
65 #include "insets/insetcommand.h"
66 #include "insets/insetert.h"
67 #include "insets/insetexternal.h"
68 #include "insets/insetfloat.h"
69 #include "insets/insetgraphics.h"
70 #include "insets/insetinclude.h"
71 #include "insets/insetnote.h"
72 #include "insets/insettabular.h"
73 #include "insets/insetvspace.h"
74 #include "insets/insetwrap.h"
76 #include "frontends/Application.h"
77 #include "frontends/Alert.h"
78 #include "frontends/Dialogs.h"
79 #include "frontends/FileDialog.h"
80 #include "frontends/FontLoader.h"
81 #include "frontends/Gui.h"
82 #include "frontends/LyXKeySym.h"
83 #include "frontends/LyXView.h"
84 #include "frontends/Menubar.h"
85 #include "frontends/Toolbars.h"
87 #include "support/environment.h"
88 #include "support/filefilterlist.h"
89 #include "support/filetools.h"
90 #include "support/forkedcontr.h"
91 #include "support/fs_extras.h"
92 #include "support/lstrings.h"
93 #include "support/path.h"
94 #include "support/package.h"
95 #include "support/systemcall.h"
96 #include "support/convert.h"
97 #include "support/os.h"
99 #include <boost/current_function.hpp>
100 #include <boost/filesystem/operations.hpp>
107 using bv_funcs::freefont2string;
109 using support::absolutePath;
110 using support::addName;
111 using support::addPath;
112 using support::bformat;
113 using support::changeExtension;
114 using support::contains;
115 using support::FileFilterList;
116 using support::fileSearch;
117 using support::ForkedcallsController;
118 using support::i18nLibFileSearch;
119 using support::isDirWriteable;
120 using support::isFileReadable;
121 using support::isStrInt;
122 using support::makeAbsPath;
123 using support::makeDisplayPath;
124 using support::package;
125 using support::quoteName;
126 using support::rtrim;
127 using support::split;
128 using support::subst;
129 using support::Systemcall;
130 using support::token;
132 using support::prefixIs;
135 using std::make_pair;
138 using std::istringstream;
139 using std::ostringstream;
141 namespace Alert = frontend::Alert;
142 namespace fs = boost::filesystem;
146 extern tex_accent_struct get_accent(kb_action action);
151 bool getLocalStatus(LCursor cursor,
152 FuncRequest const & cmd, FuncStatus & status)
154 // Try to fix cursor in case it is broken.
155 cursor.fixIfBroken();
157 // This is, of course, a mess. Better create a new doc iterator and use
158 // this in Inset::getStatus. This might require an additional
159 // BufferView * arg, though (which should be avoided)
160 //LCursor safe = *this;
162 for ( ; cursor.depth(); cursor.pop()) {
163 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
164 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
165 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
166 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
168 // The inset's getStatus() will return 'true' if it made
169 // a definitive decision on whether it want to handle the
170 // request or not. The result of this decision is put into
171 // the 'status' parameter.
172 if (cursor.inset().getStatus(cursor, cmd, status)) {
181 /** Return the change status at cursor position, taking in account the
182 * status at each level of the document iterator (a table in a deleted
183 * footnote is deleted).
184 * When \param outer is true, the top slice is not looked at.
186 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
188 size_t const depth = dit.depth() - (outer ? 1 : 0);
190 for (size_t i = 0 ; i < depth ; ++i) {
191 CursorSlice const & slice = dit[i];
192 if (!slice.inset().inMathed()
193 && slice.pos() < slice.paragraph().size()) {
194 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
195 if (ch != Change::UNCHANGED)
199 return Change::UNCHANGED;
207 meta_fake_bit(key_modifier::none)
212 void LyXFunc::initKeySequences(kb_keymap * kb)
214 keyseq.reset(new kb_sequence(kb, kb));
215 cancel_meta_seq.reset(new kb_sequence(kb, kb));
219 void LyXFunc::setLyXView(LyXView * lv)
225 void LyXFunc::handleKeyFunc(kb_action action)
227 char_type c = encoded_last_key;
229 if (keyseq->length())
232 lyx_view_->view()->getIntl().getTransManager().deadkey(
233 c, get_accent(action).accent, view()->getLyXText(), view()->cursor());
234 // Need to clear, in case the minibuffer calls these
237 // copied verbatim from do_accent_char
238 view()->cursor().resetAnchor();
243 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
245 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
247 // Do nothing if we have nothing (JMarc)
248 if (!keysym->isOK()) {
249 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
254 if (keysym->isModifier()) {
255 lyxerr[Debug::KEY] << "isModifier true" << endl;
259 //Encoding const * encoding = view()->cursor().getEncoding();
260 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
261 size_t encoded_last_key = keysym->getUCSEncoded();
263 // Do a one-deep top-level lookup for
264 // cancel and meta-fake keys. RVDK_PATCH_5
265 cancel_meta_seq->reset();
267 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
268 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
269 << " action first set to [" << func.action << ']'
272 // When not cancel or meta-fake, do the normal lookup.
273 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
274 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
275 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
276 // remove Caps Lock and Mod2 as a modifiers
277 func = keyseq->addkey(keysym, (state | meta_fake_bit));
278 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
279 << "action now set to ["
280 << func.action << ']' << endl;
283 // Dont remove this unless you know what you are doing.
284 meta_fake_bit = key_modifier::none;
286 // Can this happen now ?
287 if (func.action == LFUN_NOACTION) {
288 func = FuncRequest(LFUN_COMMAND_PREFIX);
291 if (lyxerr.debugging(Debug::KEY)) {
292 lyxerr << BOOST_CURRENT_FUNCTION
294 << func.action << "]["
295 << keyseq->print() << ']'
299 // already here we know if it any point in going further
300 // why not return already here if action == -1 and
301 // num_bytes == 0? (Lgb)
303 if (keyseq->length() > 1) {
304 lyx_view_->message(from_utf8(keyseq->print()));
308 // Maybe user can only reach the key via holding down shift.
309 // Let's see. But only if shift is the only modifier
310 if (func.action == LFUN_UNKNOWN_ACTION &&
311 state == key_modifier::shift) {
312 lyxerr[Debug::KEY] << "Trying without shift" << endl;
313 func = keyseq->addkey(keysym, key_modifier::none);
314 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
317 if (func.action == LFUN_UNKNOWN_ACTION) {
318 // Hmm, we didn't match any of the keysequences. See
319 // if it's normal insertable text not already covered
321 if (keysym->isText() && keyseq->length() == 1) {
322 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
323 func = FuncRequest(LFUN_SELF_INSERT,
324 FuncRequest::KEYBOARD);
326 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
327 lyx_view_->message(_("Unknown function."));
332 if (func.action == LFUN_SELF_INSERT) {
333 if (encoded_last_key != 0) {
334 docstring const arg(1, encoded_last_key);
335 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
336 FuncRequest::KEYBOARD));
338 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
346 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
348 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
351 if (cmd.action == LFUN_LYX_QUIT) {
352 flag.message(from_utf8(N_("Exiting")));
355 } else if (cmd.action == LFUN_BOOKMARK_GOTO) {
356 // bookmarks can be valid even if there is no opened buffer
357 flag.enabled(LyX::ref().session().bookmarks().isValid(convert<unsigned int>(to_utf8(cmd.argument()))));
359 } else if (cmd.action == LFUN_BOOKMARK_CLEAR) {
360 flag.enabled(LyX::ref().session().bookmarks().size() > 0);
362 } else if (cmd.action == LFUN_TOOLBAR_TOGGLE_STATE) {
363 ToolbarBackend::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
364 if (!(flags & ToolbarBackend::AUTO))
365 flag.setOnOff(flags & ToolbarBackend::ON);
369 LCursor & cur = view()->cursor();
371 /* In LyX/Mac, when a dialog is open, the menus of the
372 application can still be accessed without giving focus to
373 the main window. In this case, we want to disable the menu
374 entries that are buffer-related.
376 Note that this code is not perfect, as bug 1941 attests:
377 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
379 Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
380 if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
383 if (cmd.action == LFUN_NOACTION) {
384 flag.message(from_utf8(N_("Nothing to do")));
389 switch (cmd.action) {
390 case LFUN_UNKNOWN_ACTION:
391 #ifndef HAVE_LIBAIKSAURUS
392 case LFUN_THESAURUS_ENTRY:
402 if (flag.unknown()) {
403 flag.message(from_utf8(N_("Unknown action")));
407 if (!flag.enabled()) {
408 if (flag.message().empty())
409 flag.message(from_utf8(N_("Command disabled")));
413 // Check whether we need a buffer
414 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
416 flag.message(from_utf8(N_("Command not allowed with"
417 "out any document open")));
422 // I would really like to avoid having this switch and rather try to
423 // encode this in the function itself.
424 // -- And I'd rather let an inset decide which LFUNs it is willing
425 // to handle (Andre')
427 switch (cmd.action) {
428 case LFUN_BUFFER_TOGGLE_READ_ONLY:
429 flag.setOnOff(buf->isReadonly());
432 case LFUN_BUFFER_SWITCH:
433 // toggle on the current buffer, but do not toggle off
434 // the other ones (is that a good idea?)
435 if (to_utf8(cmd.argument()) == buf->fileName())
439 case LFUN_BUFFER_EXPORT:
440 enable = cmd.argument() == "custom"
441 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
444 case LFUN_BUFFER_CHKTEX:
445 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
448 case LFUN_BUILD_PROGRAM:
449 enable = Exporter::isExportable(*buf, "program");
452 case LFUN_LAYOUT_TABULAR:
453 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
457 case LFUN_LAYOUT_PARAGRAPH:
458 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
461 case LFUN_VC_REGISTER:
462 enable = !buf->lyxvc().inUse();
464 case LFUN_VC_CHECK_IN:
465 enable = buf->lyxvc().inUse() && !buf->isReadonly();
467 case LFUN_VC_CHECK_OUT:
468 enable = buf->lyxvc().inUse() && buf->isReadonly();
471 case LFUN_VC_UNDO_LAST:
472 enable = buf->lyxvc().inUse();
474 case LFUN_BUFFER_RELOAD:
475 enable = !buf->isUnnamed() && !buf->isClean();
478 case LFUN_INSET_SETTINGS: {
482 InsetBase::Code code = cur.inset().lyxCode();
484 case InsetBase::TABULAR_CODE:
485 enable = cmd.argument() == "tabular";
487 case InsetBase::ERT_CODE:
488 enable = cmd.argument() == "ert";
490 case InsetBase::FLOAT_CODE:
491 enable = cmd.argument() == "float";
493 case InsetBase::WRAP_CODE:
494 enable = cmd.argument() == "wrap";
496 case InsetBase::NOTE_CODE:
497 enable = cmd.argument() == "note";
499 case InsetBase::BRANCH_CODE:
500 enable = cmd.argument() == "branch";
502 case InsetBase::BOX_CODE:
503 enable = cmd.argument() == "box";
511 case LFUN_INSET_APPLY: {
512 string const name = cmd.getArg(0);
513 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
515 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
517 bool const success = inset->getStatus(cur, fr, fs);
518 // Every inset is supposed to handle this
519 BOOST_ASSERT(success);
522 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
523 flag |= getStatus(fr);
525 enable = flag.enabled();
529 case LFUN_DIALOG_SHOW: {
530 string const name = cmd.getArg(0);
532 enable = name == "aboutlyx"
536 || name == "texinfo";
537 else if (name == "print")
538 enable = Exporter::isExportable(*buf, "dvi")
539 && lyxrc.print_command != "none";
540 else if (name == "character" || name == "mathpanel")
541 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
542 else if (name == "latexlog")
543 enable = isFileReadable(buf->getLogName().second);
544 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
545 else if (name == "spellchecker")
548 else if (name == "vclog")
549 enable = buf->lyxvc().inUse();
550 else if (name == "view-source")
555 case LFUN_DIALOG_SHOW_NEW_INSET:
556 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
559 case LFUN_DIALOG_UPDATE: {
560 string const name = cmd.getArg(0);
562 enable = name == "prefs";
566 case LFUN_CITATION_INSERT: {
567 FuncRequest fr(LFUN_INSET_INSERT, "citation");
568 enable = getStatus(fr).enabled();
572 case LFUN_BUFFER_WRITE: {
573 enable = view()->buffer()->isUnnamed()
574 || !view()->buffer()->isClean();
579 // this one is difficult to get right. As a half-baked
580 // solution, we consider only the first action of the sequence
581 case LFUN_COMMAND_SEQUENCE: {
582 // argument contains ';'-terminated commands
583 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
584 FuncRequest func(lyxaction.lookupFunc(firstcmd));
585 func.origin = cmd.origin;
586 flag = getStatus(func);
589 case LFUN_BUFFER_NEW:
590 case LFUN_BUFFER_NEW_TEMPLATE:
591 case LFUN_WORD_FIND_FORWARD:
592 case LFUN_WORD_FIND_BACKWARD:
593 case LFUN_COMMAND_PREFIX:
594 case LFUN_COMMAND_EXECUTE:
596 case LFUN_META_PREFIX:
597 case LFUN_BUFFER_CLOSE:
598 case LFUN_BUFFER_WRITE_AS:
599 case LFUN_BUFFER_UPDATE:
600 case LFUN_BUFFER_VIEW:
601 case LFUN_BUFFER_IMPORT:
603 case LFUN_BUFFER_AUTO_SAVE:
604 case LFUN_RECONFIGURE:
608 case LFUN_DROP_LAYOUTS_CHOICE:
610 case LFUN_SERVER_GET_NAME:
611 case LFUN_SERVER_NOTIFY:
612 case LFUN_SERVER_GOTO_FILE_ROW:
613 case LFUN_DIALOG_HIDE:
614 case LFUN_DIALOG_DISCONNECT_INSET:
615 case LFUN_BUFFER_CHILD_OPEN:
616 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
617 case LFUN_KEYMAP_OFF:
618 case LFUN_KEYMAP_PRIMARY:
619 case LFUN_KEYMAP_SECONDARY:
620 case LFUN_KEYMAP_TOGGLE:
622 case LFUN_BUFFER_EXPORT_CUSTOM:
623 case LFUN_BUFFER_PRINT:
624 case LFUN_PREFERENCES_SAVE:
625 case LFUN_SCREEN_FONT_UPDATE:
628 case LFUN_EXTERNAL_EDIT:
629 case LFUN_GRAPHICS_EDIT:
630 case LFUN_ALL_INSETS_TOGGLE:
631 case LFUN_BUFFER_LANGUAGE:
632 case LFUN_TEXTCLASS_APPLY:
633 case LFUN_TEXTCLASS_LOAD:
634 case LFUN_BUFFER_SAVE_AS_DEFAULT:
635 case LFUN_BUFFER_PARAMS_APPLY:
636 case LFUN_LYXRC_APPLY:
637 case LFUN_BUFFER_NEXT:
638 case LFUN_BUFFER_PREVIOUS:
639 case LFUN_WINDOW_NEW:
640 case LFUN_WINDOW_CLOSE:
641 // these are handled in our dispatch()
645 if (!getLocalStatus(cur, cmd, flag))
646 flag = view()->getStatus(cmd);
652 // Can we use a readonly buffer?
653 if (buf && buf->isReadonly()
654 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
655 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
656 flag.message(from_utf8(N_("Document is read-only")));
660 // Are we in a DELETED change-tracking region?
661 if (buf && lookupChangeType(cur, true) == Change::DELETED
662 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
663 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
664 flag.message(from_utf8(N_("This portion of the document is deleted.")));
668 // the default error message if we disable the command
669 if (!flag.enabled() && flag.message().empty())
670 flag.message(from_utf8(N_("Command disabled")));
676 bool LyXFunc::ensureBufferClean(BufferView * bv)
678 Buffer & buf = *bv->buffer();
682 docstring const file = makeDisplayPath(buf.fileName(), 30);
683 docstring text = bformat(_("The document %1$s has unsaved "
684 "changes.\n\nDo you want to save "
685 "the document?"), file);
686 int const ret = Alert::prompt(_("Save changed document?"),
687 text, 0, 1, _("&Save"),
691 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
693 return buf.isClean();
699 void showPrintError(string const & name)
701 docstring str = bformat(_("Could not print the document %1$s.\n"
702 "Check that your printer is set up correctly."),
703 makeDisplayPath(name, 50));
704 Alert::error(_("Print document failed"), str);
708 void loadTextclass(string const & name)
710 std::pair<bool, textclass_type> const tc_pair =
711 textclasslist.numberOfClass(name);
713 if (!tc_pair.first) {
714 lyxerr << "Document class \"" << name
715 << "\" does not exist."
720 textclass_type const tc = tc_pair.second;
722 if (!textclasslist[tc].load()) {
723 docstring s = bformat(_("The document could not be converted\n"
724 "into the document class %1$s."),
725 from_utf8(textclasslist[tc].name()));
726 Alert::error(_("Could not change class"), s);
731 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
736 void LyXFunc::dispatch(FuncRequest const & cmd)
738 string const argument = to_utf8(cmd.argument());
739 kb_action const action = cmd.action;
741 lyxerr[Debug::ACTION] << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
742 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
744 // we have not done anything wrong yet.
746 dispatch_buffer.erase();
748 // redraw the screen at the end (first of the two drawing steps).
749 //This is done unless explicitely requested otherwise
750 Update::flags updateFlags = Update::FitCursor;
752 FuncStatus const flag = getStatus(cmd);
753 if (!flag.enabled()) {
754 // We cannot use this function here
755 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
756 << lyxaction.getActionName(action)
757 << " [" << action << "] is disabled at this location"
759 setErrorMessage(flag.message());
763 case LFUN_WORD_FIND_FORWARD:
764 case LFUN_WORD_FIND_BACKWARD: {
765 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
766 static string last_search;
767 string searched_string;
769 if (!argument.empty()) {
770 last_search = argument;
771 searched_string = argument;
773 searched_string = last_search;
776 if (searched_string.empty())
779 bool const fw = action == LFUN_WORD_FIND_FORWARD;
781 find2string(searched_string, true, false, fw);
782 find(view(), FuncRequest(LFUN_WORD_FIND, data));
786 case LFUN_COMMAND_PREFIX:
787 BOOST_ASSERT(lyx_view_);
788 lyx_view_->message(from_utf8(keyseq->printOptions()));
791 case LFUN_COMMAND_EXECUTE:
792 BOOST_ASSERT(lyx_view_);
793 lyx_view_->getToolbars().display("minibuffer", true);
794 lyx_view_->focus_command_buffer();
798 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
800 meta_fake_bit = key_modifier::none;
801 if (view()->buffer())
802 // cancel any selection
803 dispatch(FuncRequest(LFUN_MARK_OFF));
804 setMessage(_("Cancel"));
807 case LFUN_META_PREFIX:
808 meta_fake_bit = key_modifier::alt;
809 setMessage(from_utf8(keyseq->print()));
812 case LFUN_BUFFER_TOGGLE_READ_ONLY:
813 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
814 if (lyx_view_->buffer()->lyxvc().inUse())
815 lyx_view_->buffer()->lyxvc().toggleReadOnly();
817 lyx_view_->buffer()->setReadonly(
818 !lyx_view_->buffer()->isReadonly());
821 // --- Menus -----------------------------------------------
822 case LFUN_BUFFER_NEW:
823 menuNew(argument, false);
826 case LFUN_BUFFER_NEW_TEMPLATE:
827 menuNew(argument, true);
830 case LFUN_BUFFER_CLOSE:
835 case LFUN_BUFFER_WRITE:
836 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
837 if (!lyx_view_->buffer()->isUnnamed()) {
838 docstring const str = bformat(_("Saving document %1$s..."),
839 makeDisplayPath(lyx_view_->buffer()->fileName()));
840 lyx_view_->message(str);
841 menuWrite(lyx_view_->buffer());
842 lyx_view_->message(str + _(" done."));
844 writeAs(lyx_view_->buffer());
845 updateFlags = Update::None;
848 case LFUN_BUFFER_WRITE_AS:
849 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
850 writeAs(lyx_view_->buffer(), argument);
851 updateFlags = Update::None;
854 case LFUN_BUFFER_RELOAD: {
855 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
856 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
857 docstring text = bformat(_("Any changes will be lost. Are you sure "
858 "you want to revert to the saved version of the document %1$s?"), file);
859 int const ret = Alert::prompt(_("Revert to saved document?"),
860 text, 0, 1, _("&Revert"), _("&Cancel"));
867 case LFUN_BUFFER_UPDATE:
868 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
869 Exporter::Export(lyx_view_->buffer(), argument, true);
872 case LFUN_BUFFER_VIEW:
873 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
874 Exporter::preview(lyx_view_->buffer(), argument);
877 case LFUN_BUILD_PROGRAM:
878 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
879 Exporter::Export(lyx_view_->buffer(), "program", true);
882 case LFUN_BUFFER_CHKTEX:
883 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
884 lyx_view_->buffer()->runChktex();
887 case LFUN_BUFFER_EXPORT:
888 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
889 if (argument == "custom")
890 lyx_view_->getDialogs().show("sendto");
892 Exporter::Export(lyx_view_->buffer(), argument, false);
896 case LFUN_BUFFER_EXPORT_CUSTOM: {
897 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
899 string command = split(argument, format_name, ' ');
900 Format const * format = formats.getFormat(format_name);
902 lyxerr << "Format \"" << format_name
903 << "\" not recognized!"
908 Buffer * buffer = lyx_view_->buffer();
910 // The name of the file created by the conversion process
913 // Output to filename
914 if (format->name() == "lyx") {
915 string const latexname =
916 buffer->getLatexName(false);
917 filename = changeExtension(latexname,
918 format->extension());
919 filename = addName(buffer->temppath(), filename);
921 if (!buffer->writeFile(filename))
925 Exporter::Export(buffer, format_name, true, filename);
928 // Substitute $$FName for filename
929 if (!contains(command, "$$FName"))
930 command = "( " + command + " ) < $$FName";
931 command = subst(command, "$$FName", filename);
933 // Execute the command in the background
935 call.startscript(Systemcall::DontWait, command);
939 case LFUN_BUFFER_PRINT: {
940 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
943 string command = split(split(argument, target, ' '),
947 || target_name.empty()
948 || command.empty()) {
949 lyxerr << "Unable to parse \""
950 << argument << '"' << std::endl;
953 if (target != "printer" && target != "file") {
954 lyxerr << "Unrecognized target \""
955 << target << '"' << std::endl;
959 Buffer * buffer = lyx_view_->buffer();
961 if (!Exporter::Export(buffer, "dvi", true)) {
962 showPrintError(buffer->fileName());
966 // Push directory path.
967 string const path = buffer->temppath();
968 support::Path p(path);
970 // there are three cases here:
971 // 1. we print to a file
972 // 2. we print directly to a printer
973 // 3. we print using a spool command (print to file first)
976 string const dviname =
977 changeExtension(buffer->getLatexName(true),
980 if (target == "printer") {
981 if (!lyxrc.print_spool_command.empty()) {
982 // case 3: print using a spool
983 string const psname =
984 changeExtension(dviname,".ps");
985 command += lyxrc.print_to_file
988 + quoteName(dviname);
991 lyxrc.print_spool_command +' ';
992 if (target_name != "default") {
993 command2 += lyxrc.print_spool_printerprefix
997 command2 += quoteName(psname);
999 // If successful, then spool command
1000 res = one.startscript(
1005 res = one.startscript(
1006 Systemcall::DontWait,
1009 // case 2: print directly to a printer
1010 res = one.startscript(
1011 Systemcall::DontWait,
1012 command + quoteName(dviname));
1016 // case 1: print to a file
1017 command += lyxrc.print_to_file
1018 + quoteName(makeAbsPath(target_name,
1021 + quoteName(dviname);
1022 res = one.startscript(Systemcall::DontWait,
1027 showPrintError(buffer->fileName());
1031 case LFUN_BUFFER_IMPORT:
1036 if (argument == "closeOnly") {
1037 if (!theApp->gui().closeAll())
1042 // FIXME: this code needs to be transfered somewhere else
1043 // as lyx_view_ will most certainly be null and a same buffer
1044 // might be visible in more than one LyXView.
1045 if (lyx_view_ && lyx_view_->view()->buffer()) {
1046 // save cursor Position for opened files to .lyx/session
1047 LyX::ref().session().lastFilePos().save(lyx_view_->buffer()->fileName(),
1048 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1054 case LFUN_TOC_VIEW: {
1055 BOOST_ASSERT(lyx_view_);
1056 InsetCommandParams p("tableofcontents");
1057 string const data = InsetCommandMailer::params2string("toc", p);
1058 lyx_view_->getDialogs().show("toc", data, 0);
1062 case LFUN_BUFFER_AUTO_SAVE:
1066 case LFUN_RECONFIGURE:
1067 BOOST_ASSERT(lyx_view_);
1068 reconfigure(*lyx_view_);
1071 case LFUN_HELP_OPEN: {
1072 BOOST_ASSERT(lyx_view_);
1073 string const arg = argument;
1075 setErrorMessage(_("Missing argument"));
1078 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1079 if (fname.empty()) {
1080 lyxerr << "LyX: unable to find documentation file `"
1081 << arg << "'. Bad installation?" << endl;
1084 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1085 makeDisplayPath(fname)));
1086 lyx_view_->loadLyXFile(fname, false);
1090 // --- version control -------------------------------
1091 case LFUN_VC_REGISTER:
1092 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1093 if (!ensureBufferClean(view()))
1095 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1096 lyx_view_->buffer()->lyxvc().registrer();
1101 case LFUN_VC_CHECK_IN:
1102 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1103 if (!ensureBufferClean(view()))
1105 if (lyx_view_->buffer()->lyxvc().inUse()
1106 && !lyx_view_->buffer()->isReadonly()) {
1107 lyx_view_->buffer()->lyxvc().checkIn();
1112 case LFUN_VC_CHECK_OUT:
1113 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1114 if (!ensureBufferClean(view()))
1116 if (lyx_view_->buffer()->lyxvc().inUse()
1117 && lyx_view_->buffer()->isReadonly()) {
1118 lyx_view_->buffer()->lyxvc().checkOut();
1123 case LFUN_VC_REVERT:
1124 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1125 lyx_view_->buffer()->lyxvc().revert();
1129 case LFUN_VC_UNDO_LAST:
1130 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1131 lyx_view_->buffer()->lyxvc().undoLast();
1135 // --- buffers ----------------------------------------
1136 case LFUN_BUFFER_SWITCH:
1137 BOOST_ASSERT(lyx_view_);
1138 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1141 case LFUN_BUFFER_NEXT:
1142 BOOST_ASSERT(lyx_view_);
1143 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1146 case LFUN_BUFFER_PREVIOUS:
1147 BOOST_ASSERT(lyx_view_);
1148 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1152 BOOST_ASSERT(lyx_view_);
1153 newFile(view(), argument);
1156 case LFUN_FILE_OPEN:
1157 BOOST_ASSERT(lyx_view_);
1161 case LFUN_DROP_LAYOUTS_CHOICE:
1162 BOOST_ASSERT(lyx_view_);
1163 lyx_view_->getToolbars().openLayoutList();
1166 case LFUN_MENU_OPEN:
1167 BOOST_ASSERT(lyx_view_);
1168 lyx_view_->getMenubar().openByName(from_utf8(argument));
1171 // --- lyxserver commands ----------------------------
1172 case LFUN_SERVER_GET_NAME:
1173 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1174 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1175 lyxerr[Debug::INFO] << "FNAME["
1176 << lyx_view_->buffer()->fileName()
1180 case LFUN_SERVER_NOTIFY:
1181 dispatch_buffer = from_utf8(keyseq->print());
1182 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1185 case LFUN_SERVER_GOTO_FILE_ROW: {
1186 BOOST_ASSERT(lyx_view_);
1189 istringstream is(argument);
1190 is >> file_name >> row;
1191 if (prefixIs(file_name, package().temp_dir())) {
1192 // Needed by inverse dvi search. If it is a file
1193 // in tmpdir, call the apropriated function
1194 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1196 // Must replace extension of the file to be .lyx
1197 // and get full path
1198 string const s = changeExtension(file_name, ".lyx");
1199 // Either change buffer or load the file
1200 if (theBufferList().exists(s)) {
1201 lyx_view_->setBuffer(theBufferList().getBuffer(s));
1203 lyx_view_->loadLyXFile(s);
1207 view()->setCursorFromRow(row);
1210 // see BufferView::center()
1214 case LFUN_DIALOG_SHOW: {
1215 BOOST_ASSERT(lyx_view_);
1216 string const name = cmd.getArg(0);
1217 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1219 if (name == "character") {
1220 data = freefont2string();
1222 lyx_view_->getDialogs().show("character", data);
1223 } else if (name == "latexlog") {
1224 pair<Buffer::LogType, string> const logfile =
1225 lyx_view_->buffer()->getLogName();
1226 switch (logfile.first) {
1227 case Buffer::latexlog:
1230 case Buffer::buildlog:
1234 data += LyXLex::quoteString(logfile.second);
1235 lyx_view_->getDialogs().show("log", data);
1236 } else if (name == "vclog") {
1237 string const data = "vc " +
1238 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1239 lyx_view_->getDialogs().show("log", data);
1241 lyx_view_->getDialogs().show(name, data);
1245 case LFUN_DIALOG_SHOW_NEW_INSET: {
1246 BOOST_ASSERT(lyx_view_);
1247 string const name = cmd.getArg(0);
1248 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1249 if (name == "bibitem" ||
1253 name == "nomenclature" ||
1257 InsetCommandParams p(name);
1258 data = InsetCommandMailer::params2string(name, p);
1259 } else if (name == "include") {
1260 InsetCommandParams p(data);
1261 data = InsetIncludeMailer::params2string(p);
1262 } else if (name == "box") {
1263 // \c data == "Boxed" || "Frameless" etc
1264 InsetBoxParams p(data);
1265 data = InsetBoxMailer::params2string(p);
1266 } else if (name == "branch") {
1267 InsetBranchParams p;
1268 data = InsetBranchMailer::params2string(p);
1269 } else if (name == "citation") {
1270 InsetCommandParams p("cite");
1271 data = InsetCommandMailer::params2string(name, p);
1272 } else if (name == "ert") {
1273 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1274 } else if (name == "external") {
1275 InsetExternalParams p;
1276 Buffer const & buffer = *lyx_view_->buffer();
1277 data = InsetExternalMailer::params2string(p, buffer);
1278 } else if (name == "float") {
1280 data = InsetFloatMailer::params2string(p);
1281 } else if (name == "graphics") {
1282 InsetGraphicsParams p;
1283 Buffer const & buffer = *lyx_view_->buffer();
1284 data = InsetGraphicsMailer::params2string(p, buffer);
1285 } else if (name == "note") {
1287 data = InsetNoteMailer::params2string(p);
1288 } else if (name == "vspace") {
1290 data = InsetVSpaceMailer::params2string(space);
1291 } else if (name == "wrap") {
1293 data = InsetWrapMailer::params2string(p);
1295 lyx_view_->getDialogs().show(name, data, 0);
1299 case LFUN_DIALOG_UPDATE: {
1300 BOOST_ASSERT(lyx_view_);
1301 string const & name = argument;
1302 // Can only update a dialog connected to an existing inset
1303 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1305 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1306 inset->dispatch(view()->cursor(), fr);
1307 } else if (name == "paragraph") {
1308 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1309 } else if (name == "prefs") {
1310 lyx_view_->getDialogs().update(name, string());
1315 case LFUN_DIALOG_HIDE:
1316 Dialogs::hide(argument, 0);
1319 case LFUN_DIALOG_DISCONNECT_INSET:
1320 BOOST_ASSERT(lyx_view_);
1321 lyx_view_->getDialogs().disconnect(argument);
1325 case LFUN_CITATION_INSERT: {
1326 BOOST_ASSERT(lyx_view_);
1327 if (!argument.empty()) {
1328 // we can have one optional argument, delimited by '|'
1329 // citation-insert <key>|<text_before>
1330 // this should be enhanced to also support text_after
1331 // and citation style
1332 string arg = argument;
1334 if (contains(argument, "|")) {
1335 arg = token(argument, '|', 0);
1336 opt1 = '[' + token(argument, '|', 1) + ']';
1338 std::ostringstream os;
1339 os << "citation LatexCommand\n"
1340 << "\\cite" << opt1 << "{" << arg << "}\n"
1342 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1345 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1349 case LFUN_BUFFER_CHILD_OPEN: {
1350 BOOST_ASSERT(lyx_view_);
1351 string const filename =
1352 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1353 // FIXME Should use bformat
1354 setMessage(_("Opening child document ") +
1355 makeDisplayPath(filename) + "...");
1356 view()->saveBookmark(false);
1357 string const parentfilename = lyx_view_->buffer()->fileName();
1358 if (theBufferList().exists(filename))
1359 lyx_view_->setBuffer(theBufferList().getBuffer(filename));
1361 lyx_view_->loadLyXFile(filename);
1362 // Set the parent name of the child document.
1363 // This makes insertion of citations and references in the child work,
1364 // when the target is in the parent or another child document.
1365 lyx_view_->buffer()->setParentName(parentfilename);
1369 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1370 BOOST_ASSERT(lyx_view_);
1371 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1374 case LFUN_KEYMAP_OFF:
1375 BOOST_ASSERT(lyx_view_);
1376 lyx_view_->view()->getIntl().keyMapOn(false);
1379 case LFUN_KEYMAP_PRIMARY:
1380 BOOST_ASSERT(lyx_view_);
1381 lyx_view_->view()->getIntl().keyMapPrim();
1384 case LFUN_KEYMAP_SECONDARY:
1385 BOOST_ASSERT(lyx_view_);
1386 lyx_view_->view()->getIntl().keyMapSec();
1389 case LFUN_KEYMAP_TOGGLE:
1390 BOOST_ASSERT(lyx_view_);
1391 lyx_view_->view()->getIntl().toggleKeyMap();
1397 string rest = split(argument, countstr, ' ');
1398 istringstream is(countstr);
1401 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1402 for (int i = 0; i < count; ++i)
1403 dispatch(lyxaction.lookupFunc(rest));
1407 case LFUN_COMMAND_SEQUENCE: {
1408 // argument contains ';'-terminated commands
1409 string arg = argument;
1410 while (!arg.empty()) {
1412 arg = split(arg, first, ';');
1413 FuncRequest func(lyxaction.lookupFunc(first));
1414 func.origin = cmd.origin;
1420 case LFUN_PREFERENCES_SAVE: {
1421 support::Path p(package().user_support());
1422 lyxrc.write("preferences", false);
1426 case LFUN_SCREEN_FONT_UPDATE:
1427 BOOST_ASSERT(lyx_view_);
1428 // handle the screen font changes.
1429 lyxrc.set_font_norm_type();
1430 theFontLoader().update();
1431 // All visible buffers will need resize
1435 case LFUN_SET_COLOR: {
1437 string const x11_name = split(argument, lyx_name, ' ');
1438 if (lyx_name.empty() || x11_name.empty()) {
1439 setErrorMessage(_("Syntax: set-color <lyx_name>"
1444 bool const graphicsbg_changed =
1445 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1446 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1448 if (!lcolor.setColor(lyx_name, x11_name)) {
1450 bformat(_("Set-color \"%1$s\" failed "
1451 "- color is undefined or "
1452 "may not be redefined"),
1453 from_utf8(lyx_name)));
1457 theApp->updateColor(lcolor.getFromLyXName(lyx_name));
1459 if (graphicsbg_changed) {
1460 #ifdef WITH_WARNINGS
1461 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1464 graphics::GCache::get().changeDisplay(true);
1471 BOOST_ASSERT(lyx_view_);
1472 lyx_view_->message(from_utf8(argument));
1475 case LFUN_EXTERNAL_EDIT: {
1476 BOOST_ASSERT(lyx_view_);
1477 FuncRequest fr(action, argument);
1478 InsetExternal().dispatch(view()->cursor(), fr);
1482 case LFUN_GRAPHICS_EDIT: {
1483 FuncRequest fr(action, argument);
1484 InsetGraphics().dispatch(view()->cursor(), fr);
1488 case LFUN_INSET_APPLY: {
1489 BOOST_ASSERT(lyx_view_);
1490 string const name = cmd.getArg(0);
1491 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1493 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1494 inset->dispatch(view()->cursor(), fr);
1496 FuncRequest fr(LFUN_INSET_INSERT, argument);
1499 // ideally, the update flag should be set by the insets,
1500 // but this is not possible currently
1501 updateFlags = Update::Force | Update::FitCursor;
1505 case LFUN_ALL_INSETS_TOGGLE: {
1506 BOOST_ASSERT(lyx_view_);
1508 string const name = split(argument, action, ' ');
1509 InsetBase::Code const inset_code =
1510 InsetBase::translate(name);
1512 LCursor & cur = view()->cursor();
1513 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1515 InsetBase & inset = lyx_view_->buffer()->inset();
1516 InsetIterator it = inset_iterator_begin(inset);
1517 InsetIterator const end = inset_iterator_end(inset);
1518 for (; it != end; ++it) {
1519 if (inset_code == InsetBase::NO_CODE
1520 || inset_code == it->lyxCode()) {
1521 LCursor tmpcur = cur;
1522 tmpcur.pushLeft(*it);
1523 it->dispatch(tmpcur, fr);
1526 updateFlags = Update::Force | Update::FitCursor;
1530 case LFUN_BUFFER_LANGUAGE: {
1531 BOOST_ASSERT(lyx_view_);
1532 Buffer & buffer = *lyx_view_->buffer();
1533 Language const * oldL = buffer.params().language;
1534 Language const * newL = languages.getLanguage(argument);
1535 if (!newL || oldL == newL)
1538 if (oldL->rightToLeft() == newL->rightToLeft()
1539 && !buffer.isMultiLingual())
1540 buffer.changeLanguage(oldL, newL);
1542 buffer.updateDocLang(newL);
1546 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1547 string const fname =
1548 addName(addPath(package().user_support(), "templates/"),
1550 Buffer defaults(fname);
1552 istringstream ss(argument);
1555 int const unknown_tokens = defaults.readHeader(lex);
1557 if (unknown_tokens != 0) {
1558 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1559 << unknown_tokens << " unknown token"
1560 << (unknown_tokens == 1 ? "" : "s")
1564 if (defaults.writeFile(defaults.fileName()))
1565 // FIXME Should use bformat
1566 setMessage(_("Document defaults saved in ")
1567 + makeDisplayPath(fname));
1569 setErrorMessage(_("Unable to save document defaults"));
1573 case LFUN_BUFFER_PARAMS_APPLY: {
1574 BOOST_ASSERT(lyx_view_);
1575 biblio::CiteEngine const engine =
1576 lyx_view_->buffer()->params().cite_engine;
1578 istringstream ss(argument);
1581 int const unknown_tokens =
1582 lyx_view_->buffer()->readHeader(lex);
1584 if (unknown_tokens != 0) {
1585 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1586 << unknown_tokens << " unknown token"
1587 << (unknown_tokens == 1 ? "" : "s")
1590 if (engine == lyx_view_->buffer()->params().cite_engine)
1593 LCursor & cur = view()->cursor();
1594 FuncRequest fr(LFUN_INSET_REFRESH);
1596 InsetBase & inset = lyx_view_->buffer()->inset();
1597 InsetIterator it = inset_iterator_begin(inset);
1598 InsetIterator const end = inset_iterator_end(inset);
1599 for (; it != end; ++it)
1600 if (it->lyxCode() == InsetBase::CITE_CODE)
1601 it->dispatch(cur, fr);
1605 case LFUN_TEXTCLASS_APPLY: {
1606 BOOST_ASSERT(lyx_view_);
1607 Buffer * buffer = lyx_view_->buffer();
1609 textclass_type const old_class =
1610 buffer->params().textclass;
1612 loadTextclass(argument);
1614 std::pair<bool, textclass_type> const tc_pair =
1615 textclasslist.numberOfClass(argument);
1620 textclass_type const new_class = tc_pair.second;
1621 if (old_class == new_class)
1625 lyx_view_->message(_("Converting document to new document class..."));
1626 recordUndoFullDocument(view());
1627 buffer->params().textclass = new_class;
1628 StableDocIterator backcur(view()->cursor());
1629 ErrorList & el = buffer->errorList("Class Switch");
1630 cap::switchBetweenClasses(
1631 old_class, new_class,
1632 static_cast<InsetText &>(buffer->inset()), el);
1634 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1636 buffer->errors("Class Switch");
1637 updateLabels(*buffer);
1638 updateFlags = Update::Force | Update::FitCursor;
1642 case LFUN_TEXTCLASS_LOAD:
1643 loadTextclass(argument);
1646 case LFUN_LYXRC_APPLY: {
1647 LyXRC const lyxrc_orig = lyxrc;
1649 istringstream ss(argument);
1650 bool const success = lyxrc.read(ss) == 0;
1653 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1654 << "Unable to read lyxrc data"
1659 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1663 case LFUN_WINDOW_NEW:
1664 LyX::ref().newLyXView();
1667 case LFUN_WINDOW_CLOSE:
1668 BOOST_ASSERT(lyx_view_);
1669 BOOST_ASSERT(theApp);
1671 // We return here because lyx_view does not exists anymore.
1674 case LFUN_BOOKMARK_GOTO: {
1675 BOOST_ASSERT(lyx_view_);
1676 unsigned int idx = convert<unsigned int>(to_utf8(cmd.argument()));
1677 BookmarksSection::Bookmark const bm = LyX::ref().session().bookmarks().bookmark(idx);
1678 BOOST_ASSERT(!bm.filename.empty());
1679 // if the file is not opened, open it.
1680 if (!theBufferList().exists(bm.filename))
1681 dispatch(FuncRequest(LFUN_FILE_OPEN, bm.filename));
1682 // open may fail, so we need to test it again
1683 if (theBufferList().exists(bm.filename)) {
1684 // if the current buffer is not that one, switch to it.
1685 if (lyx_view_->buffer()->fileName() != bm.filename)
1686 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, bm.filename));
1687 // BOOST_ASSERT(lyx_view_->buffer()->fileName() != bm.filename);
1688 view()->moveToPosition(bm.par_id, bm.par_pos);
1693 case LFUN_BOOKMARK_CLEAR:
1694 LyX::ref().session().bookmarks().clear();
1697 case LFUN_TOOLBAR_TOGGLE_STATE:
1698 lyx_view_->toggleToolbarState(argument);
1702 BOOST_ASSERT(lyx_view_);
1703 view()->cursor().dispatch(cmd);
1704 updateFlags = view()->cursor().result().update();
1705 if (!view()->cursor().result().dispatched())
1706 if (view()->dispatch(cmd))
1707 updateFlags = Update::Force | Update::FitCursor;
1712 if (lyx_view_ && view()->buffer()) {
1713 // BufferView::update() updates the ViewMetricsInfo and
1714 // also initializes the position cache for all insets in
1715 // (at least partially) visible top-level paragraphs.
1716 std::pair<bool, bool> needSecondUpdate
1717 = view()->update(updateFlags);
1719 // Redraw screen unless explicitly told otherwise.
1720 if (needSecondUpdate.first)
1721 // Buffer::changed() signals that a repaint is needed.
1722 // The frontend (WorkArea) knows which area to repaint
1723 // thanks to the ViewMetricsInfo updated above.
1724 view()->buffer()->changed();
1726 lyx_view_->updateStatusBar();
1728 // if we executed a mutating lfun, mark the buffer as dirty
1730 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1731 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1732 view()->buffer()->markDirty();
1734 if (view()->cursor().inTexted()) {
1735 lyx_view_->updateLayoutChoice();
1740 lyx_view_->updateMenubar();
1741 lyx_view_->updateToolbars();
1742 sendDispatchMessage(getMessage(), cmd);
1747 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1749 const bool verbose = (cmd.origin == FuncRequest::MENU
1750 || cmd.origin == FuncRequest::TOOLBAR
1751 || cmd.origin == FuncRequest::COMMANDBUFFER);
1753 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1754 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1756 lyx_view_->message(msg);
1760 docstring dispatch_msg = msg;
1761 if (!dispatch_msg.empty())
1762 dispatch_msg += ' ';
1764 string comname = lyxaction.getActionName(cmd.action);
1766 bool argsadded = false;
1768 if (!cmd.argument().empty()) {
1769 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1770 comname += ' ' + to_utf8(cmd.argument());
1775 string const shortcuts = theTopLevelKeymap().printbindings(cmd);
1777 if (!shortcuts.empty())
1778 comname += ": " + shortcuts;
1779 else if (!argsadded && !cmd.argument().empty())
1780 comname += ' ' + to_utf8(cmd.argument());
1782 if (!comname.empty()) {
1783 comname = rtrim(comname);
1784 dispatch_msg += from_utf8('(' + rtrim(comname) + ')');
1787 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1788 << to_utf8(dispatch_msg) << endl;
1789 if (!dispatch_msg.empty())
1790 lyx_view_->message(dispatch_msg);
1794 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1796 string initpath = lyxrc.document_path;
1797 string filename(name);
1799 if (view()->buffer()) {
1800 string const trypath = lyx_view_->buffer()->filePath();
1801 // If directory is writeable, use this as default.
1802 if (isDirWriteable(trypath))
1806 static int newfile_number;
1808 if (filename.empty()) {
1809 filename = addName(lyxrc.document_path,
1810 "newfile" + convert<string>(++newfile_number) + ".lyx");
1811 while (theBufferList().exists(filename) || fs::is_readable(filename)) {
1813 filename = addName(lyxrc.document_path,
1814 "newfile" + convert<string>(newfile_number) +
1819 // The template stuff
1822 FileDialog fileDlg(_("Select template file"),
1823 LFUN_SELECT_FILE_SYNC,
1824 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1825 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1827 FileDialog::Result result =
1828 fileDlg.open(from_utf8(lyxrc.template_path),
1829 FileFilterList(_("LyX Documents (*.lyx)")),
1832 if (result.first == FileDialog::Later)
1834 if (result.second.empty())
1836 templname = to_utf8(result.second);
1839 Buffer * const b = newFile(filename, templname, !name.empty());
1841 lyx_view_->setBuffer(b);
1845 void LyXFunc::open(string const & fname)
1847 string initpath = lyxrc.document_path;
1849 if (view()->buffer()) {
1850 string const trypath = lyx_view_->buffer()->filePath();
1851 // If directory is writeable, use this as default.
1852 if (isDirWriteable(trypath))
1858 if (fname.empty()) {
1859 FileDialog fileDlg(_("Select document to open"),
1861 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1862 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1864 FileDialog::Result result =
1865 fileDlg.open(from_utf8(initpath),
1866 FileFilterList(_("LyX Documents (*.lyx)")),
1869 if (result.first == FileDialog::Later)
1872 filename = to_utf8(result.second);
1874 // check selected filename
1875 if (filename.empty()) {
1876 lyx_view_->message(_("Canceled."));
1882 // get absolute path of file and add ".lyx" to the filename if
1884 string const fullpath = fileSearch(string(), filename, "lyx");
1885 if (!fullpath.empty()) {
1886 filename = fullpath;
1889 docstring const disp_fn = makeDisplayPath(filename);
1891 // if the file doesn't exist, let the user create one
1892 if (!fs::exists(filename)) {
1893 // the user specifically chose this name. Believe him.
1894 Buffer * const b = newFile(filename, string(), true);
1896 lyx_view_->setBuffer(b);
1900 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1903 if (lyx_view_->loadLyXFile(filename)) {
1904 str2 = bformat(_("Document %1$s opened."), disp_fn);
1906 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1908 lyx_view_->message(str2);
1912 void LyXFunc::doImport(string const & argument)
1915 string filename = split(argument, format, ' ');
1917 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1918 << " file: " << filename << endl;
1920 // need user interaction
1921 if (filename.empty()) {
1922 string initpath = lyxrc.document_path;
1924 if (view()->buffer()) {
1925 string const trypath = lyx_view_->buffer()->filePath();
1926 // If directory is writeable, use this as default.
1927 if (isDirWriteable(trypath))
1931 docstring const text = bformat(_("Select %1$s file to import"),
1932 formats.prettyName(format));
1934 FileDialog fileDlg(text,
1936 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1937 make_pair(_("Examples|#E#e"),
1938 from_utf8(addPath(package().system_support(), "examples"))));
1940 docstring filter = formats.prettyName(format);
1943 filter += from_utf8(formats.extension(format));
1946 FileDialog::Result result =
1947 fileDlg.open(from_utf8(initpath),
1948 FileFilterList(filter),
1951 if (result.first == FileDialog::Later)
1954 filename = to_utf8(result.second);
1956 // check selected filename
1957 if (filename.empty())
1958 lyx_view_->message(_("Canceled."));
1961 if (filename.empty())
1964 // get absolute path of file
1965 filename = makeAbsPath(filename);
1967 string const lyxfile = changeExtension(filename, ".lyx");
1969 // Check if the document already is open
1970 if (use_gui && theBufferList().exists(lyxfile)) {
1971 if (!theBufferList().close(theBufferList().getBuffer(lyxfile), true)) {
1972 lyx_view_->message(_("Canceled."));
1977 // if the file exists already, and we didn't do
1978 // -i lyx thefile.lyx, warn
1979 if (fs::exists(lyxfile) && filename != lyxfile) {
1980 docstring const file = makeDisplayPath(lyxfile, 30);
1982 docstring text = bformat(_("The document %1$s already exists.\n\n"
1983 "Do you want to over-write that document?"), file);
1984 int const ret = Alert::prompt(_("Over-write document?"),
1985 text, 0, 1, _("&Over-write"), _("&Cancel"));
1988 lyx_view_->message(_("Canceled."));
1993 ErrorList errorList;
1994 Importer::Import(lyx_view_, filename, format, errorList);
1995 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1999 void LyXFunc::closeBuffer()
2001 // save current cursor position
2002 LyX::ref().session().lastFilePos().save(lyx_view_->buffer()->fileName(),
2003 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2004 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2005 if (theBufferList().empty()) {
2006 // need this otherwise SEGV may occur while
2007 // trying to set variables that don't exist
2008 // since there's no current buffer
2009 lyx_view_->getDialogs().hideBufferDependent();
2011 lyx_view_->setBuffer(theBufferList().first());
2017 // Each "lyx_view_" should have it's own message method. lyxview and
2018 // the minibuffer would use the minibuffer, but lyxserver would
2019 // send an ERROR signal to its client. Alejandro 970603
2020 // This function is bit problematic when it comes to NLS, to make the
2021 // lyx servers client be language indepenent we must not translate
2022 // strings sent to this func.
2023 void LyXFunc::setErrorMessage(docstring const & m) const
2025 dispatch_buffer = m;
2030 void LyXFunc::setMessage(docstring const & m) const
2032 dispatch_buffer = m;
2036 string const LyXFunc::viewStatusMessage()
2038 // When meta-fake key is pressed, show the key sequence so far + "M-".
2040 return keyseq->print() + "M-";
2042 // Else, when a non-complete key sequence is pressed,
2043 // show the available options.
2044 if (keyseq->length() > 0 && !keyseq->deleted())
2045 return keyseq->printOptions();
2047 if (!view()->buffer())
2048 return to_utf8(_("Welcome to LyX!"));
2050 return view()->cursor().currentState();
2054 BufferView * LyXFunc::view() const
2056 BOOST_ASSERT(lyx_view_);
2057 return lyx_view_->view();
2061 bool LyXFunc::wasMetaKey() const
2063 return (meta_fake_bit != key_modifier::none);
2069 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2071 // Why the switch you might ask. It is a trick to ensure that all
2072 // the elements in the LyXRCTags enum is handled. As you can see
2073 // there are no breaks at all. So it is just a huge fall-through.
2074 // The nice thing is that we will get a warning from the compiler
2075 // if we forget an element.
2076 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2078 case LyXRC::RC_ACCEPT_COMPOUND:
2079 case LyXRC::RC_ALT_LANG:
2080 case LyXRC::RC_ASCIIROFF_COMMAND:
2081 case LyXRC::RC_ASCII_LINELEN:
2082 case LyXRC::RC_AUTOREGIONDELETE:
2083 case LyXRC::RC_AUTORESET_OPTIONS:
2084 case LyXRC::RC_AUTOSAVE:
2085 case LyXRC::RC_AUTO_NUMBER:
2086 case LyXRC::RC_BACKUPDIR_PATH:
2087 case LyXRC::RC_BIBTEX_COMMAND:
2088 case LyXRC::RC_BINDFILE:
2089 case LyXRC::RC_CHECKLASTFILES:
2090 case LyXRC::RC_USELASTFILEPOS:
2091 case LyXRC::RC_LOADSESSION:
2092 case LyXRC::RC_CHKTEX_COMMAND:
2093 case LyXRC::RC_CONVERTER:
2094 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2095 case LyXRC::RC_COPIER:
2096 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2097 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2098 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2099 case LyXRC::RC_DATE_INSERT_FORMAT:
2100 case LyXRC::RC_DEFAULT_LANGUAGE:
2101 case LyXRC::RC_DEFAULT_PAPERSIZE:
2102 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2103 case LyXRC::RC_DISPLAY_GRAPHICS:
2104 case LyXRC::RC_DOCUMENTPATH:
2105 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2106 if (fs::exists(lyxrc_new.document_path) &&
2107 fs::is_directory(lyxrc_new.document_path)) {
2108 support::package().document_dir() = lyxrc.document_path;
2111 case LyXRC::RC_ESC_CHARS:
2112 case LyXRC::RC_FONT_ENCODING:
2113 case LyXRC::RC_FORMAT:
2114 case LyXRC::RC_INDEX_COMMAND:
2115 case LyXRC::RC_INPUT:
2116 case LyXRC::RC_KBMAP:
2117 case LyXRC::RC_KBMAP_PRIMARY:
2118 case LyXRC::RC_KBMAP_SECONDARY:
2119 case LyXRC::RC_LABEL_INIT_LENGTH:
2120 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2121 case LyXRC::RC_LANGUAGE_AUTO_END:
2122 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2123 case LyXRC::RC_LANGUAGE_COMMAND_END:
2124 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2125 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2126 case LyXRC::RC_LANGUAGE_PACKAGE:
2127 case LyXRC::RC_LANGUAGE_USE_BABEL:
2128 case LyXRC::RC_MAKE_BACKUP:
2129 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2130 case LyXRC::RC_NUMLASTFILES:
2131 case LyXRC::RC_PATH_PREFIX:
2132 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2133 support::prependEnvPath("PATH", lyxrc.path_prefix);
2135 case LyXRC::RC_PERS_DICT:
2136 case LyXRC::RC_POPUP_BOLD_FONT:
2137 case LyXRC::RC_POPUP_FONT_ENCODING:
2138 case LyXRC::RC_POPUP_NORMAL_FONT:
2139 case LyXRC::RC_PREVIEW:
2140 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2141 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2142 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2143 case LyXRC::RC_PRINTCOPIESFLAG:
2144 case LyXRC::RC_PRINTER:
2145 case LyXRC::RC_PRINTEVENPAGEFLAG:
2146 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2147 case LyXRC::RC_PRINTFILEEXTENSION:
2148 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2149 case LyXRC::RC_PRINTODDPAGEFLAG:
2150 case LyXRC::RC_PRINTPAGERANGEFLAG:
2151 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2152 case LyXRC::RC_PRINTPAPERFLAG:
2153 case LyXRC::RC_PRINTREVERSEFLAG:
2154 case LyXRC::RC_PRINTSPOOL_COMMAND:
2155 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2156 case LyXRC::RC_PRINTTOFILE:
2157 case LyXRC::RC_PRINTTOPRINTER:
2158 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2159 case LyXRC::RC_PRINT_COMMAND:
2160 case LyXRC::RC_RTL_SUPPORT:
2161 case LyXRC::RC_SCREEN_DPI:
2162 case LyXRC::RC_SCREEN_FONT_ENCODING:
2163 case LyXRC::RC_SCREEN_FONT_ROMAN:
2164 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2165 case LyXRC::RC_SCREEN_FONT_SANS:
2166 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2167 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2168 case LyXRC::RC_SCREEN_FONT_SIZES:
2169 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2170 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2171 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2172 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2173 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2174 case LyXRC::RC_SCREEN_ZOOM:
2175 case LyXRC::RC_SERVERPIPE:
2176 case LyXRC::RC_SET_COLOR:
2177 case LyXRC::RC_SHOW_BANNER:
2178 case LyXRC::RC_SPELL_COMMAND:
2179 case LyXRC::RC_TEMPDIRPATH:
2180 case LyXRC::RC_TEMPLATEPATH:
2181 case LyXRC::RC_TEX_ALLOWS_SPACES:
2182 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2183 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2184 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2186 case LyXRC::RC_UIFILE:
2187 case LyXRC::RC_USER_EMAIL:
2188 case LyXRC::RC_USER_NAME:
2189 case LyXRC::RC_USETEMPDIR:
2190 case LyXRC::RC_USE_ALT_LANG:
2191 case LyXRC::RC_USE_CONVERTER_CACHE:
2192 case LyXRC::RC_USE_ESC_CHARS:
2193 case LyXRC::RC_USE_INP_ENC:
2194 case LyXRC::RC_USE_PERS_DICT:
2195 case LyXRC::RC_USE_SPELL_LIB:
2196 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2197 case LyXRC::RC_VIEWER:
2198 case LyXRC::RC_LAST: