3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
9 * \author Angus Leeming
11 * \author André Pönitz
14 * \author Martin Vermeer
15 * \author Jürgen Vigna
17 * Full author contact details are available in file CREDITS.
24 #include "BranchList.h"
26 #include "buffer_funcs.h"
27 #include "bufferlist.h"
28 #include "bufferparams.h"
29 #include "BufferView.h"
31 #include "CutAndPaste.h"
33 #include "dispatchresult.h"
35 #include "errorlist.h"
38 #include "funcrequest.h"
39 #include "FuncStatus.h"
42 #include "insetiterator.h"
50 #include "LyXAction.h"
55 #include "lyxserver.h"
56 #include "lyxtextclasslist.h"
58 #include "paragraph.h"
59 #include "pariterator.h"
60 #include "ParagraphParameters.h"
63 #include "insets/insetbox.h"
64 #include "insets/insetbranch.h"
65 #include "insets/insetcommand.h"
66 #include "insets/insetert.h"
67 #include "insets/insetexternal.h"
68 #include "insets/insetfloat.h"
69 #include "insets/insetgraphics.h"
70 #include "insets/insetinclude.h"
71 #include "insets/insetnote.h"
72 #include "insets/insettabular.h"
73 #include "insets/insetvspace.h"
74 #include "insets/insetwrap.h"
76 #include "frontends/Application.h"
77 #include "frontends/Alert.h"
78 #include "frontends/Dialogs.h"
79 #include "frontends/FileDialog.h"
80 #include "frontends/FontLoader.h"
81 #include "frontends/Gui.h"
82 #include "frontends/LyXKeySym.h"
83 #include "frontends/LyXView.h"
84 #include "frontends/Menubar.h"
85 #include "frontends/Toolbars.h"
87 #include "support/environment.h"
88 #include "support/filefilterlist.h"
89 #include "support/filetools.h"
90 #include "support/forkedcontr.h"
91 #include "support/fs_extras.h"
92 #include "support/lstrings.h"
93 #include "support/path.h"
94 #include "support/package.h"
95 #include "support/systemcall.h"
96 #include "support/convert.h"
97 #include "support/os.h"
99 #include <boost/current_function.hpp>
100 #include <boost/filesystem/operations.hpp>
107 using bv_funcs::freefont2string;
109 using support::absolutePath;
110 using support::addName;
111 using support::addPath;
112 using support::bformat;
113 using support::changeExtension;
114 using support::contains;
115 using support::FileFilterList;
116 using support::fileSearch;
117 using support::ForkedcallsController;
118 using support::i18nLibFileSearch;
119 using support::isDirWriteable;
120 using support::isFileReadable;
121 using support::isStrInt;
122 using support::makeAbsPath;
123 using support::makeDisplayPath;
124 using support::package;
125 using support::quoteName;
126 using support::rtrim;
127 using support::split;
128 using support::subst;
129 using support::Systemcall;
130 using support::token;
132 using support::prefixIs;
135 using std::make_pair;
138 using std::istringstream;
139 using std::ostringstream;
141 namespace Alert = frontend::Alert;
142 namespace fs = boost::filesystem;
146 extern tex_accent_struct get_accent(kb_action action);
151 bool getLocalStatus(LCursor cursor,
152 FuncRequest const & cmd, FuncStatus & status)
154 // Try to fix cursor in case it is broken.
155 cursor.fixIfBroken();
157 // This is, of course, a mess. Better create a new doc iterator and use
158 // this in Inset::getStatus. This might require an additional
159 // BufferView * arg, though (which should be avoided)
160 //LCursor safe = *this;
162 for ( ; cursor.depth(); cursor.pop()) {
163 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
164 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
165 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
166 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
168 // The inset's getStatus() will return 'true' if it made
169 // a definitive decision on whether it want to handle the
170 // request or not. The result of this decision is put into
171 // the 'status' parameter.
172 if (cursor.inset().getStatus(cursor, cmd, status)) {
181 /** Return the change status at cursor position, taking in account the
182 * status at each level of the document iterator (a table in a deleted
183 * footnote is deleted).
184 * When \param outer is true, the top slice is not looked at.
186 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
188 size_t const depth = dit.depth() - (outer ? 1 : 0);
190 for (size_t i = 0 ; i < depth ; ++i) {
191 CursorSlice const & slice = dit[i];
192 if (!slice.inset().inMathed()
193 && slice.pos() < slice.paragraph().size()) {
194 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
195 if (ch != Change::UNCHANGED)
199 return Change::UNCHANGED;
207 meta_fake_bit(key_modifier::none)
212 void LyXFunc::initKeySequences(kb_keymap * kb)
214 keyseq.reset(new kb_sequence(kb, kb));
215 cancel_meta_seq.reset(new kb_sequence(kb, kb));
219 void LyXFunc::setLyXView(LyXView * lv)
225 void LyXFunc::handleKeyFunc(kb_action action)
227 char_type c = encoded_last_key;
229 if (keyseq->length())
232 lyx_view_->view()->getIntl().getTransManager()
233 .deadkey(c, get_accent(action).accent, view()->getLyXText());
234 // Need to clear, in case the minibuffer calls these
237 // copied verbatim from do_accent_char
238 view()->cursor().resetAnchor();
243 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
245 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
247 // Do nothing if we have nothing (JMarc)
248 if (!keysym->isOK()) {
249 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
254 if (keysym->isModifier()) {
255 lyxerr[Debug::KEY] << "isModifier true" << endl;
259 //Encoding const * encoding = view()->cursor().getEncoding();
260 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
261 size_t encoded_last_key = keysym->getUCSEncoded();
263 // Do a one-deep top-level lookup for
264 // cancel and meta-fake keys. RVDK_PATCH_5
265 cancel_meta_seq->reset();
267 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
268 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
269 << " action first set to [" << func.action << ']'
272 // When not cancel or meta-fake, do the normal lookup.
273 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
274 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
275 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
276 // remove Caps Lock and Mod2 as a modifiers
277 func = keyseq->addkey(keysym, (state | meta_fake_bit));
278 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
279 << "action now set to ["
280 << func.action << ']' << endl;
283 // Dont remove this unless you know what you are doing.
284 meta_fake_bit = key_modifier::none;
286 // Can this happen now ?
287 if (func.action == LFUN_NOACTION) {
288 func = FuncRequest(LFUN_COMMAND_PREFIX);
291 if (lyxerr.debugging(Debug::KEY)) {
292 lyxerr << BOOST_CURRENT_FUNCTION
294 << func.action << "]["
295 << keyseq->print() << ']'
299 // already here we know if it any point in going further
300 // why not return already here if action == -1 and
301 // num_bytes == 0? (Lgb)
303 if (keyseq->length() > 1) {
304 lyx_view_->message(from_utf8(keyseq->print()));
308 // Maybe user can only reach the key via holding down shift.
309 // Let's see. But only if shift is the only modifier
310 if (func.action == LFUN_UNKNOWN_ACTION &&
311 state == key_modifier::shift) {
312 lyxerr[Debug::KEY] << "Trying without shift" << endl;
313 func = keyseq->addkey(keysym, key_modifier::none);
314 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
317 if (func.action == LFUN_UNKNOWN_ACTION) {
318 // Hmm, we didn't match any of the keysequences. See
319 // if it's normal insertable text not already covered
321 if (keysym->isText() && keyseq->length() == 1) {
322 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
323 func = FuncRequest(LFUN_SELF_INSERT,
324 FuncRequest::KEYBOARD);
326 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
327 lyx_view_->message(_("Unknown function."));
332 if (func.action == LFUN_SELF_INSERT) {
333 if (encoded_last_key != 0) {
334 docstring const arg(1, encoded_last_key);
335 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
336 FuncRequest::KEYBOARD));
338 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
346 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
348 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
351 if (cmd.action == LFUN_LYX_QUIT) {
352 flag.message(from_utf8(N_("Exiting")));
357 LCursor & cur = view()->cursor();
359 /* In LyX/Mac, when a dialog is open, the menus of the
360 application can still be accessed without giving focus to
361 the main window. In this case, we want to disable the menu
362 entries that are buffer-related.
364 Note that this code is not perfect, as bug 1941 attests:
365 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
368 if (cmd.origin == FuncRequest::UI && !lyx_view_->hasFocus())
371 buf = lyx_view_->buffer();
373 if (cmd.action == LFUN_NOACTION) {
374 flag.message(from_utf8(N_("Nothing to do")));
379 switch (cmd.action) {
380 case LFUN_UNKNOWN_ACTION:
381 #ifndef HAVE_LIBAIKSAURUS
382 case LFUN_THESAURUS_ENTRY:
392 if (flag.unknown()) {
393 flag.message(from_utf8(N_("Unknown action")));
397 if (!flag.enabled()) {
398 if (flag.message().empty())
399 flag.message(from_utf8(N_("Command disabled")));
403 // Check whether we need a buffer
404 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
406 flag.message(from_utf8(N_("Command not allowed with"
407 "out any document open")));
412 // I would really like to avoid having this switch and rather try to
413 // encode this in the function itself.
414 // -- And I'd rather let an inset decide which LFUNs it is willing
415 // to handle (Andre')
417 switch (cmd.action) {
418 case LFUN_BUFFER_TOGGLE_READ_ONLY:
419 flag.setOnOff(buf->isReadonly());
422 case LFUN_BUFFER_SWITCH:
423 // toggle on the current buffer, but do not toggle off
424 // the other ones (is that a good idea?)
425 if (to_utf8(cmd.argument()) == buf->fileName())
429 case LFUN_BUFFER_EXPORT:
430 enable = cmd.argument() == "custom"
431 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
434 case LFUN_BUFFER_CHKTEX:
435 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
438 case LFUN_BUILD_PROGRAM:
439 enable = Exporter::isExportable(*buf, "program");
442 case LFUN_LAYOUT_TABULAR:
443 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
447 case LFUN_LAYOUT_PARAGRAPH:
448 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
451 case LFUN_VC_REGISTER:
452 enable = !buf->lyxvc().inUse();
454 case LFUN_VC_CHECK_IN:
455 enable = buf->lyxvc().inUse() && !buf->isReadonly();
457 case LFUN_VC_CHECK_OUT:
458 enable = buf->lyxvc().inUse() && buf->isReadonly();
461 case LFUN_VC_UNDO_LAST:
462 enable = buf->lyxvc().inUse();
464 case LFUN_BUFFER_RELOAD:
465 enable = !buf->isUnnamed() && !buf->isClean();
468 case LFUN_INSET_SETTINGS: {
472 InsetBase::Code code = cur.inset().lyxCode();
474 case InsetBase::TABULAR_CODE:
475 enable = cmd.argument() == "tabular";
477 case InsetBase::ERT_CODE:
478 enable = cmd.argument() == "ert";
480 case InsetBase::FLOAT_CODE:
481 enable = cmd.argument() == "float";
483 case InsetBase::WRAP_CODE:
484 enable = cmd.argument() == "wrap";
486 case InsetBase::NOTE_CODE:
487 enable = cmd.argument() == "note";
489 case InsetBase::BRANCH_CODE:
490 enable = cmd.argument() == "branch";
492 case InsetBase::BOX_CODE:
493 enable = cmd.argument() == "box";
501 case LFUN_INSET_APPLY: {
502 string const name = cmd.getArg(0);
503 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
505 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
507 bool const success = inset->getStatus(cur, fr, fs);
508 // Every inset is supposed to handle this
509 BOOST_ASSERT(success);
512 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
513 flag |= getStatus(fr);
515 enable = flag.enabled();
519 case LFUN_DIALOG_SHOW: {
520 string const name = cmd.getArg(0);
522 enable = name == "aboutlyx"
526 || name == "texinfo";
527 else if (name == "print")
528 enable = Exporter::isExportable(*buf, "dvi")
529 && lyxrc.print_command != "none";
530 else if (name == "character" || name == "mathpanel")
531 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
532 else if (name == "latexlog")
533 enable = isFileReadable(buf->getLogName().second);
534 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
535 else if (name == "spellchecker")
538 else if (name == "vclog")
539 enable = buf->lyxvc().inUse();
540 else if (name == "view-source")
545 case LFUN_DIALOG_SHOW_NEW_INSET:
546 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
549 case LFUN_DIALOG_UPDATE: {
550 string const name = cmd.getArg(0);
552 enable = name == "prefs";
556 case LFUN_CITATION_INSERT: {
557 FuncRequest fr(LFUN_INSET_INSERT, "citation");
558 enable = getStatus(fr).enabled();
562 case LFUN_BUFFER_WRITE: {
563 enable = view()->buffer()->isUnnamed()
564 || !view()->buffer()->isClean();
568 // this one is difficult to get right. As a half-baked
569 // solution, we consider only the first action of the sequence
570 case LFUN_COMMAND_SEQUENCE: {
571 // argument contains ';'-terminated commands
572 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
573 FuncRequest func(lyxaction.lookupFunc(firstcmd));
574 func.origin = cmd.origin;
575 flag = getStatus(func);
578 case LFUN_BUFFER_NEW:
579 case LFUN_BUFFER_NEW_TEMPLATE:
580 case LFUN_WORD_FIND_FORWARD:
581 case LFUN_WORD_FIND_BACKWARD:
582 case LFUN_COMMAND_PREFIX:
583 case LFUN_COMMAND_EXECUTE:
585 case LFUN_META_PREFIX:
586 case LFUN_BUFFER_CLOSE:
587 case LFUN_BUFFER_WRITE_AS:
588 case LFUN_BUFFER_UPDATE:
589 case LFUN_BUFFER_VIEW:
590 case LFUN_BUFFER_IMPORT:
592 case LFUN_BUFFER_AUTO_SAVE:
593 case LFUN_RECONFIGURE:
597 case LFUN_DROP_LAYOUTS_CHOICE:
599 case LFUN_SERVER_GET_NAME:
600 case LFUN_SERVER_NOTIFY:
601 case LFUN_SERVER_GOTO_FILE_ROW:
602 case LFUN_DIALOG_HIDE:
603 case LFUN_DIALOG_DISCONNECT_INSET:
604 case LFUN_BUFFER_CHILD_OPEN:
605 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
606 case LFUN_KEYMAP_OFF:
607 case LFUN_KEYMAP_PRIMARY:
608 case LFUN_KEYMAP_SECONDARY:
609 case LFUN_KEYMAP_TOGGLE:
611 case LFUN_BUFFER_EXPORT_CUSTOM:
612 case LFUN_BUFFER_PRINT:
613 case LFUN_PREFERENCES_SAVE:
614 case LFUN_SCREEN_FONT_UPDATE:
617 case LFUN_EXTERNAL_EDIT:
618 case LFUN_GRAPHICS_EDIT:
619 case LFUN_ALL_INSETS_TOGGLE:
620 case LFUN_BUFFER_LANGUAGE:
621 case LFUN_TEXTCLASS_APPLY:
622 case LFUN_TEXTCLASS_LOAD:
623 case LFUN_BUFFER_SAVE_AS_DEFAULT:
624 case LFUN_BUFFER_PARAMS_APPLY:
625 case LFUN_LYXRC_APPLY:
626 case LFUN_BUFFER_NEXT:
627 case LFUN_BUFFER_PREVIOUS:
628 case LFUN_WINDOW_NEW:
629 case LFUN_WINDOW_CLOSE:
630 // these are handled in our dispatch()
634 if (!getLocalStatus(cur, cmd, flag))
635 flag = view()->getStatus(cmd);
641 // Can we use a readonly buffer?
642 if (buf && buf->isReadonly()
643 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
644 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
645 flag.message(from_utf8(N_("Document is read-only")));
649 // Are we in a DELETED change-tracking region?
650 if (buf && lookupChangeType(cur, true) == Change::DELETED
651 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
652 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
653 flag.message(from_utf8(N_("This portion of the document is deleted.")));
657 // the default error message if we disable the command
658 if (!flag.enabled() && flag.message().empty())
659 flag.message(from_utf8(N_("Command disabled")));
665 bool LyXFunc::ensureBufferClean(BufferView * bv)
667 Buffer & buf = *bv->buffer();
671 docstring const file = makeDisplayPath(buf.fileName(), 30);
672 docstring text = bformat(_("The document %1$s has unsaved "
673 "changes.\n\nDo you want to save "
674 "the document?"), file);
675 int const ret = Alert::prompt(_("Save changed document?"),
676 text, 0, 1, _("&Save"),
680 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
682 return buf.isClean();
688 void showPrintError(string const & name)
690 docstring str = bformat(_("Could not print the document %1$s.\n"
691 "Check that your printer is set up correctly."),
692 makeDisplayPath(name, 50));
693 Alert::error(_("Print document failed"), str);
697 void loadTextclass(string const & name)
699 std::pair<bool, textclass_type> const tc_pair =
700 textclasslist.numberOfClass(name);
702 if (!tc_pair.first) {
703 lyxerr << "Document class \"" << name
704 << "\" does not exist."
709 textclass_type const tc = tc_pair.second;
711 if (!textclasslist[tc].load()) {
712 docstring s = bformat(_("The document could not be converted\n"
713 "into the document class %1$s."),
714 from_utf8(textclasslist[tc].name()));
715 Alert::error(_("Could not change class"), s);
720 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
725 void LyXFunc::dispatch(FuncRequest const & cmd)
727 string const argument = to_utf8(cmd.argument());
728 kb_action const action = cmd.action;
730 lyxerr[Debug::ACTION] << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
731 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
733 // we have not done anything wrong yet.
735 dispatch_buffer.erase();
737 // redraw the screen at the end (first of the two drawing steps).
738 //This is done unless explicitely requested otherwise
739 Update::flags updateFlags = Update::FitCursor;
741 FuncStatus const flag = getStatus(cmd);
742 if (!flag.enabled()) {
743 // We cannot use this function here
744 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
745 << lyxaction.getActionName(action)
746 << " [" << action << "] is disabled at this location"
748 setErrorMessage(flag.message());
752 case LFUN_WORD_FIND_FORWARD:
753 case LFUN_WORD_FIND_BACKWARD: {
754 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
755 static string last_search;
756 string searched_string;
758 if (!argument.empty()) {
759 last_search = argument;
760 searched_string = argument;
762 searched_string = last_search;
765 if (searched_string.empty())
768 bool const fw = action == LFUN_WORD_FIND_FORWARD;
770 find2string(searched_string, true, false, fw);
771 find(view(), FuncRequest(LFUN_WORD_FIND, data));
775 case LFUN_COMMAND_PREFIX:
776 BOOST_ASSERT(lyx_view_);
777 lyx_view_->message(from_utf8(keyseq->printOptions()));
780 case LFUN_COMMAND_EXECUTE:
781 BOOST_ASSERT(lyx_view_);
782 lyx_view_->getToolbars().display("minibuffer", true);
783 lyx_view_->focus_command_buffer();
787 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
789 meta_fake_bit = key_modifier::none;
790 if (view()->buffer())
791 // cancel any selection
792 dispatch(FuncRequest(LFUN_MARK_OFF));
793 setMessage(_("Cancel"));
796 case LFUN_META_PREFIX:
797 meta_fake_bit = key_modifier::alt;
798 setMessage(from_utf8(keyseq->print()));
801 case LFUN_BUFFER_TOGGLE_READ_ONLY:
802 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
803 if (lyx_view_->buffer()->lyxvc().inUse())
804 lyx_view_->buffer()->lyxvc().toggleReadOnly();
806 lyx_view_->buffer()->setReadonly(
807 !lyx_view_->buffer()->isReadonly());
810 // --- Menus -----------------------------------------------
811 case LFUN_BUFFER_NEW:
812 menuNew(argument, false);
815 case LFUN_BUFFER_NEW_TEMPLATE:
816 menuNew(argument, true);
819 case LFUN_BUFFER_CLOSE:
824 case LFUN_BUFFER_WRITE:
825 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
826 if (!lyx_view_->buffer()->isUnnamed()) {
827 docstring const str = bformat(_("Saving document %1$s..."),
828 makeDisplayPath(lyx_view_->buffer()->fileName()));
829 lyx_view_->message(str);
830 menuWrite(lyx_view_->buffer());
831 lyx_view_->message(str + _(" done."));
833 writeAs(lyx_view_->buffer());
834 updateFlags = Update::None;
837 case LFUN_BUFFER_WRITE_AS:
838 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
839 writeAs(lyx_view_->buffer(), argument);
840 updateFlags = Update::None;
843 case LFUN_BUFFER_RELOAD: {
844 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
845 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
846 docstring text = bformat(_("Any changes will be lost. Are you sure "
847 "you want to revert to the saved version of the document %1$s?"), file);
848 int const ret = Alert::prompt(_("Revert to saved document?"),
849 text, 0, 1, _("&Revert"), _("&Cancel"));
856 case LFUN_BUFFER_UPDATE:
857 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
858 Exporter::Export(lyx_view_->buffer(), argument, true);
861 case LFUN_BUFFER_VIEW:
862 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
863 Exporter::preview(lyx_view_->buffer(), argument);
866 case LFUN_BUILD_PROGRAM:
867 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
868 Exporter::Export(lyx_view_->buffer(), "program", true);
871 case LFUN_BUFFER_CHKTEX:
872 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
873 lyx_view_->buffer()->runChktex();
876 case LFUN_BUFFER_EXPORT:
877 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
878 if (argument == "custom")
879 lyx_view_->getDialogs().show("sendto");
881 Exporter::Export(lyx_view_->buffer(), argument, false);
885 case LFUN_BUFFER_EXPORT_CUSTOM: {
886 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
888 string command = split(argument, format_name, ' ');
889 Format const * format = formats.getFormat(format_name);
891 lyxerr << "Format \"" << format_name
892 << "\" not recognized!"
897 Buffer * buffer = lyx_view_->buffer();
899 // The name of the file created by the conversion process
902 // Output to filename
903 if (format->name() == "lyx") {
904 string const latexname =
905 buffer->getLatexName(false);
906 filename = changeExtension(latexname,
907 format->extension());
908 filename = addName(buffer->temppath(), filename);
910 if (!buffer->writeFile(filename))
914 Exporter::Export(buffer, format_name, true, filename);
917 // Substitute $$FName for filename
918 if (!contains(command, "$$FName"))
919 command = "( " + command + " ) < $$FName";
920 command = subst(command, "$$FName", filename);
922 // Execute the command in the background
924 call.startscript(Systemcall::DontWait, command);
928 case LFUN_BUFFER_PRINT: {
929 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
932 string command = split(split(argument, target, ' '),
936 || target_name.empty()
937 || command.empty()) {
938 lyxerr << "Unable to parse \""
939 << argument << '"' << std::endl;
942 if (target != "printer" && target != "file") {
943 lyxerr << "Unrecognized target \""
944 << target << '"' << std::endl;
948 Buffer * buffer = lyx_view_->buffer();
950 if (!Exporter::Export(buffer, "dvi", true)) {
951 showPrintError(buffer->fileName());
955 // Push directory path.
956 string const path = buffer->temppath();
957 support::Path p(path);
959 // there are three cases here:
960 // 1. we print to a file
961 // 2. we print directly to a printer
962 // 3. we print using a spool command (print to file first)
965 string const dviname =
966 changeExtension(buffer->getLatexName(true),
969 if (target == "printer") {
970 if (!lyxrc.print_spool_command.empty()) {
971 // case 3: print using a spool
972 string const psname =
973 changeExtension(dviname,".ps");
974 command += lyxrc.print_to_file
977 + quoteName(dviname);
980 lyxrc.print_spool_command +' ';
981 if (target_name != "default") {
982 command2 += lyxrc.print_spool_printerprefix
986 command2 += quoteName(psname);
988 // If successful, then spool command
989 res = one.startscript(
994 res = one.startscript(
995 Systemcall::DontWait,
998 // case 2: print directly to a printer
999 res = one.startscript(
1000 Systemcall::DontWait,
1001 command + quoteName(dviname));
1005 // case 1: print to a file
1006 command += lyxrc.print_to_file
1007 + quoteName(makeAbsPath(target_name,
1010 + quoteName(dviname);
1011 res = one.startscript(Systemcall::DontWait,
1016 showPrintError(buffer->fileName());
1020 case LFUN_BUFFER_IMPORT:
1025 if (lyx_view_ && lyx_view_->view()->buffer()) {
1026 // save cursor Position for opened files to .lyx/session
1027 LyX::ref().session().saveFilePosition(lyx_view_->buffer()->fileName(),
1028 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1029 // save bookmarks to .lyx/session
1030 view()->saveSavedPositions();
1032 LyX::ref().quit(argument == "force");
1035 case LFUN_TOC_VIEW: {
1036 BOOST_ASSERT(lyx_view_);
1037 InsetCommandParams p("tableofcontents");
1038 string const data = InsetCommandMailer::params2string("toc", p);
1039 lyx_view_->getDialogs().show("toc", data, 0);
1043 case LFUN_BUFFER_AUTO_SAVE:
1047 case LFUN_RECONFIGURE:
1048 reconfigure(view());
1051 case LFUN_HELP_OPEN: {
1052 BOOST_ASSERT(lyx_view_);
1053 string const arg = argument;
1055 setErrorMessage(_("Missing argument"));
1058 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1059 if (fname.empty()) {
1060 lyxerr << "LyX: unable to find documentation file `"
1061 << arg << "'. Bad installation?" << endl;
1064 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1065 makeDisplayPath(fname)));
1066 lyx_view_->loadLyXFile(fname, false);
1070 // --- version control -------------------------------
1071 case LFUN_VC_REGISTER:
1072 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1073 if (!ensureBufferClean(view()))
1075 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1076 lyx_view_->buffer()->lyxvc().registrer();
1081 case LFUN_VC_CHECK_IN:
1082 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1083 if (!ensureBufferClean(view()))
1085 if (lyx_view_->buffer()->lyxvc().inUse()
1086 && !lyx_view_->buffer()->isReadonly()) {
1087 lyx_view_->buffer()->lyxvc().checkIn();
1092 case LFUN_VC_CHECK_OUT:
1093 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1094 if (!ensureBufferClean(view()))
1096 if (lyx_view_->buffer()->lyxvc().inUse()
1097 && lyx_view_->buffer()->isReadonly()) {
1098 lyx_view_->buffer()->lyxvc().checkOut();
1103 case LFUN_VC_REVERT:
1104 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1105 lyx_view_->buffer()->lyxvc().revert();
1109 case LFUN_VC_UNDO_LAST:
1110 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1111 lyx_view_->buffer()->lyxvc().undoLast();
1115 // --- buffers ----------------------------------------
1116 case LFUN_BUFFER_SWITCH:
1117 BOOST_ASSERT(lyx_view_);
1118 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1121 case LFUN_BUFFER_NEXT:
1122 BOOST_ASSERT(lyx_view_);
1123 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1126 case LFUN_BUFFER_PREVIOUS:
1127 BOOST_ASSERT(lyx_view_);
1128 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1132 BOOST_ASSERT(lyx_view_);
1133 newFile(view(), argument);
1136 case LFUN_FILE_OPEN:
1137 BOOST_ASSERT(lyx_view_);
1141 case LFUN_DROP_LAYOUTS_CHOICE:
1142 BOOST_ASSERT(lyx_view_);
1143 lyx_view_->getToolbars().openLayoutList();
1146 case LFUN_MENU_OPEN:
1147 BOOST_ASSERT(lyx_view_);
1148 lyx_view_->getMenubar().openByName(from_utf8(argument));
1151 // --- lyxserver commands ----------------------------
1152 case LFUN_SERVER_GET_NAME:
1153 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1154 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1155 lyxerr[Debug::INFO] << "FNAME["
1156 << lyx_view_->buffer()->fileName()
1160 case LFUN_SERVER_NOTIFY:
1161 dispatch_buffer = from_utf8(keyseq->print());
1162 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1165 case LFUN_SERVER_GOTO_FILE_ROW: {
1166 BOOST_ASSERT(lyx_view_);
1169 istringstream is(argument);
1170 is >> file_name >> row;
1171 if (prefixIs(file_name, package().temp_dir())) {
1172 // Needed by inverse dvi search. If it is a file
1173 // in tmpdir, call the apropriated function
1174 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1176 // Must replace extension of the file to be .lyx
1177 // and get full path
1178 string const s = changeExtension(file_name, ".lyx");
1179 // Either change buffer or load the file
1180 if (theBufferList().exists(s)) {
1181 lyx_view_->setBuffer(theBufferList().getBuffer(s));
1183 lyx_view_->loadLyXFile(s);
1187 view()->setCursorFromRow(row);
1190 // see BufferView::center()
1194 case LFUN_DIALOG_SHOW: {
1195 BOOST_ASSERT(lyx_view_);
1196 string const name = cmd.getArg(0);
1197 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1199 if (name == "character") {
1200 data = freefont2string();
1202 lyx_view_->getDialogs().show("character", data);
1203 } else if (name == "latexlog") {
1204 pair<Buffer::LogType, string> const logfile =
1205 lyx_view_->buffer()->getLogName();
1206 switch (logfile.first) {
1207 case Buffer::latexlog:
1210 case Buffer::buildlog:
1214 data += LyXLex::quoteString(logfile.second);
1215 lyx_view_->getDialogs().show("log", data);
1216 } else if (name == "vclog") {
1217 string const data = "vc " +
1218 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1219 lyx_view_->getDialogs().show("log", data);
1221 lyx_view_->getDialogs().show(name, data);
1225 case LFUN_DIALOG_SHOW_NEW_INSET: {
1226 BOOST_ASSERT(lyx_view_);
1227 string const name = cmd.getArg(0);
1228 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1229 if (name == "bibitem" ||
1236 InsetCommandParams p(name);
1237 data = InsetCommandMailer::params2string(name, p);
1238 } else if (name == "include") {
1239 InsetCommandParams p(data);
1240 data = InsetIncludeMailer::params2string(p);
1241 } else if (name == "box") {
1242 // \c data == "Boxed" || "Frameless" etc
1243 InsetBoxParams p(data);
1244 data = InsetBoxMailer::params2string(p);
1245 } else if (name == "branch") {
1246 InsetBranchParams p;
1247 data = InsetBranchMailer::params2string(p);
1248 } else if (name == "citation") {
1249 InsetCommandParams p("cite");
1250 data = InsetCommandMailer::params2string(name, p);
1251 } else if (name == "ert") {
1252 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1253 } else if (name == "external") {
1254 InsetExternalParams p;
1255 Buffer const & buffer = *lyx_view_->buffer();
1256 data = InsetExternalMailer::params2string(p, buffer);
1257 } else if (name == "float") {
1259 data = InsetFloatMailer::params2string(p);
1260 } else if (name == "graphics") {
1261 InsetGraphicsParams p;
1262 Buffer const & buffer = *lyx_view_->buffer();
1263 data = InsetGraphicsMailer::params2string(p, buffer);
1264 } else if (name == "note") {
1266 data = InsetNoteMailer::params2string(p);
1267 } else if (name == "vspace") {
1269 data = InsetVSpaceMailer::params2string(space);
1270 } else if (name == "wrap") {
1272 data = InsetWrapMailer::params2string(p);
1274 lyx_view_->getDialogs().show(name, data, 0);
1278 case LFUN_DIALOG_UPDATE: {
1279 BOOST_ASSERT(lyx_view_);
1280 string const & name = argument;
1281 // Can only update a dialog connected to an existing inset
1282 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1284 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1285 inset->dispatch(view()->cursor(), fr);
1286 } else if (name == "paragraph") {
1287 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1288 } else if (name == "prefs") {
1289 lyx_view_->getDialogs().update(name, string());
1294 case LFUN_DIALOG_HIDE:
1295 Dialogs::hide(argument, 0);
1298 case LFUN_DIALOG_DISCONNECT_INSET:
1299 BOOST_ASSERT(lyx_view_);
1300 lyx_view_->getDialogs().disconnect(argument);
1304 case LFUN_CITATION_INSERT: {
1305 BOOST_ASSERT(lyx_view_);
1306 if (!argument.empty()) {
1307 // we can have one optional argument, delimited by '|'
1308 // citation-insert <key>|<text_before>
1309 // this should be enhanced to also support text_after
1310 // and citation style
1311 string arg = argument;
1313 if (contains(argument, "|")) {
1314 arg = token(argument, '|', 0);
1315 opt1 = '[' + token(argument, '|', 1) + ']';
1317 std::ostringstream os;
1318 os << "citation LatexCommand\n"
1319 << "\\cite" << opt1 << "{" << arg << "}\n"
1321 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1324 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1328 case LFUN_BUFFER_CHILD_OPEN: {
1329 BOOST_ASSERT(lyx_view_);
1330 string const filename =
1331 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1332 // FIXME Should use bformat
1333 setMessage(_("Opening child document ") +
1334 makeDisplayPath(filename) + "...");
1335 view()->savePosition(0);
1336 string const parentfilename = lyx_view_->buffer()->fileName();
1337 if (theBufferList().exists(filename))
1338 lyx_view_->setBuffer(theBufferList().getBuffer(filename));
1340 lyx_view_->loadLyXFile(filename);
1341 // Set the parent name of the child document.
1342 // This makes insertion of citations and references in the child work,
1343 // when the target is in the parent or another child document.
1344 lyx_view_->buffer()->setParentName(parentfilename);
1348 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1349 BOOST_ASSERT(lyx_view_);
1350 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1353 case LFUN_KEYMAP_OFF:
1354 BOOST_ASSERT(lyx_view_);
1355 lyx_view_->view()->getIntl().keyMapOn(false);
1358 case LFUN_KEYMAP_PRIMARY:
1359 BOOST_ASSERT(lyx_view_);
1360 lyx_view_->view()->getIntl().keyMapPrim();
1363 case LFUN_KEYMAP_SECONDARY:
1364 BOOST_ASSERT(lyx_view_);
1365 lyx_view_->view()->getIntl().keyMapSec();
1368 case LFUN_KEYMAP_TOGGLE:
1369 BOOST_ASSERT(lyx_view_);
1370 lyx_view_->view()->getIntl().toggleKeyMap();
1376 string rest = split(argument, countstr, ' ');
1377 istringstream is(countstr);
1380 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1381 for (int i = 0; i < count; ++i)
1382 dispatch(lyxaction.lookupFunc(rest));
1386 case LFUN_COMMAND_SEQUENCE: {
1387 // argument contains ';'-terminated commands
1388 string arg = argument;
1389 while (!arg.empty()) {
1391 arg = split(arg, first, ';');
1392 FuncRequest func(lyxaction.lookupFunc(first));
1393 func.origin = cmd.origin;
1399 case LFUN_PREFERENCES_SAVE: {
1400 support::Path p(package().user_support());
1401 lyxrc.write("preferences", false);
1405 case LFUN_SCREEN_FONT_UPDATE:
1406 BOOST_ASSERT(lyx_view_);
1407 // handle the screen font changes.
1408 lyxrc.set_font_norm_type();
1409 theFontLoader().update();
1410 // All visible buffers will need resize
1414 case LFUN_SET_COLOR: {
1416 string const x11_name = split(argument, lyx_name, ' ');
1417 if (lyx_name.empty() || x11_name.empty()) {
1418 setErrorMessage(_("Syntax: set-color <lyx_name>"
1423 bool const graphicsbg_changed =
1424 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1425 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1427 if (!lcolor.setColor(lyx_name, x11_name)) {
1429 bformat(_("Set-color \"%1$s\" failed "
1430 "- color is undefined or "
1431 "may not be redefined"),
1432 from_utf8(lyx_name)));
1436 theApp->updateColor(lcolor.getFromLyXName(lyx_name));
1438 if (graphicsbg_changed) {
1439 #ifdef WITH_WARNINGS
1440 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1443 graphics::GCache::get().changeDisplay(true);
1450 BOOST_ASSERT(lyx_view_);
1451 lyx_view_->message(from_utf8(argument));
1454 case LFUN_EXTERNAL_EDIT: {
1455 BOOST_ASSERT(lyx_view_);
1456 FuncRequest fr(action, argument);
1457 InsetExternal().dispatch(view()->cursor(), fr);
1461 case LFUN_GRAPHICS_EDIT: {
1462 FuncRequest fr(action, argument);
1463 InsetGraphics().dispatch(view()->cursor(), fr);
1467 case LFUN_INSET_APPLY: {
1468 BOOST_ASSERT(lyx_view_);
1469 string const name = cmd.getArg(0);
1470 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1472 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1473 inset->dispatch(view()->cursor(), fr);
1475 FuncRequest fr(LFUN_INSET_INSERT, argument);
1478 // ideally, the update flag should be set by the insets,
1479 // but this is not possible currently
1480 updateFlags = Update::Force | Update::FitCursor;
1484 case LFUN_ALL_INSETS_TOGGLE: {
1485 BOOST_ASSERT(lyx_view_);
1487 string const name = split(argument, action, ' ');
1488 InsetBase::Code const inset_code =
1489 InsetBase::translate(name);
1491 LCursor & cur = view()->cursor();
1492 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1494 InsetBase & inset = lyx_view_->buffer()->inset();
1495 InsetIterator it = inset_iterator_begin(inset);
1496 InsetIterator const end = inset_iterator_end(inset);
1497 for (; it != end; ++it) {
1498 if (inset_code == InsetBase::NO_CODE
1499 || inset_code == it->lyxCode()) {
1500 LCursor tmpcur = cur;
1501 tmpcur.pushLeft(*it);
1502 it->dispatch(tmpcur, fr);
1505 updateFlags = Update::Force | Update::FitCursor;
1509 case LFUN_BUFFER_LANGUAGE: {
1510 BOOST_ASSERT(lyx_view_);
1511 Buffer & buffer = *lyx_view_->buffer();
1512 Language const * oldL = buffer.params().language;
1513 Language const * newL = languages.getLanguage(argument);
1514 if (!newL || oldL == newL)
1517 if (oldL->rightToLeft() == newL->rightToLeft()
1518 && !buffer.isMultiLingual())
1519 buffer.changeLanguage(oldL, newL);
1521 buffer.updateDocLang(newL);
1525 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1526 string const fname =
1527 addName(addPath(package().user_support(), "templates/"),
1529 Buffer defaults(fname);
1531 istringstream ss(argument);
1534 int const unknown_tokens = defaults.readHeader(lex);
1536 if (unknown_tokens != 0) {
1537 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1538 << unknown_tokens << " unknown token"
1539 << (unknown_tokens == 1 ? "" : "s")
1543 if (defaults.writeFile(defaults.fileName()))
1544 // FIXME Should use bformat
1545 setMessage(_("Document defaults saved in ")
1546 + makeDisplayPath(fname));
1548 setErrorMessage(_("Unable to save document defaults"));
1552 case LFUN_BUFFER_PARAMS_APPLY: {
1553 BOOST_ASSERT(lyx_view_);
1554 biblio::CiteEngine const engine =
1555 lyx_view_->buffer()->params().cite_engine;
1557 istringstream ss(argument);
1560 int const unknown_tokens =
1561 lyx_view_->buffer()->readHeader(lex);
1563 if (unknown_tokens != 0) {
1564 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1565 << unknown_tokens << " unknown token"
1566 << (unknown_tokens == 1 ? "" : "s")
1569 if (engine == lyx_view_->buffer()->params().cite_engine)
1572 LCursor & cur = view()->cursor();
1573 FuncRequest fr(LFUN_INSET_REFRESH);
1575 InsetBase & inset = lyx_view_->buffer()->inset();
1576 InsetIterator it = inset_iterator_begin(inset);
1577 InsetIterator const end = inset_iterator_end(inset);
1578 for (; it != end; ++it)
1579 if (it->lyxCode() == InsetBase::CITE_CODE)
1580 it->dispatch(cur, fr);
1584 case LFUN_TEXTCLASS_APPLY: {
1585 BOOST_ASSERT(lyx_view_);
1586 Buffer * buffer = lyx_view_->buffer();
1588 textclass_type const old_class =
1589 buffer->params().textclass;
1591 loadTextclass(argument);
1593 std::pair<bool, textclass_type> const tc_pair =
1594 textclasslist.numberOfClass(argument);
1599 textclass_type const new_class = tc_pair.second;
1600 if (old_class == new_class)
1604 lyx_view_->message(_("Converting document to new document class..."));
1605 recordUndoFullDocument(view());
1606 buffer->params().textclass = new_class;
1607 StableDocIterator backcur(view()->cursor());
1608 ErrorList & el = buffer->errorList("Class Switch");
1609 cap::switchBetweenClasses(
1610 old_class, new_class,
1611 static_cast<InsetText &>(buffer->inset()), el);
1613 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1615 buffer->errors("Class Switch");
1616 updateLabels(*buffer);
1617 updateFlags = Update::Force | Update::FitCursor;
1621 case LFUN_TEXTCLASS_LOAD:
1622 loadTextclass(argument);
1625 case LFUN_LYXRC_APPLY: {
1626 LyXRC const lyxrc_orig = lyxrc;
1628 istringstream ss(argument);
1629 bool const success = lyxrc.read(ss) == 0;
1632 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1633 << "Unable to read lyxrc data"
1638 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1642 case LFUN_WINDOW_NEW:
1643 LyX::ref().newLyXView();
1646 case LFUN_WINDOW_CLOSE:
1647 BOOST_ASSERT(lyx_view_);
1648 BOOST_ASSERT(theApp);
1650 // We return here because lyx_view does not exists anymore.
1654 BOOST_ASSERT(lyx_view_);
1655 view()->cursor().dispatch(cmd);
1656 updateFlags = view()->cursor().result().update();
1657 if (!view()->cursor().result().dispatched())
1658 if (view()->dispatch(cmd))
1659 updateFlags = Update::Force | Update::FitCursor;
1664 if (lyx_view_ && view()->buffer()) {
1665 // Redraw screen unless explicitly told otherwise.
1666 // This also initializes the position cache for all insets
1667 // in (at least partially) visible top-level paragraphs.
1668 bool needSecondUpdate = false;
1669 if (updateFlags != Update::None)
1670 view()->update(updateFlags);
1672 needSecondUpdate = view()->fitCursor();
1674 if (needSecondUpdate || updateFlags != Update::None) {
1675 view()->buffer()->changed();
1677 lyx_view_->updateStatusBar();
1679 // if we executed a mutating lfun, mark the buffer as dirty
1681 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1682 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1683 view()->buffer()->markDirty();
1685 if (view()->cursor().inTexted()) {
1686 lyx_view_->updateLayoutChoice();
1691 // FIXME UNICODE: _() does not support anything but ascii.
1692 // Do we need a to_ascii() method?
1693 sendDispatchMessage(getMessage(), cmd);
1697 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1699 /* When an action did not originate from the UI/kbd, it makes
1700 * sense to avoid updating the GUI. It turns out that this
1701 * fixes bug 1941, for reasons that are described here:
1702 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1704 if (cmd.origin != FuncRequest::INTERNAL) {
1705 lyx_view_->updateMenubar();
1706 lyx_view_->updateToolbars();
1709 const bool verbose = (cmd.origin == FuncRequest::UI
1710 || cmd.origin == FuncRequest::COMMANDBUFFER);
1712 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1713 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1715 lyx_view_->message(msg);
1719 docstring dispatch_msg = msg;
1720 if (!dispatch_msg.empty())
1721 dispatch_msg += ' ';
1723 string comname = lyxaction.getActionName(cmd.action);
1725 bool argsadded = false;
1727 if (!cmd.argument().empty()) {
1728 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1729 comname += ' ' + to_utf8(cmd.argument());
1734 string const shortcuts = theTopLevelKeymap().printbindings(cmd);
1736 if (!shortcuts.empty())
1737 comname += ": " + shortcuts;
1738 else if (!argsadded && !cmd.argument().empty())
1739 comname += ' ' + to_utf8(cmd.argument());
1741 if (!comname.empty()) {
1742 comname = rtrim(comname);
1743 dispatch_msg += from_utf8('(' + rtrim(comname) + ')');
1746 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1747 << to_utf8(dispatch_msg) << endl;
1748 if (!dispatch_msg.empty())
1749 lyx_view_->message(dispatch_msg);
1753 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1755 string initpath = lyxrc.document_path;
1756 string filename(name);
1758 if (view()->buffer()) {
1759 string const trypath = lyx_view_->buffer()->filePath();
1760 // If directory is writeable, use this as default.
1761 if (isDirWriteable(trypath))
1765 static int newfile_number;
1767 if (filename.empty()) {
1768 filename = addName(lyxrc.document_path,
1769 "newfile" + convert<string>(++newfile_number) + ".lyx");
1770 while (theBufferList().exists(filename) || fs::is_readable(filename)) {
1772 filename = addName(lyxrc.document_path,
1773 "newfile" + convert<string>(newfile_number) +
1778 // The template stuff
1781 FileDialog fileDlg(_("Select template file"),
1782 LFUN_SELECT_FILE_SYNC,
1783 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1784 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1786 FileDialog::Result result =
1787 fileDlg.open(from_utf8(lyxrc.template_path),
1788 FileFilterList(_("LyX Documents (*.lyx)")),
1791 if (result.first == FileDialog::Later)
1793 if (result.second.empty())
1795 templname = to_utf8(result.second);
1798 Buffer * const b = newFile(filename, templname, !name.empty());
1800 lyx_view_->setBuffer(b);
1804 void LyXFunc::open(string const & fname)
1806 string initpath = lyxrc.document_path;
1808 if (view()->buffer()) {
1809 string const trypath = lyx_view_->buffer()->filePath();
1810 // If directory is writeable, use this as default.
1811 if (isDirWriteable(trypath))
1817 if (fname.empty()) {
1818 FileDialog fileDlg(_("Select document to open"),
1820 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1821 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1823 FileDialog::Result result =
1824 fileDlg.open(from_utf8(initpath),
1825 FileFilterList(_("LyX Documents (*.lyx)")),
1828 if (result.first == FileDialog::Later)
1831 filename = to_utf8(result.second);
1833 // check selected filename
1834 if (filename.empty()) {
1835 lyx_view_->message(_("Canceled."));
1841 // get absolute path of file and add ".lyx" to the filename if
1843 string const fullpath = fileSearch(string(), filename, "lyx");
1844 if (!fullpath.empty()) {
1845 filename = fullpath;
1848 docstring const disp_fn = makeDisplayPath(filename);
1850 // if the file doesn't exist, let the user create one
1851 if (!fs::exists(filename)) {
1852 // the user specifically chose this name. Believe him.
1853 Buffer * const b = newFile(filename, string(), true);
1855 lyx_view_->setBuffer(b);
1859 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1862 if (lyx_view_->loadLyXFile(filename)) {
1863 str2 = bformat(_("Document %1$s opened."), disp_fn);
1865 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1867 lyx_view_->message(str2);
1871 void LyXFunc::doImport(string const & argument)
1874 string filename = split(argument, format, ' ');
1876 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1877 << " file: " << filename << endl;
1879 // need user interaction
1880 if (filename.empty()) {
1881 string initpath = lyxrc.document_path;
1883 if (view()->buffer()) {
1884 string const trypath = lyx_view_->buffer()->filePath();
1885 // If directory is writeable, use this as default.
1886 if (isDirWriteable(trypath))
1890 docstring const text = bformat(_("Select %1$s file to import"),
1891 formats.prettyName(format));
1893 FileDialog fileDlg(text,
1895 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1896 make_pair(_("Examples|#E#e"),
1897 from_utf8(addPath(package().system_support(), "examples"))));
1899 docstring filter = formats.prettyName(format);
1902 filter += from_utf8(formats.extension(format));
1905 FileDialog::Result result =
1906 fileDlg.open(from_utf8(initpath),
1907 FileFilterList(filter),
1910 if (result.first == FileDialog::Later)
1913 filename = to_utf8(result.second);
1915 // check selected filename
1916 if (filename.empty())
1917 lyx_view_->message(_("Canceled."));
1920 if (filename.empty())
1923 // get absolute path of file
1924 filename = makeAbsPath(filename);
1926 string const lyxfile = changeExtension(filename, ".lyx");
1928 // Check if the document already is open
1929 if (use_gui && theBufferList().exists(lyxfile)) {
1930 if (!theBufferList().close(theBufferList().getBuffer(lyxfile), true)) {
1931 lyx_view_->message(_("Canceled."));
1936 // if the file exists already, and we didn't do
1937 // -i lyx thefile.lyx, warn
1938 if (fs::exists(lyxfile) && filename != lyxfile) {
1939 docstring const file = makeDisplayPath(lyxfile, 30);
1941 docstring text = bformat(_("The document %1$s already exists.\n\n"
1942 "Do you want to over-write that document?"), file);
1943 int const ret = Alert::prompt(_("Over-write document?"),
1944 text, 0, 1, _("&Over-write"), _("&Cancel"));
1947 lyx_view_->message(_("Canceled."));
1952 ErrorList errorList;
1953 Importer::Import(lyx_view_, filename, format, errorList);
1954 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1958 void LyXFunc::closeBuffer()
1960 // save current cursor position
1961 LyX::ref().session().saveFilePosition(lyx_view_->buffer()->fileName(),
1962 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1963 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
1964 if (theBufferList().empty()) {
1965 // need this otherwise SEGV may occur while
1966 // trying to set variables that don't exist
1967 // since there's no current buffer
1968 lyx_view_->getDialogs().hideBufferDependent();
1970 lyx_view_->setBuffer(theBufferList().first());
1976 // Each "lyx_view_" should have it's own message method. lyxview and
1977 // the minibuffer would use the minibuffer, but lyxserver would
1978 // send an ERROR signal to its client. Alejandro 970603
1979 // This function is bit problematic when it comes to NLS, to make the
1980 // lyx servers client be language indepenent we must not translate
1981 // strings sent to this func.
1982 void LyXFunc::setErrorMessage(docstring const & m) const
1984 dispatch_buffer = m;
1989 void LyXFunc::setMessage(docstring const & m) const
1991 dispatch_buffer = m;
1995 string const LyXFunc::viewStatusMessage()
1997 // When meta-fake key is pressed, show the key sequence so far + "M-".
1999 return keyseq->print() + "M-";
2001 // Else, when a non-complete key sequence is pressed,
2002 // show the available options.
2003 if (keyseq->length() > 0 && !keyseq->deleted())
2004 return keyseq->printOptions();
2006 if (!view()->buffer())
2007 return to_utf8(_("Welcome to LyX!"));
2009 return view()->cursor().currentState();
2013 BufferView * LyXFunc::view() const
2015 BOOST_ASSERT(lyx_view_);
2016 return lyx_view_->view();
2020 bool LyXFunc::wasMetaKey() const
2022 return (meta_fake_bit != key_modifier::none);
2028 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2030 // Why the switch you might ask. It is a trick to ensure that all
2031 // the elements in the LyXRCTags enum is handled. As you can see
2032 // there are no breaks at all. So it is just a huge fall-through.
2033 // The nice thing is that we will get a warning from the compiler
2034 // if we forget an element.
2035 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2037 case LyXRC::RC_ACCEPT_COMPOUND:
2038 case LyXRC::RC_ALT_LANG:
2039 case LyXRC::RC_ASCIIROFF_COMMAND:
2040 case LyXRC::RC_ASCII_LINELEN:
2041 case LyXRC::RC_AUTOREGIONDELETE:
2042 case LyXRC::RC_AUTORESET_OPTIONS:
2043 case LyXRC::RC_AUTOSAVE:
2044 case LyXRC::RC_AUTO_NUMBER:
2045 case LyXRC::RC_BACKUPDIR_PATH:
2046 case LyXRC::RC_BIBTEX_COMMAND:
2047 case LyXRC::RC_BINDFILE:
2048 case LyXRC::RC_CHECKLASTFILES:
2049 case LyXRC::RC_USELASTFILEPOS:
2050 case LyXRC::RC_LOADSESSION:
2051 case LyXRC::RC_CHKTEX_COMMAND:
2052 case LyXRC::RC_CONVERTER:
2053 case LyXRC::RC_COPIER:
2054 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2055 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2056 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2057 case LyXRC::RC_DATE_INSERT_FORMAT:
2058 case LyXRC::RC_DEFAULT_LANGUAGE:
2059 case LyXRC::RC_DEFAULT_PAPERSIZE:
2060 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2061 case LyXRC::RC_DISPLAY_GRAPHICS:
2062 case LyXRC::RC_DOCUMENTPATH:
2063 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2064 if (fs::exists(lyxrc_new.document_path) &&
2065 fs::is_directory(lyxrc_new.document_path)) {
2066 support::package().document_dir() = lyxrc.document_path;
2069 case LyXRC::RC_ESC_CHARS:
2070 case LyXRC::RC_FONT_ENCODING:
2071 case LyXRC::RC_FORMAT:
2072 case LyXRC::RC_INDEX_COMMAND:
2073 case LyXRC::RC_INPUT:
2074 case LyXRC::RC_KBMAP:
2075 case LyXRC::RC_KBMAP_PRIMARY:
2076 case LyXRC::RC_KBMAP_SECONDARY:
2077 case LyXRC::RC_LABEL_INIT_LENGTH:
2078 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2079 case LyXRC::RC_LANGUAGE_AUTO_END:
2080 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2081 case LyXRC::RC_LANGUAGE_COMMAND_END:
2082 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2083 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2084 case LyXRC::RC_LANGUAGE_PACKAGE:
2085 case LyXRC::RC_LANGUAGE_USE_BABEL:
2086 case LyXRC::RC_MAKE_BACKUP:
2087 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2088 case LyXRC::RC_NUMLASTFILES:
2089 case LyXRC::RC_PATH_PREFIX:
2090 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2091 support::prependEnvPath("PATH", lyxrc.path_prefix);
2093 case LyXRC::RC_PERS_DICT:
2094 case LyXRC::RC_POPUP_BOLD_FONT:
2095 case LyXRC::RC_POPUP_FONT_ENCODING:
2096 case LyXRC::RC_POPUP_NORMAL_FONT:
2097 case LyXRC::RC_PREVIEW:
2098 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2099 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2100 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2101 case LyXRC::RC_PRINTCOPIESFLAG:
2102 case LyXRC::RC_PRINTER:
2103 case LyXRC::RC_PRINTEVENPAGEFLAG:
2104 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2105 case LyXRC::RC_PRINTFILEEXTENSION:
2106 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2107 case LyXRC::RC_PRINTODDPAGEFLAG:
2108 case LyXRC::RC_PRINTPAGERANGEFLAG:
2109 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2110 case LyXRC::RC_PRINTPAPERFLAG:
2111 case LyXRC::RC_PRINTREVERSEFLAG:
2112 case LyXRC::RC_PRINTSPOOL_COMMAND:
2113 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2114 case LyXRC::RC_PRINTTOFILE:
2115 case LyXRC::RC_PRINTTOPRINTER:
2116 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2117 case LyXRC::RC_PRINT_COMMAND:
2118 case LyXRC::RC_RTL_SUPPORT:
2119 case LyXRC::RC_SCREEN_DPI:
2120 case LyXRC::RC_SCREEN_FONT_ENCODING:
2121 case LyXRC::RC_SCREEN_FONT_ROMAN:
2122 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2123 case LyXRC::RC_SCREEN_FONT_SANS:
2124 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2125 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2126 case LyXRC::RC_SCREEN_FONT_SIZES:
2127 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2128 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2129 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2130 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2131 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2132 case LyXRC::RC_SCREEN_ZOOM:
2133 case LyXRC::RC_SERVERPIPE:
2134 case LyXRC::RC_SET_COLOR:
2135 case LyXRC::RC_SHOW_BANNER:
2136 case LyXRC::RC_SPELL_COMMAND:
2137 case LyXRC::RC_TEMPDIRPATH:
2138 case LyXRC::RC_TEMPLATEPATH:
2139 case LyXRC::RC_TEX_ALLOWS_SPACES:
2140 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2141 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2142 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2144 case LyXRC::RC_UIFILE:
2145 case LyXRC::RC_USER_EMAIL:
2146 case LyXRC::RC_USER_NAME:
2147 case LyXRC::RC_USETEMPDIR:
2148 case LyXRC::RC_USE_ALT_LANG:
2149 case LyXRC::RC_USE_ESC_CHARS:
2150 case LyXRC::RC_USE_INP_ENC:
2151 case LyXRC::RC_USE_PERS_DICT:
2152 case LyXRC::RC_USE_SPELL_LIB:
2153 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2154 case LyXRC::RC_VIEWER:
2155 case LyXRC::RC_LAST: