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 if (!LyX::ref().session().bookmarks().isValid(idx))
1678 BookmarksSection::Bookmark const bm = LyX::ref().session().bookmarks().bookmark(idx);
1679 BOOST_ASSERT(!bm.filename.empty());
1680 string const file = bm.filename.absFilename();
1681 // if the file is not opened, open it.
1682 if (!theBufferList().exists(file))
1683 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
1684 // open may fail, so we need to test it again
1685 if (theBufferList().exists(file)) {
1686 // if the current buffer is not that one, switch to it.
1687 if (lyx_view_->buffer()->fileName() != file)
1688 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
1689 // BOOST_ASSERT(lyx_view_->buffer()->fileName() != file);
1690 view()->moveToPosition(bm.par_id, bm.par_pos);
1695 case LFUN_BOOKMARK_CLEAR:
1696 LyX::ref().session().bookmarks().clear();
1699 case LFUN_TOOLBAR_TOGGLE_STATE:
1700 lyx_view_->toggleToolbarState(argument);
1704 BOOST_ASSERT(lyx_view_);
1705 view()->cursor().dispatch(cmd);
1706 updateFlags = view()->cursor().result().update();
1707 if (!view()->cursor().result().dispatched())
1708 if (view()->dispatch(cmd))
1709 updateFlags = Update::Force | Update::FitCursor;
1714 if (lyx_view_ && view()->buffer()) {
1715 // BufferView::update() updates the ViewMetricsInfo and
1716 // also initializes the position cache for all insets in
1717 // (at least partially) visible top-level paragraphs.
1718 // We will redraw the screen only if needed.
1719 if (view()->update(updateFlags)) {
1720 // Buffer::changed() signals that a repaint is needed.
1721 // The frontend (WorkArea) knows which area to repaint
1722 // thanks to the ViewMetricsInfo updated above.
1723 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 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1766 bool argsadded = false;
1768 if (!cmd.argument().empty()) {
1769 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1770 comname += ' ' + cmd.argument();
1775 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1777 if (!shortcuts.empty())
1778 comname += ": " + shortcuts;
1779 else if (!argsadded && !cmd.argument().empty())
1780 comname += ' ' + cmd.argument();
1782 if (!comname.empty()) {
1783 comname = rtrim(comname);
1784 dispatch_msg += '(' + 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 // FIXME: initpath is not used. What to do?
1797 string initpath = lyxrc.document_path;
1798 string filename(name);
1800 if (view()->buffer()) {
1801 string const trypath = lyx_view_->buffer()->filePath();
1802 // If directory is writeable, use this as default.
1803 if (isDirWriteable(FileName(trypath)))
1807 static int newfile_number;
1809 if (filename.empty()) {
1810 filename = addName(lyxrc.document_path,
1811 "newfile" + convert<string>(++newfile_number) + ".lyx");
1812 while (theBufferList().exists(filename) ||
1813 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1815 filename = addName(lyxrc.document_path,
1816 "newfile" + convert<string>(newfile_number) +
1821 // The template stuff
1824 FileDialog fileDlg(_("Select template file"),
1825 LFUN_SELECT_FILE_SYNC,
1826 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1827 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1829 FileDialog::Result result =
1830 fileDlg.open(from_utf8(lyxrc.template_path),
1831 FileFilterList(_("LyX Documents (*.lyx)")),
1834 if (result.first == FileDialog::Later)
1836 if (result.second.empty())
1838 templname = to_utf8(result.second);
1841 Buffer * const b = newFile(filename, templname, !name.empty());
1843 lyx_view_->setBuffer(b);
1847 void LyXFunc::open(string const & fname)
1849 string initpath = lyxrc.document_path;
1851 if (view()->buffer()) {
1852 string const trypath = lyx_view_->buffer()->filePath();
1853 // If directory is writeable, use this as default.
1854 if (isDirWriteable(FileName(trypath)))
1860 if (fname.empty()) {
1861 FileDialog fileDlg(_("Select document to open"),
1863 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1864 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1866 FileDialog::Result result =
1867 fileDlg.open(from_utf8(initpath),
1868 FileFilterList(_("LyX Documents (*.lyx)")),
1871 if (result.first == FileDialog::Later)
1874 filename = to_utf8(result.second);
1876 // check selected filename
1877 if (filename.empty()) {
1878 lyx_view_->message(_("Canceled."));
1884 // get absolute path of file and add ".lyx" to the filename if
1886 FileName const fullname = fileSearch(string(), filename, "lyx");
1887 if (!fullname.empty())
1888 filename = fullname.absFilename();
1890 // if the file doesn't exist, let the user create one
1891 if (!fs::exists(fullname.toFilesystemEncoding())) {
1892 // the user specifically chose this name. Believe him.
1893 Buffer * const b = newFile(filename, string(), true);
1895 lyx_view_->setBuffer(b);
1899 docstring const disp_fn = makeDisplayPath(filename);
1900 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1903 if (lyx_view_->loadLyXFile(fullname)) {
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(FileName(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 const fullname(makeAbsPath(filename));
1967 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
1969 // Check if the document already is open
1970 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
1971 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), 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.toFilesystemEncoding()) && fullname != lyxfile) {
1980 docstring const file = makeDisplayPath(lyxfile.absFilename(), 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_, fullname, 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(FileName(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 docstring const LyXFunc::viewStatusMessage()
2038 // When meta-fake key is pressed, show the key sequence so far + "M-".
2040 return keyseq->print(true) + "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(true);
2047 if (!view()->buffer())
2048 return _("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 string const encoded = FileName(
2107 lyxrc_new.document_path).toFilesystemEncoding();
2108 if (fs::exists(encoded) && fs::is_directory(encoded))
2109 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: