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"
56 #include "lyxserver.h"
57 #include "lyxtextclasslist.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/LyXKeySym.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/forkedcontr.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;
148 extern tex_accent_struct get_accent(kb_action action);
153 bool getLocalStatus(LCursor cursor,
154 FuncRequest const & cmd, FuncStatus & status)
156 // Try to fix cursor in case it is broken.
157 cursor.fixIfBroken();
159 // This is, of course, a mess. Better create a new doc iterator and use
160 // this in Inset::getStatus. This might require an additional
161 // BufferView * arg, though (which should be avoided)
162 //LCursor safe = *this;
164 for ( ; cursor.depth(); cursor.pop()) {
165 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
166 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
167 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
168 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
170 // The inset's getStatus() will return 'true' if it made
171 // a definitive decision on whether it want to handle the
172 // request or not. The result of this decision is put into
173 // the 'status' parameter.
174 if (cursor.inset().getStatus(cursor, cmd, status)) {
183 /** Return the change status at cursor position, taking in account the
184 * status at each level of the document iterator (a table in a deleted
185 * footnote is deleted).
186 * When \param outer is true, the top slice is not looked at.
188 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
190 size_t const depth = dit.depth() - (outer ? 1 : 0);
192 for (size_t i = 0 ; i < depth ; ++i) {
193 CursorSlice const & slice = dit[i];
194 if (!slice.inset().inMathed()
195 && slice.pos() < slice.paragraph().size()) {
196 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
197 if (ch != Change::UNCHANGED)
201 return Change::UNCHANGED;
209 meta_fake_bit(key_modifier::none)
214 void LyXFunc::initKeySequences(kb_keymap * kb)
216 keyseq.reset(new kb_sequence(kb, kb));
217 cancel_meta_seq.reset(new kb_sequence(kb, kb));
221 void LyXFunc::setLyXView(LyXView * lv)
227 void LyXFunc::handleKeyFunc(kb_action action)
229 char_type c = encoded_last_key;
231 if (keyseq->length())
234 lyx_view_->view()->getIntl().getTransManager().deadkey(
235 c, get_accent(action).accent, view()->getLyXText(), view()->cursor());
236 // Need to clear, in case the minibuffer calls these
239 // copied verbatim from do_accent_char
240 view()->cursor().resetAnchor();
245 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
247 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
249 // Do nothing if we have nothing (JMarc)
250 if (!keysym->isOK()) {
251 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
256 if (keysym->isModifier()) {
257 lyxerr[Debug::KEY] << "isModifier true" << endl;
261 //Encoding const * encoding = view()->cursor().getEncoding();
262 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
263 size_t encoded_last_key = keysym->getUCSEncoded();
265 // Do a one-deep top-level lookup for
266 // cancel and meta-fake keys. RVDK_PATCH_5
267 cancel_meta_seq->reset();
269 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
270 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
271 << " action first set to [" << func.action << ']'
274 // When not cancel or meta-fake, do the normal lookup.
275 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
276 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
277 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
278 // remove Caps Lock and Mod2 as a modifiers
279 func = keyseq->addkey(keysym, (state | meta_fake_bit));
280 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
281 << "action now set to ["
282 << func.action << ']' << endl;
285 // Dont remove this unless you know what you are doing.
286 meta_fake_bit = key_modifier::none;
288 // Can this happen now ?
289 if (func.action == LFUN_NOACTION) {
290 func = FuncRequest(LFUN_COMMAND_PREFIX);
293 if (lyxerr.debugging(Debug::KEY)) {
294 lyxerr << BOOST_CURRENT_FUNCTION
296 << func.action << "]["
297 << to_utf8(keyseq->print(false)) << ']'
301 // already here we know if it any point in going further
302 // why not return already here if action == -1 and
303 // num_bytes == 0? (Lgb)
305 if (keyseq->length() > 1) {
306 lyx_view_->message(keyseq->print(true));
310 // Maybe user can only reach the key via holding down shift.
311 // Let's see. But only if shift is the only modifier
312 if (func.action == LFUN_UNKNOWN_ACTION &&
313 state == key_modifier::shift) {
314 lyxerr[Debug::KEY] << "Trying without shift" << endl;
315 func = keyseq->addkey(keysym, key_modifier::none);
316 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
319 if (func.action == LFUN_UNKNOWN_ACTION) {
320 // Hmm, we didn't match any of the keysequences. See
321 // if it's normal insertable text not already covered
323 if (keysym->isText() && keyseq->length() == 1) {
324 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
325 func = FuncRequest(LFUN_SELF_INSERT,
326 FuncRequest::KEYBOARD);
328 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
329 lyx_view_->message(_("Unknown function."));
334 if (func.action == LFUN_SELF_INSERT) {
335 if (encoded_last_key != 0) {
336 docstring const arg(1, encoded_last_key);
337 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
338 FuncRequest::KEYBOARD));
340 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
348 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
350 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
353 if (cmd.action == LFUN_LYX_QUIT) {
354 flag.message(from_utf8(N_("Exiting")));
357 } else if (cmd.action == LFUN_BOOKMARK_GOTO) {
358 // bookmarks can be valid even if there is no opened buffer
359 flag.enabled(LyX::ref().session().bookmarks().isValid(convert<unsigned int>(to_utf8(cmd.argument()))));
361 } else if (cmd.action == LFUN_BOOKMARK_CLEAR) {
362 flag.enabled(LyX::ref().session().bookmarks().size() > 0);
364 } else if (cmd.action == LFUN_TOOLBAR_TOGGLE_STATE) {
365 ToolbarBackend::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
366 if (!(flags & ToolbarBackend::AUTO))
367 flag.setOnOff(flags & ToolbarBackend::ON);
371 LCursor & cur = view()->cursor();
373 /* In LyX/Mac, when a dialog is open, the menus of the
374 application can still be accessed without giving focus to
375 the main window. In this case, we want to disable the menu
376 entries that are buffer-related.
378 Note that this code is not perfect, as bug 1941 attests:
379 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
381 Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
382 if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
385 if (cmd.action == LFUN_NOACTION) {
386 flag.message(from_utf8(N_("Nothing to do")));
391 switch (cmd.action) {
392 case LFUN_UNKNOWN_ACTION:
393 #ifndef HAVE_LIBAIKSAURUS
394 case LFUN_THESAURUS_ENTRY:
404 if (flag.unknown()) {
405 flag.message(from_utf8(N_("Unknown action")));
409 if (!flag.enabled()) {
410 if (flag.message().empty())
411 flag.message(from_utf8(N_("Command disabled")));
415 // Check whether we need a buffer
416 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
418 flag.message(from_utf8(N_("Command not allowed with"
419 "out any document open")));
424 // I would really like to avoid having this switch and rather try to
425 // encode this in the function itself.
426 // -- And I'd rather let an inset decide which LFUNs it is willing
427 // to handle (Andre')
429 switch (cmd.action) {
430 case LFUN_BUFFER_TOGGLE_READ_ONLY:
431 flag.setOnOff(buf->isReadonly());
434 case LFUN_BUFFER_SWITCH:
435 // toggle on the current buffer, but do not toggle off
436 // the other ones (is that a good idea?)
437 if (to_utf8(cmd.argument()) == buf->fileName())
441 case LFUN_BUFFER_EXPORT:
442 enable = cmd.argument() == "custom"
443 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
446 case LFUN_BUFFER_CHKTEX:
447 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
450 case LFUN_BUILD_PROGRAM:
451 enable = Exporter::isExportable(*buf, "program");
454 case LFUN_LAYOUT_TABULAR:
455 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
459 case LFUN_LAYOUT_PARAGRAPH:
460 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
463 case LFUN_VC_REGISTER:
464 enable = !buf->lyxvc().inUse();
466 case LFUN_VC_CHECK_IN:
467 enable = buf->lyxvc().inUse() && !buf->isReadonly();
469 case LFUN_VC_CHECK_OUT:
470 enable = buf->lyxvc().inUse() && buf->isReadonly();
473 case LFUN_VC_UNDO_LAST:
474 enable = buf->lyxvc().inUse();
476 case LFUN_BUFFER_RELOAD:
477 enable = !buf->isUnnamed() && !buf->isClean();
480 case LFUN_INSET_SETTINGS: {
484 InsetBase::Code code = cur.inset().lyxCode();
486 case InsetBase::TABULAR_CODE:
487 enable = cmd.argument() == "tabular";
489 case InsetBase::ERT_CODE:
490 enable = cmd.argument() == "ert";
492 case InsetBase::FLOAT_CODE:
493 enable = cmd.argument() == "float";
495 case InsetBase::WRAP_CODE:
496 enable = cmd.argument() == "wrap";
498 case InsetBase::NOTE_CODE:
499 enable = cmd.argument() == "note";
501 case InsetBase::BRANCH_CODE:
502 enable = cmd.argument() == "branch";
504 case InsetBase::BOX_CODE:
505 enable = cmd.argument() == "box";
513 case LFUN_INSET_APPLY: {
514 string const name = cmd.getArg(0);
515 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
517 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
519 if (!inset->getStatus(cur, fr, fs)) {
520 // Every inset is supposed to handle this
525 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
526 flag |= getStatus(fr);
528 enable = flag.enabled();
532 case LFUN_DIALOG_SHOW: {
533 string const name = cmd.getArg(0);
535 enable = name == "aboutlyx"
539 || name == "texinfo";
540 else if (name == "print")
541 enable = Exporter::isExportable(*buf, "dvi")
542 && lyxrc.print_command != "none";
543 else if (name == "character" || name == "mathpanel")
544 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
545 else if (name == "latexlog")
546 enable = isFileReadable(FileName(buf->getLogName().second));
547 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
548 else if (name == "spellchecker")
551 else if (name == "vclog")
552 enable = buf->lyxvc().inUse();
553 else if (name == "view-source")
558 case LFUN_DIALOG_SHOW_NEW_INSET:
559 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
562 case LFUN_DIALOG_UPDATE: {
563 string const name = cmd.getArg(0);
565 enable = name == "prefs";
569 case LFUN_CITATION_INSERT: {
570 FuncRequest fr(LFUN_INSET_INSERT, "citation");
571 enable = getStatus(fr).enabled();
575 case LFUN_BUFFER_WRITE: {
576 enable = view()->buffer()->isUnnamed()
577 || !view()->buffer()->isClean();
582 // this one is difficult to get right. As a half-baked
583 // solution, we consider only the first action of the sequence
584 case LFUN_COMMAND_SEQUENCE: {
585 // argument contains ';'-terminated commands
586 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
587 FuncRequest func(lyxaction.lookupFunc(firstcmd));
588 func.origin = cmd.origin;
589 flag = getStatus(func);
592 case LFUN_BUFFER_NEW:
593 case LFUN_BUFFER_NEW_TEMPLATE:
594 case LFUN_WORD_FIND_FORWARD:
595 case LFUN_WORD_FIND_BACKWARD:
596 case LFUN_COMMAND_PREFIX:
597 case LFUN_COMMAND_EXECUTE:
599 case LFUN_META_PREFIX:
600 case LFUN_BUFFER_CLOSE:
601 case LFUN_BUFFER_WRITE_AS:
602 case LFUN_BUFFER_UPDATE:
603 case LFUN_BUFFER_VIEW:
604 case LFUN_BUFFER_IMPORT:
606 case LFUN_BUFFER_AUTO_SAVE:
607 case LFUN_RECONFIGURE:
611 case LFUN_DROP_LAYOUTS_CHOICE:
613 case LFUN_SERVER_GET_NAME:
614 case LFUN_SERVER_NOTIFY:
615 case LFUN_SERVER_GOTO_FILE_ROW:
616 case LFUN_DIALOG_HIDE:
617 case LFUN_DIALOG_DISCONNECT_INSET:
618 case LFUN_BUFFER_CHILD_OPEN:
619 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
620 case LFUN_KEYMAP_OFF:
621 case LFUN_KEYMAP_PRIMARY:
622 case LFUN_KEYMAP_SECONDARY:
623 case LFUN_KEYMAP_TOGGLE:
625 case LFUN_BUFFER_EXPORT_CUSTOM:
626 case LFUN_BUFFER_PRINT:
627 case LFUN_PREFERENCES_SAVE:
628 case LFUN_SCREEN_FONT_UPDATE:
631 case LFUN_EXTERNAL_EDIT:
632 case LFUN_GRAPHICS_EDIT:
633 case LFUN_ALL_INSETS_TOGGLE:
634 case LFUN_BUFFER_LANGUAGE:
635 case LFUN_TEXTCLASS_APPLY:
636 case LFUN_TEXTCLASS_LOAD:
637 case LFUN_BUFFER_SAVE_AS_DEFAULT:
638 case LFUN_BUFFER_PARAMS_APPLY:
639 case LFUN_LYXRC_APPLY:
640 case LFUN_BUFFER_NEXT:
641 case LFUN_BUFFER_PREVIOUS:
642 case LFUN_WINDOW_NEW:
643 case LFUN_WINDOW_CLOSE:
644 // these are handled in our dispatch()
648 if (!getLocalStatus(cur, cmd, flag))
649 flag = view()->getStatus(cmd);
655 // Can we use a readonly buffer?
656 if (buf && buf->isReadonly()
657 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
658 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
659 flag.message(from_utf8(N_("Document is read-only")));
663 // Are we in a DELETED change-tracking region?
664 if (buf && lookupChangeType(cur, true) == Change::DELETED
665 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
666 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
667 flag.message(from_utf8(N_("This portion of the document is deleted.")));
671 // the default error message if we disable the command
672 if (!flag.enabled() && flag.message().empty())
673 flag.message(from_utf8(N_("Command disabled")));
679 bool LyXFunc::ensureBufferClean(BufferView * bv)
681 Buffer & buf = *bv->buffer();
685 docstring const file = makeDisplayPath(buf.fileName(), 30);
686 docstring text = bformat(_("The document %1$s has unsaved "
687 "changes.\n\nDo you want to save "
688 "the document?"), file);
689 int const ret = Alert::prompt(_("Save changed document?"),
690 text, 0, 1, _("&Save"),
694 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
696 return buf.isClean();
702 void showPrintError(string const & name)
704 docstring str = bformat(_("Could not print the document %1$s.\n"
705 "Check that your printer is set up correctly."),
706 makeDisplayPath(name, 50));
707 Alert::error(_("Print document failed"), str);
711 void loadTextclass(string const & name)
713 std::pair<bool, textclass_type> const tc_pair =
714 textclasslist.numberOfClass(name);
716 if (!tc_pair.first) {
717 lyxerr << "Document class \"" << name
718 << "\" does not exist."
723 textclass_type const tc = tc_pair.second;
725 if (!textclasslist[tc].load()) {
726 docstring s = bformat(_("The document could not be converted\n"
727 "into the document class %1$s."),
728 from_utf8(textclasslist[tc].name()));
729 Alert::error(_("Could not change class"), s);
734 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
739 void LyXFunc::dispatch(FuncRequest const & cmd)
741 string const argument = to_utf8(cmd.argument());
742 kb_action const action = cmd.action;
744 lyxerr[Debug::ACTION] << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
745 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
747 // we have not done anything wrong yet.
749 dispatch_buffer.erase();
751 // redraw the screen at the end (first of the two drawing steps).
752 //This is done unless explicitely requested otherwise
753 Update::flags updateFlags = Update::FitCursor;
755 FuncStatus const flag = getStatus(cmd);
756 if (!flag.enabled()) {
757 // We cannot use this function here
758 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
759 << lyxaction.getActionName(action)
760 << " [" << action << "] is disabled at this location"
762 setErrorMessage(flag.message());
766 case LFUN_WORD_FIND_FORWARD:
767 case LFUN_WORD_FIND_BACKWARD: {
768 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
769 static docstring last_search;
770 docstring searched_string;
772 if (!cmd.argument().empty()) {
773 last_search = cmd.argument();
774 searched_string = cmd.argument();
776 searched_string = last_search;
779 if (searched_string.empty())
782 bool const fw = action == LFUN_WORD_FIND_FORWARD;
783 docstring const data =
784 find2string(searched_string, true, false, fw);
785 find(view(), FuncRequest(LFUN_WORD_FIND, data));
789 case LFUN_COMMAND_PREFIX:
790 BOOST_ASSERT(lyx_view_);
791 lyx_view_->message(keyseq->printOptions(true));
794 case LFUN_COMMAND_EXECUTE:
795 BOOST_ASSERT(lyx_view_);
796 lyx_view_->getToolbars().display("minibuffer", true);
797 lyx_view_->focus_command_buffer();
801 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
803 meta_fake_bit = key_modifier::none;
804 if (view()->buffer())
805 // cancel any selection
806 dispatch(FuncRequest(LFUN_MARK_OFF));
807 setMessage(_("Cancel"));
810 case LFUN_META_PREFIX:
811 meta_fake_bit = key_modifier::alt;
812 setMessage(keyseq->print(true));
815 case LFUN_BUFFER_TOGGLE_READ_ONLY:
816 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
817 if (lyx_view_->buffer()->lyxvc().inUse())
818 lyx_view_->buffer()->lyxvc().toggleReadOnly();
820 lyx_view_->buffer()->setReadonly(
821 !lyx_view_->buffer()->isReadonly());
824 // --- Menus -----------------------------------------------
825 case LFUN_BUFFER_NEW:
826 menuNew(argument, false);
829 case LFUN_BUFFER_NEW_TEMPLATE:
830 menuNew(argument, true);
833 case LFUN_BUFFER_CLOSE:
838 case LFUN_BUFFER_WRITE:
839 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
840 if (!lyx_view_->buffer()->isUnnamed()) {
841 docstring const str = bformat(_("Saving document %1$s..."),
842 makeDisplayPath(lyx_view_->buffer()->fileName()));
843 lyx_view_->message(str);
844 menuWrite(lyx_view_->buffer());
845 lyx_view_->message(str + _(" done."));
847 writeAs(lyx_view_->buffer());
848 updateFlags = Update::None;
851 case LFUN_BUFFER_WRITE_AS:
852 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
853 writeAs(lyx_view_->buffer(), argument);
854 updateFlags = Update::None;
857 case LFUN_BUFFER_RELOAD: {
858 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
859 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
860 docstring text = bformat(_("Any changes will be lost. Are you sure "
861 "you want to revert to the saved version of the document %1$s?"), file);
862 int const ret = Alert::prompt(_("Revert to saved document?"),
863 text, 0, 1, _("&Revert"), _("&Cancel"));
870 case LFUN_BUFFER_UPDATE:
871 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
872 Exporter::Export(lyx_view_->buffer(), argument, true);
875 case LFUN_BUFFER_VIEW:
876 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
877 Exporter::preview(lyx_view_->buffer(), argument);
880 case LFUN_BUILD_PROGRAM:
881 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
882 Exporter::Export(lyx_view_->buffer(), "program", true);
885 case LFUN_BUFFER_CHKTEX:
886 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
887 lyx_view_->buffer()->runChktex();
890 case LFUN_BUFFER_EXPORT:
891 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
892 if (argument == "custom")
893 lyx_view_->getDialogs().show("sendto");
895 Exporter::Export(lyx_view_->buffer(), argument, false);
899 case LFUN_BUFFER_EXPORT_CUSTOM: {
900 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
902 string command = split(argument, format_name, ' ');
903 Format const * format = formats.getFormat(format_name);
905 lyxerr << "Format \"" << format_name
906 << "\" not recognized!"
911 Buffer * buffer = lyx_view_->buffer();
913 // The name of the file created by the conversion process
916 // Output to filename
917 if (format->name() == "lyx") {
918 string const latexname =
919 buffer->getLatexName(false);
920 filename = changeExtension(latexname,
921 format->extension());
922 filename = addName(buffer->temppath(), filename);
924 if (!buffer->writeFile(FileName(filename)))
928 Exporter::Export(buffer, format_name, true, filename);
931 // Substitute $$FName for filename
932 if (!contains(command, "$$FName"))
933 command = "( " + command + " ) < $$FName";
934 command = subst(command, "$$FName", filename);
936 // Execute the command in the background
938 call.startscript(Systemcall::DontWait, command);
942 case LFUN_BUFFER_PRINT: {
943 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
946 string command = split(split(argument, target, ' '),
950 || target_name.empty()
951 || command.empty()) {
952 lyxerr << "Unable to parse \""
953 << argument << '"' << std::endl;
956 if (target != "printer" && target != "file") {
957 lyxerr << "Unrecognized target \""
958 << target << '"' << std::endl;
962 Buffer * buffer = lyx_view_->buffer();
964 if (!Exporter::Export(buffer, "dvi", true)) {
965 showPrintError(buffer->fileName());
969 // Push directory path.
970 string const path = buffer->temppath();
971 support::Path p(path);
973 // there are three cases here:
974 // 1. we print to a file
975 // 2. we print directly to a printer
976 // 3. we print using a spool command (print to file first)
979 string const dviname =
980 changeExtension(buffer->getLatexName(true),
983 if (target == "printer") {
984 if (!lyxrc.print_spool_command.empty()) {
985 // case 3: print using a spool
986 string const psname =
987 changeExtension(dviname,".ps");
988 command += lyxrc.print_to_file
991 + quoteName(dviname);
994 lyxrc.print_spool_command +' ';
995 if (target_name != "default") {
996 command2 += lyxrc.print_spool_printerprefix
1000 command2 += quoteName(psname);
1002 // If successful, then spool command
1003 res = one.startscript(
1008 res = one.startscript(
1009 Systemcall::DontWait,
1012 // case 2: print directly to a printer
1013 res = one.startscript(
1014 Systemcall::DontWait,
1015 command + quoteName(dviname));
1019 // case 1: print to a file
1020 command += lyxrc.print_to_file
1021 + quoteName(makeAbsPath(target_name,
1022 path).toFilesystemEncoding())
1024 + quoteName(dviname);
1025 res = one.startscript(Systemcall::DontWait,
1030 showPrintError(buffer->fileName());
1034 case LFUN_BUFFER_IMPORT:
1039 // quitting is triggered by the gui code
1040 // (leaving the event loop).
1041 if (theBufferList().quitWriteAll())
1042 theApp()->gui().closeAllViews();
1045 case LFUN_TOC_VIEW: {
1046 BOOST_ASSERT(lyx_view_);
1047 InsetCommandParams p("tableofcontents");
1048 string const data = InsetCommandMailer::params2string("toc", p);
1049 lyx_view_->getDialogs().show("toc", data, 0);
1053 case LFUN_BUFFER_AUTO_SAVE:
1057 case LFUN_RECONFIGURE:
1058 BOOST_ASSERT(lyx_view_);
1059 reconfigure(*lyx_view_);
1062 case LFUN_HELP_OPEN: {
1063 BOOST_ASSERT(lyx_view_);
1064 string const arg = argument;
1066 setErrorMessage(_("Missing argument"));
1069 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1070 if (fname.empty()) {
1071 lyxerr << "LyX: unable to find documentation file `"
1072 << arg << "'. Bad installation?" << endl;
1075 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1076 makeDisplayPath(fname.absFilename())));
1077 lyx_view_->loadLyXFile(fname, false);
1081 // --- version control -------------------------------
1082 case LFUN_VC_REGISTER:
1083 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1084 if (!ensureBufferClean(view()))
1086 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1087 lyx_view_->buffer()->lyxvc().registrer();
1092 case LFUN_VC_CHECK_IN:
1093 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1094 if (!ensureBufferClean(view()))
1096 if (lyx_view_->buffer()->lyxvc().inUse()
1097 && !lyx_view_->buffer()->isReadonly()) {
1098 lyx_view_->buffer()->lyxvc().checkIn();
1103 case LFUN_VC_CHECK_OUT:
1104 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1105 if (!ensureBufferClean(view()))
1107 if (lyx_view_->buffer()->lyxvc().inUse()
1108 && lyx_view_->buffer()->isReadonly()) {
1109 lyx_view_->buffer()->lyxvc().checkOut();
1114 case LFUN_VC_REVERT:
1115 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1116 lyx_view_->buffer()->lyxvc().revert();
1120 case LFUN_VC_UNDO_LAST:
1121 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1122 lyx_view_->buffer()->lyxvc().undoLast();
1126 // --- buffers ----------------------------------------
1127 case LFUN_BUFFER_SWITCH:
1128 BOOST_ASSERT(lyx_view_);
1129 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1132 case LFUN_BUFFER_NEXT:
1133 BOOST_ASSERT(lyx_view_);
1134 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1137 case LFUN_BUFFER_PREVIOUS:
1138 BOOST_ASSERT(lyx_view_);
1139 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1143 BOOST_ASSERT(lyx_view_);
1144 newFile(view(), argument);
1147 case LFUN_FILE_OPEN:
1148 BOOST_ASSERT(lyx_view_);
1152 case LFUN_DROP_LAYOUTS_CHOICE:
1153 BOOST_ASSERT(lyx_view_);
1154 lyx_view_->getToolbars().openLayoutList();
1157 case LFUN_MENU_OPEN:
1158 BOOST_ASSERT(lyx_view_);
1159 lyx_view_->getMenubar().openByName(from_utf8(argument));
1162 // --- lyxserver commands ----------------------------
1163 case LFUN_SERVER_GET_NAME:
1164 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1165 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1166 lyxerr[Debug::INFO] << "FNAME["
1167 << lyx_view_->buffer()->fileName()
1171 case LFUN_SERVER_NOTIFY:
1172 dispatch_buffer = keyseq->print(false);
1173 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1176 case LFUN_SERVER_GOTO_FILE_ROW: {
1177 BOOST_ASSERT(lyx_view_);
1180 istringstream is(argument);
1181 is >> file_name >> row;
1182 if (prefixIs(file_name, package().temp_dir())) {
1183 // Needed by inverse dvi search. If it is a file
1184 // in tmpdir, call the apropriated function
1185 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1187 // Must replace extension of the file to be .lyx
1188 // and get full path
1189 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1190 // Either change buffer or load the file
1191 if (theBufferList().exists(s.absFilename())) {
1192 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1194 lyx_view_->loadLyXFile(s);
1198 view()->setCursorFromRow(row);
1201 // see BufferView::center()
1205 case LFUN_DIALOG_SHOW: {
1206 BOOST_ASSERT(lyx_view_);
1207 string const name = cmd.getArg(0);
1208 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1210 if (name == "character") {
1211 data = freefont2string();
1213 lyx_view_->getDialogs().show("character", data);
1214 } else if (name == "latexlog") {
1215 pair<Buffer::LogType, string> const logfile =
1216 lyx_view_->buffer()->getLogName();
1217 switch (logfile.first) {
1218 case Buffer::latexlog:
1221 case Buffer::buildlog:
1225 data += LyXLex::quoteString(logfile.second);
1226 lyx_view_->getDialogs().show("log", data);
1227 } else if (name == "vclog") {
1228 string const data = "vc " +
1229 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1230 lyx_view_->getDialogs().show("log", data);
1232 lyx_view_->getDialogs().show(name, data);
1236 case LFUN_DIALOG_SHOW_NEW_INSET: {
1237 BOOST_ASSERT(lyx_view_);
1238 string const name = cmd.getArg(0);
1239 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1240 if (name == "bibitem" ||
1244 name == "nomenclature" ||
1248 InsetCommandParams p(name);
1249 data = InsetCommandMailer::params2string(name, p);
1250 } else if (name == "include") {
1251 // data is the include type: one of "include",
1252 // "input", "verbatiminput" or "verbatiminput*"
1254 // default type is requested
1256 InsetCommandParams p(data);
1257 data = InsetIncludeMailer::params2string(p);
1258 } else if (name == "box") {
1259 // \c data == "Boxed" || "Frameless" etc
1260 InsetBoxParams p(data);
1261 data = InsetBoxMailer::params2string(p);
1262 } else if (name == "branch") {
1263 InsetBranchParams p;
1264 data = InsetBranchMailer::params2string(p);
1265 } else if (name == "citation") {
1266 InsetCommandParams p("cite");
1267 data = InsetCommandMailer::params2string(name, p);
1268 } else if (name == "ert") {
1269 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1270 } else if (name == "external") {
1271 InsetExternalParams p;
1272 Buffer const & buffer = *lyx_view_->buffer();
1273 data = InsetExternalMailer::params2string(p, buffer);
1274 } else if (name == "float") {
1276 data = InsetFloatMailer::params2string(p);
1277 } else if (name == "graphics") {
1278 InsetGraphicsParams p;
1279 Buffer const & buffer = *lyx_view_->buffer();
1280 data = InsetGraphicsMailer::params2string(p, buffer);
1281 } else if (name == "note") {
1283 data = InsetNoteMailer::params2string(p);
1284 } else if (name == "vspace") {
1286 data = InsetVSpaceMailer::params2string(space);
1287 } else if (name == "wrap") {
1289 data = InsetWrapMailer::params2string(p);
1291 lyx_view_->getDialogs().show(name, data, 0);
1295 case LFUN_DIALOG_UPDATE: {
1296 BOOST_ASSERT(lyx_view_);
1297 string const & name = argument;
1298 // Can only update a dialog connected to an existing inset
1299 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1301 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1302 inset->dispatch(view()->cursor(), fr);
1303 } else if (name == "paragraph") {
1304 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1305 } else if (name == "prefs") {
1306 lyx_view_->getDialogs().update(name, string());
1311 case LFUN_DIALOG_HIDE:
1312 Dialogs::hide(argument, 0);
1315 case LFUN_DIALOG_DISCONNECT_INSET:
1316 BOOST_ASSERT(lyx_view_);
1317 lyx_view_->getDialogs().disconnect(argument);
1321 case LFUN_CITATION_INSERT: {
1322 BOOST_ASSERT(lyx_view_);
1323 if (!argument.empty()) {
1324 // we can have one optional argument, delimited by '|'
1325 // citation-insert <key>|<text_before>
1326 // this should be enhanced to also support text_after
1327 // and citation style
1328 string arg = argument;
1330 if (contains(argument, "|")) {
1331 arg = token(argument, '|', 0);
1332 opt1 = '[' + token(argument, '|', 1) + ']';
1334 std::ostringstream os;
1335 os << "citation LatexCommand\n"
1336 << "\\cite" << opt1 << "{" << arg << "}\n"
1338 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1341 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1345 case LFUN_BUFFER_CHILD_OPEN: {
1346 BOOST_ASSERT(lyx_view_);
1347 FileName const filename =
1348 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1349 // FIXME Should use bformat
1350 setMessage(_("Opening child document ") +
1351 makeDisplayPath(filename.absFilename()) + "...");
1352 view()->saveBookmark(false);
1353 string const parentfilename = lyx_view_->buffer()->fileName();
1354 if (theBufferList().exists(filename.absFilename()))
1355 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1357 lyx_view_->loadLyXFile(filename);
1358 // Set the parent name of the child document.
1359 // This makes insertion of citations and references in the child work,
1360 // when the target is in the parent or another child document.
1361 lyx_view_->buffer()->setParentName(parentfilename);
1365 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1366 BOOST_ASSERT(lyx_view_);
1367 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1370 case LFUN_KEYMAP_OFF:
1371 BOOST_ASSERT(lyx_view_);
1372 lyx_view_->view()->getIntl().keyMapOn(false);
1375 case LFUN_KEYMAP_PRIMARY:
1376 BOOST_ASSERT(lyx_view_);
1377 lyx_view_->view()->getIntl().keyMapPrim();
1380 case LFUN_KEYMAP_SECONDARY:
1381 BOOST_ASSERT(lyx_view_);
1382 lyx_view_->view()->getIntl().keyMapSec();
1385 case LFUN_KEYMAP_TOGGLE:
1386 BOOST_ASSERT(lyx_view_);
1387 lyx_view_->view()->getIntl().toggleKeyMap();
1393 string rest = split(argument, countstr, ' ');
1394 istringstream is(countstr);
1397 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1398 for (int i = 0; i < count; ++i)
1399 dispatch(lyxaction.lookupFunc(rest));
1403 case LFUN_COMMAND_SEQUENCE: {
1404 // argument contains ';'-terminated commands
1405 string arg = argument;
1406 while (!arg.empty()) {
1408 arg = split(arg, first, ';');
1409 FuncRequest func(lyxaction.lookupFunc(first));
1410 func.origin = cmd.origin;
1416 case LFUN_PREFERENCES_SAVE: {
1417 lyxrc.write(makeAbsPath("preferences",
1418 package().user_support()),
1423 case LFUN_SCREEN_FONT_UPDATE:
1424 BOOST_ASSERT(lyx_view_);
1425 // handle the screen font changes.
1426 lyxrc.set_font_norm_type();
1427 theFontLoader().update();
1428 // All visible buffers will need resize
1432 case LFUN_SET_COLOR: {
1434 string const x11_name = split(argument, lyx_name, ' ');
1435 if (lyx_name.empty() || x11_name.empty()) {
1436 setErrorMessage(_("Syntax: set-color <lyx_name>"
1441 bool const graphicsbg_changed =
1442 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1443 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1445 if (!lcolor.setColor(lyx_name, x11_name)) {
1447 bformat(_("Set-color \"%1$s\" failed "
1448 "- color is undefined or "
1449 "may not be redefined"),
1450 from_utf8(lyx_name)));
1454 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1456 if (graphicsbg_changed) {
1457 #ifdef WITH_WARNINGS
1458 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1461 graphics::GCache::get().changeDisplay(true);
1468 BOOST_ASSERT(lyx_view_);
1469 lyx_view_->message(from_utf8(argument));
1472 case LFUN_EXTERNAL_EDIT: {
1473 BOOST_ASSERT(lyx_view_);
1474 FuncRequest fr(action, argument);
1475 InsetExternal().dispatch(view()->cursor(), fr);
1479 case LFUN_GRAPHICS_EDIT: {
1480 FuncRequest fr(action, argument);
1481 InsetGraphics().dispatch(view()->cursor(), fr);
1485 case LFUN_INSET_APPLY: {
1486 BOOST_ASSERT(lyx_view_);
1487 string const name = cmd.getArg(0);
1488 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1490 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1491 inset->dispatch(view()->cursor(), fr);
1493 FuncRequest fr(LFUN_INSET_INSERT, argument);
1496 // ideally, the update flag should be set by the insets,
1497 // but this is not possible currently
1498 updateFlags = Update::Force | Update::FitCursor;
1502 case LFUN_ALL_INSETS_TOGGLE: {
1503 BOOST_ASSERT(lyx_view_);
1505 string const name = split(argument, action, ' ');
1506 InsetBase::Code const inset_code =
1507 InsetBase::translate(name);
1509 LCursor & cur = view()->cursor();
1510 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1512 InsetBase & inset = lyx_view_->buffer()->inset();
1513 InsetIterator it = inset_iterator_begin(inset);
1514 InsetIterator const end = inset_iterator_end(inset);
1515 for (; it != end; ++it) {
1516 if (inset_code == InsetBase::NO_CODE
1517 || inset_code == it->lyxCode()) {
1518 LCursor tmpcur = cur;
1519 tmpcur.pushLeft(*it);
1520 it->dispatch(tmpcur, fr);
1523 updateFlags = Update::Force | Update::FitCursor;
1527 case LFUN_BUFFER_LANGUAGE: {
1528 BOOST_ASSERT(lyx_view_);
1529 Buffer & buffer = *lyx_view_->buffer();
1530 Language const * oldL = buffer.params().language;
1531 Language const * newL = languages.getLanguage(argument);
1532 if (!newL || oldL == newL)
1535 if (oldL->rightToLeft() == newL->rightToLeft()
1536 && !buffer.isMultiLingual())
1537 buffer.changeLanguage(oldL, newL);
1539 buffer.updateDocLang(newL);
1543 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1544 string const fname =
1545 addName(addPath(package().user_support(), "templates/"),
1547 Buffer defaults(fname);
1549 istringstream ss(argument);
1552 int const unknown_tokens = defaults.readHeader(lex);
1554 if (unknown_tokens != 0) {
1555 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1556 << unknown_tokens << " unknown token"
1557 << (unknown_tokens == 1 ? "" : "s")
1561 if (defaults.writeFile(FileName(defaults.fileName())))
1562 // FIXME Should use bformat
1563 setMessage(_("Document defaults saved in ")
1564 + makeDisplayPath(fname));
1566 setErrorMessage(_("Unable to save document defaults"));
1570 case LFUN_BUFFER_PARAMS_APPLY: {
1571 BOOST_ASSERT(lyx_view_);
1572 biblio::CiteEngine const engine =
1573 lyx_view_->buffer()->params().cite_engine;
1575 istringstream ss(argument);
1578 int const unknown_tokens =
1579 lyx_view_->buffer()->readHeader(lex);
1581 if (unknown_tokens != 0) {
1582 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1583 << unknown_tokens << " unknown token"
1584 << (unknown_tokens == 1 ? "" : "s")
1587 if (engine == lyx_view_->buffer()->params().cite_engine)
1590 LCursor & cur = view()->cursor();
1591 FuncRequest fr(LFUN_INSET_REFRESH);
1593 InsetBase & inset = lyx_view_->buffer()->inset();
1594 InsetIterator it = inset_iterator_begin(inset);
1595 InsetIterator const end = inset_iterator_end(inset);
1596 for (; it != end; ++it)
1597 if (it->lyxCode() == InsetBase::CITE_CODE)
1598 it->dispatch(cur, fr);
1602 case LFUN_TEXTCLASS_APPLY: {
1603 BOOST_ASSERT(lyx_view_);
1604 Buffer * buffer = lyx_view_->buffer();
1606 textclass_type const old_class =
1607 buffer->params().textclass;
1609 loadTextclass(argument);
1611 std::pair<bool, textclass_type> const tc_pair =
1612 textclasslist.numberOfClass(argument);
1617 textclass_type const new_class = tc_pair.second;
1618 if (old_class == new_class)
1622 lyx_view_->message(_("Converting document to new document class..."));
1623 recordUndoFullDocument(view());
1624 buffer->params().textclass = new_class;
1625 StableDocIterator backcur(view()->cursor());
1626 ErrorList & el = buffer->errorList("Class Switch");
1627 cap::switchBetweenClasses(
1628 old_class, new_class,
1629 static_cast<InsetText &>(buffer->inset()), el);
1631 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1633 buffer->errors("Class Switch");
1634 updateLabels(*buffer);
1635 updateFlags = Update::Force | Update::FitCursor;
1639 case LFUN_TEXTCLASS_LOAD:
1640 loadTextclass(argument);
1643 case LFUN_LYXRC_APPLY: {
1644 LyXRC const lyxrc_orig = lyxrc;
1646 istringstream ss(argument);
1647 bool const success = lyxrc.read(ss) == 0;
1650 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1651 << "Unable to read lyxrc data"
1656 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1660 case LFUN_WINDOW_NEW:
1661 LyX::ref().newLyXView();
1664 case LFUN_WINDOW_CLOSE:
1665 BOOST_ASSERT(lyx_view_);
1666 BOOST_ASSERT(theApp());
1667 // ask the user for saving changes or cancel quit
1668 if (!theBufferList().quitWriteAll())
1673 case LFUN_BOOKMARK_GOTO: {
1674 BOOST_ASSERT(lyx_view_);
1675 unsigned int idx = convert<unsigned int>(to_utf8(cmd.argument()));
1676 BookmarksSection::Bookmark const bm = LyX::ref().session().bookmarks().bookmark(idx);
1677 BOOST_ASSERT(!bm.filename.empty());
1678 string const file = bm.filename.absFilename();
1679 // if the file is not opened, open it.
1680 if (!theBufferList().exists(file))
1681 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
1682 // open may fail, so we need to test it again
1683 if (theBufferList().exists(file)) {
1684 // if the current buffer is not that one, switch to it.
1685 if (lyx_view_->buffer()->fileName() != file)
1686 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
1687 // BOOST_ASSERT(lyx_view_->buffer()->fileName() != file);
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 // We will redraw the screen only if needed.
1717 if (view()->update(updateFlags)) {
1718 // Buffer::changed() signals that a repaint is needed.
1719 // The frontend (WorkArea) knows which area to repaint
1720 // thanks to the ViewMetricsInfo updated above.
1721 view()->buffer()->changed();
1724 lyx_view_->updateStatusBar();
1726 // if we executed a mutating lfun, mark the buffer as dirty
1728 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1729 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1730 view()->buffer()->markDirty();
1732 if (view()->cursor().inTexted()) {
1733 lyx_view_->updateLayoutChoice();
1738 lyx_view_->updateMenubar();
1739 lyx_view_->updateToolbars();
1740 sendDispatchMessage(getMessage(), cmd);
1745 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1747 const bool verbose = (cmd.origin == FuncRequest::MENU
1748 || cmd.origin == FuncRequest::TOOLBAR
1749 || cmd.origin == FuncRequest::COMMANDBUFFER);
1751 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1752 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1754 lyx_view_->message(msg);
1758 docstring dispatch_msg = msg;
1759 if (!dispatch_msg.empty())
1760 dispatch_msg += ' ';
1762 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1764 bool argsadded = false;
1766 if (!cmd.argument().empty()) {
1767 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1768 comname += ' ' + cmd.argument();
1773 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1775 if (!shortcuts.empty())
1776 comname += ": " + shortcuts;
1777 else if (!argsadded && !cmd.argument().empty())
1778 comname += ' ' + cmd.argument();
1780 if (!comname.empty()) {
1781 comname = rtrim(comname);
1782 dispatch_msg += '(' + rtrim(comname) + ')';
1785 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1786 << to_utf8(dispatch_msg) << endl;
1787 if (!dispatch_msg.empty())
1788 lyx_view_->message(dispatch_msg);
1792 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1794 // FIXME: initpath is not used. What to do?
1795 string initpath = lyxrc.document_path;
1796 string filename(name);
1798 if (view()->buffer()) {
1799 string const trypath = lyx_view_->buffer()->filePath();
1800 // If directory is writeable, use this as default.
1801 if (isDirWriteable(FileName(trypath)))
1805 static int newfile_number;
1807 if (filename.empty()) {
1808 filename = addName(lyxrc.document_path,
1809 "newfile" + convert<string>(++newfile_number) + ".lyx");
1810 while (theBufferList().exists(filename) ||
1811 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
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(FileName(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 FileName const fullname = fileSearch(string(), filename, "lyx");
1885 if (!fullname.empty())
1886 filename = fullname.absFilename();
1888 // if the file doesn't exist, let the user create one
1889 if (!fs::exists(fullname.toFilesystemEncoding())) {
1890 // the user specifically chose this name. Believe him.
1891 Buffer * const b = newFile(filename, string(), true);
1893 lyx_view_->setBuffer(b);
1897 docstring const disp_fn = makeDisplayPath(filename);
1898 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1901 if (lyx_view_->loadLyXFile(fullname)) {
1902 str2 = bformat(_("Document %1$s opened."), disp_fn);
1904 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1906 lyx_view_->message(str2);
1910 void LyXFunc::doImport(string const & argument)
1913 string filename = split(argument, format, ' ');
1915 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1916 << " file: " << filename << endl;
1918 // need user interaction
1919 if (filename.empty()) {
1920 string initpath = lyxrc.document_path;
1922 if (view()->buffer()) {
1923 string const trypath = lyx_view_->buffer()->filePath();
1924 // If directory is writeable, use this as default.
1925 if (isDirWriteable(FileName(trypath)))
1929 docstring const text = bformat(_("Select %1$s file to import"),
1930 formats.prettyName(format));
1932 FileDialog fileDlg(text,
1934 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1935 make_pair(_("Examples|#E#e"),
1936 from_utf8(addPath(package().system_support(), "examples"))));
1938 docstring filter = formats.prettyName(format);
1941 filter += from_utf8(formats.extension(format));
1944 FileDialog::Result result =
1945 fileDlg.open(from_utf8(initpath),
1946 FileFilterList(filter),
1949 if (result.first == FileDialog::Later)
1952 filename = to_utf8(result.second);
1954 // check selected filename
1955 if (filename.empty())
1956 lyx_view_->message(_("Canceled."));
1959 if (filename.empty())
1962 // get absolute path of file
1963 FileName const fullname(makeAbsPath(filename));
1965 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
1967 // Check if the document already is open
1968 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
1969 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
1970 lyx_view_->message(_("Canceled."));
1975 // if the file exists already, and we didn't do
1976 // -i lyx thefile.lyx, warn
1977 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
1978 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
1980 docstring text = bformat(_("The document %1$s already exists.\n\n"
1981 "Do you want to over-write that document?"), file);
1982 int const ret = Alert::prompt(_("Over-write document?"),
1983 text, 0, 1, _("&Over-write"), _("&Cancel"));
1986 lyx_view_->message(_("Canceled."));
1991 ErrorList errorList;
1992 Importer::Import(lyx_view_, fullname, format, errorList);
1993 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1997 void LyXFunc::closeBuffer()
1999 // save current cursor position
2000 LyX::ref().session().lastFilePos().save(FileName(lyx_view_->buffer()->fileName()),
2001 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2002 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2003 if (theBufferList().empty()) {
2004 // need this otherwise SEGV may occur while
2005 // trying to set variables that don't exist
2006 // since there's no current buffer
2007 lyx_view_->getDialogs().hideBufferDependent();
2009 lyx_view_->setBuffer(theBufferList().first());
2015 // Each "lyx_view_" should have it's own message method. lyxview and
2016 // the minibuffer would use the minibuffer, but lyxserver would
2017 // send an ERROR signal to its client. Alejandro 970603
2018 // This function is bit problematic when it comes to NLS, to make the
2019 // lyx servers client be language indepenent we must not translate
2020 // strings sent to this func.
2021 void LyXFunc::setErrorMessage(docstring const & m) const
2023 dispatch_buffer = m;
2028 void LyXFunc::setMessage(docstring const & m) const
2030 dispatch_buffer = m;
2034 docstring const LyXFunc::viewStatusMessage()
2036 // When meta-fake key is pressed, show the key sequence so far + "M-".
2038 return keyseq->print(true) + "M-";
2040 // Else, when a non-complete key sequence is pressed,
2041 // show the available options.
2042 if (keyseq->length() > 0 && !keyseq->deleted())
2043 return keyseq->printOptions(true);
2045 if (!view()->buffer())
2046 return _("Welcome to LyX!");
2048 return view()->cursor().currentState();
2052 BufferView * LyXFunc::view() const
2054 BOOST_ASSERT(lyx_view_);
2055 return lyx_view_->view();
2059 bool LyXFunc::wasMetaKey() const
2061 return (meta_fake_bit != key_modifier::none);
2067 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2069 // Why the switch you might ask. It is a trick to ensure that all
2070 // the elements in the LyXRCTags enum is handled. As you can see
2071 // there are no breaks at all. So it is just a huge fall-through.
2072 // The nice thing is that we will get a warning from the compiler
2073 // if we forget an element.
2074 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2076 case LyXRC::RC_ACCEPT_COMPOUND:
2077 case LyXRC::RC_ALT_LANG:
2078 case LyXRC::RC_ASCIIROFF_COMMAND:
2079 case LyXRC::RC_ASCII_LINELEN:
2080 case LyXRC::RC_AUTOREGIONDELETE:
2081 case LyXRC::RC_AUTORESET_OPTIONS:
2082 case LyXRC::RC_AUTOSAVE:
2083 case LyXRC::RC_AUTO_NUMBER:
2084 case LyXRC::RC_BACKUPDIR_PATH:
2085 case LyXRC::RC_BIBTEX_COMMAND:
2086 case LyXRC::RC_BINDFILE:
2087 case LyXRC::RC_CHECKLASTFILES:
2088 case LyXRC::RC_USELASTFILEPOS:
2089 case LyXRC::RC_LOADSESSION:
2090 case LyXRC::RC_CHKTEX_COMMAND:
2091 case LyXRC::RC_CONVERTER:
2092 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2093 case LyXRC::RC_COPIER:
2094 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2095 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2096 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2097 case LyXRC::RC_DATE_INSERT_FORMAT:
2098 case LyXRC::RC_DEFAULT_LANGUAGE:
2099 case LyXRC::RC_DEFAULT_PAPERSIZE:
2100 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2101 case LyXRC::RC_DISPLAY_GRAPHICS:
2102 case LyXRC::RC_DOCUMENTPATH:
2103 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2104 string const encoded = FileName(
2105 lyxrc_new.document_path).toFilesystemEncoding();
2106 if (fs::exists(encoded) && fs::is_directory(encoded))
2107 support::package().document_dir() = lyxrc.document_path;
2109 case LyXRC::RC_ESC_CHARS:
2110 case LyXRC::RC_FONT_ENCODING:
2111 case LyXRC::RC_FORMAT:
2112 case LyXRC::RC_INDEX_COMMAND:
2113 case LyXRC::RC_INPUT:
2114 case LyXRC::RC_KBMAP:
2115 case LyXRC::RC_KBMAP_PRIMARY:
2116 case LyXRC::RC_KBMAP_SECONDARY:
2117 case LyXRC::RC_LABEL_INIT_LENGTH:
2118 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2119 case LyXRC::RC_LANGUAGE_AUTO_END:
2120 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2121 case LyXRC::RC_LANGUAGE_COMMAND_END:
2122 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2123 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2124 case LyXRC::RC_LANGUAGE_PACKAGE:
2125 case LyXRC::RC_LANGUAGE_USE_BABEL:
2126 case LyXRC::RC_MAKE_BACKUP:
2127 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2128 case LyXRC::RC_NUMLASTFILES:
2129 case LyXRC::RC_PATH_PREFIX:
2130 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2131 support::prependEnvPath("PATH", lyxrc.path_prefix);
2133 case LyXRC::RC_PERS_DICT:
2134 case LyXRC::RC_POPUP_BOLD_FONT:
2135 case LyXRC::RC_POPUP_FONT_ENCODING:
2136 case LyXRC::RC_POPUP_NORMAL_FONT:
2137 case LyXRC::RC_PREVIEW:
2138 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2139 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2140 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2141 case LyXRC::RC_PRINTCOPIESFLAG:
2142 case LyXRC::RC_PRINTER:
2143 case LyXRC::RC_PRINTEVENPAGEFLAG:
2144 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2145 case LyXRC::RC_PRINTFILEEXTENSION:
2146 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2147 case LyXRC::RC_PRINTODDPAGEFLAG:
2148 case LyXRC::RC_PRINTPAGERANGEFLAG:
2149 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2150 case LyXRC::RC_PRINTPAPERFLAG:
2151 case LyXRC::RC_PRINTREVERSEFLAG:
2152 case LyXRC::RC_PRINTSPOOL_COMMAND:
2153 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2154 case LyXRC::RC_PRINTTOFILE:
2155 case LyXRC::RC_PRINTTOPRINTER:
2156 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2157 case LyXRC::RC_PRINT_COMMAND:
2158 case LyXRC::RC_RTL_SUPPORT:
2159 case LyXRC::RC_SCREEN_DPI:
2160 case LyXRC::RC_SCREEN_FONT_ENCODING:
2161 case LyXRC::RC_SCREEN_FONT_ROMAN:
2162 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2163 case LyXRC::RC_SCREEN_FONT_SANS:
2164 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2165 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2166 case LyXRC::RC_SCREEN_FONT_SIZES:
2167 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2168 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2169 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2170 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2171 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2172 case LyXRC::RC_SCREEN_ZOOM:
2173 case LyXRC::RC_SERVERPIPE:
2174 case LyXRC::RC_SET_COLOR:
2175 case LyXRC::RC_SHOW_BANNER:
2176 case LyXRC::RC_SPELL_COMMAND:
2177 case LyXRC::RC_TEMPDIRPATH:
2178 case LyXRC::RC_TEMPLATEPATH:
2179 case LyXRC::RC_TEX_ALLOWS_SPACES:
2180 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2181 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2182 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2184 case LyXRC::RC_UIFILE:
2185 case LyXRC::RC_USER_EMAIL:
2186 case LyXRC::RC_USER_NAME:
2187 case LyXRC::RC_USETEMPDIR:
2188 case LyXRC::RC_USE_ALT_LANG:
2189 case LyXRC::RC_USE_CONVERTER_CACHE:
2190 case LyXRC::RC_USE_ESC_CHARS:
2191 case LyXRC::RC_USE_INP_ENC:
2192 case LyXRC::RC_USE_PERS_DICT:
2193 case LyXRC::RC_USE_SPELL_LIB:
2194 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2195 case LyXRC::RC_VIEWER:
2196 case LyXRC::RC_LAST: