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().deadkey(
233 c, get_accent(action).accent, view()->getLyXText(), 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::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 (argument != "force") {
1026 if (!theApp->gui().closeAll())
1031 // FIXME: this code needs to be transfered somewhere else
1032 // as lyx_view_ will most certainly be null and a same buffer
1033 // might be visible in more than one LyXView.
1034 if (lyx_view_ && lyx_view_->view()->buffer()) {
1035 // save cursor Position for opened files to .lyx/session
1036 LyX::ref().session().lastFilePos().save(lyx_view_->buffer()->fileName(),
1037 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1038 // save bookmarks to .lyx/session
1039 view()->saveSavedPositions();
1045 case LFUN_TOC_VIEW: {
1046 BOOST_ASSERT(lyx_view_);
1047 InsetCommandParams p("tableofcontents");
1048 string const data = InsetCommandMailer::params2string("toc", p);
1049 lyx_view_->getDialogs().show("toc", data, 0);
1053 case LFUN_BUFFER_AUTO_SAVE:
1057 case LFUN_RECONFIGURE:
1058 reconfigure(view());
1061 case LFUN_HELP_OPEN: {
1062 BOOST_ASSERT(lyx_view_);
1063 string const arg = argument;
1065 setErrorMessage(_("Missing argument"));
1068 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1069 if (fname.empty()) {
1070 lyxerr << "LyX: unable to find documentation file `"
1071 << arg << "'. Bad installation?" << endl;
1074 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1075 makeDisplayPath(fname)));
1076 lyx_view_->loadLyXFile(fname, false);
1080 // --- version control -------------------------------
1081 case LFUN_VC_REGISTER:
1082 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1083 if (!ensureBufferClean(view()))
1085 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1086 lyx_view_->buffer()->lyxvc().registrer();
1091 case LFUN_VC_CHECK_IN:
1092 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1093 if (!ensureBufferClean(view()))
1095 if (lyx_view_->buffer()->lyxvc().inUse()
1096 && !lyx_view_->buffer()->isReadonly()) {
1097 lyx_view_->buffer()->lyxvc().checkIn();
1102 case LFUN_VC_CHECK_OUT:
1103 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1104 if (!ensureBufferClean(view()))
1106 if (lyx_view_->buffer()->lyxvc().inUse()
1107 && lyx_view_->buffer()->isReadonly()) {
1108 lyx_view_->buffer()->lyxvc().checkOut();
1113 case LFUN_VC_REVERT:
1114 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1115 lyx_view_->buffer()->lyxvc().revert();
1119 case LFUN_VC_UNDO_LAST:
1120 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1121 lyx_view_->buffer()->lyxvc().undoLast();
1125 // --- buffers ----------------------------------------
1126 case LFUN_BUFFER_SWITCH:
1127 BOOST_ASSERT(lyx_view_);
1128 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1131 case LFUN_BUFFER_NEXT:
1132 BOOST_ASSERT(lyx_view_);
1133 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1136 case LFUN_BUFFER_PREVIOUS:
1137 BOOST_ASSERT(lyx_view_);
1138 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1142 BOOST_ASSERT(lyx_view_);
1143 newFile(view(), argument);
1146 case LFUN_FILE_OPEN:
1147 BOOST_ASSERT(lyx_view_);
1151 case LFUN_DROP_LAYOUTS_CHOICE:
1152 BOOST_ASSERT(lyx_view_);
1153 lyx_view_->getToolbars().openLayoutList();
1156 case LFUN_MENU_OPEN:
1157 BOOST_ASSERT(lyx_view_);
1158 lyx_view_->getMenubar().openByName(from_utf8(argument));
1161 // --- lyxserver commands ----------------------------
1162 case LFUN_SERVER_GET_NAME:
1163 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1164 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1165 lyxerr[Debug::INFO] << "FNAME["
1166 << lyx_view_->buffer()->fileName()
1170 case LFUN_SERVER_NOTIFY:
1171 dispatch_buffer = from_utf8(keyseq->print());
1172 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1175 case LFUN_SERVER_GOTO_FILE_ROW: {
1176 BOOST_ASSERT(lyx_view_);
1179 istringstream is(argument);
1180 is >> file_name >> row;
1181 if (prefixIs(file_name, package().temp_dir())) {
1182 // Needed by inverse dvi search. If it is a file
1183 // in tmpdir, call the apropriated function
1184 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1186 // Must replace extension of the file to be .lyx
1187 // and get full path
1188 string const s = changeExtension(file_name, ".lyx");
1189 // Either change buffer or load the file
1190 if (theBufferList().exists(s)) {
1191 lyx_view_->setBuffer(theBufferList().getBuffer(s));
1193 lyx_view_->loadLyXFile(s);
1197 view()->setCursorFromRow(row);
1200 // see BufferView::center()
1204 case LFUN_DIALOG_SHOW: {
1205 BOOST_ASSERT(lyx_view_);
1206 string const name = cmd.getArg(0);
1207 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1209 if (name == "character") {
1210 data = freefont2string();
1212 lyx_view_->getDialogs().show("character", data);
1213 } else if (name == "latexlog") {
1214 pair<Buffer::LogType, string> const logfile =
1215 lyx_view_->buffer()->getLogName();
1216 switch (logfile.first) {
1217 case Buffer::latexlog:
1220 case Buffer::buildlog:
1224 data += LyXLex::quoteString(logfile.second);
1225 lyx_view_->getDialogs().show("log", data);
1226 } else if (name == "vclog") {
1227 string const data = "vc " +
1228 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1229 lyx_view_->getDialogs().show("log", data);
1231 lyx_view_->getDialogs().show(name, data);
1235 case LFUN_DIALOG_SHOW_NEW_INSET: {
1236 BOOST_ASSERT(lyx_view_);
1237 string const name = cmd.getArg(0);
1238 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1239 if (name == "bibitem" ||
1246 InsetCommandParams p(name);
1247 data = InsetCommandMailer::params2string(name, p);
1248 } else if (name == "include") {
1249 InsetCommandParams p(data);
1250 data = InsetIncludeMailer::params2string(p);
1251 } else if (name == "box") {
1252 // \c data == "Boxed" || "Frameless" etc
1253 InsetBoxParams p(data);
1254 data = InsetBoxMailer::params2string(p);
1255 } else if (name == "branch") {
1256 InsetBranchParams p;
1257 data = InsetBranchMailer::params2string(p);
1258 } else if (name == "citation") {
1259 InsetCommandParams p("cite");
1260 data = InsetCommandMailer::params2string(name, p);
1261 } else if (name == "ert") {
1262 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1263 } else if (name == "external") {
1264 InsetExternalParams p;
1265 Buffer const & buffer = *lyx_view_->buffer();
1266 data = InsetExternalMailer::params2string(p, buffer);
1267 } else if (name == "float") {
1269 data = InsetFloatMailer::params2string(p);
1270 } else if (name == "graphics") {
1271 InsetGraphicsParams p;
1272 Buffer const & buffer = *lyx_view_->buffer();
1273 data = InsetGraphicsMailer::params2string(p, buffer);
1274 } else if (name == "note") {
1276 data = InsetNoteMailer::params2string(p);
1277 } else if (name == "vspace") {
1279 data = InsetVSpaceMailer::params2string(space);
1280 } else if (name == "wrap") {
1282 data = InsetWrapMailer::params2string(p);
1284 lyx_view_->getDialogs().show(name, data, 0);
1288 case LFUN_DIALOG_UPDATE: {
1289 BOOST_ASSERT(lyx_view_);
1290 string const & name = argument;
1291 // Can only update a dialog connected to an existing inset
1292 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1294 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1295 inset->dispatch(view()->cursor(), fr);
1296 } else if (name == "paragraph") {
1297 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1298 } else if (name == "prefs") {
1299 lyx_view_->getDialogs().update(name, string());
1304 case LFUN_DIALOG_HIDE:
1305 Dialogs::hide(argument, 0);
1308 case LFUN_DIALOG_DISCONNECT_INSET:
1309 BOOST_ASSERT(lyx_view_);
1310 lyx_view_->getDialogs().disconnect(argument);
1314 case LFUN_CITATION_INSERT: {
1315 BOOST_ASSERT(lyx_view_);
1316 if (!argument.empty()) {
1317 // we can have one optional argument, delimited by '|'
1318 // citation-insert <key>|<text_before>
1319 // this should be enhanced to also support text_after
1320 // and citation style
1321 string arg = argument;
1323 if (contains(argument, "|")) {
1324 arg = token(argument, '|', 0);
1325 opt1 = '[' + token(argument, '|', 1) + ']';
1327 std::ostringstream os;
1328 os << "citation LatexCommand\n"
1329 << "\\cite" << opt1 << "{" << arg << "}\n"
1331 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1334 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1338 case LFUN_BUFFER_CHILD_OPEN: {
1339 BOOST_ASSERT(lyx_view_);
1340 string const filename =
1341 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1342 // FIXME Should use bformat
1343 setMessage(_("Opening child document ") +
1344 makeDisplayPath(filename) + "...");
1345 view()->savePosition(0);
1346 string const parentfilename = lyx_view_->buffer()->fileName();
1347 if (theBufferList().exists(filename))
1348 lyx_view_->setBuffer(theBufferList().getBuffer(filename));
1350 lyx_view_->loadLyXFile(filename);
1351 // Set the parent name of the child document.
1352 // This makes insertion of citations and references in the child work,
1353 // when the target is in the parent or another child document.
1354 lyx_view_->buffer()->setParentName(parentfilename);
1358 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1359 BOOST_ASSERT(lyx_view_);
1360 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1363 case LFUN_KEYMAP_OFF:
1364 BOOST_ASSERT(lyx_view_);
1365 lyx_view_->view()->getIntl().keyMapOn(false);
1368 case LFUN_KEYMAP_PRIMARY:
1369 BOOST_ASSERT(lyx_view_);
1370 lyx_view_->view()->getIntl().keyMapPrim();
1373 case LFUN_KEYMAP_SECONDARY:
1374 BOOST_ASSERT(lyx_view_);
1375 lyx_view_->view()->getIntl().keyMapSec();
1378 case LFUN_KEYMAP_TOGGLE:
1379 BOOST_ASSERT(lyx_view_);
1380 lyx_view_->view()->getIntl().toggleKeyMap();
1386 string rest = split(argument, countstr, ' ');
1387 istringstream is(countstr);
1390 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1391 for (int i = 0; i < count; ++i)
1392 dispatch(lyxaction.lookupFunc(rest));
1396 case LFUN_COMMAND_SEQUENCE: {
1397 // argument contains ';'-terminated commands
1398 string arg = argument;
1399 while (!arg.empty()) {
1401 arg = split(arg, first, ';');
1402 FuncRequest func(lyxaction.lookupFunc(first));
1403 func.origin = cmd.origin;
1409 case LFUN_PREFERENCES_SAVE: {
1410 support::Path p(package().user_support());
1411 lyxrc.write("preferences", false);
1415 case LFUN_SCREEN_FONT_UPDATE:
1416 BOOST_ASSERT(lyx_view_);
1417 // handle the screen font changes.
1418 lyxrc.set_font_norm_type();
1419 theFontLoader().update();
1420 // All visible buffers will need resize
1424 case LFUN_SET_COLOR: {
1426 string const x11_name = split(argument, lyx_name, ' ');
1427 if (lyx_name.empty() || x11_name.empty()) {
1428 setErrorMessage(_("Syntax: set-color <lyx_name>"
1433 bool const graphicsbg_changed =
1434 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1435 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1437 if (!lcolor.setColor(lyx_name, x11_name)) {
1439 bformat(_("Set-color \"%1$s\" failed "
1440 "- color is undefined or "
1441 "may not be redefined"),
1442 from_utf8(lyx_name)));
1446 theApp->updateColor(lcolor.getFromLyXName(lyx_name));
1448 if (graphicsbg_changed) {
1449 #ifdef WITH_WARNINGS
1450 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1453 graphics::GCache::get().changeDisplay(true);
1460 BOOST_ASSERT(lyx_view_);
1461 lyx_view_->message(from_utf8(argument));
1464 case LFUN_EXTERNAL_EDIT: {
1465 BOOST_ASSERT(lyx_view_);
1466 FuncRequest fr(action, argument);
1467 InsetExternal().dispatch(view()->cursor(), fr);
1471 case LFUN_GRAPHICS_EDIT: {
1472 FuncRequest fr(action, argument);
1473 InsetGraphics().dispatch(view()->cursor(), fr);
1477 case LFUN_INSET_APPLY: {
1478 BOOST_ASSERT(lyx_view_);
1479 string const name = cmd.getArg(0);
1480 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1482 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1483 inset->dispatch(view()->cursor(), fr);
1485 FuncRequest fr(LFUN_INSET_INSERT, argument);
1488 // ideally, the update flag should be set by the insets,
1489 // but this is not possible currently
1490 updateFlags = Update::Force | Update::FitCursor;
1494 case LFUN_ALL_INSETS_TOGGLE: {
1495 BOOST_ASSERT(lyx_view_);
1497 string const name = split(argument, action, ' ');
1498 InsetBase::Code const inset_code =
1499 InsetBase::translate(name);
1501 LCursor & cur = view()->cursor();
1502 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1504 InsetBase & inset = lyx_view_->buffer()->inset();
1505 InsetIterator it = inset_iterator_begin(inset);
1506 InsetIterator const end = inset_iterator_end(inset);
1507 for (; it != end; ++it) {
1508 if (inset_code == InsetBase::NO_CODE
1509 || inset_code == it->lyxCode()) {
1510 LCursor tmpcur = cur;
1511 tmpcur.pushLeft(*it);
1512 it->dispatch(tmpcur, fr);
1515 updateFlags = Update::Force | Update::FitCursor;
1519 case LFUN_BUFFER_LANGUAGE: {
1520 BOOST_ASSERT(lyx_view_);
1521 Buffer & buffer = *lyx_view_->buffer();
1522 Language const * oldL = buffer.params().language;
1523 Language const * newL = languages.getLanguage(argument);
1524 if (!newL || oldL == newL)
1527 if (oldL->rightToLeft() == newL->rightToLeft()
1528 && !buffer.isMultiLingual())
1529 buffer.changeLanguage(oldL, newL);
1531 buffer.updateDocLang(newL);
1535 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1536 string const fname =
1537 addName(addPath(package().user_support(), "templates/"),
1539 Buffer defaults(fname);
1541 istringstream ss(argument);
1544 int const unknown_tokens = defaults.readHeader(lex);
1546 if (unknown_tokens != 0) {
1547 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1548 << unknown_tokens << " unknown token"
1549 << (unknown_tokens == 1 ? "" : "s")
1553 if (defaults.writeFile(defaults.fileName()))
1554 // FIXME Should use bformat
1555 setMessage(_("Document defaults saved in ")
1556 + makeDisplayPath(fname));
1558 setErrorMessage(_("Unable to save document defaults"));
1562 case LFUN_BUFFER_PARAMS_APPLY: {
1563 BOOST_ASSERT(lyx_view_);
1564 biblio::CiteEngine const engine =
1565 lyx_view_->buffer()->params().cite_engine;
1567 istringstream ss(argument);
1570 int const unknown_tokens =
1571 lyx_view_->buffer()->readHeader(lex);
1573 if (unknown_tokens != 0) {
1574 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1575 << unknown_tokens << " unknown token"
1576 << (unknown_tokens == 1 ? "" : "s")
1579 if (engine == lyx_view_->buffer()->params().cite_engine)
1582 LCursor & cur = view()->cursor();
1583 FuncRequest fr(LFUN_INSET_REFRESH);
1585 InsetBase & inset = lyx_view_->buffer()->inset();
1586 InsetIterator it = inset_iterator_begin(inset);
1587 InsetIterator const end = inset_iterator_end(inset);
1588 for (; it != end; ++it)
1589 if (it->lyxCode() == InsetBase::CITE_CODE)
1590 it->dispatch(cur, fr);
1594 case LFUN_TEXTCLASS_APPLY: {
1595 BOOST_ASSERT(lyx_view_);
1596 Buffer * buffer = lyx_view_->buffer();
1598 textclass_type const old_class =
1599 buffer->params().textclass;
1601 loadTextclass(argument);
1603 std::pair<bool, textclass_type> const tc_pair =
1604 textclasslist.numberOfClass(argument);
1609 textclass_type const new_class = tc_pair.second;
1610 if (old_class == new_class)
1614 lyx_view_->message(_("Converting document to new document class..."));
1615 recordUndoFullDocument(view());
1616 buffer->params().textclass = new_class;
1617 StableDocIterator backcur(view()->cursor());
1618 ErrorList & el = buffer->errorList("Class Switch");
1619 cap::switchBetweenClasses(
1620 old_class, new_class,
1621 static_cast<InsetText &>(buffer->inset()), el);
1623 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1625 buffer->errors("Class Switch");
1626 updateLabels(*buffer);
1627 updateFlags = Update::Force | Update::FitCursor;
1631 case LFUN_TEXTCLASS_LOAD:
1632 loadTextclass(argument);
1635 case LFUN_LYXRC_APPLY: {
1636 LyXRC const lyxrc_orig = lyxrc;
1638 istringstream ss(argument);
1639 bool const success = lyxrc.read(ss) == 0;
1642 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1643 << "Unable to read lyxrc data"
1648 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1652 case LFUN_WINDOW_NEW:
1653 LyX::ref().newLyXView();
1656 case LFUN_WINDOW_CLOSE:
1657 BOOST_ASSERT(lyx_view_);
1658 BOOST_ASSERT(theApp);
1660 // We return here because lyx_view does not exists anymore.
1664 BOOST_ASSERT(lyx_view_);
1665 view()->cursor().dispatch(cmd);
1666 updateFlags = view()->cursor().result().update();
1667 if (!view()->cursor().result().dispatched())
1668 if (view()->dispatch(cmd))
1669 updateFlags = Update::Force | Update::FitCursor;
1674 if (lyx_view_ && view()->buffer()) {
1675 // Redraw screen unless explicitly told otherwise.
1676 // This also initializes the position cache for all insets
1677 // in (at least partially) visible top-level paragraphs.
1678 bool needSecondUpdate = false;
1679 if (updateFlags != Update::None)
1680 view()->update(updateFlags);
1682 needSecondUpdate = view()->fitCursor();
1684 if (needSecondUpdate || updateFlags != Update::None) {
1685 view()->buffer()->changed();
1687 lyx_view_->updateStatusBar();
1689 // if we executed a mutating lfun, mark the buffer as dirty
1691 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1692 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1693 view()->buffer()->markDirty();
1695 if (view()->cursor().inTexted()) {
1696 lyx_view_->updateLayoutChoice();
1701 // FIXME UNICODE: _() does not support anything but ascii.
1702 // Do we need a to_ascii() method?
1703 sendDispatchMessage(getMessage(), cmd);
1707 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1709 /* When an action did not originate from the UI/kbd, it makes
1710 * sense to avoid updating the GUI. It turns out that this
1711 * fixes bug 1941, for reasons that are described here:
1712 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1714 if (cmd.origin != FuncRequest::INTERNAL) {
1715 lyx_view_->updateMenubar();
1716 lyx_view_->updateToolbars();
1719 const bool verbose = (cmd.origin == FuncRequest::UI
1720 || cmd.origin == FuncRequest::COMMANDBUFFER);
1722 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1723 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1725 lyx_view_->message(msg);
1729 docstring dispatch_msg = msg;
1730 if (!dispatch_msg.empty())
1731 dispatch_msg += ' ';
1733 string comname = lyxaction.getActionName(cmd.action);
1735 bool argsadded = false;
1737 if (!cmd.argument().empty()) {
1738 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1739 comname += ' ' + to_utf8(cmd.argument());
1744 string const shortcuts = theTopLevelKeymap().printbindings(cmd);
1746 if (!shortcuts.empty())
1747 comname += ": " + shortcuts;
1748 else if (!argsadded && !cmd.argument().empty())
1749 comname += ' ' + to_utf8(cmd.argument());
1751 if (!comname.empty()) {
1752 comname = rtrim(comname);
1753 dispatch_msg += from_utf8('(' + rtrim(comname) + ')');
1756 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1757 << to_utf8(dispatch_msg) << endl;
1758 if (!dispatch_msg.empty())
1759 lyx_view_->message(dispatch_msg);
1763 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1765 string initpath = lyxrc.document_path;
1766 string filename(name);
1768 if (view()->buffer()) {
1769 string const trypath = lyx_view_->buffer()->filePath();
1770 // If directory is writeable, use this as default.
1771 if (isDirWriteable(trypath))
1775 static int newfile_number;
1777 if (filename.empty()) {
1778 filename = addName(lyxrc.document_path,
1779 "newfile" + convert<string>(++newfile_number) + ".lyx");
1780 while (theBufferList().exists(filename) || fs::is_readable(filename)) {
1782 filename = addName(lyxrc.document_path,
1783 "newfile" + convert<string>(newfile_number) +
1788 // The template stuff
1791 FileDialog fileDlg(_("Select template file"),
1792 LFUN_SELECT_FILE_SYNC,
1793 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1794 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1796 FileDialog::Result result =
1797 fileDlg.open(from_utf8(lyxrc.template_path),
1798 FileFilterList(_("LyX Documents (*.lyx)")),
1801 if (result.first == FileDialog::Later)
1803 if (result.second.empty())
1805 templname = to_utf8(result.second);
1808 Buffer * const b = newFile(filename, templname, !name.empty());
1810 lyx_view_->setBuffer(b);
1814 void LyXFunc::open(string const & fname)
1816 string initpath = lyxrc.document_path;
1818 if (view()->buffer()) {
1819 string const trypath = lyx_view_->buffer()->filePath();
1820 // If directory is writeable, use this as default.
1821 if (isDirWriteable(trypath))
1827 if (fname.empty()) {
1828 FileDialog fileDlg(_("Select document to open"),
1830 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1831 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1833 FileDialog::Result result =
1834 fileDlg.open(from_utf8(initpath),
1835 FileFilterList(_("LyX Documents (*.lyx)")),
1838 if (result.first == FileDialog::Later)
1841 filename = to_utf8(result.second);
1843 // check selected filename
1844 if (filename.empty()) {
1845 lyx_view_->message(_("Canceled."));
1851 // get absolute path of file and add ".lyx" to the filename if
1853 string const fullpath = fileSearch(string(), filename, "lyx");
1854 if (!fullpath.empty()) {
1855 filename = fullpath;
1858 docstring const disp_fn = makeDisplayPath(filename);
1860 // if the file doesn't exist, let the user create one
1861 if (!fs::exists(filename)) {
1862 // the user specifically chose this name. Believe him.
1863 Buffer * const b = newFile(filename, string(), true);
1865 lyx_view_->setBuffer(b);
1869 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1872 if (lyx_view_->loadLyXFile(filename)) {
1873 str2 = bformat(_("Document %1$s opened."), disp_fn);
1875 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1877 lyx_view_->message(str2);
1881 void LyXFunc::doImport(string const & argument)
1884 string filename = split(argument, format, ' ');
1886 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1887 << " file: " << filename << endl;
1889 // need user interaction
1890 if (filename.empty()) {
1891 string initpath = lyxrc.document_path;
1893 if (view()->buffer()) {
1894 string const trypath = lyx_view_->buffer()->filePath();
1895 // If directory is writeable, use this as default.
1896 if (isDirWriteable(trypath))
1900 docstring const text = bformat(_("Select %1$s file to import"),
1901 formats.prettyName(format));
1903 FileDialog fileDlg(text,
1905 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1906 make_pair(_("Examples|#E#e"),
1907 from_utf8(addPath(package().system_support(), "examples"))));
1909 docstring filter = formats.prettyName(format);
1912 filter += from_utf8(formats.extension(format));
1915 FileDialog::Result result =
1916 fileDlg.open(from_utf8(initpath),
1917 FileFilterList(filter),
1920 if (result.first == FileDialog::Later)
1923 filename = to_utf8(result.second);
1925 // check selected filename
1926 if (filename.empty())
1927 lyx_view_->message(_("Canceled."));
1930 if (filename.empty())
1933 // get absolute path of file
1934 filename = makeAbsPath(filename);
1936 string const lyxfile = changeExtension(filename, ".lyx");
1938 // Check if the document already is open
1939 if (use_gui && theBufferList().exists(lyxfile)) {
1940 if (!theBufferList().close(theBufferList().getBuffer(lyxfile), true)) {
1941 lyx_view_->message(_("Canceled."));
1946 // if the file exists already, and we didn't do
1947 // -i lyx thefile.lyx, warn
1948 if (fs::exists(lyxfile) && filename != lyxfile) {
1949 docstring const file = makeDisplayPath(lyxfile, 30);
1951 docstring text = bformat(_("The document %1$s already exists.\n\n"
1952 "Do you want to over-write that document?"), file);
1953 int const ret = Alert::prompt(_("Over-write document?"),
1954 text, 0, 1, _("&Over-write"), _("&Cancel"));
1957 lyx_view_->message(_("Canceled."));
1962 ErrorList errorList;
1963 Importer::Import(lyx_view_, filename, format, errorList);
1964 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1968 void LyXFunc::closeBuffer()
1970 // save current cursor position
1971 LyX::ref().session().lastFilePos().save(lyx_view_->buffer()->fileName(),
1972 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1973 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
1974 if (theBufferList().empty()) {
1975 // need this otherwise SEGV may occur while
1976 // trying to set variables that don't exist
1977 // since there's no current buffer
1978 lyx_view_->getDialogs().hideBufferDependent();
1980 lyx_view_->setBuffer(theBufferList().first());
1986 // Each "lyx_view_" should have it's own message method. lyxview and
1987 // the minibuffer would use the minibuffer, but lyxserver would
1988 // send an ERROR signal to its client. Alejandro 970603
1989 // This function is bit problematic when it comes to NLS, to make the
1990 // lyx servers client be language indepenent we must not translate
1991 // strings sent to this func.
1992 void LyXFunc::setErrorMessage(docstring const & m) const
1994 dispatch_buffer = m;
1999 void LyXFunc::setMessage(docstring const & m) const
2001 dispatch_buffer = m;
2005 string const LyXFunc::viewStatusMessage()
2007 // When meta-fake key is pressed, show the key sequence so far + "M-".
2009 return keyseq->print() + "M-";
2011 // Else, when a non-complete key sequence is pressed,
2012 // show the available options.
2013 if (keyseq->length() > 0 && !keyseq->deleted())
2014 return keyseq->printOptions();
2016 if (!view()->buffer())
2017 return to_utf8(_("Welcome to LyX!"));
2019 return view()->cursor().currentState();
2023 BufferView * LyXFunc::view() const
2025 BOOST_ASSERT(lyx_view_);
2026 return lyx_view_->view();
2030 bool LyXFunc::wasMetaKey() const
2032 return (meta_fake_bit != key_modifier::none);
2038 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2040 // Why the switch you might ask. It is a trick to ensure that all
2041 // the elements in the LyXRCTags enum is handled. As you can see
2042 // there are no breaks at all. So it is just a huge fall-through.
2043 // The nice thing is that we will get a warning from the compiler
2044 // if we forget an element.
2045 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2047 case LyXRC::RC_ACCEPT_COMPOUND:
2048 case LyXRC::RC_ALT_LANG:
2049 case LyXRC::RC_ASCIIROFF_COMMAND:
2050 case LyXRC::RC_ASCII_LINELEN:
2051 case LyXRC::RC_AUTOREGIONDELETE:
2052 case LyXRC::RC_AUTORESET_OPTIONS:
2053 case LyXRC::RC_AUTOSAVE:
2054 case LyXRC::RC_AUTO_NUMBER:
2055 case LyXRC::RC_BACKUPDIR_PATH:
2056 case LyXRC::RC_BIBTEX_COMMAND:
2057 case LyXRC::RC_BINDFILE:
2058 case LyXRC::RC_CHECKLASTFILES:
2059 case LyXRC::RC_USELASTFILEPOS:
2060 case LyXRC::RC_LOADSESSION:
2061 case LyXRC::RC_CHKTEX_COMMAND:
2062 case LyXRC::RC_CONVERTER:
2063 case LyXRC::RC_COPIER:
2064 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2065 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2066 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2067 case LyXRC::RC_DATE_INSERT_FORMAT:
2068 case LyXRC::RC_DEFAULT_LANGUAGE:
2069 case LyXRC::RC_DEFAULT_PAPERSIZE:
2070 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2071 case LyXRC::RC_DISPLAY_GRAPHICS:
2072 case LyXRC::RC_DOCUMENTPATH:
2073 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2074 if (fs::exists(lyxrc_new.document_path) &&
2075 fs::is_directory(lyxrc_new.document_path)) {
2076 support::package().document_dir() = lyxrc.document_path;
2079 case LyXRC::RC_ESC_CHARS:
2080 case LyXRC::RC_FONT_ENCODING:
2081 case LyXRC::RC_FORMAT:
2082 case LyXRC::RC_INDEX_COMMAND:
2083 case LyXRC::RC_INPUT:
2084 case LyXRC::RC_KBMAP:
2085 case LyXRC::RC_KBMAP_PRIMARY:
2086 case LyXRC::RC_KBMAP_SECONDARY:
2087 case LyXRC::RC_LABEL_INIT_LENGTH:
2088 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2089 case LyXRC::RC_LANGUAGE_AUTO_END:
2090 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2091 case LyXRC::RC_LANGUAGE_COMMAND_END:
2092 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2093 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2094 case LyXRC::RC_LANGUAGE_PACKAGE:
2095 case LyXRC::RC_LANGUAGE_USE_BABEL:
2096 case LyXRC::RC_MAKE_BACKUP:
2097 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2098 case LyXRC::RC_NUMLASTFILES:
2099 case LyXRC::RC_PATH_PREFIX:
2100 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2101 support::prependEnvPath("PATH", lyxrc.path_prefix);
2103 case LyXRC::RC_PERS_DICT:
2104 case LyXRC::RC_POPUP_BOLD_FONT:
2105 case LyXRC::RC_POPUP_FONT_ENCODING:
2106 case LyXRC::RC_POPUP_NORMAL_FONT:
2107 case LyXRC::RC_PREVIEW:
2108 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2109 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2110 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2111 case LyXRC::RC_PRINTCOPIESFLAG:
2112 case LyXRC::RC_PRINTER:
2113 case LyXRC::RC_PRINTEVENPAGEFLAG:
2114 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2115 case LyXRC::RC_PRINTFILEEXTENSION:
2116 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2117 case LyXRC::RC_PRINTODDPAGEFLAG:
2118 case LyXRC::RC_PRINTPAGERANGEFLAG:
2119 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2120 case LyXRC::RC_PRINTPAPERFLAG:
2121 case LyXRC::RC_PRINTREVERSEFLAG:
2122 case LyXRC::RC_PRINTSPOOL_COMMAND:
2123 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2124 case LyXRC::RC_PRINTTOFILE:
2125 case LyXRC::RC_PRINTTOPRINTER:
2126 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2127 case LyXRC::RC_PRINT_COMMAND:
2128 case LyXRC::RC_RTL_SUPPORT:
2129 case LyXRC::RC_SCREEN_DPI:
2130 case LyXRC::RC_SCREEN_FONT_ENCODING:
2131 case LyXRC::RC_SCREEN_FONT_ROMAN:
2132 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2133 case LyXRC::RC_SCREEN_FONT_SANS:
2134 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2135 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2136 case LyXRC::RC_SCREEN_FONT_SIZES:
2137 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2138 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2139 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2140 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2141 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2142 case LyXRC::RC_SCREEN_ZOOM:
2143 case LyXRC::RC_SERVERPIPE:
2144 case LyXRC::RC_SET_COLOR:
2145 case LyXRC::RC_SHOW_BANNER:
2146 case LyXRC::RC_SPELL_COMMAND:
2147 case LyXRC::RC_TEMPDIRPATH:
2148 case LyXRC::RC_TEMPLATEPATH:
2149 case LyXRC::RC_TEX_ALLOWS_SPACES:
2150 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2151 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2152 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2154 case LyXRC::RC_UIFILE:
2155 case LyXRC::RC_USER_EMAIL:
2156 case LyXRC::RC_USER_NAME:
2157 case LyXRC::RC_USETEMPDIR:
2158 case LyXRC::RC_USE_ALT_LANG:
2159 case LyXRC::RC_USE_ESC_CHARS:
2160 case LyXRC::RC_USE_INP_ENC:
2161 case LyXRC::RC_USE_PERS_DICT:
2162 case LyXRC::RC_USE_SPELL_LIB:
2163 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2164 case LyXRC::RC_VIEWER:
2165 case LyXRC::RC_LAST: