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 BOOST_ASSERT(lyx_view_);
1059 reconfigure(*lyx_view_);
1062 case LFUN_HELP_OPEN: {
1063 BOOST_ASSERT(lyx_view_);
1064 string const arg = argument;
1066 setErrorMessage(_("Missing argument"));
1069 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1070 if (fname.empty()) {
1071 lyxerr << "LyX: unable to find documentation file `"
1072 << arg << "'. Bad installation?" << endl;
1075 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1076 makeDisplayPath(fname)));
1077 lyx_view_->loadLyXFile(fname, false);
1081 // --- version control -------------------------------
1082 case LFUN_VC_REGISTER:
1083 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1084 if (!ensureBufferClean(view()))
1086 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1087 lyx_view_->buffer()->lyxvc().registrer();
1092 case LFUN_VC_CHECK_IN:
1093 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1094 if (!ensureBufferClean(view()))
1096 if (lyx_view_->buffer()->lyxvc().inUse()
1097 && !lyx_view_->buffer()->isReadonly()) {
1098 lyx_view_->buffer()->lyxvc().checkIn();
1103 case LFUN_VC_CHECK_OUT:
1104 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1105 if (!ensureBufferClean(view()))
1107 if (lyx_view_->buffer()->lyxvc().inUse()
1108 && lyx_view_->buffer()->isReadonly()) {
1109 lyx_view_->buffer()->lyxvc().checkOut();
1114 case LFUN_VC_REVERT:
1115 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1116 lyx_view_->buffer()->lyxvc().revert();
1120 case LFUN_VC_UNDO_LAST:
1121 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1122 lyx_view_->buffer()->lyxvc().undoLast();
1126 // --- buffers ----------------------------------------
1127 case LFUN_BUFFER_SWITCH:
1128 BOOST_ASSERT(lyx_view_);
1129 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1132 case LFUN_BUFFER_NEXT:
1133 BOOST_ASSERT(lyx_view_);
1134 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1137 case LFUN_BUFFER_PREVIOUS:
1138 BOOST_ASSERT(lyx_view_);
1139 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1143 BOOST_ASSERT(lyx_view_);
1144 newFile(view(), argument);
1147 case LFUN_FILE_OPEN:
1148 BOOST_ASSERT(lyx_view_);
1152 case LFUN_DROP_LAYOUTS_CHOICE:
1153 BOOST_ASSERT(lyx_view_);
1154 lyx_view_->getToolbars().openLayoutList();
1157 case LFUN_MENU_OPEN:
1158 BOOST_ASSERT(lyx_view_);
1159 lyx_view_->getMenubar().openByName(from_utf8(argument));
1162 // --- lyxserver commands ----------------------------
1163 case LFUN_SERVER_GET_NAME:
1164 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1165 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1166 lyxerr[Debug::INFO] << "FNAME["
1167 << lyx_view_->buffer()->fileName()
1171 case LFUN_SERVER_NOTIFY:
1172 dispatch_buffer = from_utf8(keyseq->print());
1173 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1176 case LFUN_SERVER_GOTO_FILE_ROW: {
1177 BOOST_ASSERT(lyx_view_);
1180 istringstream is(argument);
1181 is >> file_name >> row;
1182 if (prefixIs(file_name, package().temp_dir())) {
1183 // Needed by inverse dvi search. If it is a file
1184 // in tmpdir, call the apropriated function
1185 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1187 // Must replace extension of the file to be .lyx
1188 // and get full path
1189 string const s = changeExtension(file_name, ".lyx");
1190 // Either change buffer or load the file
1191 if (theBufferList().exists(s)) {
1192 lyx_view_->setBuffer(theBufferList().getBuffer(s));
1194 lyx_view_->loadLyXFile(s);
1198 view()->setCursorFromRow(row);
1201 // see BufferView::center()
1205 case LFUN_DIALOG_SHOW: {
1206 BOOST_ASSERT(lyx_view_);
1207 string const name = cmd.getArg(0);
1208 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1210 if (name == "character") {
1211 data = freefont2string();
1213 lyx_view_->getDialogs().show("character", data);
1214 } else if (name == "latexlog") {
1215 pair<Buffer::LogType, string> const logfile =
1216 lyx_view_->buffer()->getLogName();
1217 switch (logfile.first) {
1218 case Buffer::latexlog:
1221 case Buffer::buildlog:
1225 data += LyXLex::quoteString(logfile.second);
1226 lyx_view_->getDialogs().show("log", data);
1227 } else if (name == "vclog") {
1228 string const data = "vc " +
1229 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1230 lyx_view_->getDialogs().show("log", data);
1232 lyx_view_->getDialogs().show(name, data);
1236 case LFUN_DIALOG_SHOW_NEW_INSET: {
1237 BOOST_ASSERT(lyx_view_);
1238 string const name = cmd.getArg(0);
1239 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1240 if (name == "bibitem" ||
1247 InsetCommandParams p(name);
1248 data = InsetCommandMailer::params2string(name, p);
1249 } else if (name == "include") {
1250 InsetCommandParams p(data);
1251 data = InsetIncludeMailer::params2string(p);
1252 } else if (name == "box") {
1253 // \c data == "Boxed" || "Frameless" etc
1254 InsetBoxParams p(data);
1255 data = InsetBoxMailer::params2string(p);
1256 } else if (name == "branch") {
1257 InsetBranchParams p;
1258 data = InsetBranchMailer::params2string(p);
1259 } else if (name == "citation") {
1260 InsetCommandParams p("cite");
1261 data = InsetCommandMailer::params2string(name, p);
1262 } else if (name == "ert") {
1263 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1264 } else if (name == "external") {
1265 InsetExternalParams p;
1266 Buffer const & buffer = *lyx_view_->buffer();
1267 data = InsetExternalMailer::params2string(p, buffer);
1268 } else if (name == "float") {
1270 data = InsetFloatMailer::params2string(p);
1271 } else if (name == "graphics") {
1272 InsetGraphicsParams p;
1273 Buffer const & buffer = *lyx_view_->buffer();
1274 data = InsetGraphicsMailer::params2string(p, buffer);
1275 } else if (name == "note") {
1277 data = InsetNoteMailer::params2string(p);
1278 } else if (name == "vspace") {
1280 data = InsetVSpaceMailer::params2string(space);
1281 } else if (name == "wrap") {
1283 data = InsetWrapMailer::params2string(p);
1285 lyx_view_->getDialogs().show(name, data, 0);
1289 case LFUN_DIALOG_UPDATE: {
1290 BOOST_ASSERT(lyx_view_);
1291 string const & name = argument;
1292 // Can only update a dialog connected to an existing inset
1293 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1295 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1296 inset->dispatch(view()->cursor(), fr);
1297 } else if (name == "paragraph") {
1298 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1299 } else if (name == "prefs") {
1300 lyx_view_->getDialogs().update(name, string());
1305 case LFUN_DIALOG_HIDE:
1306 Dialogs::hide(argument, 0);
1309 case LFUN_DIALOG_DISCONNECT_INSET:
1310 BOOST_ASSERT(lyx_view_);
1311 lyx_view_->getDialogs().disconnect(argument);
1315 case LFUN_CITATION_INSERT: {
1316 BOOST_ASSERT(lyx_view_);
1317 if (!argument.empty()) {
1318 // we can have one optional argument, delimited by '|'
1319 // citation-insert <key>|<text_before>
1320 // this should be enhanced to also support text_after
1321 // and citation style
1322 string arg = argument;
1324 if (contains(argument, "|")) {
1325 arg = token(argument, '|', 0);
1326 opt1 = '[' + token(argument, '|', 1) + ']';
1328 std::ostringstream os;
1329 os << "citation LatexCommand\n"
1330 << "\\cite" << opt1 << "{" << arg << "}\n"
1332 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1335 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1339 case LFUN_BUFFER_CHILD_OPEN: {
1340 BOOST_ASSERT(lyx_view_);
1341 string const filename =
1342 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1343 // FIXME Should use bformat
1344 setMessage(_("Opening child document ") +
1345 makeDisplayPath(filename) + "...");
1346 view()->savePosition(0);
1347 string const parentfilename = lyx_view_->buffer()->fileName();
1348 if (theBufferList().exists(filename))
1349 lyx_view_->setBuffer(theBufferList().getBuffer(filename));
1351 lyx_view_->loadLyXFile(filename);
1352 // Set the parent name of the child document.
1353 // This makes insertion of citations and references in the child work,
1354 // when the target is in the parent or another child document.
1355 lyx_view_->buffer()->setParentName(parentfilename);
1359 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1360 BOOST_ASSERT(lyx_view_);
1361 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1364 case LFUN_KEYMAP_OFF:
1365 BOOST_ASSERT(lyx_view_);
1366 lyx_view_->view()->getIntl().keyMapOn(false);
1369 case LFUN_KEYMAP_PRIMARY:
1370 BOOST_ASSERT(lyx_view_);
1371 lyx_view_->view()->getIntl().keyMapPrim();
1374 case LFUN_KEYMAP_SECONDARY:
1375 BOOST_ASSERT(lyx_view_);
1376 lyx_view_->view()->getIntl().keyMapSec();
1379 case LFUN_KEYMAP_TOGGLE:
1380 BOOST_ASSERT(lyx_view_);
1381 lyx_view_->view()->getIntl().toggleKeyMap();
1387 string rest = split(argument, countstr, ' ');
1388 istringstream is(countstr);
1391 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1392 for (int i = 0; i < count; ++i)
1393 dispatch(lyxaction.lookupFunc(rest));
1397 case LFUN_COMMAND_SEQUENCE: {
1398 // argument contains ';'-terminated commands
1399 string arg = argument;
1400 while (!arg.empty()) {
1402 arg = split(arg, first, ';');
1403 FuncRequest func(lyxaction.lookupFunc(first));
1404 func.origin = cmd.origin;
1410 case LFUN_PREFERENCES_SAVE: {
1411 support::Path p(package().user_support());
1412 lyxrc.write("preferences", false);
1416 case LFUN_SCREEN_FONT_UPDATE:
1417 BOOST_ASSERT(lyx_view_);
1418 // handle the screen font changes.
1419 lyxrc.set_font_norm_type();
1420 theFontLoader().update();
1421 // All visible buffers will need resize
1425 case LFUN_SET_COLOR: {
1427 string const x11_name = split(argument, lyx_name, ' ');
1428 if (lyx_name.empty() || x11_name.empty()) {
1429 setErrorMessage(_("Syntax: set-color <lyx_name>"
1434 bool const graphicsbg_changed =
1435 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1436 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1438 if (!lcolor.setColor(lyx_name, x11_name)) {
1440 bformat(_("Set-color \"%1$s\" failed "
1441 "- color is undefined or "
1442 "may not be redefined"),
1443 from_utf8(lyx_name)));
1447 theApp->updateColor(lcolor.getFromLyXName(lyx_name));
1449 if (graphicsbg_changed) {
1450 #ifdef WITH_WARNINGS
1451 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1454 graphics::GCache::get().changeDisplay(true);
1461 BOOST_ASSERT(lyx_view_);
1462 lyx_view_->message(from_utf8(argument));
1465 case LFUN_EXTERNAL_EDIT: {
1466 BOOST_ASSERT(lyx_view_);
1467 FuncRequest fr(action, argument);
1468 InsetExternal().dispatch(view()->cursor(), fr);
1472 case LFUN_GRAPHICS_EDIT: {
1473 FuncRequest fr(action, argument);
1474 InsetGraphics().dispatch(view()->cursor(), fr);
1478 case LFUN_INSET_APPLY: {
1479 BOOST_ASSERT(lyx_view_);
1480 string const name = cmd.getArg(0);
1481 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1483 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1484 inset->dispatch(view()->cursor(), fr);
1486 FuncRequest fr(LFUN_INSET_INSERT, argument);
1489 // ideally, the update flag should be set by the insets,
1490 // but this is not possible currently
1491 updateFlags = Update::Force | Update::FitCursor;
1495 case LFUN_ALL_INSETS_TOGGLE: {
1496 BOOST_ASSERT(lyx_view_);
1498 string const name = split(argument, action, ' ');
1499 InsetBase::Code const inset_code =
1500 InsetBase::translate(name);
1502 LCursor & cur = view()->cursor();
1503 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1505 InsetBase & inset = lyx_view_->buffer()->inset();
1506 InsetIterator it = inset_iterator_begin(inset);
1507 InsetIterator const end = inset_iterator_end(inset);
1508 for (; it != end; ++it) {
1509 if (inset_code == InsetBase::NO_CODE
1510 || inset_code == it->lyxCode()) {
1511 LCursor tmpcur = cur;
1512 tmpcur.pushLeft(*it);
1513 it->dispatch(tmpcur, fr);
1516 updateFlags = Update::Force | Update::FitCursor;
1520 case LFUN_BUFFER_LANGUAGE: {
1521 BOOST_ASSERT(lyx_view_);
1522 Buffer & buffer = *lyx_view_->buffer();
1523 Language const * oldL = buffer.params().language;
1524 Language const * newL = languages.getLanguage(argument);
1525 if (!newL || oldL == newL)
1528 if (oldL->rightToLeft() == newL->rightToLeft()
1529 && !buffer.isMultiLingual())
1530 buffer.changeLanguage(oldL, newL);
1532 buffer.updateDocLang(newL);
1536 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1537 string const fname =
1538 addName(addPath(package().user_support(), "templates/"),
1540 Buffer defaults(fname);
1542 istringstream ss(argument);
1545 int const unknown_tokens = defaults.readHeader(lex);
1547 if (unknown_tokens != 0) {
1548 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1549 << unknown_tokens << " unknown token"
1550 << (unknown_tokens == 1 ? "" : "s")
1554 if (defaults.writeFile(defaults.fileName()))
1555 // FIXME Should use bformat
1556 setMessage(_("Document defaults saved in ")
1557 + makeDisplayPath(fname));
1559 setErrorMessage(_("Unable to save document defaults"));
1563 case LFUN_BUFFER_PARAMS_APPLY: {
1564 BOOST_ASSERT(lyx_view_);
1565 biblio::CiteEngine const engine =
1566 lyx_view_->buffer()->params().cite_engine;
1568 istringstream ss(argument);
1571 int const unknown_tokens =
1572 lyx_view_->buffer()->readHeader(lex);
1574 if (unknown_tokens != 0) {
1575 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1576 << unknown_tokens << " unknown token"
1577 << (unknown_tokens == 1 ? "" : "s")
1580 if (engine == lyx_view_->buffer()->params().cite_engine)
1583 LCursor & cur = view()->cursor();
1584 FuncRequest fr(LFUN_INSET_REFRESH);
1586 InsetBase & inset = lyx_view_->buffer()->inset();
1587 InsetIterator it = inset_iterator_begin(inset);
1588 InsetIterator const end = inset_iterator_end(inset);
1589 for (; it != end; ++it)
1590 if (it->lyxCode() == InsetBase::CITE_CODE)
1591 it->dispatch(cur, fr);
1595 case LFUN_TEXTCLASS_APPLY: {
1596 BOOST_ASSERT(lyx_view_);
1597 Buffer * buffer = lyx_view_->buffer();
1599 textclass_type const old_class =
1600 buffer->params().textclass;
1602 loadTextclass(argument);
1604 std::pair<bool, textclass_type> const tc_pair =
1605 textclasslist.numberOfClass(argument);
1610 textclass_type const new_class = tc_pair.second;
1611 if (old_class == new_class)
1615 lyx_view_->message(_("Converting document to new document class..."));
1616 recordUndoFullDocument(view());
1617 buffer->params().textclass = new_class;
1618 StableDocIterator backcur(view()->cursor());
1619 ErrorList & el = buffer->errorList("Class Switch");
1620 cap::switchBetweenClasses(
1621 old_class, new_class,
1622 static_cast<InsetText &>(buffer->inset()), el);
1624 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1626 buffer->errors("Class Switch");
1627 updateLabels(*buffer);
1628 updateFlags = Update::Force | Update::FitCursor;
1632 case LFUN_TEXTCLASS_LOAD:
1633 loadTextclass(argument);
1636 case LFUN_LYXRC_APPLY: {
1637 LyXRC const lyxrc_orig = lyxrc;
1639 istringstream ss(argument);
1640 bool const success = lyxrc.read(ss) == 0;
1643 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1644 << "Unable to read lyxrc data"
1649 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1653 case LFUN_WINDOW_NEW:
1654 LyX::ref().newLyXView();
1657 case LFUN_WINDOW_CLOSE:
1658 BOOST_ASSERT(lyx_view_);
1659 BOOST_ASSERT(theApp);
1661 // We return here because lyx_view does not exists anymore.
1665 BOOST_ASSERT(lyx_view_);
1666 view()->cursor().dispatch(cmd);
1667 updateFlags = view()->cursor().result().update();
1668 if (!view()->cursor().result().dispatched())
1669 if (view()->dispatch(cmd))
1670 updateFlags = Update::Force | Update::FitCursor;
1675 if (lyx_view_ && view()->buffer()) {
1676 // Redraw screen unless explicitly told otherwise.
1677 // This also initializes the position cache for all insets
1678 // in (at least partially) visible top-level paragraphs.
1679 bool needSecondUpdate = false;
1680 if (updateFlags != Update::None)
1681 view()->update(updateFlags);
1683 needSecondUpdate = view()->fitCursor();
1685 if (needSecondUpdate || updateFlags != Update::None) {
1686 view()->buffer()->changed();
1688 lyx_view_->updateStatusBar();
1690 // if we executed a mutating lfun, mark the buffer as dirty
1692 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1693 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1694 view()->buffer()->markDirty();
1696 if (view()->cursor().inTexted()) {
1697 lyx_view_->updateLayoutChoice();
1702 // FIXME UNICODE: _() does not support anything but ascii.
1703 // Do we need a to_ascii() method?
1704 sendDispatchMessage(getMessage(), cmd);
1708 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1710 /* When an action did not originate from the UI/kbd, it makes
1711 * sense to avoid updating the GUI. It turns out that this
1712 * fixes bug 1941, for reasons that are described here:
1713 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1715 if (cmd.origin != FuncRequest::INTERNAL) {
1716 lyx_view_->updateMenubar();
1717 lyx_view_->updateToolbars();
1720 const bool verbose = (cmd.origin == FuncRequest::UI
1721 || cmd.origin == FuncRequest::COMMANDBUFFER);
1723 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1724 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1726 lyx_view_->message(msg);
1730 docstring dispatch_msg = msg;
1731 if (!dispatch_msg.empty())
1732 dispatch_msg += ' ';
1734 string comname = lyxaction.getActionName(cmd.action);
1736 bool argsadded = false;
1738 if (!cmd.argument().empty()) {
1739 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1740 comname += ' ' + to_utf8(cmd.argument());
1745 string const shortcuts = theTopLevelKeymap().printbindings(cmd);
1747 if (!shortcuts.empty())
1748 comname += ": " + shortcuts;
1749 else if (!argsadded && !cmd.argument().empty())
1750 comname += ' ' + to_utf8(cmd.argument());
1752 if (!comname.empty()) {
1753 comname = rtrim(comname);
1754 dispatch_msg += from_utf8('(' + rtrim(comname) + ')');
1757 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1758 << to_utf8(dispatch_msg) << endl;
1759 if (!dispatch_msg.empty())
1760 lyx_view_->message(dispatch_msg);
1764 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1766 string initpath = lyxrc.document_path;
1767 string filename(name);
1769 if (view()->buffer()) {
1770 string const trypath = lyx_view_->buffer()->filePath();
1771 // If directory is writeable, use this as default.
1772 if (isDirWriteable(trypath))
1776 static int newfile_number;
1778 if (filename.empty()) {
1779 filename = addName(lyxrc.document_path,
1780 "newfile" + convert<string>(++newfile_number) + ".lyx");
1781 while (theBufferList().exists(filename) || fs::is_readable(filename)) {
1783 filename = addName(lyxrc.document_path,
1784 "newfile" + convert<string>(newfile_number) +
1789 // The template stuff
1792 FileDialog fileDlg(_("Select template file"),
1793 LFUN_SELECT_FILE_SYNC,
1794 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1795 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1797 FileDialog::Result result =
1798 fileDlg.open(from_utf8(lyxrc.template_path),
1799 FileFilterList(_("LyX Documents (*.lyx)")),
1802 if (result.first == FileDialog::Later)
1804 if (result.second.empty())
1806 templname = to_utf8(result.second);
1809 Buffer * const b = newFile(filename, templname, !name.empty());
1811 lyx_view_->setBuffer(b);
1815 void LyXFunc::open(string const & fname)
1817 string initpath = lyxrc.document_path;
1819 if (view()->buffer()) {
1820 string const trypath = lyx_view_->buffer()->filePath();
1821 // If directory is writeable, use this as default.
1822 if (isDirWriteable(trypath))
1828 if (fname.empty()) {
1829 FileDialog fileDlg(_("Select document to open"),
1831 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1832 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1834 FileDialog::Result result =
1835 fileDlg.open(from_utf8(initpath),
1836 FileFilterList(_("LyX Documents (*.lyx)")),
1839 if (result.first == FileDialog::Later)
1842 filename = to_utf8(result.second);
1844 // check selected filename
1845 if (filename.empty()) {
1846 lyx_view_->message(_("Canceled."));
1852 // get absolute path of file and add ".lyx" to the filename if
1854 string const fullpath = fileSearch(string(), filename, "lyx");
1855 if (!fullpath.empty()) {
1856 filename = fullpath;
1859 docstring const disp_fn = makeDisplayPath(filename);
1861 // if the file doesn't exist, let the user create one
1862 if (!fs::exists(filename)) {
1863 // the user specifically chose this name. Believe him.
1864 Buffer * const b = newFile(filename, string(), true);
1866 lyx_view_->setBuffer(b);
1870 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1873 if (lyx_view_->loadLyXFile(filename)) {
1874 str2 = bformat(_("Document %1$s opened."), disp_fn);
1876 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1878 lyx_view_->message(str2);
1882 void LyXFunc::doImport(string const & argument)
1885 string filename = split(argument, format, ' ');
1887 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1888 << " file: " << filename << endl;
1890 // need user interaction
1891 if (filename.empty()) {
1892 string initpath = lyxrc.document_path;
1894 if (view()->buffer()) {
1895 string const trypath = lyx_view_->buffer()->filePath();
1896 // If directory is writeable, use this as default.
1897 if (isDirWriteable(trypath))
1901 docstring const text = bformat(_("Select %1$s file to import"),
1902 formats.prettyName(format));
1904 FileDialog fileDlg(text,
1906 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1907 make_pair(_("Examples|#E#e"),
1908 from_utf8(addPath(package().system_support(), "examples"))));
1910 docstring filter = formats.prettyName(format);
1913 filter += from_utf8(formats.extension(format));
1916 FileDialog::Result result =
1917 fileDlg.open(from_utf8(initpath),
1918 FileFilterList(filter),
1921 if (result.first == FileDialog::Later)
1924 filename = to_utf8(result.second);
1926 // check selected filename
1927 if (filename.empty())
1928 lyx_view_->message(_("Canceled."));
1931 if (filename.empty())
1934 // get absolute path of file
1935 filename = makeAbsPath(filename);
1937 string const lyxfile = changeExtension(filename, ".lyx");
1939 // Check if the document already is open
1940 if (use_gui && theBufferList().exists(lyxfile)) {
1941 if (!theBufferList().close(theBufferList().getBuffer(lyxfile), true)) {
1942 lyx_view_->message(_("Canceled."));
1947 // if the file exists already, and we didn't do
1948 // -i lyx thefile.lyx, warn
1949 if (fs::exists(lyxfile) && filename != lyxfile) {
1950 docstring const file = makeDisplayPath(lyxfile, 30);
1952 docstring text = bformat(_("The document %1$s already exists.\n\n"
1953 "Do you want to over-write that document?"), file);
1954 int const ret = Alert::prompt(_("Over-write document?"),
1955 text, 0, 1, _("&Over-write"), _("&Cancel"));
1958 lyx_view_->message(_("Canceled."));
1963 ErrorList errorList;
1964 Importer::Import(lyx_view_, filename, format, errorList);
1965 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1969 void LyXFunc::closeBuffer()
1971 // save current cursor position
1972 LyX::ref().session().lastFilePos().save(lyx_view_->buffer()->fileName(),
1973 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1974 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
1975 if (theBufferList().empty()) {
1976 // need this otherwise SEGV may occur while
1977 // trying to set variables that don't exist
1978 // since there's no current buffer
1979 lyx_view_->getDialogs().hideBufferDependent();
1981 lyx_view_->setBuffer(theBufferList().first());
1987 // Each "lyx_view_" should have it's own message method. lyxview and
1988 // the minibuffer would use the minibuffer, but lyxserver would
1989 // send an ERROR signal to its client. Alejandro 970603
1990 // This function is bit problematic when it comes to NLS, to make the
1991 // lyx servers client be language indepenent we must not translate
1992 // strings sent to this func.
1993 void LyXFunc::setErrorMessage(docstring const & m) const
1995 dispatch_buffer = m;
2000 void LyXFunc::setMessage(docstring const & m) const
2002 dispatch_buffer = m;
2006 string const LyXFunc::viewStatusMessage()
2008 // When meta-fake key is pressed, show the key sequence so far + "M-".
2010 return keyseq->print() + "M-";
2012 // Else, when a non-complete key sequence is pressed,
2013 // show the available options.
2014 if (keyseq->length() > 0 && !keyseq->deleted())
2015 return keyseq->printOptions();
2017 if (!view()->buffer())
2018 return to_utf8(_("Welcome to LyX!"));
2020 return view()->cursor().currentState();
2024 BufferView * LyXFunc::view() const
2026 BOOST_ASSERT(lyx_view_);
2027 return lyx_view_->view();
2031 bool LyXFunc::wasMetaKey() const
2033 return (meta_fake_bit != key_modifier::none);
2039 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2041 // Why the switch you might ask. It is a trick to ensure that all
2042 // the elements in the LyXRCTags enum is handled. As you can see
2043 // there are no breaks at all. So it is just a huge fall-through.
2044 // The nice thing is that we will get a warning from the compiler
2045 // if we forget an element.
2046 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2048 case LyXRC::RC_ACCEPT_COMPOUND:
2049 case LyXRC::RC_ALT_LANG:
2050 case LyXRC::RC_ASCIIROFF_COMMAND:
2051 case LyXRC::RC_ASCII_LINELEN:
2052 case LyXRC::RC_AUTOREGIONDELETE:
2053 case LyXRC::RC_AUTORESET_OPTIONS:
2054 case LyXRC::RC_AUTOSAVE:
2055 case LyXRC::RC_AUTO_NUMBER:
2056 case LyXRC::RC_BACKUPDIR_PATH:
2057 case LyXRC::RC_BIBTEX_COMMAND:
2058 case LyXRC::RC_BINDFILE:
2059 case LyXRC::RC_CHECKLASTFILES:
2060 case LyXRC::RC_USELASTFILEPOS:
2061 case LyXRC::RC_LOADSESSION:
2062 case LyXRC::RC_CHKTEX_COMMAND:
2063 case LyXRC::RC_CONVERTER:
2064 case LyXRC::RC_COPIER:
2065 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2066 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2067 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2068 case LyXRC::RC_DATE_INSERT_FORMAT:
2069 case LyXRC::RC_DEFAULT_LANGUAGE:
2070 case LyXRC::RC_DEFAULT_PAPERSIZE:
2071 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2072 case LyXRC::RC_DISPLAY_GRAPHICS:
2073 case LyXRC::RC_DOCUMENTPATH:
2074 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2075 if (fs::exists(lyxrc_new.document_path) &&
2076 fs::is_directory(lyxrc_new.document_path)) {
2077 support::package().document_dir() = lyxrc.document_path;
2080 case LyXRC::RC_ESC_CHARS:
2081 case LyXRC::RC_FONT_ENCODING:
2082 case LyXRC::RC_FORMAT:
2083 case LyXRC::RC_INDEX_COMMAND:
2084 case LyXRC::RC_INPUT:
2085 case LyXRC::RC_KBMAP:
2086 case LyXRC::RC_KBMAP_PRIMARY:
2087 case LyXRC::RC_KBMAP_SECONDARY:
2088 case LyXRC::RC_LABEL_INIT_LENGTH:
2089 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2090 case LyXRC::RC_LANGUAGE_AUTO_END:
2091 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2092 case LyXRC::RC_LANGUAGE_COMMAND_END:
2093 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2094 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2095 case LyXRC::RC_LANGUAGE_PACKAGE:
2096 case LyXRC::RC_LANGUAGE_USE_BABEL:
2097 case LyXRC::RC_MAKE_BACKUP:
2098 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2099 case LyXRC::RC_NUMLASTFILES:
2100 case LyXRC::RC_PATH_PREFIX:
2101 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2102 support::prependEnvPath("PATH", lyxrc.path_prefix);
2104 case LyXRC::RC_PERS_DICT:
2105 case LyXRC::RC_POPUP_BOLD_FONT:
2106 case LyXRC::RC_POPUP_FONT_ENCODING:
2107 case LyXRC::RC_POPUP_NORMAL_FONT:
2108 case LyXRC::RC_PREVIEW:
2109 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2110 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2111 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2112 case LyXRC::RC_PRINTCOPIESFLAG:
2113 case LyXRC::RC_PRINTER:
2114 case LyXRC::RC_PRINTEVENPAGEFLAG:
2115 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2116 case LyXRC::RC_PRINTFILEEXTENSION:
2117 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2118 case LyXRC::RC_PRINTODDPAGEFLAG:
2119 case LyXRC::RC_PRINTPAGERANGEFLAG:
2120 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2121 case LyXRC::RC_PRINTPAPERFLAG:
2122 case LyXRC::RC_PRINTREVERSEFLAG:
2123 case LyXRC::RC_PRINTSPOOL_COMMAND:
2124 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2125 case LyXRC::RC_PRINTTOFILE:
2126 case LyXRC::RC_PRINTTOPRINTER:
2127 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2128 case LyXRC::RC_PRINT_COMMAND:
2129 case LyXRC::RC_RTL_SUPPORT:
2130 case LyXRC::RC_SCREEN_DPI:
2131 case LyXRC::RC_SCREEN_FONT_ENCODING:
2132 case LyXRC::RC_SCREEN_FONT_ROMAN:
2133 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2134 case LyXRC::RC_SCREEN_FONT_SANS:
2135 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2136 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2137 case LyXRC::RC_SCREEN_FONT_SIZES:
2138 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2139 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2140 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2141 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2142 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2143 case LyXRC::RC_SCREEN_ZOOM:
2144 case LyXRC::RC_SERVERPIPE:
2145 case LyXRC::RC_SET_COLOR:
2146 case LyXRC::RC_SHOW_BANNER:
2147 case LyXRC::RC_SPELL_COMMAND:
2148 case LyXRC::RC_TEMPDIRPATH:
2149 case LyXRC::RC_TEMPLATEPATH:
2150 case LyXRC::RC_TEX_ALLOWS_SPACES:
2151 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2152 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2153 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2155 case LyXRC::RC_UIFILE:
2156 case LyXRC::RC_USER_EMAIL:
2157 case LyXRC::RC_USER_NAME:
2158 case LyXRC::RC_USETEMPDIR:
2159 case LyXRC::RC_USE_ALT_LANG:
2160 case LyXRC::RC_USE_ESC_CHARS:
2161 case LyXRC::RC_USE_INP_ENC:
2162 case LyXRC::RC_USE_PERS_DICT:
2163 case LyXRC::RC_USE_SPELL_LIB:
2164 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2165 case LyXRC::RC_VIEWER:
2166 case LyXRC::RC_LAST: