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::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
247 BOOST_ASSERT(lyx_view_);
248 if (!LyX::ref().session().bookmarks().isValid(idx))
250 BookmarksSection::Bookmark const & bm = LyX::ref().session().bookmarks().bookmark(idx);
251 BOOST_ASSERT(!bm.filename.empty());
252 string const file = bm.filename.absFilename();
253 // if the file is not opened, open it.
254 if (!theBufferList().exists(file)) {
256 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
260 // open may fail, so we need to test it again
261 if (theBufferList().exists(file)) {
262 // if the current buffer is not that one, switch to it.
263 if (lyx_view_->buffer()->fileName() != file) {
265 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
269 // moveToPosition use par_id, and par_pit and return new par_id.
272 boost::tie(new_pit, new_id) = view()->moveToPosition(bm.par_pit, bm.par_id, bm.par_pos);
273 // if par_id or pit has been changed, reset par_pit and par_id
274 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
275 if (bm.par_pit != new_pit || bm.par_id != new_id)
276 const_cast<BookmarksSection::Bookmark &>(bm).setPos(new_pit, new_id);
281 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
283 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
285 // Do nothing if we have nothing (JMarc)
286 if (!keysym->isOK()) {
287 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
292 if (keysym->isModifier()) {
293 lyxerr[Debug::KEY] << "isModifier true" << endl;
297 //Encoding const * encoding = view()->cursor().getEncoding();
298 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
299 size_t encoded_last_key = keysym->getUCSEncoded();
301 // Do a one-deep top-level lookup for
302 // cancel and meta-fake keys. RVDK_PATCH_5
303 cancel_meta_seq->reset();
305 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
306 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
307 << " action first set to [" << func.action << ']'
310 // When not cancel or meta-fake, do the normal lookup.
311 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
312 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
313 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
314 // remove Caps Lock and Mod2 as a modifiers
315 func = keyseq->addkey(keysym, (state | meta_fake_bit));
316 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
317 << "action now set to ["
318 << func.action << ']' << endl;
321 // Dont remove this unless you know what you are doing.
322 meta_fake_bit = key_modifier::none;
324 // Can this happen now ?
325 if (func.action == LFUN_NOACTION) {
326 func = FuncRequest(LFUN_COMMAND_PREFIX);
329 if (lyxerr.debugging(Debug::KEY)) {
330 lyxerr << BOOST_CURRENT_FUNCTION
332 << func.action << "]["
333 << to_utf8(keyseq->print(false)) << ']'
337 // already here we know if it any point in going further
338 // why not return already here if action == -1 and
339 // num_bytes == 0? (Lgb)
341 if (keyseq->length() > 1) {
342 lyx_view_->message(keyseq->print(true));
346 // Maybe user can only reach the key via holding down shift.
347 // Let's see. But only if shift is the only modifier
348 if (func.action == LFUN_UNKNOWN_ACTION &&
349 state == key_modifier::shift) {
350 lyxerr[Debug::KEY] << "Trying without shift" << endl;
351 func = keyseq->addkey(keysym, key_modifier::none);
352 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
355 if (func.action == LFUN_UNKNOWN_ACTION) {
356 // Hmm, we didn't match any of the keysequences. See
357 // if it's normal insertable text not already covered
359 if (keysym->isText() && keyseq->length() == 1) {
360 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
361 func = FuncRequest(LFUN_SELF_INSERT,
362 FuncRequest::KEYBOARD);
364 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
365 lyx_view_->message(_("Unknown function."));
370 if (func.action == LFUN_SELF_INSERT) {
371 if (encoded_last_key != 0) {
372 docstring const arg(1, encoded_last_key);
373 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
374 FuncRequest::KEYBOARD));
376 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
384 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
386 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
389 LCursor & cur = view()->cursor();
391 /* In LyX/Mac, when a dialog is open, the menus of the
392 application can still be accessed without giving focus to
393 the main window. In this case, we want to disable the menu
394 entries that are buffer-related.
396 Note that this code is not perfect, as bug 1941 attests:
397 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
399 Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
400 if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
403 if (cmd.action == LFUN_NOACTION) {
404 flag.message(from_utf8(N_("Nothing to do")));
409 switch (cmd.action) {
410 case LFUN_UNKNOWN_ACTION:
411 #ifndef HAVE_LIBAIKSAURUS
412 case LFUN_THESAURUS_ENTRY:
422 if (flag.unknown()) {
423 flag.message(from_utf8(N_("Unknown action")));
427 if (!flag.enabled()) {
428 if (flag.message().empty())
429 flag.message(from_utf8(N_("Command disabled")));
433 // Check whether we need a buffer
434 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
436 flag.message(from_utf8(N_("Command not allowed with"
437 "out any document open")));
442 // I would really like to avoid having this switch and rather try to
443 // encode this in the function itself.
444 // -- And I'd rather let an inset decide which LFUNs it is willing
445 // to handle (Andre')
447 switch (cmd.action) {
448 case LFUN_BUFFER_TOGGLE_READ_ONLY:
449 flag.setOnOff(buf->isReadonly());
452 case LFUN_BUFFER_SWITCH:
453 // toggle on the current buffer, but do not toggle off
454 // the other ones (is that a good idea?)
455 if (to_utf8(cmd.argument()) == buf->fileName())
459 case LFUN_BUFFER_EXPORT:
460 enable = cmd.argument() == "custom"
461 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
464 case LFUN_BUFFER_CHKTEX:
465 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
468 case LFUN_BUILD_PROGRAM:
469 enable = Exporter::isExportable(*buf, "program");
472 case LFUN_LAYOUT_TABULAR:
473 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
477 case LFUN_LAYOUT_PARAGRAPH:
478 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
481 case LFUN_VC_REGISTER:
482 enable = !buf->lyxvc().inUse();
484 case LFUN_VC_CHECK_IN:
485 enable = buf->lyxvc().inUse() && !buf->isReadonly();
487 case LFUN_VC_CHECK_OUT:
488 enable = buf->lyxvc().inUse() && buf->isReadonly();
491 case LFUN_VC_UNDO_LAST:
492 enable = buf->lyxvc().inUse();
494 case LFUN_BUFFER_RELOAD:
495 enable = !buf->isUnnamed() && !buf->isClean();
498 case LFUN_INSET_SETTINGS: {
502 InsetBase::Code code = cur.inset().lyxCode();
504 case InsetBase::TABULAR_CODE:
505 enable = cmd.argument() == "tabular";
507 case InsetBase::ERT_CODE:
508 enable = cmd.argument() == "ert";
510 case InsetBase::FLOAT_CODE:
511 enable = cmd.argument() == "float";
513 case InsetBase::WRAP_CODE:
514 enable = cmd.argument() == "wrap";
516 case InsetBase::NOTE_CODE:
517 enable = cmd.argument() == "note";
519 case InsetBase::BRANCH_CODE:
520 enable = cmd.argument() == "branch";
522 case InsetBase::BOX_CODE:
523 enable = cmd.argument() == "box";
531 case LFUN_INSET_APPLY: {
532 string const name = cmd.getArg(0);
533 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
535 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
537 if (!inset->getStatus(cur, fr, fs)) {
538 // Every inset is supposed to handle this
543 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
544 flag |= getStatus(fr);
546 enable = flag.enabled();
550 case LFUN_DIALOG_SHOW: {
551 string const name = cmd.getArg(0);
553 enable = name == "aboutlyx"
557 || name == "texinfo";
558 else if (name == "print")
559 enable = Exporter::isExportable(*buf, "dvi")
560 && lyxrc.print_command != "none";
561 else if (name == "character" || name == "mathpanel")
562 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
563 else if (name == "latexlog")
564 enable = isFileReadable(FileName(buf->getLogName().second));
565 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
566 else if (name == "spellchecker")
569 else if (name == "vclog")
570 enable = buf->lyxvc().inUse();
571 else if (name == "view-source")
576 case LFUN_DIALOG_SHOW_NEW_INSET:
577 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
580 case LFUN_DIALOG_UPDATE: {
581 string const name = cmd.getArg(0);
583 enable = name == "prefs";
587 case LFUN_CITATION_INSERT: {
588 FuncRequest fr(LFUN_INSET_INSERT, "citation");
589 enable = getStatus(fr).enabled();
593 case LFUN_BUFFER_WRITE: {
594 enable = view()->buffer()->isUnnamed()
595 || !view()->buffer()->isClean();
599 case LFUN_BOOKMARK_GOTO: {
600 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
601 enable = LyX::ref().session().bookmarks().isValid(num);
605 case LFUN_BOOKMARK_CLEAR:
606 enable = LyX::ref().session().bookmarks().size() > 0;
609 case LFUN_TOOLBAR_TOGGLE_STATE: {
610 ToolbarBackend::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
611 if (!(flags & ToolbarBackend::AUTO))
612 flag.setOnOff(flags & ToolbarBackend::ON);
616 // this one is difficult to get right. As a half-baked
617 // solution, we consider only the first action of the sequence
618 case LFUN_COMMAND_SEQUENCE: {
619 // argument contains ';'-terminated commands
620 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
621 FuncRequest func(lyxaction.lookupFunc(firstcmd));
622 func.origin = cmd.origin;
623 flag = getStatus(func);
626 case LFUN_BUFFER_NEW:
627 case LFUN_BUFFER_NEW_TEMPLATE:
628 case LFUN_WORD_FIND_FORWARD:
629 case LFUN_WORD_FIND_BACKWARD:
630 case LFUN_COMMAND_PREFIX:
631 case LFUN_COMMAND_EXECUTE:
633 case LFUN_META_PREFIX:
634 case LFUN_BUFFER_CLOSE:
635 case LFUN_BUFFER_WRITE_AS:
636 case LFUN_BUFFER_UPDATE:
637 case LFUN_BUFFER_VIEW:
638 case LFUN_BUFFER_IMPORT:
640 case LFUN_BUFFER_AUTO_SAVE:
641 case LFUN_RECONFIGURE:
645 case LFUN_DROP_LAYOUTS_CHOICE:
647 case LFUN_SERVER_GET_NAME:
648 case LFUN_SERVER_NOTIFY:
649 case LFUN_SERVER_GOTO_FILE_ROW:
650 case LFUN_DIALOG_HIDE:
651 case LFUN_DIALOG_DISCONNECT_INSET:
652 case LFUN_BUFFER_CHILD_OPEN:
653 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
654 case LFUN_KEYMAP_OFF:
655 case LFUN_KEYMAP_PRIMARY:
656 case LFUN_KEYMAP_SECONDARY:
657 case LFUN_KEYMAP_TOGGLE:
659 case LFUN_BUFFER_EXPORT_CUSTOM:
660 case LFUN_BUFFER_PRINT:
661 case LFUN_PREFERENCES_SAVE:
662 case LFUN_SCREEN_FONT_UPDATE:
665 case LFUN_EXTERNAL_EDIT:
666 case LFUN_GRAPHICS_EDIT:
667 case LFUN_ALL_INSETS_TOGGLE:
668 case LFUN_BUFFER_LANGUAGE:
669 case LFUN_TEXTCLASS_APPLY:
670 case LFUN_TEXTCLASS_LOAD:
671 case LFUN_BUFFER_SAVE_AS_DEFAULT:
672 case LFUN_BUFFER_PARAMS_APPLY:
673 case LFUN_LYXRC_APPLY:
674 case LFUN_BUFFER_NEXT:
675 case LFUN_BUFFER_PREVIOUS:
676 case LFUN_WINDOW_NEW:
677 case LFUN_WINDOW_CLOSE:
679 // these are handled in our dispatch()
683 if (!getLocalStatus(cur, cmd, flag))
684 flag = view()->getStatus(cmd);
690 // Can we use a readonly buffer?
691 if (buf && buf->isReadonly()
692 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
693 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
694 flag.message(from_utf8(N_("Document is read-only")));
698 // Are we in a DELETED change-tracking region?
699 if (buf && lookupChangeType(cur, true) == Change::DELETED
700 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
701 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
702 flag.message(from_utf8(N_("This portion of the document is deleted.")));
706 // the default error message if we disable the command
707 if (!flag.enabled() && flag.message().empty())
708 flag.message(from_utf8(N_("Command disabled")));
714 bool LyXFunc::ensureBufferClean(BufferView * bv)
716 Buffer & buf = *bv->buffer();
720 docstring const file = makeDisplayPath(buf.fileName(), 30);
721 docstring text = bformat(_("The document %1$s has unsaved "
722 "changes.\n\nDo you want to save "
723 "the document?"), file);
724 int const ret = Alert::prompt(_("Save changed document?"),
725 text, 0, 1, _("&Save"),
729 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
731 return buf.isClean();
737 void showPrintError(string const & name)
739 docstring str = bformat(_("Could not print the document %1$s.\n"
740 "Check that your printer is set up correctly."),
741 makeDisplayPath(name, 50));
742 Alert::error(_("Print document failed"), str);
746 void loadTextclass(string const & name)
748 std::pair<bool, textclass_type> const tc_pair =
749 textclasslist.numberOfClass(name);
751 if (!tc_pair.first) {
752 lyxerr << "Document class \"" << name
753 << "\" does not exist."
758 textclass_type const tc = tc_pair.second;
760 if (!textclasslist[tc].load()) {
761 docstring s = bformat(_("The document could not be converted\n"
762 "into the document class %1$s."),
763 from_utf8(textclasslist[tc].name()));
764 Alert::error(_("Could not change class"), s);
769 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
774 void LyXFunc::dispatch(FuncRequest const & cmd)
776 string const argument = to_utf8(cmd.argument());
777 kb_action const action = cmd.action;
779 lyxerr[Debug::ACTION] << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
780 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
782 // we have not done anything wrong yet.
784 dispatch_buffer.erase();
786 // redraw the screen at the end (first of the two drawing steps).
787 //This is done unless explicitely requested otherwise
788 Update::flags updateFlags = Update::FitCursor;
790 FuncStatus const flag = getStatus(cmd);
791 if (!flag.enabled()) {
792 // We cannot use this function here
793 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
794 << lyxaction.getActionName(action)
795 << " [" << action << "] is disabled at this location"
797 setErrorMessage(flag.message());
801 case LFUN_WORD_FIND_FORWARD:
802 case LFUN_WORD_FIND_BACKWARD: {
803 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
804 static docstring last_search;
805 docstring searched_string;
807 if (!cmd.argument().empty()) {
808 last_search = cmd.argument();
809 searched_string = cmd.argument();
811 searched_string = last_search;
814 if (searched_string.empty())
817 bool const fw = action == LFUN_WORD_FIND_FORWARD;
818 docstring const data =
819 find2string(searched_string, true, false, fw);
820 find(view(), FuncRequest(LFUN_WORD_FIND, data));
824 case LFUN_COMMAND_PREFIX:
825 BOOST_ASSERT(lyx_view_);
826 lyx_view_->message(keyseq->printOptions(true));
829 case LFUN_COMMAND_EXECUTE:
830 BOOST_ASSERT(lyx_view_);
831 lyx_view_->getToolbars().display("minibuffer", true);
832 lyx_view_->focus_command_buffer();
836 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
838 meta_fake_bit = key_modifier::none;
839 if (view()->buffer())
840 // cancel any selection
841 dispatch(FuncRequest(LFUN_MARK_OFF));
842 setMessage(_("Cancel"));
845 case LFUN_META_PREFIX:
846 meta_fake_bit = key_modifier::alt;
847 setMessage(keyseq->print(true));
850 case LFUN_BUFFER_TOGGLE_READ_ONLY:
851 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
852 if (lyx_view_->buffer()->lyxvc().inUse())
853 lyx_view_->buffer()->lyxvc().toggleReadOnly();
855 lyx_view_->buffer()->setReadonly(
856 !lyx_view_->buffer()->isReadonly());
859 // --- Menus -----------------------------------------------
860 case LFUN_BUFFER_NEW:
861 menuNew(argument, false);
864 case LFUN_BUFFER_NEW_TEMPLATE:
865 menuNew(argument, true);
868 case LFUN_BUFFER_CLOSE:
873 case LFUN_BUFFER_WRITE:
874 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
875 if (!lyx_view_->buffer()->isUnnamed()) {
876 docstring const str = bformat(_("Saving document %1$s..."),
877 makeDisplayPath(lyx_view_->buffer()->fileName()));
878 lyx_view_->message(str);
879 menuWrite(lyx_view_->buffer());
880 lyx_view_->message(str + _(" done."));
882 writeAs(lyx_view_->buffer());
883 updateFlags = Update::None;
886 case LFUN_BUFFER_WRITE_AS:
887 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
888 writeAs(lyx_view_->buffer(), argument);
889 updateFlags = Update::None;
892 case LFUN_BUFFER_RELOAD: {
893 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
894 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
895 docstring text = bformat(_("Any changes will be lost. Are you sure "
896 "you want to revert to the saved version of the document %1$s?"), file);
897 int const ret = Alert::prompt(_("Revert to saved document?"),
898 text, 0, 1, _("&Revert"), _("&Cancel"));
905 case LFUN_BUFFER_UPDATE:
906 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
907 Exporter::Export(lyx_view_->buffer(), argument, true);
910 case LFUN_BUFFER_VIEW:
911 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
912 Exporter::preview(lyx_view_->buffer(), argument);
915 case LFUN_BUILD_PROGRAM:
916 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
917 Exporter::Export(lyx_view_->buffer(), "program", true);
920 case LFUN_BUFFER_CHKTEX:
921 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
922 lyx_view_->buffer()->runChktex();
925 case LFUN_BUFFER_EXPORT:
926 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
927 if (argument == "custom")
928 lyx_view_->getDialogs().show("sendto");
930 Exporter::Export(lyx_view_->buffer(), argument, false);
934 case LFUN_BUFFER_EXPORT_CUSTOM: {
935 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
937 string command = split(argument, format_name, ' ');
938 Format const * format = formats.getFormat(format_name);
940 lyxerr << "Format \"" << format_name
941 << "\" not recognized!"
946 Buffer * buffer = lyx_view_->buffer();
948 // The name of the file created by the conversion process
951 // Output to filename
952 if (format->name() == "lyx") {
953 string const latexname =
954 buffer->getLatexName(false);
955 filename = changeExtension(latexname,
956 format->extension());
957 filename = addName(buffer->temppath(), filename);
959 if (!buffer->writeFile(FileName(filename)))
963 Exporter::Export(buffer, format_name, true, filename);
966 // Substitute $$FName for filename
967 if (!contains(command, "$$FName"))
968 command = "( " + command + " ) < $$FName";
969 command = subst(command, "$$FName", filename);
971 // Execute the command in the background
973 call.startscript(Systemcall::DontWait, command);
977 case LFUN_BUFFER_PRINT: {
978 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
981 string command = split(split(argument, target, ' '),
985 || target_name.empty()
986 || command.empty()) {
987 lyxerr << "Unable to parse \""
988 << argument << '"' << std::endl;
991 if (target != "printer" && target != "file") {
992 lyxerr << "Unrecognized target \""
993 << target << '"' << std::endl;
997 Buffer * buffer = lyx_view_->buffer();
999 if (!Exporter::Export(buffer, "dvi", true)) {
1000 showPrintError(buffer->fileName());
1004 // Push directory path.
1005 string const path = buffer->temppath();
1006 support::Path p(path);
1008 // there are three cases here:
1009 // 1. we print to a file
1010 // 2. we print directly to a printer
1011 // 3. we print using a spool command (print to file first)
1014 string const dviname =
1015 changeExtension(buffer->getLatexName(true),
1018 if (target == "printer") {
1019 if (!lyxrc.print_spool_command.empty()) {
1020 // case 3: print using a spool
1021 string const psname =
1022 changeExtension(dviname,".ps");
1023 command += lyxrc.print_to_file
1026 + quoteName(dviname);
1029 lyxrc.print_spool_command +' ';
1030 if (target_name != "default") {
1031 command2 += lyxrc.print_spool_printerprefix
1035 command2 += quoteName(psname);
1037 // If successful, then spool command
1038 res = one.startscript(
1043 res = one.startscript(
1044 Systemcall::DontWait,
1047 // case 2: print directly to a printer
1048 res = one.startscript(
1049 Systemcall::DontWait,
1050 command + quoteName(dviname));
1054 // case 1: print to a file
1055 command += lyxrc.print_to_file
1056 + quoteName(makeAbsPath(target_name,
1057 path).toFilesystemEncoding())
1059 + quoteName(dviname);
1060 res = one.startscript(Systemcall::DontWait,
1065 showPrintError(buffer->fileName());
1069 case LFUN_BUFFER_IMPORT:
1074 // quitting is triggered by the gui code
1075 // (leaving the event loop).
1076 lyx_view_->message(from_utf8(N_("Exiting.")));
1077 if (theBufferList().quitWriteAll())
1078 theApp()->gui().closeAllViews();
1081 case LFUN_TOC_VIEW: {
1082 BOOST_ASSERT(lyx_view_);
1083 InsetCommandParams p("tableofcontents");
1084 string const data = InsetCommandMailer::params2string("toc", p);
1085 lyx_view_->getDialogs().show("toc", data, 0);
1089 case LFUN_BUFFER_AUTO_SAVE:
1093 case LFUN_RECONFIGURE:
1094 BOOST_ASSERT(lyx_view_);
1095 reconfigure(*lyx_view_);
1098 case LFUN_HELP_OPEN: {
1099 BOOST_ASSERT(lyx_view_);
1100 string const arg = argument;
1102 setErrorMessage(_("Missing argument"));
1105 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1106 if (fname.empty()) {
1107 lyxerr << "LyX: unable to find documentation file `"
1108 << arg << "'. Bad installation?" << endl;
1111 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1112 makeDisplayPath(fname.absFilename())));
1113 lyx_view_->loadLyXFile(fname, false);
1117 // --- version control -------------------------------
1118 case LFUN_VC_REGISTER:
1119 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1120 if (!ensureBufferClean(view()))
1122 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1123 lyx_view_->buffer()->lyxvc().registrer();
1126 updateFlags = Update::Force;
1129 case LFUN_VC_CHECK_IN:
1130 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1131 if (!ensureBufferClean(view()))
1133 if (lyx_view_->buffer()->lyxvc().inUse()
1134 && !lyx_view_->buffer()->isReadonly()) {
1135 lyx_view_->buffer()->lyxvc().checkIn();
1140 case LFUN_VC_CHECK_OUT:
1141 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1142 if (!ensureBufferClean(view()))
1144 if (lyx_view_->buffer()->lyxvc().inUse()
1145 && lyx_view_->buffer()->isReadonly()) {
1146 lyx_view_->buffer()->lyxvc().checkOut();
1151 case LFUN_VC_REVERT:
1152 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1153 lyx_view_->buffer()->lyxvc().revert();
1157 case LFUN_VC_UNDO_LAST:
1158 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1159 lyx_view_->buffer()->lyxvc().undoLast();
1163 // --- buffers ----------------------------------------
1164 case LFUN_BUFFER_SWITCH:
1165 BOOST_ASSERT(lyx_view_);
1166 // update bookmark pit of the current buffer before switch
1167 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1168 gotoBookmark(i+1, false, false);
1169 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1172 case LFUN_BUFFER_NEXT:
1173 BOOST_ASSERT(lyx_view_);
1174 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1177 case LFUN_BUFFER_PREVIOUS:
1178 BOOST_ASSERT(lyx_view_);
1179 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1183 BOOST_ASSERT(lyx_view_);
1184 newFile(view(), argument);
1187 case LFUN_FILE_OPEN:
1188 BOOST_ASSERT(lyx_view_);
1192 case LFUN_DROP_LAYOUTS_CHOICE:
1193 BOOST_ASSERT(lyx_view_);
1194 lyx_view_->getToolbars().openLayoutList();
1197 case LFUN_MENU_OPEN:
1198 BOOST_ASSERT(lyx_view_);
1199 lyx_view_->getMenubar().openByName(from_utf8(argument));
1202 // --- lyxserver commands ----------------------------
1203 case LFUN_SERVER_GET_NAME:
1204 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1205 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1206 lyxerr[Debug::INFO] << "FNAME["
1207 << lyx_view_->buffer()->fileName()
1211 case LFUN_SERVER_NOTIFY:
1212 dispatch_buffer = keyseq->print(false);
1213 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1216 case LFUN_SERVER_GOTO_FILE_ROW: {
1217 BOOST_ASSERT(lyx_view_);
1220 istringstream is(argument);
1221 is >> file_name >> row;
1222 if (prefixIs(file_name, package().temp_dir())) {
1223 // Needed by inverse dvi search. If it is a file
1224 // in tmpdir, call the apropriated function
1225 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1227 // Must replace extension of the file to be .lyx
1228 // and get full path
1229 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1230 // Either change buffer or load the file
1231 if (theBufferList().exists(s.absFilename())) {
1232 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1234 lyx_view_->loadLyXFile(s);
1238 view()->setCursorFromRow(row);
1241 // see BufferView::center()
1245 case LFUN_DIALOG_SHOW: {
1246 BOOST_ASSERT(lyx_view_);
1247 string const name = cmd.getArg(0);
1248 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1250 if (name == "character") {
1251 data = freefont2string();
1253 lyx_view_->getDialogs().show("character", data);
1254 } else if (name == "latexlog") {
1255 pair<Buffer::LogType, string> const logfile =
1256 lyx_view_->buffer()->getLogName();
1257 switch (logfile.first) {
1258 case Buffer::latexlog:
1261 case Buffer::buildlog:
1265 data += LyXLex::quoteString(logfile.second);
1266 lyx_view_->getDialogs().show("log", data);
1267 } else if (name == "vclog") {
1268 string const data = "vc " +
1269 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1270 lyx_view_->getDialogs().show("log", data);
1272 lyx_view_->getDialogs().show(name, data);
1276 case LFUN_DIALOG_SHOW_NEW_INSET: {
1277 BOOST_ASSERT(lyx_view_);
1278 string const name = cmd.getArg(0);
1279 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1280 if (name == "bibitem" ||
1284 name == "nomenclature" ||
1288 InsetCommandParams p(name);
1289 data = InsetCommandMailer::params2string(name, p);
1290 } else if (name == "include") {
1291 // data is the include type: one of "include",
1292 // "input", "verbatiminput" or "verbatiminput*"
1294 // default type is requested
1296 InsetCommandParams p(data);
1297 data = InsetIncludeMailer::params2string(p);
1298 } else if (name == "box") {
1299 // \c data == "Boxed" || "Frameless" etc
1300 InsetBoxParams p(data);
1301 data = InsetBoxMailer::params2string(p);
1302 } else if (name == "branch") {
1303 InsetBranchParams p;
1304 data = InsetBranchMailer::params2string(p);
1305 } else if (name == "citation") {
1306 InsetCommandParams p("cite");
1307 data = InsetCommandMailer::params2string(name, p);
1308 } else if (name == "ert") {
1309 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1310 } else if (name == "external") {
1311 InsetExternalParams p;
1312 Buffer const & buffer = *lyx_view_->buffer();
1313 data = InsetExternalMailer::params2string(p, buffer);
1314 } else if (name == "float") {
1316 data = InsetFloatMailer::params2string(p);
1317 } else if (name == "graphics") {
1318 InsetGraphicsParams p;
1319 Buffer const & buffer = *lyx_view_->buffer();
1320 data = InsetGraphicsMailer::params2string(p, buffer);
1321 } else if (name == "note") {
1323 data = InsetNoteMailer::params2string(p);
1324 } else if (name == "vspace") {
1326 data = InsetVSpaceMailer::params2string(space);
1327 } else if (name == "wrap") {
1329 data = InsetWrapMailer::params2string(p);
1331 lyx_view_->getDialogs().show(name, data, 0);
1335 case LFUN_DIALOG_UPDATE: {
1336 BOOST_ASSERT(lyx_view_);
1337 string const & name = argument;
1338 // Can only update a dialog connected to an existing inset
1339 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1341 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1342 inset->dispatch(view()->cursor(), fr);
1343 } else if (name == "paragraph") {
1344 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1345 } else if (name == "prefs") {
1346 lyx_view_->getDialogs().update(name, string());
1351 case LFUN_DIALOG_HIDE:
1352 Dialogs::hide(argument, 0);
1355 case LFUN_DIALOG_DISCONNECT_INSET:
1356 BOOST_ASSERT(lyx_view_);
1357 lyx_view_->getDialogs().disconnect(argument);
1361 case LFUN_CITATION_INSERT: {
1362 BOOST_ASSERT(lyx_view_);
1363 if (!argument.empty()) {
1364 // we can have one optional argument, delimited by '|'
1365 // citation-insert <key>|<text_before>
1366 // this should be enhanced to also support text_after
1367 // and citation style
1368 string arg = argument;
1370 if (contains(argument, "|")) {
1371 arg = token(argument, '|', 0);
1372 opt1 = '[' + token(argument, '|', 1) + ']';
1374 std::ostringstream os;
1375 os << "citation LatexCommand\n"
1376 << "\\cite" << opt1 << "{" << arg << "}\n"
1378 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1381 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1385 case LFUN_BUFFER_CHILD_OPEN: {
1386 BOOST_ASSERT(lyx_view_);
1387 FileName const filename =
1388 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1389 // FIXME Should use bformat
1390 setMessage(_("Opening child document ") +
1391 makeDisplayPath(filename.absFilename()) + "...");
1392 view()->saveBookmark(false);
1393 string const parentfilename = lyx_view_->buffer()->fileName();
1394 if (theBufferList().exists(filename.absFilename()))
1395 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1397 lyx_view_->loadLyXFile(filename);
1398 // Set the parent name of the child document.
1399 // This makes insertion of citations and references in the child work,
1400 // when the target is in the parent or another child document.
1401 lyx_view_->buffer()->setParentName(parentfilename);
1405 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1406 BOOST_ASSERT(lyx_view_);
1407 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1410 case LFUN_KEYMAP_OFF:
1411 BOOST_ASSERT(lyx_view_);
1412 lyx_view_->view()->getIntl().keyMapOn(false);
1415 case LFUN_KEYMAP_PRIMARY:
1416 BOOST_ASSERT(lyx_view_);
1417 lyx_view_->view()->getIntl().keyMapPrim();
1420 case LFUN_KEYMAP_SECONDARY:
1421 BOOST_ASSERT(lyx_view_);
1422 lyx_view_->view()->getIntl().keyMapSec();
1425 case LFUN_KEYMAP_TOGGLE:
1426 BOOST_ASSERT(lyx_view_);
1427 lyx_view_->view()->getIntl().toggleKeyMap();
1433 string rest = split(argument, countstr, ' ');
1434 istringstream is(countstr);
1437 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1438 for (int i = 0; i < count; ++i)
1439 dispatch(lyxaction.lookupFunc(rest));
1443 case LFUN_COMMAND_SEQUENCE: {
1444 // argument contains ';'-terminated commands
1445 string arg = argument;
1446 while (!arg.empty()) {
1448 arg = split(arg, first, ';');
1449 FuncRequest func(lyxaction.lookupFunc(first));
1450 func.origin = cmd.origin;
1456 case LFUN_PREFERENCES_SAVE: {
1457 lyxrc.write(makeAbsPath("preferences",
1458 package().user_support()),
1463 case LFUN_SCREEN_FONT_UPDATE:
1464 BOOST_ASSERT(lyx_view_);
1465 // handle the screen font changes.
1466 lyxrc.set_font_norm_type();
1467 theFontLoader().update();
1468 /// FIXME: only the current view will be updated. the Gui
1469 /// class is able to furnish the list of views.
1470 updateFlags = Update::Force;
1473 case LFUN_SET_COLOR: {
1475 string const x11_name = split(argument, lyx_name, ' ');
1476 if (lyx_name.empty() || x11_name.empty()) {
1477 setErrorMessage(_("Syntax: set-color <lyx_name>"
1482 bool const graphicsbg_changed =
1483 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1484 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1486 if (!lcolor.setColor(lyx_name, x11_name)) {
1488 bformat(_("Set-color \"%1$s\" failed "
1489 "- color is undefined or "
1490 "may not be redefined"),
1491 from_utf8(lyx_name)));
1495 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1497 if (graphicsbg_changed) {
1498 #ifdef WITH_WARNINGS
1499 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1502 graphics::GCache::get().changeDisplay(true);
1509 BOOST_ASSERT(lyx_view_);
1510 lyx_view_->message(from_utf8(argument));
1513 case LFUN_EXTERNAL_EDIT: {
1514 BOOST_ASSERT(lyx_view_);
1515 FuncRequest fr(action, argument);
1516 InsetExternal().dispatch(view()->cursor(), fr);
1520 case LFUN_GRAPHICS_EDIT: {
1521 FuncRequest fr(action, argument);
1522 InsetGraphics().dispatch(view()->cursor(), fr);
1526 case LFUN_INSET_APPLY: {
1527 BOOST_ASSERT(lyx_view_);
1528 string const name = cmd.getArg(0);
1529 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1531 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1532 inset->dispatch(view()->cursor(), fr);
1534 FuncRequest fr(LFUN_INSET_INSERT, argument);
1537 // ideally, the update flag should be set by the insets,
1538 // but this is not possible currently
1539 updateFlags = Update::Force | Update::FitCursor;
1543 case LFUN_ALL_INSETS_TOGGLE: {
1544 BOOST_ASSERT(lyx_view_);
1546 string const name = split(argument, action, ' ');
1547 InsetBase::Code const inset_code =
1548 InsetBase::translate(name);
1550 LCursor & cur = view()->cursor();
1551 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1553 InsetBase & inset = lyx_view_->buffer()->inset();
1554 InsetIterator it = inset_iterator_begin(inset);
1555 InsetIterator const end = inset_iterator_end(inset);
1556 for (; it != end; ++it) {
1557 if (inset_code == InsetBase::NO_CODE
1558 || inset_code == it->lyxCode()) {
1559 LCursor tmpcur = cur;
1560 tmpcur.pushLeft(*it);
1561 it->dispatch(tmpcur, fr);
1564 updateFlags = Update::Force | Update::FitCursor;
1568 case LFUN_BUFFER_LANGUAGE: {
1569 BOOST_ASSERT(lyx_view_);
1570 Buffer & buffer = *lyx_view_->buffer();
1571 Language const * oldL = buffer.params().language;
1572 Language const * newL = languages.getLanguage(argument);
1573 if (!newL || oldL == newL)
1576 if (oldL->rightToLeft() == newL->rightToLeft()
1577 && !buffer.isMultiLingual())
1578 buffer.changeLanguage(oldL, newL);
1580 buffer.updateDocLang(newL);
1584 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1585 string const fname =
1586 addName(addPath(package().user_support(), "templates/"),
1588 Buffer defaults(fname);
1590 istringstream ss(argument);
1593 int const unknown_tokens = defaults.readHeader(lex);
1595 if (unknown_tokens != 0) {
1596 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1597 << unknown_tokens << " unknown token"
1598 << (unknown_tokens == 1 ? "" : "s")
1602 if (defaults.writeFile(FileName(defaults.fileName())))
1603 // FIXME Should use bformat
1604 setMessage(_("Document defaults saved in ")
1605 + makeDisplayPath(fname));
1607 setErrorMessage(_("Unable to save document defaults"));
1611 case LFUN_BUFFER_PARAMS_APPLY: {
1612 BOOST_ASSERT(lyx_view_);
1613 biblio::CiteEngine const engine =
1614 lyx_view_->buffer()->params().cite_engine;
1616 istringstream ss(argument);
1619 int const unknown_tokens =
1620 lyx_view_->buffer()->readHeader(lex);
1622 if (unknown_tokens != 0) {
1623 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1624 << unknown_tokens << " unknown token"
1625 << (unknown_tokens == 1 ? "" : "s")
1628 if (engine == lyx_view_->buffer()->params().cite_engine)
1631 LCursor & cur = view()->cursor();
1632 FuncRequest fr(LFUN_INSET_REFRESH);
1634 InsetBase & inset = lyx_view_->buffer()->inset();
1635 InsetIterator it = inset_iterator_begin(inset);
1636 InsetIterator const end = inset_iterator_end(inset);
1637 for (; it != end; ++it)
1638 if (it->lyxCode() == InsetBase::CITE_CODE)
1639 it->dispatch(cur, fr);
1643 case LFUN_TEXTCLASS_APPLY: {
1644 BOOST_ASSERT(lyx_view_);
1645 Buffer * buffer = lyx_view_->buffer();
1647 textclass_type const old_class =
1648 buffer->params().textclass;
1650 loadTextclass(argument);
1652 std::pair<bool, textclass_type> const tc_pair =
1653 textclasslist.numberOfClass(argument);
1658 textclass_type const new_class = tc_pair.second;
1659 if (old_class == new_class)
1663 lyx_view_->message(_("Converting document to new document class..."));
1664 recordUndoFullDocument(view());
1665 buffer->params().textclass = new_class;
1666 StableDocIterator backcur(view()->cursor());
1667 ErrorList & el = buffer->errorList("Class Switch");
1668 cap::switchBetweenClasses(
1669 old_class, new_class,
1670 static_cast<InsetText &>(buffer->inset()), el);
1672 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1674 buffer->errors("Class Switch");
1675 updateLabels(*buffer);
1676 updateFlags = Update::Force | Update::FitCursor;
1680 case LFUN_TEXTCLASS_LOAD:
1681 loadTextclass(argument);
1684 case LFUN_LYXRC_APPLY: {
1685 LyXRC const lyxrc_orig = lyxrc;
1687 istringstream ss(argument);
1688 bool const success = lyxrc.read(ss) == 0;
1691 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1692 << "Unable to read lyxrc data"
1697 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1699 /// We force the redraw in any case because there might be
1700 /// some screen font changes.
1701 /// FIXME: only the current view will be updated. the Gui
1702 /// class is able to furnish the list of views.
1703 updateFlags = Update::Force;
1707 case LFUN_WINDOW_NEW:
1708 LyX::ref().newLyXView();
1711 case LFUN_WINDOW_CLOSE:
1712 BOOST_ASSERT(lyx_view_);
1713 BOOST_ASSERT(theApp());
1714 // update bookmark pit of the current buffer before window close
1715 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1716 gotoBookmark(i+1, false, false);
1717 // ask the user for saving changes or cancel quit
1718 if (!theBufferList().quitWriteAll())
1723 case LFUN_BOOKMARK_GOTO:
1724 // go to bookmark, open unopened file and switch to buffer if necessary
1725 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1728 case LFUN_BOOKMARK_CLEAR:
1729 LyX::ref().session().bookmarks().clear();
1732 case LFUN_TOOLBAR_TOGGLE_STATE:
1733 lyx_view_->toggleToolbarState(argument);
1737 BOOST_ASSERT(lyx_view_);
1738 view()->cursor().dispatch(cmd);
1739 updateFlags = view()->cursor().result().update();
1740 if (!view()->cursor().result().dispatched())
1741 if (view()->dispatch(cmd))
1742 updateFlags = Update::Force | Update::FitCursor;
1747 if (lyx_view_ && view()->buffer()) {
1748 // BufferView::update() updates the ViewMetricsInfo and
1749 // also initializes the position cache for all insets in
1750 // (at least partially) visible top-level paragraphs.
1751 // We will redraw the screen only if needed.
1752 if (view()->update(updateFlags)) {
1753 // Buffer::changed() signals that a repaint is needed.
1754 // The frontend (WorkArea) knows which area to repaint
1755 // thanks to the ViewMetricsInfo updated above.
1756 view()->buffer()->changed();
1759 lyx_view_->updateStatusBar();
1761 // if we executed a mutating lfun, mark the buffer as dirty
1763 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1764 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1765 view()->buffer()->markDirty();
1767 if (view()->cursor().inTexted()) {
1768 lyx_view_->updateLayoutChoice();
1773 lyx_view_->updateMenubar();
1774 lyx_view_->updateToolbars();
1775 sendDispatchMessage(getMessage(), cmd);
1780 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1782 const bool verbose = (cmd.origin == FuncRequest::MENU
1783 || cmd.origin == FuncRequest::TOOLBAR
1784 || cmd.origin == FuncRequest::COMMANDBUFFER);
1786 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1787 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1789 lyx_view_->message(msg);
1793 docstring dispatch_msg = msg;
1794 if (!dispatch_msg.empty())
1795 dispatch_msg += ' ';
1797 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1799 bool argsadded = false;
1801 if (!cmd.argument().empty()) {
1802 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1803 comname += ' ' + cmd.argument();
1808 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1810 if (!shortcuts.empty())
1811 comname += ": " + shortcuts;
1812 else if (!argsadded && !cmd.argument().empty())
1813 comname += ' ' + cmd.argument();
1815 if (!comname.empty()) {
1816 comname = rtrim(comname);
1817 dispatch_msg += '(' + rtrim(comname) + ')';
1820 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1821 << to_utf8(dispatch_msg) << endl;
1822 if (!dispatch_msg.empty())
1823 lyx_view_->message(dispatch_msg);
1827 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1829 // FIXME: initpath is not used. What to do?
1830 string initpath = lyxrc.document_path;
1831 string filename(name);
1833 if (view()->buffer()) {
1834 string const trypath = lyx_view_->buffer()->filePath();
1835 // If directory is writeable, use this as default.
1836 if (isDirWriteable(FileName(trypath)))
1840 static int newfile_number;
1842 if (filename.empty()) {
1843 filename = addName(lyxrc.document_path,
1844 "newfile" + convert<string>(++newfile_number) + ".lyx");
1845 while (theBufferList().exists(filename) ||
1846 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1848 filename = addName(lyxrc.document_path,
1849 "newfile" + convert<string>(newfile_number) +
1854 // The template stuff
1857 FileDialog fileDlg(_("Select template file"),
1858 LFUN_SELECT_FILE_SYNC,
1859 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1860 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1862 FileDialog::Result result =
1863 fileDlg.open(from_utf8(lyxrc.template_path),
1864 FileFilterList(_("LyX Documents (*.lyx)")),
1867 if (result.first == FileDialog::Later)
1869 if (result.second.empty())
1871 templname = to_utf8(result.second);
1874 Buffer * const b = newFile(filename, templname, !name.empty());
1876 lyx_view_->setBuffer(b);
1880 void LyXFunc::open(string const & fname)
1882 string initpath = lyxrc.document_path;
1884 if (view()->buffer()) {
1885 string const trypath = lyx_view_->buffer()->filePath();
1886 // If directory is writeable, use this as default.
1887 if (isDirWriteable(FileName(trypath)))
1893 if (fname.empty()) {
1894 FileDialog fileDlg(_("Select document to open"),
1896 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1897 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1899 FileDialog::Result result =
1900 fileDlg.open(from_utf8(initpath),
1901 FileFilterList(_("LyX Documents (*.lyx)")),
1904 if (result.first == FileDialog::Later)
1907 filename = to_utf8(result.second);
1909 // check selected filename
1910 if (filename.empty()) {
1911 lyx_view_->message(_("Canceled."));
1917 // get absolute path of file and add ".lyx" to the filename if
1919 FileName const fullname = fileSearch(string(), filename, "lyx");
1920 if (!fullname.empty())
1921 filename = fullname.absFilename();
1923 // if the file doesn't exist, let the user create one
1924 if (!fs::exists(fullname.toFilesystemEncoding())) {
1925 // the user specifically chose this name. Believe him.
1926 Buffer * const b = newFile(filename, string(), true);
1928 lyx_view_->setBuffer(b);
1932 docstring const disp_fn = makeDisplayPath(filename);
1933 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1936 if (lyx_view_->loadLyXFile(fullname)) {
1937 str2 = bformat(_("Document %1$s opened."), disp_fn);
1939 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1941 lyx_view_->message(str2);
1945 void LyXFunc::doImport(string const & argument)
1948 string filename = split(argument, format, ' ');
1950 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1951 << " file: " << filename << endl;
1953 // need user interaction
1954 if (filename.empty()) {
1955 string initpath = lyxrc.document_path;
1957 if (view()->buffer()) {
1958 string const trypath = lyx_view_->buffer()->filePath();
1959 // If directory is writeable, use this as default.
1960 if (isDirWriteable(FileName(trypath)))
1964 docstring const text = bformat(_("Select %1$s file to import"),
1965 formats.prettyName(format));
1967 FileDialog fileDlg(text,
1969 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1970 make_pair(_("Examples|#E#e"),
1971 from_utf8(addPath(package().system_support(), "examples"))));
1973 docstring filter = formats.prettyName(format);
1976 filter += from_utf8(formats.extension(format));
1979 FileDialog::Result result =
1980 fileDlg.open(from_utf8(initpath),
1981 FileFilterList(filter),
1984 if (result.first == FileDialog::Later)
1987 filename = to_utf8(result.second);
1989 // check selected filename
1990 if (filename.empty())
1991 lyx_view_->message(_("Canceled."));
1994 if (filename.empty())
1997 // get absolute path of file
1998 FileName const fullname(makeAbsPath(filename));
2000 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2002 // Check if the document already is open
2003 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2004 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2005 lyx_view_->message(_("Canceled."));
2010 // if the file exists already, and we didn't do
2011 // -i lyx thefile.lyx, warn
2012 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2013 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2015 docstring text = bformat(_("The document %1$s already exists.\n\n"
2016 "Do you want to over-write that document?"), file);
2017 int const ret = Alert::prompt(_("Over-write document?"),
2018 text, 0, 1, _("&Over-write"), _("&Cancel"));
2021 lyx_view_->message(_("Canceled."));
2026 ErrorList errorList;
2027 Importer::Import(lyx_view_, fullname, format, errorList);
2028 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2032 void LyXFunc::closeBuffer()
2034 // save current cursor position
2035 LyX::ref().session().lastFilePos().save(FileName(lyx_view_->buffer()->fileName()),
2036 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2037 // goto bookmark to update bookmark pit.
2038 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2039 gotoBookmark(i+1, false, false);
2040 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2041 if (theBufferList().empty()) {
2042 // need this otherwise SEGV may occur while
2043 // trying to set variables that don't exist
2044 // since there's no current buffer
2045 lyx_view_->getDialogs().hideBufferDependent();
2047 lyx_view_->setBuffer(theBufferList().first());
2053 void LyXFunc::reloadBuffer()
2055 FileName filename(lyx_view_->buffer()->fileName());
2057 lyx_view_->loadLyXFile(filename);
2060 // Each "lyx_view_" should have it's own message method. lyxview and
2061 // the minibuffer would use the minibuffer, but lyxserver would
2062 // send an ERROR signal to its client. Alejandro 970603
2063 // This function is bit problematic when it comes to NLS, to make the
2064 // lyx servers client be language indepenent we must not translate
2065 // strings sent to this func.
2066 void LyXFunc::setErrorMessage(docstring const & m) const
2068 dispatch_buffer = m;
2073 void LyXFunc::setMessage(docstring const & m) const
2075 dispatch_buffer = m;
2079 docstring const LyXFunc::viewStatusMessage()
2081 // When meta-fake key is pressed, show the key sequence so far + "M-".
2083 return keyseq->print(true) + "M-";
2085 // Else, when a non-complete key sequence is pressed,
2086 // show the available options.
2087 if (keyseq->length() > 0 && !keyseq->deleted())
2088 return keyseq->printOptions(true);
2090 if (!view()->buffer())
2091 return _("Welcome to LyX!");
2093 return view()->cursor().currentState();
2097 BufferView * LyXFunc::view() const
2099 BOOST_ASSERT(lyx_view_);
2100 return lyx_view_->view();
2104 bool LyXFunc::wasMetaKey() const
2106 return (meta_fake_bit != key_modifier::none);
2112 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2114 // Why the switch you might ask. It is a trick to ensure that all
2115 // the elements in the LyXRCTags enum is handled. As you can see
2116 // there are no breaks at all. So it is just a huge fall-through.
2117 // The nice thing is that we will get a warning from the compiler
2118 // if we forget an element.
2119 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2121 case LyXRC::RC_ACCEPT_COMPOUND:
2122 case LyXRC::RC_ALT_LANG:
2123 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2124 case LyXRC::RC_PLAINTEXT_LINELEN:
2125 case LyXRC::RC_AUTOREGIONDELETE:
2126 case LyXRC::RC_AUTORESET_OPTIONS:
2127 case LyXRC::RC_AUTOSAVE:
2128 case LyXRC::RC_AUTO_NUMBER:
2129 case LyXRC::RC_BACKUPDIR_PATH:
2130 case LyXRC::RC_BIBTEX_COMMAND:
2131 case LyXRC::RC_BINDFILE:
2132 case LyXRC::RC_CHECKLASTFILES:
2133 case LyXRC::RC_USELASTFILEPOS:
2134 case LyXRC::RC_LOADSESSION:
2135 case LyXRC::RC_CHKTEX_COMMAND:
2136 case LyXRC::RC_CONVERTER:
2137 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2138 case LyXRC::RC_COPIER:
2139 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2140 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2141 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2142 case LyXRC::RC_DATE_INSERT_FORMAT:
2143 case LyXRC::RC_DEFAULT_LANGUAGE:
2144 case LyXRC::RC_DEFAULT_PAPERSIZE:
2145 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2146 case LyXRC::RC_DISPLAY_GRAPHICS:
2147 case LyXRC::RC_DOCUMENTPATH:
2148 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2149 string const encoded = FileName(
2150 lyxrc_new.document_path).toFilesystemEncoding();
2151 if (fs::exists(encoded) && fs::is_directory(encoded))
2152 support::package().document_dir() = lyxrc.document_path;
2154 case LyXRC::RC_ESC_CHARS:
2155 case LyXRC::RC_FONT_ENCODING:
2156 case LyXRC::RC_FORMAT:
2157 case LyXRC::RC_INDEX_COMMAND:
2158 case LyXRC::RC_INPUT:
2159 case LyXRC::RC_KBMAP:
2160 case LyXRC::RC_KBMAP_PRIMARY:
2161 case LyXRC::RC_KBMAP_SECONDARY:
2162 case LyXRC::RC_LABEL_INIT_LENGTH:
2163 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2164 case LyXRC::RC_LANGUAGE_AUTO_END:
2165 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2166 case LyXRC::RC_LANGUAGE_COMMAND_END:
2167 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2168 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2169 case LyXRC::RC_LANGUAGE_PACKAGE:
2170 case LyXRC::RC_LANGUAGE_USE_BABEL:
2171 case LyXRC::RC_MAKE_BACKUP:
2172 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2173 case LyXRC::RC_NUMLASTFILES:
2174 case LyXRC::RC_PATH_PREFIX:
2175 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2176 support::prependEnvPath("PATH", lyxrc.path_prefix);
2178 case LyXRC::RC_PERS_DICT:
2179 case LyXRC::RC_POPUP_BOLD_FONT:
2180 case LyXRC::RC_POPUP_FONT_ENCODING:
2181 case LyXRC::RC_POPUP_NORMAL_FONT:
2182 case LyXRC::RC_PREVIEW:
2183 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2184 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2185 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2186 case LyXRC::RC_PRINTCOPIESFLAG:
2187 case LyXRC::RC_PRINTER:
2188 case LyXRC::RC_PRINTEVENPAGEFLAG:
2189 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2190 case LyXRC::RC_PRINTFILEEXTENSION:
2191 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2192 case LyXRC::RC_PRINTODDPAGEFLAG:
2193 case LyXRC::RC_PRINTPAGERANGEFLAG:
2194 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2195 case LyXRC::RC_PRINTPAPERFLAG:
2196 case LyXRC::RC_PRINTREVERSEFLAG:
2197 case LyXRC::RC_PRINTSPOOL_COMMAND:
2198 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2199 case LyXRC::RC_PRINTTOFILE:
2200 case LyXRC::RC_PRINTTOPRINTER:
2201 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2202 case LyXRC::RC_PRINT_COMMAND:
2203 case LyXRC::RC_RTL_SUPPORT:
2204 case LyXRC::RC_SCREEN_DPI:
2205 case LyXRC::RC_SCREEN_FONT_ENCODING:
2206 case LyXRC::RC_SCREEN_FONT_ROMAN:
2207 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2208 case LyXRC::RC_SCREEN_FONT_SANS:
2209 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2210 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2211 case LyXRC::RC_SCREEN_FONT_SIZES:
2212 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2213 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2214 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2215 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2216 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2217 case LyXRC::RC_SCREEN_ZOOM:
2218 case LyXRC::RC_SERVERPIPE:
2219 case LyXRC::RC_SET_COLOR:
2220 case LyXRC::RC_SHOW_BANNER:
2221 case LyXRC::RC_SPELL_COMMAND:
2222 case LyXRC::RC_TEMPDIRPATH:
2223 case LyXRC::RC_TEMPLATEPATH:
2224 case LyXRC::RC_TEX_ALLOWS_SPACES:
2225 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2226 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2227 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2229 case LyXRC::RC_UIFILE:
2230 case LyXRC::RC_USER_EMAIL:
2231 case LyXRC::RC_USER_NAME:
2232 case LyXRC::RC_USETEMPDIR:
2233 case LyXRC::RC_USE_ALT_LANG:
2234 case LyXRC::RC_USE_CONVERTER_CACHE:
2235 case LyXRC::RC_USE_ESC_CHARS:
2236 case LyXRC::RC_USE_INP_ENC:
2237 case LyXRC::RC_USE_PERS_DICT:
2238 case LyXRC::RC_USE_SPELL_LIB:
2239 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2240 case LyXRC::RC_VIEWER:
2241 case LyXRC::RC_LAST: