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/LyXKeySym.h"
82 #include "frontends/LyXView.h"
83 #include "frontends/Menubar.h"
84 #include "frontends/Toolbars.h"
86 #include "support/environment.h"
87 #include "support/filefilterlist.h"
88 #include "support/filetools.h"
89 #include "support/forkedcontr.h"
90 #include "support/fs_extras.h"
91 #include "support/lstrings.h"
92 #include "support/path.h"
93 #include "support/package.h"
94 #include "support/systemcall.h"
95 #include "support/convert.h"
96 #include "support/os.h"
98 #include <boost/current_function.hpp>
99 #include <boost/filesystem/operations.hpp>
106 using bv_funcs::freefont2string;
108 using support::absolutePath;
109 using support::addName;
110 using support::addPath;
111 using support::bformat;
112 using support::changeExtension;
113 using support::contains;
114 using support::FileFilterList;
115 using support::fileSearch;
116 using support::ForkedcallsController;
117 using support::i18nLibFileSearch;
118 using support::isDirWriteable;
119 using support::isFileReadable;
120 using support::isStrInt;
121 using support::makeAbsPath;
122 using support::makeDisplayPath;
123 using support::package;
124 using support::quoteName;
125 using support::rtrim;
126 using support::split;
127 using support::subst;
128 using support::Systemcall;
129 using support::token;
131 using support::prefixIs;
134 using std::make_pair;
137 using std::istringstream;
138 using std::ostringstream;
140 namespace Alert = frontend::Alert;
141 namespace fs = boost::filesystem;
145 extern tex_accent_struct get_accent(kb_action action);
150 bool getLocalStatus(LCursor cursor,
151 FuncRequest const & cmd, FuncStatus & status)
153 // Try to fix cursor in case it is broken.
154 cursor.fixIfBroken();
156 // This is, of course, a mess. Better create a new doc iterator and use
157 // this in Inset::getStatus. This might require an additional
158 // BufferView * arg, though (which should be avoided)
159 //LCursor safe = *this;
161 for ( ; cursor.depth(); cursor.pop()) {
162 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
163 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
164 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
165 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
167 // The inset's getStatus() will return 'true' if it made
168 // a definitive decision on whether it want to handle the
169 // request or not. The result of this decision is put into
170 // the 'status' parameter.
171 if (cursor.inset().getStatus(cursor, cmd, status)) {
180 /** Return the change status at cursor position, taking in account the
181 * status at each level of the document iterator (a table in a deleted
182 * footnote is deleted).
183 * When \param outer is true, the top slice is not looked at.
185 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
187 size_t const depth = dit.depth() - (outer ? 1 : 0);
189 for (size_t i = 0 ; i < depth ; ++i) {
190 CursorSlice const & slice = dit[i];
191 if (!slice.inset().inMathed()
192 && slice.pos() < slice.paragraph().size()) {
193 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
194 if (ch != Change::UNCHANGED)
198 return Change::UNCHANGED;
206 meta_fake_bit(key_modifier::none)
211 void LyXFunc::initKeySequences(kb_keymap * kb)
213 keyseq.reset(new kb_sequence(kb, kb));
214 cancel_meta_seq.reset(new kb_sequence(kb, kb));
218 void LyXFunc::setLyXView(LyXView * lv)
224 void LyXFunc::handleKeyFunc(kb_action action)
226 char c = encoded_last_key;
228 if (keyseq->length()) {
232 lyx_view_->view()->getIntl().getTransManager()
233 .deadkey(c, get_accent(action).accent, view()->getLyXText());
234 // Need to clear, in case the minibuffer calls these
237 // copied verbatim from do_accent_char
238 view()->cursor().resetAnchor();
243 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
245 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
247 // Do nothing if we have nothing (JMarc)
248 if (!keysym->isOK()) {
249 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
254 if (keysym->isModifier()) {
255 lyxerr[Debug::KEY] << "isModifier true" << endl;
259 //Encoding const * encoding = view()->cursor().getEncoding();
260 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
261 size_t encoded_last_key = keysym->getUCSEncoded();
263 // Do a one-deep top-level lookup for
264 // cancel and meta-fake keys. RVDK_PATCH_5
265 cancel_meta_seq->reset();
267 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
268 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
269 << " action first set to [" << func.action << ']'
272 // When not cancel or meta-fake, do the normal lookup.
273 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
274 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
275 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
276 // remove Caps Lock and Mod2 as a modifiers
277 func = keyseq->addkey(keysym, (state | meta_fake_bit));
278 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
279 << "action now set to ["
280 << func.action << ']' << endl;
283 // Dont remove this unless you know what you are doing.
284 meta_fake_bit = key_modifier::none;
286 // Can this happen now ?
287 if (func.action == LFUN_NOACTION) {
288 func = FuncRequest(LFUN_COMMAND_PREFIX);
291 if (lyxerr.debugging(Debug::KEY)) {
292 lyxerr << BOOST_CURRENT_FUNCTION
294 << func.action << "]["
295 << keyseq->print() << ']'
299 // already here we know if it any point in going further
300 // why not return already here if action == -1 and
301 // num_bytes == 0? (Lgb)
303 if (keyseq->length() > 1) {
304 lyx_view_->message(from_utf8(keyseq->print()));
308 // Maybe user can only reach the key via holding down shift.
309 // Let's see. But only if shift is the only modifier
310 if (func.action == LFUN_UNKNOWN_ACTION &&
311 state == key_modifier::shift) {
312 lyxerr[Debug::KEY] << "Trying without shift" << endl;
313 func = keyseq->addkey(keysym, key_modifier::none);
314 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
317 if (func.action == LFUN_UNKNOWN_ACTION) {
318 // Hmm, we didn't match any of the keysequences. See
319 // if it's normal insertable text not already covered
321 if (keysym->isText() && keyseq->length() == 1) {
322 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
323 func = FuncRequest(LFUN_SELF_INSERT,
324 FuncRequest::KEYBOARD);
326 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
327 lyx_view_->message(_("Unknown function."));
332 if (func.action == LFUN_SELF_INSERT) {
333 if (encoded_last_key != 0) {
334 docstring const arg(1, encoded_last_key);
335 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
336 FuncRequest::KEYBOARD));
338 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
346 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
348 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
350 LCursor & cur = view()->cursor();
352 /* In LyX/Mac, when a dialog is open, the menus of the
353 application can still be accessed without giving focus to
354 the main window. In this case, we want to disable the menu
355 entries that are buffer-related.
357 Note that this code is not perfect, as bug 1941 attests:
358 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
361 if (cmd.origin == FuncRequest::UI && !lyx_view_->hasFocus())
364 buf = lyx_view_->buffer();
366 if (cmd.action == LFUN_NOACTION) {
367 flag.message(from_utf8(N_("Nothing to do")));
372 switch (cmd.action) {
373 case LFUN_UNKNOWN_ACTION:
374 #ifndef HAVE_LIBAIKSAURUS
375 case LFUN_THESAURUS_ENTRY:
385 if (flag.unknown()) {
386 flag.message(from_utf8(N_("Unknown action")));
390 if (!flag.enabled()) {
391 if (flag.message().empty())
392 flag.message(from_utf8(N_("Command disabled")));
396 // Check whether we need a buffer
397 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
399 flag.message(from_utf8(N_("Command not allowed with"
400 "out any document open")));
405 // I would really like to avoid having this switch and rather try to
406 // encode this in the function itself.
407 // -- And I'd rather let an inset decide which LFUNs it is willing
408 // to handle (Andre')
410 switch (cmd.action) {
411 case LFUN_BUFFER_TOGGLE_READ_ONLY:
412 flag.setOnOff(buf->isReadonly());
415 case LFUN_BUFFER_SWITCH:
416 // toggle on the current buffer, but do not toggle off
417 // the other ones (is that a good idea?)
418 if (to_utf8(cmd.argument()) == buf->fileName())
422 case LFUN_BUFFER_EXPORT:
423 enable = cmd.argument() == "custom"
424 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
427 case LFUN_BUFFER_CHKTEX:
428 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
431 case LFUN_BUILD_PROGRAM:
432 enable = Exporter::isExportable(*buf, "program");
435 case LFUN_LAYOUT_TABULAR:
436 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
440 case LFUN_LAYOUT_PARAGRAPH:
441 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
444 case LFUN_VC_REGISTER:
445 enable = !buf->lyxvc().inUse();
447 case LFUN_VC_CHECK_IN:
448 enable = buf->lyxvc().inUse() && !buf->isReadonly();
450 case LFUN_VC_CHECK_OUT:
451 enable = buf->lyxvc().inUse() && buf->isReadonly();
454 case LFUN_VC_UNDO_LAST:
455 enable = buf->lyxvc().inUse();
457 case LFUN_BUFFER_RELOAD:
458 enable = !buf->isUnnamed() && !buf->isClean();
461 case LFUN_INSET_SETTINGS: {
465 InsetBase::Code code = cur.inset().lyxCode();
467 case InsetBase::TABULAR_CODE:
468 enable = cmd.argument() == "tabular";
470 case InsetBase::ERT_CODE:
471 enable = cmd.argument() == "ert";
473 case InsetBase::FLOAT_CODE:
474 enable = cmd.argument() == "float";
476 case InsetBase::WRAP_CODE:
477 enable = cmd.argument() == "wrap";
479 case InsetBase::NOTE_CODE:
480 enable = cmd.argument() == "note";
482 case InsetBase::BRANCH_CODE:
483 enable = cmd.argument() == "branch";
485 case InsetBase::BOX_CODE:
486 enable = cmd.argument() == "box";
494 case LFUN_INSET_APPLY: {
495 string const name = cmd.getArg(0);
496 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
498 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
500 bool const success = inset->getStatus(cur, fr, fs);
501 // Every inset is supposed to handle this
502 BOOST_ASSERT(success);
505 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
506 flag |= getStatus(fr);
508 enable = flag.enabled();
512 case LFUN_DIALOG_SHOW: {
513 string const name = cmd.getArg(0);
515 enable = name == "aboutlyx"
519 || name == "texinfo";
520 else if (name == "print")
521 enable = Exporter::isExportable(*buf, "dvi")
522 && lyxrc.print_command != "none";
523 else if (name == "character" || name == "mathpanel")
524 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
525 else if (name == "latexlog")
526 enable = isFileReadable(buf->getLogName().second);
527 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
528 else if (name == "spellchecker")
531 else if (name == "vclog")
532 enable = buf->lyxvc().inUse();
533 else if (name == "view-source")
538 case LFUN_DIALOG_SHOW_NEW_INSET:
539 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
542 case LFUN_DIALOG_UPDATE: {
543 string const name = cmd.getArg(0);
545 enable = name == "prefs";
549 case LFUN_CITATION_INSERT: {
550 FuncRequest fr(LFUN_INSET_INSERT, "citation");
551 enable = getStatus(fr).enabled();
555 case LFUN_BUFFER_WRITE: {
556 enable = view()->buffer()->isUnnamed()
557 || !view()->buffer()->isClean();
561 // this one is difficult to get right. As a half-baked
562 // solution, we consider only the first action of the sequence
563 case LFUN_COMMAND_SEQUENCE: {
564 // argument contains ';'-terminated commands
565 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
566 FuncRequest func(lyxaction.lookupFunc(firstcmd));
567 func.origin = cmd.origin;
568 flag = getStatus(func);
571 case LFUN_BUFFER_NEW:
572 case LFUN_BUFFER_NEW_TEMPLATE:
573 case LFUN_WORD_FIND_FORWARD:
574 case LFUN_WORD_FIND_BACKWARD:
575 case LFUN_COMMAND_PREFIX:
576 case LFUN_COMMAND_EXECUTE:
578 case LFUN_META_PREFIX:
579 case LFUN_BUFFER_CLOSE:
580 case LFUN_BUFFER_WRITE_AS:
581 case LFUN_BUFFER_UPDATE:
582 case LFUN_BUFFER_VIEW:
583 case LFUN_BUFFER_IMPORT:
586 case LFUN_BUFFER_AUTO_SAVE:
587 case LFUN_RECONFIGURE:
591 case LFUN_DROP_LAYOUTS_CHOICE:
593 case LFUN_SERVER_GET_NAME:
594 case LFUN_SERVER_NOTIFY:
595 case LFUN_SERVER_GOTO_FILE_ROW:
596 case LFUN_DIALOG_HIDE:
597 case LFUN_DIALOG_DISCONNECT_INSET:
598 case LFUN_BUFFER_CHILD_OPEN:
599 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
600 case LFUN_KEYMAP_OFF:
601 case LFUN_KEYMAP_PRIMARY:
602 case LFUN_KEYMAP_SECONDARY:
603 case LFUN_KEYMAP_TOGGLE:
605 case LFUN_BUFFER_EXPORT_CUSTOM:
606 case LFUN_BUFFER_PRINT:
607 case LFUN_PREFERENCES_SAVE:
608 case LFUN_SCREEN_FONT_UPDATE:
611 case LFUN_EXTERNAL_EDIT:
612 case LFUN_GRAPHICS_EDIT:
613 case LFUN_ALL_INSETS_TOGGLE:
614 case LFUN_BUFFER_LANGUAGE:
615 case LFUN_TEXTCLASS_APPLY:
616 case LFUN_TEXTCLASS_LOAD:
617 case LFUN_BUFFER_SAVE_AS_DEFAULT:
618 case LFUN_BUFFER_PARAMS_APPLY:
619 case LFUN_LYXRC_APPLY:
620 case LFUN_BUFFER_NEXT:
621 case LFUN_BUFFER_PREVIOUS:
622 // these are handled in our dispatch()
626 if (!getLocalStatus(cur, cmd, flag))
627 flag = view()->getStatus(cmd);
633 // Can we use a readonly buffer?
634 if (buf && buf->isReadonly()
635 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
636 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
637 flag.message(from_utf8(N_("Document is read-only")));
641 // Are we in a DELETED change-tracking region?
642 if (buf && lookupChangeType(cur, true) == Change::DELETED
643 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
644 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
645 flag.message(from_utf8(N_("This portion of the document is deleted.")));
649 // the default error message if we disable the command
650 if (!flag.enabled() && flag.message().empty())
651 flag.message(from_utf8(N_("Command disabled")));
657 bool LyXFunc::ensureBufferClean(BufferView * bv)
659 Buffer & buf = *bv->buffer();
663 docstring const file = makeDisplayPath(buf.fileName(), 30);
664 docstring text = bformat(_("The document %1$s has unsaved "
665 "changes.\n\nDo you want to save "
666 "the document?"), file);
667 int const ret = Alert::prompt(_("Save changed document?"),
668 text, 0, 1, _("&Save"),
672 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
674 return buf.isClean();
680 void showPrintError(string const & name)
682 docstring str = bformat(_("Could not print the document %1$s.\n"
683 "Check that your printer is set up correctly."),
684 makeDisplayPath(name, 50));
685 Alert::error(_("Print document failed"), str);
689 void loadTextclass(string const & name)
691 std::pair<bool, textclass_type> const tc_pair =
692 textclasslist.numberOfClass(name);
694 if (!tc_pair.first) {
695 lyxerr << "Document class \"" << name
696 << "\" does not exist."
701 textclass_type const tc = tc_pair.second;
703 if (!textclasslist[tc].load()) {
704 docstring s = bformat(_("The document could not be converted\n"
705 "into the document class %1$s."),
706 from_utf8(textclasslist[tc].name()));
707 Alert::error(_("Could not change class"), s);
712 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
717 void LyXFunc::dispatch(FuncRequest const & cmd)
719 BOOST_ASSERT(view());
720 string const argument = to_utf8(cmd.argument());
721 kb_action const action = cmd.action;
723 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
724 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
726 // we have not done anything wrong yet.
728 dispatch_buffer.erase();
730 // redraw the screen at the end (first of the two drawing steps).
731 //This is done unless explicitely requested otherwise
733 // also do the second redrawing step. Only done if requested.
734 bool updateforce = false;
736 FuncStatus const flag = getStatus(cmd);
737 if (!flag.enabled()) {
738 // We cannot use this function here
739 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
740 << lyxaction.getActionName(action)
741 << " [" << action << "] is disabled at this location"
743 setErrorMessage(flag.message());
747 case LFUN_WORD_FIND_FORWARD:
748 case LFUN_WORD_FIND_BACKWARD: {
749 static string last_search;
750 string searched_string;
752 if (!argument.empty()) {
753 last_search = argument;
754 searched_string = argument;
756 searched_string = last_search;
759 if (searched_string.empty())
762 bool const fw = action == LFUN_WORD_FIND_FORWARD;
764 find2string(searched_string, true, false, fw);
765 find(view(), FuncRequest(LFUN_WORD_FIND, data));
769 case LFUN_COMMAND_PREFIX:
770 lyx_view_->message(from_utf8(keyseq->printOptions()));
773 case LFUN_COMMAND_EXECUTE:
774 lyx_view_->getToolbars().display("minibuffer", true);
775 lyx_view_->focus_command_buffer();
780 meta_fake_bit = key_modifier::none;
781 if (view()->buffer())
782 // cancel any selection
783 dispatch(FuncRequest(LFUN_MARK_OFF));
784 setMessage(_("Cancel"));
787 case LFUN_META_PREFIX:
788 meta_fake_bit = key_modifier::alt;
789 setMessage(from_utf8(keyseq->print()));
792 case LFUN_BUFFER_TOGGLE_READ_ONLY:
793 if (lyx_view_->buffer()->lyxvc().inUse())
794 lyx_view_->buffer()->lyxvc().toggleReadOnly();
796 lyx_view_->buffer()->setReadonly(
797 !lyx_view_->buffer()->isReadonly());
800 // --- Menus -----------------------------------------------
801 case LFUN_BUFFER_NEW:
802 menuNew(argument, false);
805 case LFUN_BUFFER_NEW_TEMPLATE:
806 menuNew(argument, true);
809 case LFUN_BUFFER_CLOSE:
813 case LFUN_BUFFER_WRITE:
814 if (!lyx_view_->buffer()->isUnnamed()) {
815 docstring const str = bformat(_("Saving document %1$s..."),
816 makeDisplayPath(lyx_view_->buffer()->fileName()));
817 lyx_view_->message(str);
818 menuWrite(lyx_view_->buffer());
819 lyx_view_->message(str + _(" done."));
821 writeAs(lyx_view_->buffer());
825 case LFUN_BUFFER_WRITE_AS:
826 writeAs(lyx_view_->buffer(), argument);
830 case LFUN_BUFFER_RELOAD: {
831 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
832 docstring text = bformat(_("Any changes will be lost. Are you sure "
833 "you want to revert to the saved version of the document %1$s?"), file);
834 int const ret = Alert::prompt(_("Revert to saved document?"),
835 text, 0, 1, _("&Revert"), _("&Cancel"));
842 case LFUN_BUFFER_UPDATE:
843 Exporter::Export(lyx_view_->buffer(), argument, true);
846 case LFUN_BUFFER_VIEW:
847 Exporter::preview(lyx_view_->buffer(), argument);
850 case LFUN_BUILD_PROGRAM:
851 Exporter::Export(lyx_view_->buffer(), "program", true);
854 case LFUN_BUFFER_CHKTEX:
855 lyx_view_->buffer()->runChktex();
858 case LFUN_BUFFER_EXPORT:
859 if (argument == "custom")
860 lyx_view_->getDialogs().show("sendto");
862 Exporter::Export(lyx_view_->buffer(), argument, false);
866 case LFUN_BUFFER_EXPORT_CUSTOM: {
868 string command = split(argument, format_name, ' ');
869 Format const * format = formats.getFormat(format_name);
871 lyxerr << "Format \"" << format_name
872 << "\" not recognized!"
877 Buffer * buffer = lyx_view_->buffer();
879 // The name of the file created by the conversion process
882 // Output to filename
883 if (format->name() == "lyx") {
884 string const latexname =
885 buffer->getLatexName(false);
886 filename = changeExtension(latexname,
887 format->extension());
888 filename = addName(buffer->temppath(), filename);
890 if (!buffer->writeFile(filename))
894 Exporter::Export(buffer, format_name, true, filename);
897 // Substitute $$FName for filename
898 if (!contains(command, "$$FName"))
899 command = "( " + command + " ) < $$FName";
900 command = subst(command, "$$FName", filename);
902 // Execute the command in the background
904 call.startscript(Systemcall::DontWait, command);
908 case LFUN_BUFFER_PRINT: {
911 string command = split(split(argument, target, ' '),
915 || target_name.empty()
916 || command.empty()) {
917 lyxerr << "Unable to parse \""
918 << argument << '"' << std::endl;
921 if (target != "printer" && target != "file") {
922 lyxerr << "Unrecognized target \""
923 << target << '"' << std::endl;
927 Buffer * buffer = lyx_view_->buffer();
929 if (!Exporter::Export(buffer, "dvi", true)) {
930 showPrintError(buffer->fileName());
934 // Push directory path.
935 string const path = buffer->temppath();
936 support::Path p(path);
938 // there are three cases here:
939 // 1. we print to a file
940 // 2. we print directly to a printer
941 // 3. we print using a spool command (print to file first)
944 string const dviname =
945 changeExtension(buffer->getLatexName(true),
948 if (target == "printer") {
949 if (!lyxrc.print_spool_command.empty()) {
950 // case 3: print using a spool
951 string const psname =
952 changeExtension(dviname,".ps");
953 command += lyxrc.print_to_file
956 + quoteName(dviname);
959 lyxrc.print_spool_command +' ';
960 if (target_name != "default") {
961 command2 += lyxrc.print_spool_printerprefix
965 command2 += quoteName(psname);
967 // If successful, then spool command
968 res = one.startscript(
973 res = one.startscript(
974 Systemcall::DontWait,
977 // case 2: print directly to a printer
978 res = one.startscript(
979 Systemcall::DontWait,
980 command + quoteName(dviname));
984 // case 1: print to a file
985 command += lyxrc.print_to_file
986 + quoteName(makeAbsPath(target_name,
989 + quoteName(dviname);
990 res = one.startscript(Systemcall::DontWait,
995 showPrintError(buffer->fileName());
999 case LFUN_BUFFER_IMPORT:
1004 if (view()->buffer()) {
1005 // save cursor Position for opened files to .lyx/session
1006 LyX::ref().session().saveFilePosition(lyx_view_->buffer()->fileName(),
1007 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1008 // save bookmarks to .lyx/session
1009 view()->saveSavedPositions();
1011 LyX::ref().quit(argument == "force");
1014 case LFUN_TOC_VIEW: {
1015 InsetCommandParams p("tableofcontents");
1016 string const data = InsetCommandMailer::params2string("toc", p);
1017 lyx_view_->getDialogs().show("toc", data, 0);
1021 case LFUN_BUFFER_AUTO_SAVE:
1025 case LFUN_RECONFIGURE:
1026 reconfigure(view());
1029 case LFUN_HELP_OPEN: {
1030 string const arg = argument;
1032 setErrorMessage(_("Missing argument"));
1035 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1036 if (fname.empty()) {
1037 lyxerr << "LyX: unable to find documentation file `"
1038 << arg << "'. Bad installation?" << endl;
1041 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1042 makeDisplayPath(fname)));
1043 lyx_view_->loadLyXFile(fname, false);
1047 // --- version control -------------------------------
1048 case LFUN_VC_REGISTER:
1049 if (!ensureBufferClean(view()))
1051 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1052 lyx_view_->buffer()->lyxvc().registrer();
1057 case LFUN_VC_CHECK_IN:
1058 if (!ensureBufferClean(view()))
1060 if (lyx_view_->buffer()->lyxvc().inUse()
1061 && !lyx_view_->buffer()->isReadonly()) {
1062 lyx_view_->buffer()->lyxvc().checkIn();
1067 case LFUN_VC_CHECK_OUT:
1068 if (!ensureBufferClean(view()))
1070 if (lyx_view_->buffer()->lyxvc().inUse()
1071 && lyx_view_->buffer()->isReadonly()) {
1072 lyx_view_->buffer()->lyxvc().checkOut();
1077 case LFUN_VC_REVERT:
1078 lyx_view_->buffer()->lyxvc().revert();
1082 case LFUN_VC_UNDO_LAST:
1083 lyx_view_->buffer()->lyxvc().undoLast();
1087 // --- buffers ----------------------------------------
1088 case LFUN_BUFFER_SWITCH:
1089 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1092 case LFUN_BUFFER_NEXT:
1093 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1096 case LFUN_BUFFER_PREVIOUS:
1097 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1101 newFile(view(), argument);
1104 case LFUN_FILE_OPEN:
1108 case LFUN_DROP_LAYOUTS_CHOICE:
1109 lyx_view_->getToolbars().openLayoutList();
1112 case LFUN_MENU_OPEN:
1113 lyx_view_->getMenubar().openByName(from_utf8(argument));
1116 // --- lyxserver commands ----------------------------
1117 case LFUN_SERVER_GET_NAME:
1118 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1119 lyxerr[Debug::INFO] << "FNAME["
1120 << lyx_view_->buffer()->fileName()
1124 case LFUN_SERVER_NOTIFY:
1125 dispatch_buffer = from_utf8(keyseq->print());
1126 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1129 case LFUN_SERVER_GOTO_FILE_ROW: {
1132 istringstream is(argument);
1133 is >> file_name >> row;
1134 if (prefixIs(file_name, package().temp_dir())) {
1135 // Needed by inverse dvi search. If it is a file
1136 // in tmpdir, call the apropriated function
1137 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1139 // Must replace extension of the file to be .lyx
1140 // and get full path
1141 string const s = changeExtension(file_name, ".lyx");
1142 // Either change buffer or load the file
1143 if (theBufferList().exists(s)) {
1144 lyx_view_->setBuffer(theBufferList().getBuffer(s));
1146 lyx_view_->loadLyXFile(s);
1150 view()->setCursorFromRow(row);
1153 // see BufferView::center()
1157 case LFUN_DIALOG_SHOW: {
1158 string const name = cmd.getArg(0);
1159 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1161 if (name == "character") {
1162 data = freefont2string();
1164 lyx_view_->getDialogs().show("character", data);
1165 } else if (name == "latexlog") {
1166 pair<Buffer::LogType, string> const logfile =
1167 lyx_view_->buffer()->getLogName();
1168 switch (logfile.first) {
1169 case Buffer::latexlog:
1172 case Buffer::buildlog:
1176 data += LyXLex::quoteString(logfile.second);
1177 lyx_view_->getDialogs().show("log", data);
1178 } else if (name == "vclog") {
1179 string const data = "vc " +
1180 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1181 lyx_view_->getDialogs().show("log", data);
1183 lyx_view_->getDialogs().show(name, data);
1187 case LFUN_DIALOG_SHOW_NEW_INSET: {
1188 string const name = cmd.getArg(0);
1189 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1190 if (name == "bibitem" ||
1197 InsetCommandParams p(name);
1198 data = InsetCommandMailer::params2string(name, p);
1199 } else if (name == "include") {
1200 InsetCommandParams p(data);
1201 data = InsetIncludeMailer::params2string(p);
1202 } else if (name == "box") {
1203 // \c data == "Boxed" || "Frameless" etc
1204 InsetBoxParams p(data);
1205 data = InsetBoxMailer::params2string(p);
1206 } else if (name == "branch") {
1207 InsetBranchParams p;
1208 data = InsetBranchMailer::params2string(p);
1209 } else if (name == "citation") {
1210 InsetCommandParams p("cite");
1211 data = InsetCommandMailer::params2string(name, p);
1212 } else if (name == "ert") {
1213 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1214 } else if (name == "external") {
1215 InsetExternalParams p;
1216 Buffer const & buffer = *lyx_view_->buffer();
1217 data = InsetExternalMailer::params2string(p, buffer);
1218 } else if (name == "float") {
1220 data = InsetFloatMailer::params2string(p);
1221 } else if (name == "graphics") {
1222 InsetGraphicsParams p;
1223 Buffer const & buffer = *lyx_view_->buffer();
1224 data = InsetGraphicsMailer::params2string(p, buffer);
1225 } else if (name == "note") {
1227 data = InsetNoteMailer::params2string(p);
1228 } else if (name == "vspace") {
1230 data = InsetVSpaceMailer::params2string(space);
1231 } else if (name == "wrap") {
1233 data = InsetWrapMailer::params2string(p);
1235 lyx_view_->getDialogs().show(name, data, 0);
1239 case LFUN_DIALOG_UPDATE: {
1240 string const & name = argument;
1241 // Can only update a dialog connected to an existing inset
1242 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1244 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1245 inset->dispatch(view()->cursor(), fr);
1246 } else if (name == "paragraph") {
1247 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1248 } else if (name == "prefs") {
1249 lyx_view_->getDialogs().update(name, string());
1254 case LFUN_DIALOG_HIDE:
1255 Dialogs::hide(argument, 0);
1258 case LFUN_DIALOG_DISCONNECT_INSET:
1259 lyx_view_->getDialogs().disconnect(argument);
1263 case LFUN_CITATION_INSERT: {
1264 if (!argument.empty()) {
1265 // we can have one optional argument, delimited by '|'
1266 // citation-insert <key>|<text_before>
1267 // this should be enhanced to also support text_after
1268 // and citation style
1269 string arg = argument;
1271 if (contains(argument, "|")) {
1272 arg = token(argument, '|', 0);
1273 opt1 = '[' + token(argument, '|', 1) + ']';
1275 std::ostringstream os;
1276 os << "citation LatexCommand\n"
1277 << "\\cite" << opt1 << "{" << arg << "}\n"
1279 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1282 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1286 case LFUN_BUFFER_CHILD_OPEN: {
1287 string const filename =
1288 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1289 // FIXME Should use bformat
1290 setMessage(_("Opening child document ") +
1291 makeDisplayPath(filename) + "...");
1292 view()->savePosition(0);
1293 string const parentfilename = lyx_view_->buffer()->fileName();
1294 if (theBufferList().exists(filename))
1295 lyx_view_->setBuffer(theBufferList().getBuffer(filename));
1297 lyx_view_->loadLyXFile(filename);
1298 // Set the parent name of the child document.
1299 // This makes insertion of citations and references in the child work,
1300 // when the target is in the parent or another child document.
1301 lyx_view_->buffer()->setParentName(parentfilename);
1305 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1306 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1309 case LFUN_KEYMAP_OFF:
1310 lyx_view_->view()->getIntl().keyMapOn(false);
1313 case LFUN_KEYMAP_PRIMARY:
1314 lyx_view_->view()->getIntl().keyMapPrim();
1317 case LFUN_KEYMAP_SECONDARY:
1318 lyx_view_->view()->getIntl().keyMapSec();
1321 case LFUN_KEYMAP_TOGGLE:
1322 lyx_view_->view()->getIntl().toggleKeyMap();
1328 string rest = split(argument, countstr, ' ');
1329 istringstream is(countstr);
1332 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1333 for (int i = 0; i < count; ++i)
1334 dispatch(lyxaction.lookupFunc(rest));
1338 case LFUN_COMMAND_SEQUENCE: {
1339 // argument contains ';'-terminated commands
1340 string arg = argument;
1341 while (!arg.empty()) {
1343 arg = split(arg, first, ';');
1344 FuncRequest func(lyxaction.lookupFunc(first));
1345 func.origin = cmd.origin;
1351 case LFUN_PREFERENCES_SAVE: {
1352 support::Path p(package().user_support());
1353 lyxrc.write("preferences", false);
1357 case LFUN_SCREEN_FONT_UPDATE:
1358 // handle the screen font changes.
1359 lyxrc.set_font_norm_type();
1360 theFontLoader().update();
1361 // All visible buffers will need resize
1365 case LFUN_SET_COLOR: {
1367 string const x11_name = split(argument, lyx_name, ' ');
1368 if (lyx_name.empty() || x11_name.empty()) {
1369 setErrorMessage(_("Syntax: set-color <lyx_name>"
1374 bool const graphicsbg_changed =
1375 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1376 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1378 if (!lcolor.setColor(lyx_name, x11_name)) {
1380 bformat(_("Set-color \"%1$s\" failed "
1381 "- color is undefined or "
1382 "may not be redefined"),
1383 from_utf8(lyx_name)));
1387 theApp->updateColor(lcolor.getFromLyXName(lyx_name));
1389 if (graphicsbg_changed) {
1390 #ifdef WITH_WARNINGS
1391 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1394 graphics::GCache::get().changeDisplay(true);
1401 lyx_view_->message(from_utf8(argument));
1404 case LFUN_EXTERNAL_EDIT: {
1405 FuncRequest fr(action, argument);
1406 InsetExternal().dispatch(view()->cursor(), fr);
1410 case LFUN_GRAPHICS_EDIT: {
1411 FuncRequest fr(action, argument);
1412 InsetGraphics().dispatch(view()->cursor(), fr);
1416 case LFUN_INSET_APPLY: {
1417 string const name = cmd.getArg(0);
1418 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1420 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1421 inset->dispatch(view()->cursor(), fr);
1423 FuncRequest fr(LFUN_INSET_INSERT, argument);
1426 // ideally, the update flag should be set by the insets,
1427 // but this is not possible currently
1432 case LFUN_ALL_INSETS_TOGGLE: {
1434 string const name = split(argument, action, ' ');
1435 InsetBase::Code const inset_code =
1436 InsetBase::translate(name);
1438 LCursor & cur = view()->cursor();
1439 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1441 InsetBase & inset = lyx_view_->buffer()->inset();
1442 InsetIterator it = inset_iterator_begin(inset);
1443 InsetIterator const end = inset_iterator_end(inset);
1444 for (; it != end; ++it) {
1445 if (inset_code == InsetBase::NO_CODE
1446 || inset_code == it->lyxCode()) {
1447 LCursor tmpcur = cur;
1448 tmpcur.pushLeft(*it);
1449 it->dispatch(tmpcur, fr);
1456 case LFUN_BUFFER_LANGUAGE: {
1457 Buffer & buffer = *lyx_view_->buffer();
1458 Language const * oldL = buffer.params().language;
1459 Language const * newL = languages.getLanguage(argument);
1460 if (!newL || oldL == newL)
1463 if (oldL->rightToLeft() == newL->rightToLeft()
1464 && !buffer.isMultiLingual())
1465 buffer.changeLanguage(oldL, newL);
1467 buffer.updateDocLang(newL);
1471 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1472 string const fname =
1473 addName(addPath(package().user_support(), "templates/"),
1475 Buffer defaults(fname);
1477 istringstream ss(argument);
1480 int const unknown_tokens = defaults.readHeader(lex);
1482 if (unknown_tokens != 0) {
1483 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1484 << unknown_tokens << " unknown token"
1485 << (unknown_tokens == 1 ? "" : "s")
1489 if (defaults.writeFile(defaults.fileName()))
1490 // FIXME Should use bformat
1491 setMessage(_("Document defaults saved in ")
1492 + makeDisplayPath(fname));
1494 setErrorMessage(_("Unable to save document defaults"));
1498 case LFUN_BUFFER_PARAMS_APPLY: {
1499 biblio::CiteEngine const engine =
1500 lyx_view_->buffer()->params().cite_engine;
1502 istringstream ss(argument);
1505 int const unknown_tokens =
1506 lyx_view_->buffer()->readHeader(lex);
1508 if (unknown_tokens != 0) {
1509 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1510 << unknown_tokens << " unknown token"
1511 << (unknown_tokens == 1 ? "" : "s")
1514 if (engine == lyx_view_->buffer()->params().cite_engine)
1517 LCursor & cur = view()->cursor();
1518 FuncRequest fr(LFUN_INSET_REFRESH);
1520 InsetBase & inset = lyx_view_->buffer()->inset();
1521 InsetIterator it = inset_iterator_begin(inset);
1522 InsetIterator const end = inset_iterator_end(inset);
1523 for (; it != end; ++it)
1524 if (it->lyxCode() == InsetBase::CITE_CODE)
1525 it->dispatch(cur, fr);
1529 case LFUN_TEXTCLASS_APPLY: {
1530 Buffer * buffer = lyx_view_->buffer();
1532 textclass_type const old_class =
1533 buffer->params().textclass;
1535 loadTextclass(argument);
1537 std::pair<bool, textclass_type> const tc_pair =
1538 textclasslist.numberOfClass(argument);
1543 textclass_type const new_class = tc_pair.second;
1544 if (old_class == new_class)
1548 lyx_view_->message(_("Converting document to new document class..."));
1549 recordUndoFullDocument(view());
1550 buffer->params().textclass = new_class;
1551 StableDocIterator backcur(view()->cursor());
1552 ErrorList & el = buffer->errorList("Class Switch");
1553 cap::switchBetweenClasses(
1554 old_class, new_class,
1555 static_cast<InsetText &>(buffer->inset()), el);
1557 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1559 buffer->errors("Class Switch");
1560 updateLabels(*buffer);
1565 case LFUN_TEXTCLASS_LOAD:
1566 loadTextclass(argument);
1569 case LFUN_LYXRC_APPLY: {
1570 LyXRC const lyxrc_orig = lyxrc;
1572 istringstream ss(argument);
1573 bool const success = lyxrc.read(ss) == 0;
1576 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1577 << "Unable to read lyxrc data"
1582 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1587 view()->cursor().dispatch(cmd);
1588 updateforce |= view()->cursor().result().update();
1589 if (!view()->cursor().result().dispatched())
1590 updateforce |= view()->dispatch(cmd);
1595 if (view()->buffer()) {
1596 // Redraw screen unless explicitly told otherwise.
1597 // This also initializes the position cache for all insets
1598 // in (at least partially) visible top-level paragraphs.
1600 view()->update(Update::FitCursor | Update::Force);
1602 view()->update(Update::FitCursor);
1604 lyx_view_->redrawWorkArea();
1606 // if we executed a mutating lfun, mark the buffer as dirty
1608 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1609 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1610 view()->buffer()->markDirty();
1612 if (view()->cursor().inTexted()) {
1613 lyx_view_->updateLayoutChoice();
1618 // FIXME UNICODE: _() does not support anything but ascii.
1619 // Do we need a to_ascii() method?
1620 sendDispatchMessage(getMessage(), cmd);
1624 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1626 /* When an action did not originate from the UI/kbd, it makes
1627 * sense to avoid updating the GUI. It turns out that this
1628 * fixes bug 1941, for reasons that are described here:
1629 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1631 if (cmd.origin != FuncRequest::INTERNAL) {
1632 lyx_view_->updateMenubar();
1633 lyx_view_->updateToolbars();
1636 const bool verbose = (cmd.origin == FuncRequest::UI
1637 || cmd.origin == FuncRequest::COMMANDBUFFER);
1639 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1640 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1642 lyx_view_->message(msg);
1646 docstring dispatch_msg = msg;
1647 if (!dispatch_msg.empty())
1648 dispatch_msg += ' ';
1650 string comname = lyxaction.getActionName(cmd.action);
1652 bool argsadded = false;
1654 if (!cmd.argument().empty()) {
1655 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1656 comname += ' ' + to_utf8(cmd.argument());
1661 string const shortcuts = theTopLevelKeymap().printbindings(cmd);
1663 if (!shortcuts.empty())
1664 comname += ": " + shortcuts;
1665 else if (!argsadded && !cmd.argument().empty())
1666 comname += ' ' + to_utf8(cmd.argument());
1668 if (!comname.empty()) {
1669 comname = rtrim(comname);
1670 dispatch_msg += from_utf8('(' + rtrim(comname) + ')');
1673 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1674 << to_utf8(dispatch_msg) << endl;
1675 if (!dispatch_msg.empty())
1676 lyx_view_->message(dispatch_msg);
1680 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1682 string initpath = lyxrc.document_path;
1683 string filename(name);
1685 if (view()->buffer()) {
1686 string const trypath = lyx_view_->buffer()->filePath();
1687 // If directory is writeable, use this as default.
1688 if (isDirWriteable(trypath))
1692 static int newfile_number;
1694 if (filename.empty()) {
1695 filename = addName(lyxrc.document_path,
1696 "newfile" + convert<string>(++newfile_number) + ".lyx");
1697 while (theBufferList().exists(filename) || fs::is_readable(filename)) {
1699 filename = addName(lyxrc.document_path,
1700 "newfile" + convert<string>(newfile_number) +
1705 // The template stuff
1708 FileDialog fileDlg(_("Select template file"),
1709 LFUN_SELECT_FILE_SYNC,
1710 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1711 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1713 FileDialog::Result result =
1714 fileDlg.open(from_utf8(lyxrc.template_path),
1715 FileFilterList(_("LyX Documents (*.lyx)")),
1718 if (result.first == FileDialog::Later)
1720 if (result.second.empty())
1722 templname = to_utf8(result.second);
1725 Buffer * const b = newFile(filename, templname, !name.empty());
1727 lyx_view_->setBuffer(b);
1731 void LyXFunc::open(string const & fname)
1733 string initpath = lyxrc.document_path;
1735 if (view()->buffer()) {
1736 string const trypath = lyx_view_->buffer()->filePath();
1737 // If directory is writeable, use this as default.
1738 if (isDirWriteable(trypath))
1744 if (fname.empty()) {
1745 FileDialog fileDlg(_("Select document to open"),
1747 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1748 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1750 FileDialog::Result result =
1751 fileDlg.open(from_utf8(initpath),
1752 FileFilterList(_("LyX Documents (*.lyx)")),
1755 if (result.first == FileDialog::Later)
1758 filename = to_utf8(result.second);
1760 // check selected filename
1761 if (filename.empty()) {
1762 lyx_view_->message(_("Canceled."));
1768 // get absolute path of file and add ".lyx" to the filename if
1770 string const fullpath = fileSearch(string(), filename, "lyx");
1771 if (!fullpath.empty()) {
1772 filename = fullpath;
1775 docstring const disp_fn = makeDisplayPath(filename);
1777 // if the file doesn't exist, let the user create one
1778 if (!fs::exists(filename)) {
1779 // the user specifically chose this name. Believe him.
1780 Buffer * const b = newFile(filename, string(), true);
1782 lyx_view_->setBuffer(b);
1786 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1789 if (lyx_view_->loadLyXFile(filename)) {
1790 str2 = bformat(_("Document %1$s opened."), disp_fn);
1792 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1794 lyx_view_->message(str2);
1798 void LyXFunc::doImport(string const & argument)
1801 string filename = split(argument, format, ' ');
1803 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1804 << " file: " << filename << endl;
1806 // need user interaction
1807 if (filename.empty()) {
1808 string initpath = lyxrc.document_path;
1810 if (view()->buffer()) {
1811 string const trypath = lyx_view_->buffer()->filePath();
1812 // If directory is writeable, use this as default.
1813 if (isDirWriteable(trypath))
1817 docstring const text = bformat(_("Select %1$s file to import"),
1818 formats.prettyName(format));
1820 FileDialog fileDlg(text,
1822 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1823 make_pair(_("Examples|#E#e"),
1824 from_utf8(addPath(package().system_support(), "examples"))));
1826 docstring filter = formats.prettyName(format);
1829 filter += from_utf8(formats.extension(format));
1832 FileDialog::Result result =
1833 fileDlg.open(from_utf8(initpath),
1834 FileFilterList(filter),
1837 if (result.first == FileDialog::Later)
1840 filename = to_utf8(result.second);
1842 // check selected filename
1843 if (filename.empty())
1844 lyx_view_->message(_("Canceled."));
1847 if (filename.empty())
1850 // get absolute path of file
1851 filename = makeAbsPath(filename);
1853 string const lyxfile = changeExtension(filename, ".lyx");
1855 // Check if the document already is open
1856 if (use_gui && theBufferList().exists(lyxfile)) {
1857 if (!theBufferList().close(theBufferList().getBuffer(lyxfile), true)) {
1858 lyx_view_->message(_("Canceled."));
1863 // if the file exists already, and we didn't do
1864 // -i lyx thefile.lyx, warn
1865 if (fs::exists(lyxfile) && filename != lyxfile) {
1866 docstring const file = makeDisplayPath(lyxfile, 30);
1868 docstring text = bformat(_("The document %1$s already exists.\n\n"
1869 "Do you want to over-write that document?"), file);
1870 int const ret = Alert::prompt(_("Over-write document?"),
1871 text, 0, 1, _("&Over-write"), _("&Cancel"));
1874 lyx_view_->message(_("Canceled."));
1879 ErrorList errorList;
1880 Importer::Import(lyx_view_, filename, format, errorList);
1881 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1885 void LyXFunc::closeBuffer()
1887 // save current cursor position
1888 LyX::ref().session().saveFilePosition(lyx_view_->buffer()->fileName(),
1889 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1890 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
1891 if (theBufferList().empty()) {
1892 // need this otherwise SEGV may occur while
1893 // trying to set variables that don't exist
1894 // since there's no current buffer
1895 lyx_view_->getDialogs().hideBufferDependent();
1897 lyx_view_->setBuffer(theBufferList().first());
1903 // Each "lyx_view_" should have it's own message method. lyxview and
1904 // the minibuffer would use the minibuffer, but lyxserver would
1905 // send an ERROR signal to its client. Alejandro 970603
1906 // This function is bit problematic when it comes to NLS, to make the
1907 // lyx servers client be language indepenent we must not translate
1908 // strings sent to this func.
1909 void LyXFunc::setErrorMessage(docstring const & m) const
1911 dispatch_buffer = m;
1916 void LyXFunc::setMessage(docstring const & m) const
1918 dispatch_buffer = m;
1922 string const LyXFunc::viewStatusMessage()
1924 // When meta-fake key is pressed, show the key sequence so far + "M-".
1926 return keyseq->print() + "M-";
1928 // Else, when a non-complete key sequence is pressed,
1929 // show the available options.
1930 if (keyseq->length() > 0 && !keyseq->deleted())
1931 return keyseq->printOptions();
1933 if (!view()->buffer())
1934 return to_utf8(_("Welcome to LyX!"));
1936 return view()->cursor().currentState();
1940 BufferView * LyXFunc::view() const
1942 BOOST_ASSERT(lyx_view_);
1943 return lyx_view_->view();
1947 bool LyXFunc::wasMetaKey() const
1949 return (meta_fake_bit != key_modifier::none);
1955 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1957 // Why the switch you might ask. It is a trick to ensure that all
1958 // the elements in the LyXRCTags enum is handled. As you can see
1959 // there are no breaks at all. So it is just a huge fall-through.
1960 // The nice thing is that we will get a warning from the compiler
1961 // if we forget an element.
1962 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1964 case LyXRC::RC_ACCEPT_COMPOUND:
1965 case LyXRC::RC_ALT_LANG:
1966 case LyXRC::RC_ASCIIROFF_COMMAND:
1967 case LyXRC::RC_ASCII_LINELEN:
1968 case LyXRC::RC_AUTOREGIONDELETE:
1969 case LyXRC::RC_AUTORESET_OPTIONS:
1970 case LyXRC::RC_AUTOSAVE:
1971 case LyXRC::RC_AUTO_NUMBER:
1972 case LyXRC::RC_BACKUPDIR_PATH:
1973 case LyXRC::RC_BIBTEX_COMMAND:
1974 case LyXRC::RC_BINDFILE:
1975 case LyXRC::RC_CHECKLASTFILES:
1976 case LyXRC::RC_USELASTFILEPOS:
1977 case LyXRC::RC_LOADSESSION:
1978 case LyXRC::RC_CHKTEX_COMMAND:
1979 case LyXRC::RC_CONVERTER:
1980 case LyXRC::RC_COPIER:
1981 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1982 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1983 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1984 case LyXRC::RC_DATE_INSERT_FORMAT:
1985 case LyXRC::RC_DEFAULT_LANGUAGE:
1986 case LyXRC::RC_DEFAULT_PAPERSIZE:
1987 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1988 case LyXRC::RC_DISPLAY_GRAPHICS:
1989 case LyXRC::RC_DOCUMENTPATH:
1990 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1991 if (fs::exists(lyxrc_new.document_path) &&
1992 fs::is_directory(lyxrc_new.document_path)) {
1993 support::package().document_dir() = lyxrc.document_path;
1996 case LyXRC::RC_ESC_CHARS:
1997 case LyXRC::RC_FONT_ENCODING:
1998 case LyXRC::RC_FORMAT:
1999 case LyXRC::RC_INDEX_COMMAND:
2000 case LyXRC::RC_INPUT:
2001 case LyXRC::RC_KBMAP:
2002 case LyXRC::RC_KBMAP_PRIMARY:
2003 case LyXRC::RC_KBMAP_SECONDARY:
2004 case LyXRC::RC_LABEL_INIT_LENGTH:
2005 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2006 case LyXRC::RC_LANGUAGE_AUTO_END:
2007 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2008 case LyXRC::RC_LANGUAGE_COMMAND_END:
2009 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2010 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2011 case LyXRC::RC_LANGUAGE_PACKAGE:
2012 case LyXRC::RC_LANGUAGE_USE_BABEL:
2013 case LyXRC::RC_MAKE_BACKUP:
2014 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2015 case LyXRC::RC_NUMLASTFILES:
2016 case LyXRC::RC_PATH_PREFIX:
2017 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2018 support::prependEnvPath("PATH", lyxrc.path_prefix);
2020 case LyXRC::RC_PERS_DICT:
2021 case LyXRC::RC_POPUP_BOLD_FONT:
2022 case LyXRC::RC_POPUP_FONT_ENCODING:
2023 case LyXRC::RC_POPUP_NORMAL_FONT:
2024 case LyXRC::RC_PREVIEW:
2025 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2026 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2027 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2028 case LyXRC::RC_PRINTCOPIESFLAG:
2029 case LyXRC::RC_PRINTER:
2030 case LyXRC::RC_PRINTEVENPAGEFLAG:
2031 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2032 case LyXRC::RC_PRINTFILEEXTENSION:
2033 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2034 case LyXRC::RC_PRINTODDPAGEFLAG:
2035 case LyXRC::RC_PRINTPAGERANGEFLAG:
2036 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2037 case LyXRC::RC_PRINTPAPERFLAG:
2038 case LyXRC::RC_PRINTREVERSEFLAG:
2039 case LyXRC::RC_PRINTSPOOL_COMMAND:
2040 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2041 case LyXRC::RC_PRINTTOFILE:
2042 case LyXRC::RC_PRINTTOPRINTER:
2043 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2044 case LyXRC::RC_PRINT_COMMAND:
2045 case LyXRC::RC_RTL_SUPPORT:
2046 case LyXRC::RC_SCREEN_DPI:
2047 case LyXRC::RC_SCREEN_FONT_ENCODING:
2048 case LyXRC::RC_SCREEN_FONT_ROMAN:
2049 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2050 case LyXRC::RC_SCREEN_FONT_SANS:
2051 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2052 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2053 case LyXRC::RC_SCREEN_FONT_SIZES:
2054 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2055 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2056 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2057 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2058 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2059 case LyXRC::RC_SCREEN_ZOOM:
2060 case LyXRC::RC_SERVERPIPE:
2061 case LyXRC::RC_SET_COLOR:
2062 case LyXRC::RC_SHOW_BANNER:
2063 case LyXRC::RC_SPELL_COMMAND:
2064 case LyXRC::RC_TEMPDIRPATH:
2065 case LyXRC::RC_TEMPLATEPATH:
2066 case LyXRC::RC_TEX_ALLOWS_SPACES:
2067 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2068 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2069 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2071 case LyXRC::RC_UIFILE:
2072 case LyXRC::RC_USER_EMAIL:
2073 case LyXRC::RC_USER_NAME:
2074 case LyXRC::RC_USETEMPDIR:
2075 case LyXRC::RC_USE_ALT_LANG:
2076 case LyXRC::RC_USE_ESC_CHARS:
2077 case LyXRC::RC_USE_INP_ENC:
2078 case LyXRC::RC_USE_PERS_DICT:
2079 case LyXRC::RC_USE_SPELL_LIB:
2080 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2081 case LyXRC::RC_VIEWER:
2082 case LyXRC::RC_LAST: