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_type c = encoded_last_key;
228 if (keyseq->length())
231 lyx_view_->view()->getIntl().getTransManager()
232 .deadkey(c, get_accent(action).accent, view()->getLyXText());
233 // Need to clear, in case the minibuffer calls these
236 // copied verbatim from do_accent_char
237 view()->cursor().resetAnchor();
242 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
244 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
246 // Do nothing if we have nothing (JMarc)
247 if (!keysym->isOK()) {
248 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
253 if (keysym->isModifier()) {
254 lyxerr[Debug::KEY] << "isModifier true" << endl;
258 //Encoding const * encoding = view()->cursor().getEncoding();
259 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
260 size_t encoded_last_key = keysym->getUCSEncoded();
262 // Do a one-deep top-level lookup for
263 // cancel and meta-fake keys. RVDK_PATCH_5
264 cancel_meta_seq->reset();
266 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
267 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
268 << " action first set to [" << func.action << ']'
271 // When not cancel or meta-fake, do the normal lookup.
272 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
273 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
274 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
275 // remove Caps Lock and Mod2 as a modifiers
276 func = keyseq->addkey(keysym, (state | meta_fake_bit));
277 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
278 << "action now set to ["
279 << func.action << ']' << endl;
282 // Dont remove this unless you know what you are doing.
283 meta_fake_bit = key_modifier::none;
285 // Can this happen now ?
286 if (func.action == LFUN_NOACTION) {
287 func = FuncRequest(LFUN_COMMAND_PREFIX);
290 if (lyxerr.debugging(Debug::KEY)) {
291 lyxerr << BOOST_CURRENT_FUNCTION
293 << func.action << "]["
294 << keyseq->print() << ']'
298 // already here we know if it any point in going further
299 // why not return already here if action == -1 and
300 // num_bytes == 0? (Lgb)
302 if (keyseq->length() > 1) {
303 lyx_view_->message(from_utf8(keyseq->print()));
307 // Maybe user can only reach the key via holding down shift.
308 // Let's see. But only if shift is the only modifier
309 if (func.action == LFUN_UNKNOWN_ACTION &&
310 state == key_modifier::shift) {
311 lyxerr[Debug::KEY] << "Trying without shift" << endl;
312 func = keyseq->addkey(keysym, key_modifier::none);
313 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
316 if (func.action == LFUN_UNKNOWN_ACTION) {
317 // Hmm, we didn't match any of the keysequences. See
318 // if it's normal insertable text not already covered
320 if (keysym->isText() && keyseq->length() == 1) {
321 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
322 func = FuncRequest(LFUN_SELF_INSERT,
323 FuncRequest::KEYBOARD);
325 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
326 lyx_view_->message(_("Unknown function."));
331 if (func.action == LFUN_SELF_INSERT) {
332 if (encoded_last_key != 0) {
333 docstring const arg(1, encoded_last_key);
334 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
335 FuncRequest::KEYBOARD));
337 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
345 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
347 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
349 LCursor & cur = view()->cursor();
351 /* In LyX/Mac, when a dialog is open, the menus of the
352 application can still be accessed without giving focus to
353 the main window. In this case, we want to disable the menu
354 entries that are buffer-related.
356 Note that this code is not perfect, as bug 1941 attests:
357 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
360 if (cmd.origin == FuncRequest::UI && !lyx_view_->hasFocus())
363 buf = lyx_view_->buffer();
365 if (cmd.action == LFUN_NOACTION) {
366 flag.message(from_utf8(N_("Nothing to do")));
371 switch (cmd.action) {
372 case LFUN_UNKNOWN_ACTION:
373 #ifndef HAVE_LIBAIKSAURUS
374 case LFUN_THESAURUS_ENTRY:
384 if (flag.unknown()) {
385 flag.message(from_utf8(N_("Unknown action")));
389 if (!flag.enabled()) {
390 if (flag.message().empty())
391 flag.message(from_utf8(N_("Command disabled")));
395 // Check whether we need a buffer
396 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
398 flag.message(from_utf8(N_("Command not allowed with"
399 "out any document open")));
404 // I would really like to avoid having this switch and rather try to
405 // encode this in the function itself.
406 // -- And I'd rather let an inset decide which LFUNs it is willing
407 // to handle (Andre')
409 switch (cmd.action) {
410 case LFUN_BUFFER_TOGGLE_READ_ONLY:
411 flag.setOnOff(buf->isReadonly());
414 case LFUN_BUFFER_SWITCH:
415 // toggle on the current buffer, but do not toggle off
416 // the other ones (is that a good idea?)
417 if (to_utf8(cmd.argument()) == buf->fileName())
421 case LFUN_BUFFER_EXPORT:
422 enable = cmd.argument() == "custom"
423 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
426 case LFUN_BUFFER_CHKTEX:
427 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
430 case LFUN_BUILD_PROGRAM:
431 enable = Exporter::isExportable(*buf, "program");
434 case LFUN_LAYOUT_TABULAR:
435 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
439 case LFUN_LAYOUT_PARAGRAPH:
440 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
443 case LFUN_VC_REGISTER:
444 enable = !buf->lyxvc().inUse();
446 case LFUN_VC_CHECK_IN:
447 enable = buf->lyxvc().inUse() && !buf->isReadonly();
449 case LFUN_VC_CHECK_OUT:
450 enable = buf->lyxvc().inUse() && buf->isReadonly();
453 case LFUN_VC_UNDO_LAST:
454 enable = buf->lyxvc().inUse();
456 case LFUN_BUFFER_RELOAD:
457 enable = !buf->isUnnamed() && !buf->isClean();
460 case LFUN_INSET_SETTINGS: {
464 InsetBase::Code code = cur.inset().lyxCode();
466 case InsetBase::TABULAR_CODE:
467 enable = cmd.argument() == "tabular";
469 case InsetBase::ERT_CODE:
470 enable = cmd.argument() == "ert";
472 case InsetBase::FLOAT_CODE:
473 enable = cmd.argument() == "float";
475 case InsetBase::WRAP_CODE:
476 enable = cmd.argument() == "wrap";
478 case InsetBase::NOTE_CODE:
479 enable = cmd.argument() == "note";
481 case InsetBase::BRANCH_CODE:
482 enable = cmd.argument() == "branch";
484 case InsetBase::BOX_CODE:
485 enable = cmd.argument() == "box";
493 case LFUN_INSET_APPLY: {
494 string const name = cmd.getArg(0);
495 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
497 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
499 bool const success = inset->getStatus(cur, fr, fs);
500 // Every inset is supposed to handle this
501 BOOST_ASSERT(success);
504 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
505 flag |= getStatus(fr);
507 enable = flag.enabled();
511 case LFUN_DIALOG_SHOW: {
512 string const name = cmd.getArg(0);
514 enable = name == "aboutlyx"
518 || name == "texinfo";
519 else if (name == "print")
520 enable = Exporter::isExportable(*buf, "dvi")
521 && lyxrc.print_command != "none";
522 else if (name == "character" || name == "mathpanel")
523 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
524 else if (name == "latexlog")
525 enable = isFileReadable(buf->getLogName().second);
526 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
527 else if (name == "spellchecker")
530 else if (name == "vclog")
531 enable = buf->lyxvc().inUse();
532 else if (name == "view-source")
537 case LFUN_DIALOG_SHOW_NEW_INSET:
538 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
541 case LFUN_DIALOG_UPDATE: {
542 string const name = cmd.getArg(0);
544 enable = name == "prefs";
548 case LFUN_CITATION_INSERT: {
549 FuncRequest fr(LFUN_INSET_INSERT, "citation");
550 enable = getStatus(fr).enabled();
554 case LFUN_BUFFER_WRITE: {
555 enable = view()->buffer()->isUnnamed()
556 || !view()->buffer()->isClean();
560 // this one is difficult to get right. As a half-baked
561 // solution, we consider only the first action of the sequence
562 case LFUN_COMMAND_SEQUENCE: {
563 // argument contains ';'-terminated commands
564 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
565 FuncRequest func(lyxaction.lookupFunc(firstcmd));
566 func.origin = cmd.origin;
567 flag = getStatus(func);
570 case LFUN_BUFFER_NEW:
571 case LFUN_BUFFER_NEW_TEMPLATE:
572 case LFUN_WORD_FIND_FORWARD:
573 case LFUN_WORD_FIND_BACKWARD:
574 case LFUN_COMMAND_PREFIX:
575 case LFUN_COMMAND_EXECUTE:
577 case LFUN_META_PREFIX:
578 case LFUN_BUFFER_CLOSE:
579 case LFUN_BUFFER_WRITE_AS:
580 case LFUN_BUFFER_UPDATE:
581 case LFUN_BUFFER_VIEW:
582 case LFUN_BUFFER_IMPORT:
585 case LFUN_BUFFER_AUTO_SAVE:
586 case LFUN_RECONFIGURE:
590 case LFUN_DROP_LAYOUTS_CHOICE:
592 case LFUN_SERVER_GET_NAME:
593 case LFUN_SERVER_NOTIFY:
594 case LFUN_SERVER_GOTO_FILE_ROW:
595 case LFUN_DIALOG_HIDE:
596 case LFUN_DIALOG_DISCONNECT_INSET:
597 case LFUN_BUFFER_CHILD_OPEN:
598 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
599 case LFUN_KEYMAP_OFF:
600 case LFUN_KEYMAP_PRIMARY:
601 case LFUN_KEYMAP_SECONDARY:
602 case LFUN_KEYMAP_TOGGLE:
604 case LFUN_BUFFER_EXPORT_CUSTOM:
605 case LFUN_BUFFER_PRINT:
606 case LFUN_PREFERENCES_SAVE:
607 case LFUN_SCREEN_FONT_UPDATE:
610 case LFUN_EXTERNAL_EDIT:
611 case LFUN_GRAPHICS_EDIT:
612 case LFUN_ALL_INSETS_TOGGLE:
613 case LFUN_BUFFER_LANGUAGE:
614 case LFUN_TEXTCLASS_APPLY:
615 case LFUN_TEXTCLASS_LOAD:
616 case LFUN_BUFFER_SAVE_AS_DEFAULT:
617 case LFUN_BUFFER_PARAMS_APPLY:
618 case LFUN_LYXRC_APPLY:
619 case LFUN_BUFFER_NEXT:
620 case LFUN_BUFFER_PREVIOUS:
621 case LFUN_WINDOW_NEW:
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] << endl << "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);
1586 case LFUN_WINDOW_NEW:
1587 BOOST_ASSERT(theApp);
1588 LyX::ref().newLyXView();
1591 view()->cursor().dispatch(cmd);
1593 updateforce |= view()->cursor().result().update();
1594 if (!view()->cursor().result().dispatched())
1595 updateforce |= view()->dispatch(cmd);
1600 if (view()->buffer()) {
1601 // Redraw screen unless explicitly told otherwise.
1602 // This also initializes the position cache for all insets
1603 // in (at least partially) visible top-level paragraphs.
1605 view()->update(Update::FitCursor | Update::Force);
1607 view()->update(Update::FitCursor);
1609 view()->buffer()->changed();
1610 lyx_view_->updateStatusBar();
1612 // if we executed a mutating lfun, mark the buffer as dirty
1614 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1615 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1616 view()->buffer()->markDirty();
1618 if (view()->cursor().inTexted()) {
1619 lyx_view_->updateLayoutChoice();
1624 // FIXME UNICODE: _() does not support anything but ascii.
1625 // Do we need a to_ascii() method?
1626 sendDispatchMessage(getMessage(), cmd);
1630 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1632 /* When an action did not originate from the UI/kbd, it makes
1633 * sense to avoid updating the GUI. It turns out that this
1634 * fixes bug 1941, for reasons that are described here:
1635 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1637 if (cmd.origin != FuncRequest::INTERNAL) {
1638 lyx_view_->updateMenubar();
1639 lyx_view_->updateToolbars();
1642 const bool verbose = (cmd.origin == FuncRequest::UI
1643 || cmd.origin == FuncRequest::COMMANDBUFFER);
1645 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1646 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1648 lyx_view_->message(msg);
1652 docstring dispatch_msg = msg;
1653 if (!dispatch_msg.empty())
1654 dispatch_msg += ' ';
1656 string comname = lyxaction.getActionName(cmd.action);
1658 bool argsadded = false;
1660 if (!cmd.argument().empty()) {
1661 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1662 comname += ' ' + to_utf8(cmd.argument());
1667 string const shortcuts = theTopLevelKeymap().printbindings(cmd);
1669 if (!shortcuts.empty())
1670 comname += ": " + shortcuts;
1671 else if (!argsadded && !cmd.argument().empty())
1672 comname += ' ' + to_utf8(cmd.argument());
1674 if (!comname.empty()) {
1675 comname = rtrim(comname);
1676 dispatch_msg += from_utf8('(' + rtrim(comname) + ')');
1679 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1680 << to_utf8(dispatch_msg) << endl;
1681 if (!dispatch_msg.empty())
1682 lyx_view_->message(dispatch_msg);
1686 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1688 string initpath = lyxrc.document_path;
1689 string filename(name);
1691 if (view()->buffer()) {
1692 string const trypath = lyx_view_->buffer()->filePath();
1693 // If directory is writeable, use this as default.
1694 if (isDirWriteable(trypath))
1698 static int newfile_number;
1700 if (filename.empty()) {
1701 filename = addName(lyxrc.document_path,
1702 "newfile" + convert<string>(++newfile_number) + ".lyx");
1703 while (theBufferList().exists(filename) || fs::is_readable(filename)) {
1705 filename = addName(lyxrc.document_path,
1706 "newfile" + convert<string>(newfile_number) +
1711 // The template stuff
1714 FileDialog fileDlg(_("Select template file"),
1715 LFUN_SELECT_FILE_SYNC,
1716 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1717 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1719 FileDialog::Result result =
1720 fileDlg.open(from_utf8(lyxrc.template_path),
1721 FileFilterList(_("LyX Documents (*.lyx)")),
1724 if (result.first == FileDialog::Later)
1726 if (result.second.empty())
1728 templname = to_utf8(result.second);
1731 Buffer * const b = newFile(filename, templname, !name.empty());
1733 lyx_view_->setBuffer(b);
1737 void LyXFunc::open(string const & fname)
1739 string initpath = lyxrc.document_path;
1741 if (view()->buffer()) {
1742 string const trypath = lyx_view_->buffer()->filePath();
1743 // If directory is writeable, use this as default.
1744 if (isDirWriteable(trypath))
1750 if (fname.empty()) {
1751 FileDialog fileDlg(_("Select document to open"),
1753 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1754 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1756 FileDialog::Result result =
1757 fileDlg.open(from_utf8(initpath),
1758 FileFilterList(_("LyX Documents (*.lyx)")),
1761 if (result.first == FileDialog::Later)
1764 filename = to_utf8(result.second);
1766 // check selected filename
1767 if (filename.empty()) {
1768 lyx_view_->message(_("Canceled."));
1774 // get absolute path of file and add ".lyx" to the filename if
1776 string const fullpath = fileSearch(string(), filename, "lyx");
1777 if (!fullpath.empty()) {
1778 filename = fullpath;
1781 docstring const disp_fn = makeDisplayPath(filename);
1783 // if the file doesn't exist, let the user create one
1784 if (!fs::exists(filename)) {
1785 // the user specifically chose this name. Believe him.
1786 Buffer * const b = newFile(filename, string(), true);
1788 lyx_view_->setBuffer(b);
1792 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1795 if (lyx_view_->loadLyXFile(filename)) {
1796 str2 = bformat(_("Document %1$s opened."), disp_fn);
1798 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1800 lyx_view_->message(str2);
1804 void LyXFunc::doImport(string const & argument)
1807 string filename = split(argument, format, ' ');
1809 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1810 << " file: " << filename << endl;
1812 // need user interaction
1813 if (filename.empty()) {
1814 string initpath = lyxrc.document_path;
1816 if (view()->buffer()) {
1817 string const trypath = lyx_view_->buffer()->filePath();
1818 // If directory is writeable, use this as default.
1819 if (isDirWriteable(trypath))
1823 docstring const text = bformat(_("Select %1$s file to import"),
1824 formats.prettyName(format));
1826 FileDialog fileDlg(text,
1828 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1829 make_pair(_("Examples|#E#e"),
1830 from_utf8(addPath(package().system_support(), "examples"))));
1832 docstring filter = formats.prettyName(format);
1835 filter += from_utf8(formats.extension(format));
1838 FileDialog::Result result =
1839 fileDlg.open(from_utf8(initpath),
1840 FileFilterList(filter),
1843 if (result.first == FileDialog::Later)
1846 filename = to_utf8(result.second);
1848 // check selected filename
1849 if (filename.empty())
1850 lyx_view_->message(_("Canceled."));
1853 if (filename.empty())
1856 // get absolute path of file
1857 filename = makeAbsPath(filename);
1859 string const lyxfile = changeExtension(filename, ".lyx");
1861 // Check if the document already is open
1862 if (use_gui && theBufferList().exists(lyxfile)) {
1863 if (!theBufferList().close(theBufferList().getBuffer(lyxfile), true)) {
1864 lyx_view_->message(_("Canceled."));
1869 // if the file exists already, and we didn't do
1870 // -i lyx thefile.lyx, warn
1871 if (fs::exists(lyxfile) && filename != lyxfile) {
1872 docstring const file = makeDisplayPath(lyxfile, 30);
1874 docstring text = bformat(_("The document %1$s already exists.\n\n"
1875 "Do you want to over-write that document?"), file);
1876 int const ret = Alert::prompt(_("Over-write document?"),
1877 text, 0, 1, _("&Over-write"), _("&Cancel"));
1880 lyx_view_->message(_("Canceled."));
1885 ErrorList errorList;
1886 Importer::Import(lyx_view_, filename, format, errorList);
1887 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1891 void LyXFunc::closeBuffer()
1893 // save current cursor position
1894 LyX::ref().session().saveFilePosition(lyx_view_->buffer()->fileName(),
1895 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1896 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
1897 if (theBufferList().empty()) {
1898 // need this otherwise SEGV may occur while
1899 // trying to set variables that don't exist
1900 // since there's no current buffer
1901 lyx_view_->getDialogs().hideBufferDependent();
1903 lyx_view_->setBuffer(theBufferList().first());
1909 // Each "lyx_view_" should have it's own message method. lyxview and
1910 // the minibuffer would use the minibuffer, but lyxserver would
1911 // send an ERROR signal to its client. Alejandro 970603
1912 // This function is bit problematic when it comes to NLS, to make the
1913 // lyx servers client be language indepenent we must not translate
1914 // strings sent to this func.
1915 void LyXFunc::setErrorMessage(docstring const & m) const
1917 dispatch_buffer = m;
1922 void LyXFunc::setMessage(docstring const & m) const
1924 dispatch_buffer = m;
1928 string const LyXFunc::viewStatusMessage()
1930 // When meta-fake key is pressed, show the key sequence so far + "M-".
1932 return keyseq->print() + "M-";
1934 // Else, when a non-complete key sequence is pressed,
1935 // show the available options.
1936 if (keyseq->length() > 0 && !keyseq->deleted())
1937 return keyseq->printOptions();
1939 if (!view()->buffer())
1940 return to_utf8(_("Welcome to LyX!"));
1942 return view()->cursor().currentState();
1946 BufferView * LyXFunc::view() const
1948 BOOST_ASSERT(lyx_view_);
1949 return lyx_view_->view();
1953 bool LyXFunc::wasMetaKey() const
1955 return (meta_fake_bit != key_modifier::none);
1961 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1963 // Why the switch you might ask. It is a trick to ensure that all
1964 // the elements in the LyXRCTags enum is handled. As you can see
1965 // there are no breaks at all. So it is just a huge fall-through.
1966 // The nice thing is that we will get a warning from the compiler
1967 // if we forget an element.
1968 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1970 case LyXRC::RC_ACCEPT_COMPOUND:
1971 case LyXRC::RC_ALT_LANG:
1972 case LyXRC::RC_ASCIIROFF_COMMAND:
1973 case LyXRC::RC_ASCII_LINELEN:
1974 case LyXRC::RC_AUTOREGIONDELETE:
1975 case LyXRC::RC_AUTORESET_OPTIONS:
1976 case LyXRC::RC_AUTOSAVE:
1977 case LyXRC::RC_AUTO_NUMBER:
1978 case LyXRC::RC_BACKUPDIR_PATH:
1979 case LyXRC::RC_BIBTEX_COMMAND:
1980 case LyXRC::RC_BINDFILE:
1981 case LyXRC::RC_CHECKLASTFILES:
1982 case LyXRC::RC_USELASTFILEPOS:
1983 case LyXRC::RC_LOADSESSION:
1984 case LyXRC::RC_CHKTEX_COMMAND:
1985 case LyXRC::RC_CONVERTER:
1986 case LyXRC::RC_COPIER:
1987 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1988 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1989 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1990 case LyXRC::RC_DATE_INSERT_FORMAT:
1991 case LyXRC::RC_DEFAULT_LANGUAGE:
1992 case LyXRC::RC_DEFAULT_PAPERSIZE:
1993 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1994 case LyXRC::RC_DISPLAY_GRAPHICS:
1995 case LyXRC::RC_DOCUMENTPATH:
1996 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1997 if (fs::exists(lyxrc_new.document_path) &&
1998 fs::is_directory(lyxrc_new.document_path)) {
1999 support::package().document_dir() = lyxrc.document_path;
2002 case LyXRC::RC_ESC_CHARS:
2003 case LyXRC::RC_FONT_ENCODING:
2004 case LyXRC::RC_FORMAT:
2005 case LyXRC::RC_INDEX_COMMAND:
2006 case LyXRC::RC_INPUT:
2007 case LyXRC::RC_KBMAP:
2008 case LyXRC::RC_KBMAP_PRIMARY:
2009 case LyXRC::RC_KBMAP_SECONDARY:
2010 case LyXRC::RC_LABEL_INIT_LENGTH:
2011 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2012 case LyXRC::RC_LANGUAGE_AUTO_END:
2013 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2014 case LyXRC::RC_LANGUAGE_COMMAND_END:
2015 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2016 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2017 case LyXRC::RC_LANGUAGE_PACKAGE:
2018 case LyXRC::RC_LANGUAGE_USE_BABEL:
2019 case LyXRC::RC_MAKE_BACKUP:
2020 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2021 case LyXRC::RC_NUMLASTFILES:
2022 case LyXRC::RC_PATH_PREFIX:
2023 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2024 support::prependEnvPath("PATH", lyxrc.path_prefix);
2026 case LyXRC::RC_PERS_DICT:
2027 case LyXRC::RC_POPUP_BOLD_FONT:
2028 case LyXRC::RC_POPUP_FONT_ENCODING:
2029 case LyXRC::RC_POPUP_NORMAL_FONT:
2030 case LyXRC::RC_PREVIEW:
2031 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2032 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2033 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2034 case LyXRC::RC_PRINTCOPIESFLAG:
2035 case LyXRC::RC_PRINTER:
2036 case LyXRC::RC_PRINTEVENPAGEFLAG:
2037 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2038 case LyXRC::RC_PRINTFILEEXTENSION:
2039 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2040 case LyXRC::RC_PRINTODDPAGEFLAG:
2041 case LyXRC::RC_PRINTPAGERANGEFLAG:
2042 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2043 case LyXRC::RC_PRINTPAPERFLAG:
2044 case LyXRC::RC_PRINTREVERSEFLAG:
2045 case LyXRC::RC_PRINTSPOOL_COMMAND:
2046 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2047 case LyXRC::RC_PRINTTOFILE:
2048 case LyXRC::RC_PRINTTOPRINTER:
2049 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2050 case LyXRC::RC_PRINT_COMMAND:
2051 case LyXRC::RC_RTL_SUPPORT:
2052 case LyXRC::RC_SCREEN_DPI:
2053 case LyXRC::RC_SCREEN_FONT_ENCODING:
2054 case LyXRC::RC_SCREEN_FONT_ROMAN:
2055 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2056 case LyXRC::RC_SCREEN_FONT_SANS:
2057 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2058 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2059 case LyXRC::RC_SCREEN_FONT_SIZES:
2060 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2061 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2062 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2063 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2064 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2065 case LyXRC::RC_SCREEN_ZOOM:
2066 case LyXRC::RC_SERVERPIPE:
2067 case LyXRC::RC_SET_COLOR:
2068 case LyXRC::RC_SHOW_BANNER:
2069 case LyXRC::RC_SPELL_COMMAND:
2070 case LyXRC::RC_TEMPDIRPATH:
2071 case LyXRC::RC_TEMPLATEPATH:
2072 case LyXRC::RC_TEX_ALLOWS_SPACES:
2073 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2074 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2075 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2077 case LyXRC::RC_UIFILE:
2078 case LyXRC::RC_USER_EMAIL:
2079 case LyXRC::RC_USER_NAME:
2080 case LyXRC::RC_USETEMPDIR:
2081 case LyXRC::RC_USE_ALT_LANG:
2082 case LyXRC::RC_USE_ESC_CHARS:
2083 case LyXRC::RC_USE_INP_ENC:
2084 case LyXRC::RC_USE_PERS_DICT:
2085 case LyXRC::RC_USE_SPELL_LIB:
2086 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2087 case LyXRC::RC_VIEWER:
2088 case LyXRC::RC_LAST: