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.
25 #include "BranchList.h"
27 #include "buffer_funcs.h"
28 #include "BufferList.h"
29 #include "BufferParams.h"
30 #include "BufferView.h"
31 #include "bufferview_funcs.h"
33 #include "CutAndPaste.h"
35 #include "DispatchResult.h"
37 #include "ErrorList.h"
40 #include "FuncRequest.h"
41 #include "FuncStatus.h"
44 #include "InsetIterator.h"
52 #include "LyXAction.h"
58 #include "TextClassList.h"
60 #include "Paragraph.h"
61 #include "ParIterator.h"
62 #include "ParagraphParameters.h"
65 #include "insets/InsetBox.h"
66 #include "insets/InsetBranch.h"
67 #include "insets/InsetCommand.h"
68 #include "insets/InsetERT.h"
69 #include "insets/InsetExternal.h"
70 #include "insets/InsetFloat.h"
71 #include "insets/InsetListings.h"
72 #include "insets/InsetGraphics.h"
73 #include "insets/InsetInclude.h"
74 #include "insets/InsetNote.h"
75 #include "insets/InsetTabular.h"
76 #include "insets/InsetVSpace.h"
77 #include "insets/InsetWrap.h"
79 #include "frontends/Application.h"
80 #include "frontends/alert.h"
81 #include "frontends/Dialogs.h"
82 #include "frontends/FileDialog.h"
83 #include "frontends/FontLoader.h"
84 #include "frontends/Gui.h"
85 #include "frontends/KeySymbol.h"
86 #include "frontends/LyXView.h"
87 #include "frontends/Menubar.h"
88 #include "frontends/Toolbars.h"
90 #include "support/environment.h"
91 #include "support/FileFilterList.h"
92 #include "support/filetools.h"
93 #include "support/ForkedcallsController.h"
94 #include "support/fs_extras.h"
95 #include "support/lstrings.h"
96 #include "support/Path.h"
97 #include "support/Package.h"
98 #include "support/Systemcall.h"
99 #include "support/convert.h"
100 #include "support/os.h"
102 #include <boost/current_function.hpp>
103 #include <boost/filesystem/operations.hpp>
110 using bv_funcs::freefont2string;
112 using support::absolutePath;
113 using support::addName;
114 using support::addPath;
115 using support::bformat;
116 using support::changeExtension;
117 using support::contains;
118 using support::FileFilterList;
119 using support::FileName;
120 using support::fileSearch;
121 using support::ForkedcallsController;
122 using support::i18nLibFileSearch;
123 using support::isDirWriteable;
124 using support::isFileReadable;
125 using support::isStrInt;
126 using support::makeAbsPath;
127 using support::makeDisplayPath;
128 using support::package;
129 using support::quoteName;
130 using support::rtrim;
131 using support::split;
132 using support::subst;
133 using support::Systemcall;
134 using support::token;
136 using support::prefixIs;
139 using std::make_pair;
142 using std::istringstream;
143 using std::ostringstream;
145 namespace Alert = frontend::Alert;
146 namespace fs = boost::filesystem;
151 bool getLocalStatus(Cursor cursor,
152 FuncRequest const & cmd, FuncStatus & status)
154 // Try to fix cursor in case it is broken.
155 cursor.fixIfBroken();
157 // This is, of course, a mess. Better create a new doc iterator and use
158 // this in Inset::getStatus. This might require an additional
159 // BufferView * arg, though (which should be avoided)
160 //Cursor safe = *this;
162 for ( ; cursor.depth(); cursor.pop()) {
163 //lyxerr << "\nCursor::getStatus: cmd: " << cmd << endl << *this << endl;
164 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
165 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
166 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
168 // The inset's getStatus() will return 'true' if it made
169 // a definitive decision on whether it want to handle the
170 // request or not. The result of this decision is put into
171 // the 'status' parameter.
172 if (cursor.inset().getStatus(cursor, cmd, status)) {
181 /** Return the change status at cursor position, taking in account the
182 * status at each level of the document iterator (a table in a deleted
183 * footnote is deleted).
184 * When \param outer is true, the top slice is not looked at.
186 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
188 size_t const depth = dit.depth() - (outer ? 1 : 0);
190 for (size_t i = 0 ; i < depth ; ++i) {
191 CursorSlice const & slice = dit[i];
192 if (!slice.inset().inMathed()
193 && slice.pos() < slice.paragraph().size()) {
194 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
195 if (ch != Change::UNCHANGED)
199 return Change::UNCHANGED;
207 meta_fake_bit(key_modifier::none)
212 void LyXFunc::initKeySequences(KeyMap * kb)
214 keyseq.reset(new KeySequence(kb, kb));
215 cancel_meta_seq.reset(new KeySequence(kb, kb));
219 void LyXFunc::setLyXView(LyXView * lv)
225 void LyXFunc::handleKeyFunc(kb_action action)
227 char_type c = encoded_last_key;
229 if (keyseq->length())
232 lyx_view_->view()->getIntl().getTransManager().deadkey(
233 c, get_accent(action).accent, view()->cursor().innerText(), view()->cursor());
234 // Need to clear, in case the minibuffer calls these
237 // copied verbatim from do_accent_char
238 view()->cursor().resetAnchor();
243 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
245 BOOST_ASSERT(lyx_view_);
246 if (!LyX::ref().session().bookmarks().isValid(idx))
248 BookmarksSection::Bookmark const & bm = LyX::ref().session().bookmarks().bookmark(idx);
249 BOOST_ASSERT(!bm.filename.empty());
250 string const file = bm.filename.absFilename();
251 // if the file is not opened, open it.
252 if (!theBufferList().exists(file)) {
254 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
258 // open may fail, so we need to test it again
259 if (theBufferList().exists(file)) {
260 // if the current buffer is not that one, switch to it.
261 if (lyx_view_->buffer()->fileName() != file) {
263 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
267 // moveToPosition use par_id, and par_pit and return new par_id.
271 boost::tie(new_pit, new_pos, new_id) = view()->moveToPosition(bm.bottom_pit, bm.bottom_pos, bm.top_id, bm.top_pos);
272 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
273 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
274 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos || bm.top_id != new_id )
275 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(new_pit, new_pos, new_id);
280 void LyXFunc::processKeySym(KeySymbolPtr keysym, key_modifier::state state)
282 LYXERR(Debug::KEY) << "KeySym is " << keysym->getSymbolName() << endl;
284 // Do nothing if we have nothing (JMarc)
285 if (!keysym->isOK()) {
286 LYXERR(Debug::KEY) << "Empty kbd action (probably composing)"
291 if (keysym->isModifier()) {
292 LYXERR(Debug::KEY) << "isModifier true" << endl;
296 //Encoding const * encoding = view()->cursor().getEncoding();
297 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
298 // FIXME: encoded_last_key shadows the member variable of the same
299 // name. Is that intended?
300 char_type encoded_last_key = keysym->getUCSEncoded();
302 // Do a one-deep top-level lookup for
303 // cancel and meta-fake keys. RVDK_PATCH_5
304 cancel_meta_seq->reset();
306 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
307 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
308 << " action first set to [" << func.action << ']'
311 // When not cancel or meta-fake, do the normal lookup.
312 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
313 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
314 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
315 // remove Caps Lock and Mod2 as a modifiers
316 func = keyseq->addkey(keysym, (state | meta_fake_bit));
317 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
318 << "action now set to ["
319 << func.action << ']' << endl;
322 // Dont remove this unless you know what you are doing.
323 meta_fake_bit = key_modifier::none;
325 // Can this happen now ?
326 if (func.action == LFUN_NOACTION) {
327 func = FuncRequest(LFUN_COMMAND_PREFIX);
330 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
332 << func.action << "]["
333 << to_utf8(keyseq->print(false)) << ']'
336 // already here we know if it any point in going further
337 // why not return already here if action == -1 and
338 // num_bytes == 0? (Lgb)
340 if (keyseq->length() > 1) {
341 lyx_view_->message(keyseq->print(true));
345 // Maybe user can only reach the key via holding down shift.
346 // Let's see. But only if shift is the only modifier
347 if (func.action == LFUN_UNKNOWN_ACTION &&
348 state == key_modifier::shift) {
349 LYXERR(Debug::KEY) << "Trying without shift" << endl;
350 func = keyseq->addkey(keysym, key_modifier::none);
351 LYXERR(Debug::KEY) << "Action now " << func.action << endl;
354 if (func.action == LFUN_UNKNOWN_ACTION) {
355 // Hmm, we didn't match any of the keysequences. See
356 // if it's normal insertable text not already covered
358 if (keysym->isText() && keyseq->length() == 1) {
359 LYXERR(Debug::KEY) << "isText() is true, inserting." << endl;
360 func = FuncRequest(LFUN_SELF_INSERT,
361 FuncRequest::KEYBOARD);
363 LYXERR(Debug::KEY) << "Unknown, !isText() - giving up" << endl;
364 lyx_view_->message(_("Unknown function."));
369 if (func.action == LFUN_SELF_INSERT) {
370 if (encoded_last_key != 0) {
371 docstring const arg(1, encoded_last_key);
372 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
373 FuncRequest::KEYBOARD));
375 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
383 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
385 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
388 Cursor & cur = view()->cursor();
390 /* In LyX/Mac, when a dialog is open, the menus of the
391 application can still be accessed without giving focus to
392 the main window. In this case, we want to disable the menu
393 entries that are buffer-related.
395 Note that this code is not perfect, as bug 1941 attests:
396 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
398 Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
399 if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
402 if (cmd.action == LFUN_NOACTION) {
403 flag.message(from_utf8(N_("Nothing to do")));
408 switch (cmd.action) {
409 case LFUN_UNKNOWN_ACTION:
410 #ifndef HAVE_LIBAIKSAURUS
411 case LFUN_THESAURUS_ENTRY:
421 if (flag.unknown()) {
422 flag.message(from_utf8(N_("Unknown action")));
426 if (!flag.enabled()) {
427 if (flag.message().empty())
428 flag.message(from_utf8(N_("Command disabled")));
432 // Check whether we need a buffer
433 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
435 flag.message(from_utf8(N_("Command not allowed with"
436 "out any document open")));
441 // I would really like to avoid having this switch and rather try to
442 // encode this in the function itself.
443 // -- And I'd rather let an inset decide which LFUNs it is willing
444 // to handle (Andre')
446 switch (cmd.action) {
447 case LFUN_BUFFER_TOGGLE_READ_ONLY:
448 flag.setOnOff(buf->isReadonly());
451 case LFUN_BUFFER_SWITCH:
452 // toggle on the current buffer, but do not toggle off
453 // the other ones (is that a good idea?)
454 if (to_utf8(cmd.argument()) == buf->fileName())
458 case LFUN_BUFFER_EXPORT:
459 enable = cmd.argument() == "custom"
460 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
463 case LFUN_BUFFER_CHKTEX:
464 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
467 case LFUN_BUILD_PROGRAM:
468 enable = Exporter::isExportable(*buf, "program");
471 case LFUN_LAYOUT_TABULAR:
472 enable = cur.innerInsetOfType(Inset::TABULAR_CODE);
476 case LFUN_LAYOUT_PARAGRAPH:
477 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
480 case LFUN_VC_REGISTER:
481 enable = !buf->lyxvc().inUse();
483 case LFUN_VC_CHECK_IN:
484 enable = buf->lyxvc().inUse() && !buf->isReadonly();
486 case LFUN_VC_CHECK_OUT:
487 enable = buf->lyxvc().inUse() && buf->isReadonly();
490 case LFUN_VC_UNDO_LAST:
491 enable = buf->lyxvc().inUse();
493 case LFUN_BUFFER_RELOAD:
494 enable = !buf->isUnnamed() && !buf->isClean();
497 case LFUN_INSET_SETTINGS: {
501 Inset::Code code = cur.inset().lyxCode();
503 case Inset::TABULAR_CODE:
504 enable = cmd.argument() == "tabular";
506 case Inset::ERT_CODE:
507 enable = cmd.argument() == "ert";
509 case Inset::FLOAT_CODE:
510 enable = cmd.argument() == "float";
512 case Inset::WRAP_CODE:
513 enable = cmd.argument() == "wrap";
515 case Inset::NOTE_CODE:
516 enable = cmd.argument() == "note";
518 case Inset::BRANCH_CODE:
519 enable = cmd.argument() == "branch";
521 case Inset::BOX_CODE:
522 enable = cmd.argument() == "box";
524 case Inset::LISTINGS_CODE:
525 enable = cmd.argument() == "listings";
533 case LFUN_INSET_APPLY: {
534 string const name = cmd.getArg(0);
535 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
537 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
539 if (!inset->getStatus(cur, fr, fs)) {
540 // Every inset is supposed to handle this
545 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
546 flag |= getStatus(fr);
548 enable = flag.enabled();
552 case LFUN_DIALOG_TOGGLE:
553 flag.setOnOff(lyx_view_->getDialogs().visible(cmd.getArg(0)));
554 // fall through to set "enable"
555 case LFUN_DIALOG_SHOW: {
556 string const name = cmd.getArg(0);
558 enable = name == "aboutlyx"
559 || name == "file" //FIXME: should be removed.
561 || name == "texinfo";
562 else if (name == "print")
563 enable = Exporter::isExportable(*buf, "dvi")
564 && lyxrc.print_command != "none";
565 else if (name == "character")
566 enable = cur.inset().lyxCode() != Inset::ERT_CODE &&
567 cur.inset().lyxCode() != Inset::LISTINGS_CODE;
568 else if (name == "latexlog")
569 enable = isFileReadable(FileName(buf->getLogName().second));
570 else if (name == "spellchecker")
571 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
572 enable = !buf->isReadonly();
576 else if (name == "vclog")
577 enable = buf->lyxvc().inUse();
581 case LFUN_DIALOG_SHOW_NEW_INSET:
582 enable = cur.inset().lyxCode() != Inset::ERT_CODE &&
583 cur.inset().lyxCode() != Inset::LISTINGS_CODE;
584 if (cur.inset().lyxCode() == Inset::CAPTION_CODE) {
586 if (cur.inset().getStatus(cur, cmd, flag))
591 case LFUN_DIALOG_UPDATE: {
592 string const name = cmd.getArg(0);
594 enable = name == "prefs";
598 case LFUN_CITATION_INSERT: {
599 FuncRequest fr(LFUN_INSET_INSERT, "citation");
600 enable = getStatus(fr).enabled();
604 case LFUN_BUFFER_WRITE: {
605 enable = view()->buffer()->isUnnamed()
606 || !view()->buffer()->isClean();
610 case LFUN_BOOKMARK_GOTO: {
611 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
612 enable = LyX::ref().session().bookmarks().isValid(num);
616 case LFUN_BOOKMARK_CLEAR:
617 enable = LyX::ref().session().bookmarks().size() > 0;
620 case LFUN_TOOLBAR_TOGGLE_STATE: {
621 ToolbarInfo::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
622 if (!(flags & ToolbarInfo::AUTO))
623 flag.setOnOff(flags & ToolbarInfo::ON);
627 case LFUN_TOOLBAR_TOGGLE: {
628 bool const current = lyx_view_->getToolbars().visible(cmd.getArg(0));
629 flag.setOnOff(current);
633 case LFUN_WINDOW_CLOSE: {
634 enable = (theApp()->gui().viewIds().size() > 1);
638 // this one is difficult to get right. As a half-baked
639 // solution, we consider only the first action of the sequence
640 case LFUN_COMMAND_SEQUENCE: {
641 // argument contains ';'-terminated commands
642 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
643 FuncRequest func(lyxaction.lookupFunc(firstcmd));
644 func.origin = cmd.origin;
645 flag = getStatus(func);
648 case LFUN_BUFFER_NEW:
649 case LFUN_BUFFER_NEW_TEMPLATE:
650 case LFUN_WORD_FIND_FORWARD:
651 case LFUN_WORD_FIND_BACKWARD:
652 case LFUN_COMMAND_PREFIX:
653 case LFUN_COMMAND_EXECUTE:
655 case LFUN_META_PREFIX:
656 case LFUN_BUFFER_CLOSE:
657 case LFUN_BUFFER_WRITE_AS:
658 case LFUN_BUFFER_UPDATE:
659 case LFUN_BUFFER_VIEW:
660 case LFUN_BUFFER_IMPORT:
661 case LFUN_BUFFER_AUTO_SAVE:
662 case LFUN_RECONFIGURE:
666 case LFUN_DROP_LAYOUTS_CHOICE:
668 case LFUN_SERVER_GET_NAME:
669 case LFUN_SERVER_NOTIFY:
670 case LFUN_SERVER_GOTO_FILE_ROW:
671 case LFUN_DIALOG_HIDE:
672 case LFUN_DIALOG_DISCONNECT_INSET:
673 case LFUN_BUFFER_CHILD_OPEN:
674 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
675 case LFUN_KEYMAP_OFF:
676 case LFUN_KEYMAP_PRIMARY:
677 case LFUN_KEYMAP_SECONDARY:
678 case LFUN_KEYMAP_TOGGLE:
680 case LFUN_BUFFER_EXPORT_CUSTOM:
681 case LFUN_BUFFER_PRINT:
682 case LFUN_PREFERENCES_SAVE:
683 case LFUN_SCREEN_FONT_UPDATE:
686 case LFUN_EXTERNAL_EDIT:
687 case LFUN_GRAPHICS_EDIT:
688 case LFUN_ALL_INSETS_TOGGLE:
689 case LFUN_BUFFER_LANGUAGE:
690 case LFUN_TEXTCLASS_APPLY:
691 case LFUN_TEXTCLASS_LOAD:
692 case LFUN_BUFFER_SAVE_AS_DEFAULT:
693 case LFUN_BUFFER_PARAMS_APPLY:
694 case LFUN_LYXRC_APPLY:
695 case LFUN_BUFFER_NEXT:
696 case LFUN_BUFFER_PREVIOUS:
697 case LFUN_WINDOW_NEW:
699 // these are handled in our dispatch()
703 if (!getLocalStatus(cur, cmd, flag))
704 flag = view()->getStatus(cmd);
710 // Can we use a readonly buffer?
711 if (buf && buf->isReadonly()
712 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
713 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
714 flag.message(from_utf8(N_("Document is read-only")));
718 // Are we in a DELETED change-tracking region?
719 if (buf && lookupChangeType(cur, true) == Change::DELETED
720 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
721 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
722 flag.message(from_utf8(N_("This portion of the document is deleted.")));
726 // the default error message if we disable the command
727 if (!flag.enabled() && flag.message().empty())
728 flag.message(from_utf8(N_("Command disabled")));
734 bool LyXFunc::ensureBufferClean(BufferView * bv)
736 Buffer & buf = *bv->buffer();
740 docstring const file = makeDisplayPath(buf.fileName(), 30);
741 docstring text = bformat(_("The document %1$s has unsaved "
742 "changes.\n\nDo you want to save "
743 "the document?"), file);
744 int const ret = Alert::prompt(_("Save changed document?"),
745 text, 0, 1, _("&Save"),
749 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
751 return buf.isClean();
757 void showPrintError(string const & name)
759 docstring str = bformat(_("Could not print the document %1$s.\n"
760 "Check that your printer is set up correctly."),
761 makeDisplayPath(name, 50));
762 Alert::error(_("Print document failed"), str);
766 void loadTextclass(string const & name)
768 std::pair<bool, textclass_type> const tc_pair =
769 textclasslist.numberOfClass(name);
771 if (!tc_pair.first) {
772 lyxerr << "Document class \"" << name
773 << "\" does not exist."
778 textclass_type const tc = tc_pair.second;
780 if (!textclasslist[tc].load()) {
781 docstring s = bformat(_("The document could not be converted\n"
782 "into the document class %1$s."),
783 from_utf8(textclasslist[tc].name()));
784 Alert::error(_("Could not change class"), s);
789 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
794 void LyXFunc::dispatch(FuncRequest const & cmd)
796 string const argument = to_utf8(cmd.argument());
797 kb_action const action = cmd.action;
799 LYXERR(Debug::ACTION) << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
800 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
802 // we have not done anything wrong yet.
804 dispatch_buffer.erase();
806 // redraw the screen at the end (first of the two drawing steps).
807 //This is done unless explicitely requested otherwise
808 Update::flags updateFlags = Update::FitCursor;
810 FuncStatus const flag = getStatus(cmd);
811 if (!flag.enabled()) {
812 // We cannot use this function here
813 LYXERR(Debug::ACTION) << "LyXFunc::dispatch: "
814 << lyxaction.getActionName(action)
815 << " [" << action << "] is disabled at this location"
817 setErrorMessage(flag.message());
821 case LFUN_WORD_FIND_FORWARD:
822 case LFUN_WORD_FIND_BACKWARD: {
823 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
824 static docstring last_search;
825 docstring searched_string;
827 if (!cmd.argument().empty()) {
828 last_search = cmd.argument();
829 searched_string = cmd.argument();
831 searched_string = last_search;
834 if (searched_string.empty())
837 bool const fw = action == LFUN_WORD_FIND_FORWARD;
838 docstring const data =
839 find2string(searched_string, true, false, fw);
840 find(view(), FuncRequest(LFUN_WORD_FIND, data));
844 case LFUN_COMMAND_PREFIX:
845 BOOST_ASSERT(lyx_view_);
846 lyx_view_->message(keyseq->printOptions(true));
849 case LFUN_COMMAND_EXECUTE:
850 BOOST_ASSERT(lyx_view_);
851 lyx_view_->getToolbars().display("minibuffer", true);
852 lyx_view_->focus_command_buffer();
856 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
858 meta_fake_bit = key_modifier::none;
859 if (view()->buffer())
860 // cancel any selection
861 dispatch(FuncRequest(LFUN_MARK_OFF));
862 setMessage(from_ascii(N_("Cancel")));
865 case LFUN_META_PREFIX:
866 meta_fake_bit = key_modifier::alt;
867 setMessage(keyseq->print(true));
870 case LFUN_BUFFER_TOGGLE_READ_ONLY:
871 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
872 if (lyx_view_->buffer()->lyxvc().inUse())
873 lyx_view_->buffer()->lyxvc().toggleReadOnly();
875 lyx_view_->buffer()->setReadonly(
876 !lyx_view_->buffer()->isReadonly());
879 // --- Menus -----------------------------------------------
880 case LFUN_BUFFER_NEW:
881 menuNew(argument, false);
884 case LFUN_BUFFER_NEW_TEMPLATE:
885 menuNew(argument, true);
888 case LFUN_BUFFER_CLOSE:
893 case LFUN_BUFFER_WRITE:
894 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
895 if (!lyx_view_->buffer()->isUnnamed()) {
896 docstring const str = bformat(_("Saving document %1$s..."),
897 makeDisplayPath(lyx_view_->buffer()->fileName()));
898 lyx_view_->message(str);
899 menuWrite(lyx_view_->buffer());
900 lyx_view_->message(str + _(" done."));
902 writeAs(lyx_view_->buffer());
904 updateFlags = Update::None;
907 case LFUN_BUFFER_WRITE_AS:
908 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
909 writeAs(lyx_view_->buffer(), argument);
910 updateFlags = Update::None;
913 case LFUN_BUFFER_RELOAD: {
914 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
915 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
916 docstring text = bformat(_("Any changes will be lost. Are you sure "
917 "you want to revert to the saved version of the document %1$s?"), file);
918 int const ret = Alert::prompt(_("Revert to saved document?"),
919 text, 1, 1, _("&Revert"), _("&Cancel"));
926 case LFUN_BUFFER_UPDATE:
927 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
928 Exporter::Export(lyx_view_->buffer(), argument, true);
931 case LFUN_BUFFER_VIEW:
932 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
933 Exporter::preview(lyx_view_->buffer(), argument);
936 case LFUN_BUILD_PROGRAM:
937 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
938 Exporter::Export(lyx_view_->buffer(), "program", true);
941 case LFUN_BUFFER_CHKTEX:
942 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
943 lyx_view_->buffer()->runChktex();
946 case LFUN_BUFFER_EXPORT:
947 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
948 if (argument == "custom")
949 lyx_view_->getDialogs().show("sendto");
951 Exporter::Export(lyx_view_->buffer(), argument, false);
955 case LFUN_BUFFER_EXPORT_CUSTOM: {
956 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
958 string command = split(argument, format_name, ' ');
959 Format const * format = formats.getFormat(format_name);
961 lyxerr << "Format \"" << format_name
962 << "\" not recognized!"
967 Buffer * buffer = lyx_view_->buffer();
969 // The name of the file created by the conversion process
972 // Output to filename
973 if (format->name() == "lyx") {
974 string const latexname =
975 buffer->getLatexName(false);
976 filename = changeExtension(latexname,
977 format->extension());
978 filename = addName(buffer->temppath(), filename);
980 if (!buffer->writeFile(FileName(filename)))
984 Exporter::Export(buffer, format_name, true, filename);
987 // Substitute $$FName for filename
988 if (!contains(command, "$$FName"))
989 command = "( " + command + " ) < $$FName";
990 command = subst(command, "$$FName", filename);
992 // Execute the command in the background
994 call.startscript(Systemcall::DontWait, command);
998 case LFUN_BUFFER_PRINT: {
999 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1002 string command = split(split(argument, target, ' '),
1006 || target_name.empty()
1007 || command.empty()) {
1008 lyxerr << "Unable to parse \""
1009 << argument << '"' << std::endl;
1012 if (target != "printer" && target != "file") {
1013 lyxerr << "Unrecognized target \""
1014 << target << '"' << std::endl;
1018 Buffer * buffer = lyx_view_->buffer();
1020 if (!Exporter::Export(buffer, "dvi", true)) {
1021 showPrintError(buffer->fileName());
1025 // Push directory path.
1026 string const path(buffer->temppath());
1027 // Prevent the compiler from optimizing away p
1029 support::Path p(pp);
1031 // there are three cases here:
1032 // 1. we print to a file
1033 // 2. we print directly to a printer
1034 // 3. we print using a spool command (print to file first)
1037 string const dviname =
1038 changeExtension(buffer->getLatexName(true),
1041 if (target == "printer") {
1042 if (!lyxrc.print_spool_command.empty()) {
1043 // case 3: print using a spool
1044 string const psname =
1045 changeExtension(dviname,".ps");
1046 command += lyxrc.print_to_file
1049 + quoteName(dviname);
1052 lyxrc.print_spool_command +' ';
1053 if (target_name != "default") {
1054 command2 += lyxrc.print_spool_printerprefix
1058 command2 += quoteName(psname);
1060 // If successful, then spool command
1061 res = one.startscript(
1066 res = one.startscript(
1067 Systemcall::DontWait,
1070 // case 2: print directly to a printer
1071 res = one.startscript(
1072 Systemcall::DontWait,
1073 command + quoteName(dviname));
1077 // case 1: print to a file
1078 FileName const filename(makeAbsPath(target_name, path));
1079 if (fs::exists(filename.toFilesystemEncoding())) {
1080 docstring text = bformat(
1081 _("The file %1$s already exists.\n\n"
1082 "Do you want to over-write that file?"),
1083 makeDisplayPath(filename.absFilename()));
1084 if (Alert::prompt(_("Over-write file?"),
1085 text, 0, 1, _("&Over-write"), _("&Cancel")) != 0)
1088 command += lyxrc.print_to_file
1089 + quoteName(filename.toFilesystemEncoding())
1091 + quoteName(dviname);
1092 res = one.startscript(Systemcall::DontWait,
1097 showPrintError(buffer->fileName());
1101 case LFUN_BUFFER_IMPORT:
1106 // quitting is triggered by the gui code
1107 // (leaving the event loop).
1108 lyx_view_->message(from_utf8(N_("Exiting.")));
1109 if (theBufferList().quitWriteAll())
1110 theApp()->gui().closeAllViews();
1113 case LFUN_BUFFER_AUTO_SAVE:
1117 case LFUN_RECONFIGURE:
1118 BOOST_ASSERT(lyx_view_);
1119 reconfigure(*lyx_view_);
1122 case LFUN_HELP_OPEN: {
1123 BOOST_ASSERT(lyx_view_);
1124 string const arg = argument;
1126 setErrorMessage(from_ascii(N_("Missing argument")));
1129 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1130 if (fname.empty()) {
1131 lyxerr << "LyX: unable to find documentation file `"
1132 << arg << "'. Bad installation?" << endl;
1135 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1136 makeDisplayPath(fname.absFilename())));
1137 lyx_view_->loadLyXFile(fname, false);
1141 // --- version control -------------------------------
1142 case LFUN_VC_REGISTER:
1143 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1144 if (!ensureBufferClean(view()))
1146 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1147 lyx_view_->buffer()->lyxvc().registrer();
1150 updateFlags = Update::Force;
1153 case LFUN_VC_CHECK_IN:
1154 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1155 if (!ensureBufferClean(view()))
1157 if (lyx_view_->buffer()->lyxvc().inUse()
1158 && !lyx_view_->buffer()->isReadonly()) {
1159 lyx_view_->buffer()->lyxvc().checkIn();
1164 case LFUN_VC_CHECK_OUT:
1165 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1166 if (!ensureBufferClean(view()))
1168 if (lyx_view_->buffer()->lyxvc().inUse()
1169 && lyx_view_->buffer()->isReadonly()) {
1170 lyx_view_->buffer()->lyxvc().checkOut();
1175 case LFUN_VC_REVERT:
1176 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1177 lyx_view_->buffer()->lyxvc().revert();
1181 case LFUN_VC_UNDO_LAST:
1182 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1183 lyx_view_->buffer()->lyxvc().undoLast();
1187 // --- buffers ----------------------------------------
1188 case LFUN_BUFFER_SWITCH:
1189 BOOST_ASSERT(lyx_view_);
1190 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1193 case LFUN_BUFFER_NEXT:
1194 BOOST_ASSERT(lyx_view_);
1195 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1198 case LFUN_BUFFER_PREVIOUS:
1199 BOOST_ASSERT(lyx_view_);
1200 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1204 BOOST_ASSERT(lyx_view_);
1205 newFile(view(), argument);
1208 case LFUN_FILE_OPEN:
1209 BOOST_ASSERT(lyx_view_);
1213 case LFUN_DROP_LAYOUTS_CHOICE:
1214 BOOST_ASSERT(lyx_view_);
1215 lyx_view_->getToolbars().openLayoutList();
1218 case LFUN_MENU_OPEN:
1219 BOOST_ASSERT(lyx_view_);
1220 lyx_view_->getMenubar().openByName(from_utf8(argument));
1223 // --- lyxserver commands ----------------------------
1224 case LFUN_SERVER_GET_NAME:
1225 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1226 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1227 LYXERR(Debug::INFO) << "FNAME["
1228 << lyx_view_->buffer()->fileName()
1232 case LFUN_SERVER_NOTIFY:
1233 dispatch_buffer = keyseq->print(false);
1234 theServer().notifyClient(to_utf8(dispatch_buffer));
1237 case LFUN_SERVER_GOTO_FILE_ROW: {
1238 BOOST_ASSERT(lyx_view_);
1241 istringstream is(argument);
1242 is >> file_name >> row;
1243 if (prefixIs(file_name, package().temp_dir().absFilename())) {
1244 // Needed by inverse dvi search. If it is a file
1245 // in tmpdir, call the apropriated function
1246 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1248 // Must replace extension of the file to be .lyx
1249 // and get full path
1250 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1251 // Either change buffer or load the file
1252 if (theBufferList().exists(s.absFilename())) {
1253 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1255 lyx_view_->loadLyXFile(s);
1259 view()->setCursorFromRow(row);
1261 updateFlags = Update::FitCursor;
1265 case LFUN_DIALOG_SHOW: {
1266 BOOST_ASSERT(lyx_view_);
1267 string const name = cmd.getArg(0);
1268 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1270 if (name == "character") {
1271 data = freefont2string();
1273 lyx_view_->getDialogs().show("character", data);
1274 } else if (name == "latexlog") {
1275 pair<Buffer::LogType, string> const logfile =
1276 lyx_view_->buffer()->getLogName();
1277 switch (logfile.first) {
1278 case Buffer::latexlog:
1281 case Buffer::buildlog:
1285 data += Lexer::quoteString(logfile.second);
1286 lyx_view_->getDialogs().show("log", data);
1287 } else if (name == "vclog") {
1288 string const data = "vc " +
1289 Lexer::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1290 lyx_view_->getDialogs().show("log", data);
1292 lyx_view_->getDialogs().show(name, data);
1296 case LFUN_DIALOG_SHOW_NEW_INSET: {
1297 BOOST_ASSERT(lyx_view_);
1298 string const name = cmd.getArg(0);
1299 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1300 if (name == "bibitem" ||
1304 name == "nomenclature" ||
1308 InsetCommandParams p(name);
1309 data = InsetCommandMailer::params2string(name, p);
1310 } else if (name == "include") {
1311 // data is the include type: one of "include",
1312 // "input", "verbatiminput" or "verbatiminput*"
1314 // default type is requested
1316 InsetCommandParams p(data);
1317 data = InsetIncludeMailer::params2string(p);
1318 } else if (name == "box") {
1319 // \c data == "Boxed" || "Frameless" etc
1320 InsetBoxParams p(data);
1321 data = InsetBoxMailer::params2string(p);
1322 } else if (name == "branch") {
1323 InsetBranchParams p;
1324 data = InsetBranchMailer::params2string(p);
1325 } else if (name == "citation") {
1326 InsetCommandParams p("cite");
1327 data = InsetCommandMailer::params2string(name, p);
1328 } else if (name == "ert") {
1329 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1330 } else if (name == "external") {
1331 InsetExternalParams p;
1332 Buffer const & buffer = *lyx_view_->buffer();
1333 data = InsetExternalMailer::params2string(p, buffer);
1334 } else if (name == "float") {
1336 data = InsetFloatMailer::params2string(p);
1337 } else if (name == "listings") {
1338 InsetListingsParams p;
1339 data = InsetListingsMailer::params2string(p);
1340 } else if (name == "graphics") {
1341 InsetGraphicsParams p;
1342 Buffer const & buffer = *lyx_view_->buffer();
1343 data = InsetGraphicsMailer::params2string(p, buffer);
1344 } else if (name == "note") {
1346 data = InsetNoteMailer::params2string(p);
1347 } else if (name == "vspace") {
1349 data = InsetVSpaceMailer::params2string(space);
1350 } else if (name == "wrap") {
1352 data = InsetWrapMailer::params2string(p);
1354 lyx_view_->getDialogs().show(name, data, 0);
1358 case LFUN_DIALOG_UPDATE: {
1359 BOOST_ASSERT(lyx_view_);
1360 string const & name = argument;
1361 // Can only update a dialog connected to an existing inset
1362 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1364 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1365 inset->dispatch(view()->cursor(), fr);
1366 } else if (name == "paragraph") {
1367 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1368 } else if (name == "prefs") {
1369 lyx_view_->getDialogs().update(name, string());
1374 case LFUN_DIALOG_HIDE:
1375 Dialogs::hide(argument, 0);
1378 case LFUN_DIALOG_TOGGLE: {
1379 BOOST_ASSERT(lyx_view_);
1380 if (lyx_view_->getDialogs().visible(cmd.getArg(0)))
1381 dispatch(FuncRequest(LFUN_DIALOG_HIDE, argument));
1383 dispatch(FuncRequest(LFUN_DIALOG_SHOW, argument));
1387 case LFUN_DIALOG_DISCONNECT_INSET:
1388 BOOST_ASSERT(lyx_view_);
1389 lyx_view_->getDialogs().disconnect(argument);
1393 case LFUN_CITATION_INSERT: {
1394 BOOST_ASSERT(lyx_view_);
1395 if (!argument.empty()) {
1396 // we can have one optional argument, delimited by '|'
1397 // citation-insert <key>|<text_before>
1398 // this should be enhanced to also support text_after
1399 // and citation style
1400 string arg = argument;
1402 if (contains(argument, "|")) {
1403 arg = token(argument, '|', 0);
1404 opt1 = token(argument, '|', 1);
1406 InsetCommandParams icp("cite");
1407 icp["key"] = from_utf8(arg);
1409 icp["before"] = from_utf8(opt1);
1410 string icstr = InsetCommandMailer::params2string("citation", icp);
1411 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1414 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1418 case LFUN_BUFFER_CHILD_OPEN: {
1419 BOOST_ASSERT(lyx_view_);
1420 FileName const filename =
1421 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1422 view()->saveBookmark(false);
1423 string const parentfilename = lyx_view_->buffer()->fileName();
1424 if (theBufferList().exists(filename.absFilename()))
1425 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1427 if (lyx_view_->loadLyXFile(filename)) {
1428 // Set the parent name of the child document.
1429 // This makes insertion of citations and references in the child work,
1430 // when the target is in the parent or another child document.
1431 lyx_view_->buffer()->setParentName(parentfilename);
1432 setMessage(bformat(_("Opening child document %1$s..."),
1433 makeDisplayPath(filename.absFilename())));
1435 setMessage(_("Document not loaded."));
1439 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1440 BOOST_ASSERT(lyx_view_);
1441 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1444 case LFUN_KEYMAP_OFF:
1445 BOOST_ASSERT(lyx_view_);
1446 lyx_view_->view()->getIntl().keyMapOn(false);
1449 case LFUN_KEYMAP_PRIMARY:
1450 BOOST_ASSERT(lyx_view_);
1451 lyx_view_->view()->getIntl().keyMapPrim();
1454 case LFUN_KEYMAP_SECONDARY:
1455 BOOST_ASSERT(lyx_view_);
1456 lyx_view_->view()->getIntl().keyMapSec();
1459 case LFUN_KEYMAP_TOGGLE:
1460 BOOST_ASSERT(lyx_view_);
1461 lyx_view_->view()->getIntl().toggleKeyMap();
1467 string rest = split(argument, countstr, ' ');
1468 istringstream is(countstr);
1471 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1472 for (int i = 0; i < count; ++i)
1473 dispatch(lyxaction.lookupFunc(rest));
1477 case LFUN_COMMAND_SEQUENCE: {
1478 // argument contains ';'-terminated commands
1479 string arg = argument;
1480 while (!arg.empty()) {
1482 arg = split(arg, first, ';');
1483 FuncRequest func(lyxaction.lookupFunc(first));
1484 func.origin = cmd.origin;
1490 case LFUN_PREFERENCES_SAVE: {
1491 lyxrc.write(makeAbsPath("preferences",
1492 package().user_support().absFilename()),
1497 case LFUN_SCREEN_FONT_UPDATE:
1498 BOOST_ASSERT(lyx_view_);
1499 // handle the screen font changes.
1500 theFontLoader().update();
1501 /// FIXME: only the current view will be updated. the Gui
1502 /// class is able to furnish the list of views.
1503 updateFlags = Update::Force;
1506 case LFUN_SET_COLOR: {
1508 string const x11_name = split(argument, lyx_name, ' ');
1509 if (lyx_name.empty() || x11_name.empty()) {
1510 setErrorMessage(from_ascii(N_(
1511 "Syntax: set-color <lyx_name>"
1516 bool const graphicsbg_changed =
1517 (lyx_name == lcolor.getLyXName(Color::graphicsbg) &&
1518 x11_name != lcolor.getX11Name(Color::graphicsbg));
1520 if (!lcolor.setColor(lyx_name, x11_name)) {
1522 bformat(_("Set-color \"%1$s\" failed "
1523 "- color is undefined or "
1524 "may not be redefined"),
1525 from_utf8(lyx_name)));
1529 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1531 if (graphicsbg_changed) {
1532 #ifdef WITH_WARNINGS
1533 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1536 graphics::GCache::get().changeDisplay(true);
1543 BOOST_ASSERT(lyx_view_);
1544 lyx_view_->message(from_utf8(argument));
1547 case LFUN_EXTERNAL_EDIT: {
1548 BOOST_ASSERT(lyx_view_);
1549 FuncRequest fr(action, argument);
1550 InsetExternal().dispatch(view()->cursor(), fr);
1554 case LFUN_GRAPHICS_EDIT: {
1555 FuncRequest fr(action, argument);
1556 InsetGraphics().dispatch(view()->cursor(), fr);
1560 case LFUN_INSET_APPLY: {
1561 BOOST_ASSERT(lyx_view_);
1562 string const name = cmd.getArg(0);
1563 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1565 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1566 inset->dispatch(view()->cursor(), fr);
1568 FuncRequest fr(LFUN_INSET_INSERT, argument);
1571 // ideally, the update flag should be set by the insets,
1572 // but this is not possible currently
1573 updateFlags = Update::Force | Update::FitCursor;
1577 case LFUN_ALL_INSETS_TOGGLE: {
1578 BOOST_ASSERT(lyx_view_);
1580 string const name = split(argument, action, ' ');
1581 Inset::Code const inset_code =
1582 Inset::translate(name);
1584 Cursor & cur = view()->cursor();
1585 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1587 Inset & inset = lyx_view_->buffer()->inset();
1588 InsetIterator it = inset_iterator_begin(inset);
1589 InsetIterator const end = inset_iterator_end(inset);
1590 for (; it != end; ++it) {
1591 if (!it->asInsetMath()
1592 && (inset_code == Inset::NO_CODE
1593 || inset_code == it->lyxCode())) {
1594 Cursor tmpcur = cur;
1595 tmpcur.pushLeft(*it);
1596 it->dispatch(tmpcur, fr);
1599 updateFlags = Update::Force | Update::FitCursor;
1603 case LFUN_BUFFER_LANGUAGE: {
1604 BOOST_ASSERT(lyx_view_);
1605 Buffer & buffer = *lyx_view_->buffer();
1606 Language const * oldL = buffer.params().language;
1607 Language const * newL = languages.getLanguage(argument);
1608 if (!newL || oldL == newL)
1611 if (oldL->rightToLeft() == newL->rightToLeft()
1612 && !buffer.isMultiLingual())
1613 buffer.changeLanguage(oldL, newL);
1617 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1618 string const fname =
1619 addName(addPath(package().user_support().absFilename(), "templates/"),
1621 Buffer defaults(fname);
1623 istringstream ss(argument);
1626 int const unknown_tokens = defaults.readHeader(lex);
1628 if (unknown_tokens != 0) {
1629 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1630 << unknown_tokens << " unknown token"
1631 << (unknown_tokens == 1 ? "" : "s")
1635 if (defaults.writeFile(FileName(defaults.fileName())))
1636 setMessage(bformat(_("Document defaults saved in %1$s"),
1637 makeDisplayPath(fname)));
1639 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1643 case LFUN_BUFFER_PARAMS_APPLY: {
1644 BOOST_ASSERT(lyx_view_);
1645 biblio::CiteEngine const engine =
1646 lyx_view_->buffer()->params().getEngine();
1648 istringstream ss(argument);
1651 int const unknown_tokens =
1652 lyx_view_->buffer()->readHeader(lex);
1654 if (unknown_tokens != 0) {
1655 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1656 << unknown_tokens << " unknown token"
1657 << (unknown_tokens == 1 ? "" : "s")
1660 if (engine == lyx_view_->buffer()->params().getEngine())
1663 Cursor & cur = view()->cursor();
1664 FuncRequest fr(LFUN_INSET_REFRESH);
1666 Inset & inset = lyx_view_->buffer()->inset();
1667 InsetIterator it = inset_iterator_begin(inset);
1668 InsetIterator const end = inset_iterator_end(inset);
1669 for (; it != end; ++it)
1670 if (it->lyxCode() == Inset::CITE_CODE)
1671 it->dispatch(cur, fr);
1675 case LFUN_TEXTCLASS_APPLY: {
1676 BOOST_ASSERT(lyx_view_);
1677 Buffer * buffer = lyx_view_->buffer();
1679 textclass_type const old_class =
1680 buffer->params().textclass;
1682 loadTextclass(argument);
1684 std::pair<bool, textclass_type> const tc_pair =
1685 textclasslist.numberOfClass(argument);
1690 textclass_type const new_class = tc_pair.second;
1691 if (old_class == new_class)
1695 lyx_view_->message(_("Converting document to new document class..."));
1696 recordUndoFullDocument(view());
1697 buffer->params().textclass = new_class;
1698 StableDocIterator backcur(view()->cursor());
1699 ErrorList & el = buffer->errorList("Class Switch");
1700 cap::switchBetweenClasses(
1701 old_class, new_class,
1702 static_cast<InsetText &>(buffer->inset()), el);
1704 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1706 buffer->errors("Class Switch");
1707 updateLabels(*buffer);
1708 updateFlags = Update::Force | Update::FitCursor;
1712 case LFUN_TEXTCLASS_LOAD:
1713 loadTextclass(argument);
1716 case LFUN_LYXRC_APPLY: {
1717 LyXRC const lyxrc_orig = lyxrc;
1719 istringstream ss(argument);
1720 bool const success = lyxrc.read(ss) == 0;
1723 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1724 << "Unable to read lyxrc data"
1729 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1731 /// We force the redraw in any case because there might be
1732 /// some screen font changes.
1733 /// FIXME: only the current view will be updated. the Gui
1734 /// class is able to furnish the list of views.
1735 updateFlags = Update::Force;
1739 case LFUN_WINDOW_NEW:
1740 LyX::ref().newLyXView();
1743 case LFUN_WINDOW_CLOSE:
1744 BOOST_ASSERT(lyx_view_);
1745 BOOST_ASSERT(theApp());
1746 // update bookmark pit of the current buffer before window close
1747 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1748 gotoBookmark(i+1, false, false);
1749 // ask the user for saving changes or cancel quit
1750 if (!theBufferList().quitWriteAll())
1755 case LFUN_BOOKMARK_GOTO:
1756 // go to bookmark, open unopened file and switch to buffer if necessary
1757 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1760 case LFUN_BOOKMARK_CLEAR:
1761 LyX::ref().session().bookmarks().clear();
1764 case LFUN_TOOLBAR_TOGGLE_STATE:
1765 lyx_view_->toggleToolbarState(argument);
1768 case LFUN_TOOLBAR_TOGGLE: {
1769 BOOST_ASSERT(lyx_view_);
1770 string const name = to_utf8(cmd.argument());
1771 bool const current = lyx_view_->getToolbars().visible(name);
1772 lyx_view_->getToolbars().display(name, !current);
1777 BOOST_ASSERT(lyx_view_);
1778 view()->cursor().dispatch(cmd);
1779 updateFlags = view()->cursor().result().update();
1780 if (!view()->cursor().result().dispatched())
1781 updateFlags = view()->dispatch(cmd);
1786 if (lyx_view_ && view()->buffer()) {
1787 // BufferView::update() updates the ViewMetricsInfo and
1788 // also initializes the position cache for all insets in
1789 // (at least partially) visible top-level paragraphs.
1790 // We will redraw the screen only if needed.
1791 if (view()->update(updateFlags)) {
1792 // Buffer::changed() signals that a repaint is needed.
1793 // The frontend (WorkArea) knows which area to repaint
1794 // thanks to the ViewMetricsInfo updated above.
1795 view()->buffer()->changed();
1798 lyx_view_->updateStatusBar();
1800 // if we executed a mutating lfun, mark the buffer as dirty
1802 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1803 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1804 view()->buffer()->markDirty();
1806 if (view()->cursor().inTexted()) {
1807 lyx_view_->updateLayoutChoice();
1812 lyx_view_->updateMenubar();
1813 lyx_view_->updateToolbars();
1814 // Some messages may already be translated, so we cannot use _()
1815 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1820 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1822 const bool verbose = (cmd.origin == FuncRequest::MENU
1823 || cmd.origin == FuncRequest::TOOLBAR
1824 || cmd.origin == FuncRequest::COMMANDBUFFER);
1826 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1827 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1829 lyx_view_->message(msg);
1833 docstring dispatch_msg = msg;
1834 if (!dispatch_msg.empty())
1835 dispatch_msg += ' ';
1837 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1839 bool argsadded = false;
1841 if (!cmd.argument().empty()) {
1842 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1843 comname += ' ' + cmd.argument();
1848 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1850 if (!shortcuts.empty())
1851 comname += ": " + shortcuts;
1852 else if (!argsadded && !cmd.argument().empty())
1853 comname += ' ' + cmd.argument();
1855 if (!comname.empty()) {
1856 comname = rtrim(comname);
1857 dispatch_msg += '(' + rtrim(comname) + ')';
1860 LYXERR(Debug::ACTION) << "verbose dispatch msg "
1861 << to_utf8(dispatch_msg) << endl;
1862 if (!dispatch_msg.empty())
1863 lyx_view_->message(dispatch_msg);
1867 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1869 // FIXME: initpath is not used. What to do?
1870 string initpath = lyxrc.document_path;
1871 string filename(name);
1873 if (view()->buffer()) {
1874 string const trypath = lyx_view_->buffer()->filePath();
1875 // If directory is writeable, use this as default.
1876 if (isDirWriteable(FileName(trypath)))
1880 static int newfile_number;
1882 if (filename.empty()) {
1883 filename = addName(lyxrc.document_path,
1884 "newfile" + convert<string>(++newfile_number) + ".lyx");
1885 while (theBufferList().exists(filename) ||
1886 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1888 filename = addName(lyxrc.document_path,
1889 "newfile" + convert<string>(newfile_number) +
1894 // The template stuff
1897 FileDialog fileDlg(_("Select template file"),
1898 LFUN_SELECT_FILE_SYNC,
1899 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1900 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1902 FileDialog::Result result =
1903 fileDlg.open(from_utf8(lyxrc.template_path),
1904 FileFilterList(_("LyX Documents (*.lyx)")),
1907 if (result.first == FileDialog::Later)
1909 if (result.second.empty())
1911 templname = to_utf8(result.second);
1914 Buffer * const b = newFile(filename, templname, !name.empty());
1917 lyx_view_->setBuffer(b);
1922 void LyXFunc::open(string const & fname)
1924 string initpath = lyxrc.document_path;
1926 if (view()->buffer()) {
1927 string const trypath = lyx_view_->buffer()->filePath();
1928 // If directory is writeable, use this as default.
1929 if (isDirWriteable(FileName(trypath)))
1935 if (fname.empty()) {
1936 FileDialog fileDlg(_("Select document to open"),
1938 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1939 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1941 FileDialog::Result result =
1942 fileDlg.open(from_utf8(initpath),
1943 FileFilterList(_("LyX Documents (*.lyx)")),
1946 if (result.first == FileDialog::Later)
1949 filename = to_utf8(result.second);
1951 // check selected filename
1952 if (filename.empty()) {
1953 lyx_view_->message(_("Canceled."));
1959 // get absolute path of file and add ".lyx" to the filename if
1961 FileName const fullname = fileSearch(string(), filename, "lyx");
1962 if (!fullname.empty())
1963 filename = fullname.absFilename();
1965 // if the file doesn't exist, let the user create one
1966 if (!fs::exists(fullname.toFilesystemEncoding())) {
1967 // the user specifically chose this name. Believe him.
1968 Buffer * const b = newFile(filename, string(), true);
1970 lyx_view_->setBuffer(b);
1974 docstring const disp_fn = makeDisplayPath(filename);
1975 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1978 if (lyx_view_->loadLyXFile(fullname)) {
1979 str2 = bformat(_("Document %1$s opened."), disp_fn);
1981 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1983 lyx_view_->message(str2);
1987 void LyXFunc::doImport(string const & argument)
1990 string filename = split(argument, format, ' ');
1992 LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
1993 << " file: " << filename << endl;
1995 // need user interaction
1996 if (filename.empty()) {
1997 string initpath = lyxrc.document_path;
1999 if (view()->buffer()) {
2000 string const trypath = lyx_view_->buffer()->filePath();
2001 // If directory is writeable, use this as default.
2002 if (isDirWriteable(FileName(trypath)))
2006 docstring const text = bformat(_("Select %1$s file to import"),
2007 formats.prettyName(format));
2009 FileDialog fileDlg(text,
2011 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2012 make_pair(_("Examples|#E#e"),
2013 from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2015 docstring filter = formats.prettyName(format);
2018 filter += from_utf8(formats.extension(format));
2021 FileDialog::Result result =
2022 fileDlg.open(from_utf8(initpath),
2023 FileFilterList(filter),
2026 if (result.first == FileDialog::Later)
2029 filename = to_utf8(result.second);
2031 // check selected filename
2032 if (filename.empty())
2033 lyx_view_->message(_("Canceled."));
2036 if (filename.empty())
2039 // get absolute path of file
2040 FileName const fullname(makeAbsPath(filename));
2042 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2044 // Check if the document already is open
2045 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2046 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2047 lyx_view_->message(_("Canceled."));
2052 // if the file exists already, and we didn't do
2053 // -i lyx thefile.lyx, warn
2054 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2055 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2057 docstring text = bformat(_("The document %1$s already exists.\n\n"
2058 "Do you want to over-write that document?"), file);
2059 int const ret = Alert::prompt(_("Over-write document?"),
2060 text, 0, 1, _("&Over-write"), _("&Cancel"));
2063 lyx_view_->message(_("Canceled."));
2068 ErrorList errorList;
2069 Importer::Import(lyx_view_, fullname, format, errorList);
2070 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2074 void LyXFunc::closeBuffer()
2076 // goto bookmark to update bookmark pit.
2077 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2078 gotoBookmark(i+1, false, false);
2079 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2080 if (theBufferList().empty()) {
2081 // need this otherwise SEGV may occur while
2082 // trying to set variables that don't exist
2083 // since there's no current buffer
2084 lyx_view_->getDialogs().hideBufferDependent();
2086 lyx_view_->setBuffer(theBufferList().first());
2092 void LyXFunc::reloadBuffer()
2094 FileName filename(lyx_view_->buffer()->fileName());
2096 lyx_view_->loadLyXFile(filename);
2099 // Each "lyx_view_" should have it's own message method. lyxview and
2100 // the minibuffer would use the minibuffer, but lyxserver would
2101 // send an ERROR signal to its client. Alejandro 970603
2102 // This function is bit problematic when it comes to NLS, to make the
2103 // lyx servers client be language indepenent we must not translate
2104 // strings sent to this func.
2105 void LyXFunc::setErrorMessage(docstring const & m) const
2107 dispatch_buffer = m;
2112 void LyXFunc::setMessage(docstring const & m) const
2114 dispatch_buffer = m;
2118 docstring const LyXFunc::viewStatusMessage()
2120 // When meta-fake key is pressed, show the key sequence so far + "M-".
2122 return keyseq->print(true) + "M-";
2124 // Else, when a non-complete key sequence is pressed,
2125 // show the available options.
2126 if (keyseq->length() > 0 && !keyseq->deleted())
2127 return keyseq->printOptions(true);
2129 if (!view()->buffer())
2130 return _("Welcome to LyX!");
2132 return view()->cursor().currentState();
2136 BufferView * LyXFunc::view() const
2138 BOOST_ASSERT(lyx_view_);
2139 return lyx_view_->view();
2143 bool LyXFunc::wasMetaKey() const
2145 return (meta_fake_bit != key_modifier::none);
2151 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2153 // Why the switch you might ask. It is a trick to ensure that all
2154 // the elements in the LyXRCTags enum is handled. As you can see
2155 // there are no breaks at all. So it is just a huge fall-through.
2156 // The nice thing is that we will get a warning from the compiler
2157 // if we forget an element.
2158 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2160 case LyXRC::RC_ACCEPT_COMPOUND:
2161 case LyXRC::RC_ALT_LANG:
2162 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2163 case LyXRC::RC_PLAINTEXT_LINELEN:
2164 case LyXRC::RC_AUTOREGIONDELETE:
2165 case LyXRC::RC_AUTORESET_OPTIONS:
2166 case LyXRC::RC_AUTOSAVE:
2167 case LyXRC::RC_AUTO_NUMBER:
2168 case LyXRC::RC_BACKUPDIR_PATH:
2169 case LyXRC::RC_BIBTEX_COMMAND:
2170 case LyXRC::RC_BINDFILE:
2171 case LyXRC::RC_CHECKLASTFILES:
2172 case LyXRC::RC_USELASTFILEPOS:
2173 case LyXRC::RC_LOADSESSION:
2174 case LyXRC::RC_CHKTEX_COMMAND:
2175 case LyXRC::RC_CONVERTER:
2176 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2177 case LyXRC::RC_COPIER:
2178 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2179 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2180 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2181 case LyXRC::RC_DATE_INSERT_FORMAT:
2182 case LyXRC::RC_DEFAULT_LANGUAGE:
2183 case LyXRC::RC_DEFAULT_PAPERSIZE:
2184 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2185 case LyXRC::RC_DISPLAY_GRAPHICS:
2186 case LyXRC::RC_DOCUMENTPATH:
2187 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2188 string const encoded = FileName(
2189 lyxrc_new.document_path).toFilesystemEncoding();
2190 if (fs::exists(encoded) && fs::is_directory(encoded))
2191 support::package().document_dir() = FileName(lyxrc.document_path);
2193 case LyXRC::RC_ESC_CHARS:
2194 case LyXRC::RC_FONT_ENCODING:
2195 case LyXRC::RC_FORMAT:
2196 case LyXRC::RC_INDEX_COMMAND:
2197 case LyXRC::RC_INPUT:
2198 case LyXRC::RC_KBMAP:
2199 case LyXRC::RC_KBMAP_PRIMARY:
2200 case LyXRC::RC_KBMAP_SECONDARY:
2201 case LyXRC::RC_LABEL_INIT_LENGTH:
2202 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2203 case LyXRC::RC_LANGUAGE_AUTO_END:
2204 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2205 case LyXRC::RC_LANGUAGE_COMMAND_END:
2206 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2207 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2208 case LyXRC::RC_LANGUAGE_PACKAGE:
2209 case LyXRC::RC_LANGUAGE_USE_BABEL:
2210 case LyXRC::RC_MAKE_BACKUP:
2211 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2212 case LyXRC::RC_NUMLASTFILES:
2213 case LyXRC::RC_PATH_PREFIX:
2214 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2215 support::prependEnvPath("PATH", lyxrc.path_prefix);
2217 case LyXRC::RC_PERS_DICT:
2218 case LyXRC::RC_PREVIEW:
2219 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2220 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2221 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2222 case LyXRC::RC_PRINTCOPIESFLAG:
2223 case LyXRC::RC_PRINTER:
2224 case LyXRC::RC_PRINTEVENPAGEFLAG:
2225 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2226 case LyXRC::RC_PRINTFILEEXTENSION:
2227 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2228 case LyXRC::RC_PRINTODDPAGEFLAG:
2229 case LyXRC::RC_PRINTPAGERANGEFLAG:
2230 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2231 case LyXRC::RC_PRINTPAPERFLAG:
2232 case LyXRC::RC_PRINTREVERSEFLAG:
2233 case LyXRC::RC_PRINTSPOOL_COMMAND:
2234 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2235 case LyXRC::RC_PRINTTOFILE:
2236 case LyXRC::RC_PRINTTOPRINTER:
2237 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2238 case LyXRC::RC_PRINT_COMMAND:
2239 case LyXRC::RC_RTL_SUPPORT:
2240 case LyXRC::RC_SCREEN_DPI:
2241 case LyXRC::RC_SCREEN_FONT_ROMAN:
2242 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2243 case LyXRC::RC_SCREEN_FONT_SANS:
2244 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2245 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2246 case LyXRC::RC_SCREEN_FONT_SIZES:
2247 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2248 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2249 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2250 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2251 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2252 case LyXRC::RC_SCREEN_ZOOM:
2253 case LyXRC::RC_SERVERPIPE:
2254 case LyXRC::RC_SET_COLOR:
2255 case LyXRC::RC_SHOW_BANNER:
2256 case LyXRC::RC_SPELL_COMMAND:
2257 case LyXRC::RC_TEMPDIRPATH:
2258 case LyXRC::RC_TEMPLATEPATH:
2259 case LyXRC::RC_TEX_ALLOWS_SPACES:
2260 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2261 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2262 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2264 case LyXRC::RC_UIFILE:
2265 case LyXRC::RC_USER_EMAIL:
2266 case LyXRC::RC_USER_NAME:
2267 case LyXRC::RC_USETEMPDIR:
2268 case LyXRC::RC_USE_ALT_LANG:
2269 case LyXRC::RC_USE_CONVERTER_CACHE:
2270 case LyXRC::RC_USE_ESC_CHARS:
2271 case LyXRC::RC_USE_INP_ENC:
2272 case LyXRC::RC_USE_PERS_DICT:
2273 case LyXRC::RC_USE_SPELL_LIB:
2274 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2275 case LyXRC::RC_VIEWER:
2276 case LyXRC::RC_LAST: