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>
103 using bv_funcs::freefont2string;
105 using lyx::docstring;
107 using lyx::support::absolutePath;
108 using lyx::support::addName;
109 using lyx::support::addPath;
110 using lyx::support::bformat;
111 using lyx::support::changeExtension;
112 using lyx::support::contains;
113 using lyx::support::FileFilterList;
114 using lyx::support::fileSearch;
115 using lyx::support::ForkedcallsController;
116 using lyx::support::i18nLibFileSearch;
117 using lyx::support::isDirWriteable;
118 using lyx::support::isFileReadable;
119 using lyx::support::isStrInt;
120 using lyx::support::makeAbsPath;
121 using lyx::support::makeDisplayPath;
122 using lyx::support::package;
123 using lyx::support::quoteName;
124 using lyx::support::rtrim;
125 using lyx::support::split;
126 using lyx::support::subst;
127 using lyx::support::Systemcall;
128 using lyx::support::token;
129 using lyx::support::trim;
130 using lyx::support::prefixIs;
133 using std::make_pair;
136 using std::istringstream;
137 using std::ostringstream;
139 namespace Alert = lyx::frontend::Alert;
140 namespace biblio = lyx::biblio;
141 namespace fs = boost::filesystem;
144 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
147 extern tex_accent_struct get_accent(kb_action action);
155 bool getStatus(LCursor cursor,
156 FuncRequest const & cmd, FuncStatus & status)
158 // Try to fix cursor in case it is broken.
159 cursor.fixIfBroken();
161 // This is, of course, a mess. Better create a new doc iterator and use
162 // this in Inset::getStatus. This might require an additional
163 // BufferView * arg, though (which should be avoided)
164 //LCursor safe = *this;
166 for ( ; cursor.depth(); cursor.pop()) {
167 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
168 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
169 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
170 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
172 // The inset's getStatus() will return 'true' if it made
173 // a definitive decision on whether it want to handle the
174 // request or not. The result of this decision is put into
175 // the 'status' parameter.
176 if (cursor.inset().getStatus(cursor, cmd, status)) {
185 /** Return the change status at cursor position, taking in account the
186 * status at each level of the document iterator (a table in a deleted
187 * footnote is deleted).
188 * When \param outer is true, the top slice is not looked at.
190 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
192 size_t const depth = dit.depth() - (outer ? 1 : 0);
194 for (size_t i = 0 ; i < depth ; ++i) {
195 CursorSlice const & slice = dit[i];
196 if (!slice.inset().inMathed()
197 && slice.pos() < slice.paragraph().size()) {
198 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
199 if (ch != Change::UNCHANGED)
203 return Change::UNCHANGED;
208 LyXFunc::LyXFunc(LyXView * lv)
211 keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
212 cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
213 meta_fake_bit(key_modifier::none)
218 void LyXFunc::handleKeyFunc(kb_action action)
220 char c = encoded_last_key;
222 if (keyseq.length()) {
226 owner->view()->getIntl().getTransManager()
227 .deadkey(c, get_accent(action).accent, view()->getLyXText());
228 // Need to clear, in case the minibuffer calls these
231 // copied verbatim from do_accent_char
232 view()->cursor().resetAnchor();
237 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
239 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
241 // Do nothing if we have nothing (JMarc)
242 if (!keysym->isOK()) {
243 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
248 if (keysym->isModifier()) {
249 lyxerr[Debug::KEY] << "isModifier true" << endl;
253 Encoding const * encoding = view()->cursor().getEncoding();
255 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
256 size_t encoded_last_key = keysym->getUCSEncoded();
258 // Do a one-deep top-level lookup for
259 // cancel and meta-fake keys. RVDK_PATCH_5
260 cancel_meta_seq.reset();
262 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
263 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
264 << " action first set to [" << func.action << ']'
267 // When not cancel or meta-fake, do the normal lookup.
268 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
269 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
270 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
271 // remove Caps Lock and Mod2 as a modifiers
272 func = keyseq.addkey(keysym, (state | meta_fake_bit));
273 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
274 << "action now set to ["
275 << func.action << ']' << endl;
278 // Dont remove this unless you know what you are doing.
279 meta_fake_bit = key_modifier::none;
281 // Can this happen now ?
282 if (func.action == LFUN_NOACTION) {
283 func = FuncRequest(LFUN_COMMAND_PREFIX);
286 if (lyxerr.debugging(Debug::KEY)) {
287 lyxerr << BOOST_CURRENT_FUNCTION
289 << func.action << "]["
290 << keyseq.print() << ']'
294 // already here we know if it any point in going further
295 // why not return already here if action == -1 and
296 // num_bytes == 0? (Lgb)
298 if (keyseq.length() > 1) {
299 owner->message(lyx::from_utf8(keyseq.print()));
303 // Maybe user can only reach the key via holding down shift.
304 // Let's see. But only if shift is the only modifier
305 if (func.action == LFUN_UNKNOWN_ACTION &&
306 state == key_modifier::shift) {
307 lyxerr[Debug::KEY] << "Trying without shift" << endl;
308 func = keyseq.addkey(keysym, key_modifier::none);
309 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
312 if (func.action == LFUN_UNKNOWN_ACTION) {
313 // Hmm, we didn't match any of the keysequences. See
314 // if it's normal insertable text not already covered
316 if (keysym->isText() && keyseq.length() == 1) {
317 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
318 func = FuncRequest(LFUN_SELF_INSERT,
319 FuncRequest::KEYBOARD);
321 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
322 owner->message(_("Unknown function."));
327 if (func.action == LFUN_SELF_INSERT) {
328 if (encoded_last_key != 0) {
329 docstring const arg(1, encoded_last_key);
330 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
331 FuncRequest::KEYBOARD));
333 << "SelfInsert arg[`" << lyx::to_utf8(arg) << "']" << endl;
341 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
343 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
345 LCursor & cur = view()->cursor();
347 /* In LyX/Mac, when a dialog is open, the menus of the
348 application can still be accessed without giving focus to
349 the main window. In this case, we want to disable the menu
350 entries that are buffer-related.
352 Note that this code is not perfect, as bug 1941 attests:
353 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
356 if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
359 buf = owner->buffer();
361 if (cmd.action == LFUN_NOACTION) {
362 flag.message(lyx::from_utf8(N_("Nothing to do")));
367 switch (cmd.action) {
368 case LFUN_UNKNOWN_ACTION:
369 #ifndef HAVE_LIBAIKSAURUS
370 case LFUN_THESAURUS_ENTRY:
380 if (flag.unknown()) {
381 flag.message(lyx::from_utf8(N_("Unknown action")));
385 if (!flag.enabled()) {
386 if (flag.message().empty())
387 flag.message(lyx::from_utf8(N_("Command disabled")));
391 // Check whether we need a buffer
392 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
394 flag.message(lyx::from_utf8(N_("Command not allowed with"
395 "out any document open")));
400 // I would really like to avoid having this switch and rather try to
401 // encode this in the function itself.
402 // -- And I'd rather let an inset decide which LFUNs it is willing
403 // to handle (Andre')
405 switch (cmd.action) {
406 case LFUN_BUFFER_TOGGLE_READ_ONLY:
407 flag.setOnOff(buf->isReadonly());
410 case LFUN_BUFFER_SWITCH:
411 // toggle on the current buffer, but do not toggle off
412 // the other ones (is that a good idea?)
413 if (lyx::to_utf8(cmd.argument()) == buf->fileName())
417 case LFUN_BUFFER_EXPORT:
418 enable = cmd.argument() == "custom"
419 || Exporter::isExportable(*buf, lyx::to_utf8(cmd.argument()));
422 case LFUN_BUFFER_CHKTEX:
423 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
426 case LFUN_BUILD_PROGRAM:
427 enable = Exporter::isExportable(*buf, "program");
430 case LFUN_LAYOUT_TABULAR:
431 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
435 case LFUN_LAYOUT_PARAGRAPH:
436 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
439 case LFUN_VC_REGISTER:
440 enable = !buf->lyxvc().inUse();
442 case LFUN_VC_CHECK_IN:
443 enable = buf->lyxvc().inUse() && !buf->isReadonly();
445 case LFUN_VC_CHECK_OUT:
446 enable = buf->lyxvc().inUse() && buf->isReadonly();
449 case LFUN_VC_UNDO_LAST:
450 enable = buf->lyxvc().inUse();
452 case LFUN_BUFFER_RELOAD:
453 enable = !buf->isUnnamed() && !buf->isClean();
456 case LFUN_INSET_SETTINGS: {
460 InsetBase::Code code = cur.inset().lyxCode();
462 case InsetBase::TABULAR_CODE:
463 enable = cmd.argument() == "tabular";
465 case InsetBase::ERT_CODE:
466 enable = cmd.argument() == "ert";
468 case InsetBase::FLOAT_CODE:
469 enable = cmd.argument() == "float";
471 case InsetBase::WRAP_CODE:
472 enable = cmd.argument() == "wrap";
474 case InsetBase::NOTE_CODE:
475 enable = cmd.argument() == "note";
477 case InsetBase::BRANCH_CODE:
478 enable = cmd.argument() == "branch";
480 case InsetBase::BOX_CODE:
481 enable = cmd.argument() == "box";
489 case LFUN_INSET_APPLY: {
490 string const name = cmd.getArg(0);
491 InsetBase * inset = owner->getDialogs().getOpenInset(name);
493 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
495 bool const success = inset->getStatus(cur, fr, fs);
496 // Every inset is supposed to handle this
497 BOOST_ASSERT(success);
500 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
501 flag |= getStatus(fr);
503 enable = flag.enabled();
507 case LFUN_DIALOG_SHOW: {
508 string const name = cmd.getArg(0);
510 enable = name == "aboutlyx"
514 || name == "texinfo";
515 else if (name == "print")
516 enable = Exporter::isExportable(*buf, "dvi")
517 && lyxrc.print_command != "none";
518 else if (name == "character" || name == "mathpanel")
519 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
520 else if (name == "latexlog")
521 enable = isFileReadable(buf->getLogName().second);
522 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
523 else if (name == "spellchecker")
526 else if (name == "vclog")
527 enable = buf->lyxvc().inUse();
528 else if (name == "view-source")
533 case LFUN_DIALOG_SHOW_NEW_INSET:
534 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
537 case LFUN_DIALOG_UPDATE: {
538 string const name = cmd.getArg(0);
540 enable = name == "prefs";
544 case LFUN_CITATION_INSERT: {
545 FuncRequest fr(LFUN_INSET_INSERT, "citation");
546 enable = getStatus(fr).enabled();
550 case LFUN_BUFFER_WRITE: {
551 enable = view()->buffer()->isUnnamed()
552 || !view()->buffer()->isClean();
556 // this one is difficult to get right. As a half-baked
557 // solution, we consider only the first action of the sequence
558 case LFUN_COMMAND_SEQUENCE: {
559 // argument contains ';'-terminated commands
560 string const firstcmd = token(lyx::to_utf8(cmd.argument()), ';', 0);
561 FuncRequest func(lyxaction.lookupFunc(firstcmd));
562 func.origin = cmd.origin;
563 flag = getStatus(func);
566 case LFUN_BUFFER_NEW:
567 case LFUN_BUFFER_NEW_TEMPLATE:
568 case LFUN_WORD_FIND_FORWARD:
569 case LFUN_WORD_FIND_BACKWARD:
570 case LFUN_COMMAND_PREFIX:
571 case LFUN_COMMAND_EXECUTE:
573 case LFUN_META_PREFIX:
574 case LFUN_BUFFER_CLOSE:
575 case LFUN_BUFFER_WRITE_AS:
576 case LFUN_BUFFER_UPDATE:
577 case LFUN_BUFFER_VIEW:
578 case LFUN_BUFFER_IMPORT:
581 case LFUN_BUFFER_AUTO_SAVE:
582 case LFUN_RECONFIGURE:
586 case LFUN_DROP_LAYOUTS_CHOICE:
588 case LFUN_SERVER_GET_NAME:
589 case LFUN_SERVER_NOTIFY:
590 case LFUN_SERVER_GOTO_FILE_ROW:
591 case LFUN_DIALOG_HIDE:
592 case LFUN_DIALOG_DISCONNECT_INSET:
593 case LFUN_BUFFER_CHILD_OPEN:
594 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
595 case LFUN_KEYMAP_OFF:
596 case LFUN_KEYMAP_PRIMARY:
597 case LFUN_KEYMAP_SECONDARY:
598 case LFUN_KEYMAP_TOGGLE:
600 case LFUN_BUFFER_EXPORT_CUSTOM:
601 case LFUN_BUFFER_PRINT:
602 case LFUN_PREFERENCES_SAVE:
603 case LFUN_SCREEN_FONT_UPDATE:
606 case LFUN_EXTERNAL_EDIT:
607 case LFUN_GRAPHICS_EDIT:
608 case LFUN_ALL_INSETS_TOGGLE:
609 case LFUN_BUFFER_LANGUAGE:
610 case LFUN_TEXTCLASS_APPLY:
611 case LFUN_TEXTCLASS_LOAD:
612 case LFUN_BUFFER_SAVE_AS_DEFAULT:
613 case LFUN_BUFFER_PARAMS_APPLY:
614 case LFUN_LYXRC_APPLY:
615 case LFUN_BUFFER_NEXT:
616 case LFUN_BUFFER_PREVIOUS:
617 // these are handled in our dispatch()
622 if (!::getStatus(cur, cmd, flag))
623 flag = view()->getStatus(cmd);
629 // Can we use a readonly buffer?
630 if (buf && buf->isReadonly()
631 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
632 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
633 flag.message(lyx::from_utf8(N_("Document is read-only")));
637 // Are we in a DELETED change-tracking region?
638 if (buf && lookupChangeType(cur, true) == Change::DELETED
639 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
640 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
641 flag.message(lyx::from_utf8(N_("This portion of the document is deleted.")));
645 // the default error message if we disable the command
646 if (!flag.enabled() && flag.message().empty())
647 flag.message(lyx::from_utf8(N_("Command disabled")));
653 bool LyXFunc::ensureBufferClean(BufferView * bv)
655 Buffer & buf = *bv->buffer();
659 docstring const file = makeDisplayPath(buf.fileName(), 30);
660 docstring text = bformat(_("The document %1$s has unsaved "
661 "changes.\n\nDo you want to save "
662 "the document?"), file);
663 int const ret = Alert::prompt(_("Save changed document?"),
664 text, 0, 1, _("&Save"),
668 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
670 return buf.isClean();
676 void showPrintError(string const & name)
678 docstring str = bformat(_("Could not print the document %1$s.\n"
679 "Check that your printer is set up correctly."),
680 makeDisplayPath(name, 50));
681 Alert::error(_("Print document failed"), str);
685 void loadTextclass(string const & name)
687 std::pair<bool, lyx::textclass_type> const tc_pair =
688 textclasslist.numberOfClass(name);
690 if (!tc_pair.first) {
691 lyxerr << "Document class \"" << name
692 << "\" does not exist."
697 lyx::textclass_type const tc = tc_pair.second;
699 if (!textclasslist[tc].load()) {
700 docstring s = bformat(_("The document could not be converted\n"
701 "into the document class %1$s."),
702 lyx::from_utf8(textclasslist[tc].name()));
703 Alert::error(_("Could not change class"), s);
708 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
713 void LyXFunc::dispatch(FuncRequest const & cmd)
715 BOOST_ASSERT(view());
716 string const argument = lyx::to_utf8(cmd.argument());
717 kb_action const action = cmd.action;
719 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
720 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
722 // we have not done anything wrong yet.
724 dispatch_buffer.erase();
726 // redraw the screen at the end (first of the two drawing steps).
727 //This is done unless explicitely requested otherwise
729 // also do the second redrawing step. Only done if requested.
730 bool updateforce = false;
732 FuncStatus const flag = getStatus(cmd);
733 if (!flag.enabled()) {
734 // We cannot use this function here
735 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
736 << lyxaction.getActionName(action)
737 << " [" << action << "] is disabled at this location"
739 setErrorMessage(flag.message());
743 case LFUN_WORD_FIND_FORWARD:
744 case LFUN_WORD_FIND_BACKWARD: {
745 static string last_search;
746 string searched_string;
748 if (!argument.empty()) {
749 last_search = argument;
750 searched_string = argument;
752 searched_string = last_search;
755 if (searched_string.empty())
758 bool const fw = action == LFUN_WORD_FIND_FORWARD;
760 lyx::find::find2string(searched_string, true, false, fw);
761 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
765 case LFUN_COMMAND_PREFIX:
766 owner->message(lyx::from_utf8(keyseq.printOptions()));
769 case LFUN_COMMAND_EXECUTE:
770 owner->getToolbars().display("minibuffer", true);
771 owner->focus_command_buffer();
776 meta_fake_bit = key_modifier::none;
777 if (view()->buffer())
778 // cancel any selection
779 dispatch(FuncRequest(LFUN_MARK_OFF));
780 setMessage(_("Cancel"));
783 case LFUN_META_PREFIX:
784 meta_fake_bit = key_modifier::alt;
785 setMessage(lyx::from_utf8(keyseq.print()));
788 case LFUN_BUFFER_TOGGLE_READ_ONLY:
789 if (owner->buffer()->lyxvc().inUse())
790 owner->buffer()->lyxvc().toggleReadOnly();
792 owner->buffer()->setReadonly(
793 !owner->buffer()->isReadonly());
796 // --- Menus -----------------------------------------------
797 case LFUN_BUFFER_NEW:
798 menuNew(argument, false);
801 case LFUN_BUFFER_NEW_TEMPLATE:
802 menuNew(argument, true);
805 case LFUN_BUFFER_CLOSE:
809 case LFUN_BUFFER_WRITE:
810 if (!owner->buffer()->isUnnamed()) {
811 docstring const str = bformat(_("Saving document %1$s..."),
812 makeDisplayPath(owner->buffer()->fileName()));
814 menuWrite(owner->buffer());
815 owner->message(str + _(" done."));
817 writeAs(owner->buffer());
821 case LFUN_BUFFER_WRITE_AS:
822 writeAs(owner->buffer(), argument);
826 case LFUN_BUFFER_RELOAD: {
827 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
828 docstring text = bformat(_("Any changes will be lost. Are you sure "
829 "you want to revert to the saved version of the document %1$s?"), file);
830 int const ret = Alert::prompt(_("Revert to saved document?"),
831 text, 0, 1, _("&Revert"), _("&Cancel"));
838 case LFUN_BUFFER_UPDATE:
839 Exporter::Export(owner->buffer(), argument, true);
842 case LFUN_BUFFER_VIEW:
843 Exporter::preview(owner->buffer(), argument);
846 case LFUN_BUILD_PROGRAM:
847 Exporter::Export(owner->buffer(), "program", true);
850 case LFUN_BUFFER_CHKTEX:
851 owner->buffer()->runChktex();
854 case LFUN_BUFFER_EXPORT:
855 if (argument == "custom")
856 owner->getDialogs().show("sendto");
858 Exporter::Export(owner->buffer(), argument, false);
862 case LFUN_BUFFER_EXPORT_CUSTOM: {
864 string command = split(argument, format_name, ' ');
865 Format const * format = formats.getFormat(format_name);
867 lyxerr << "Format \"" << format_name
868 << "\" not recognized!"
873 Buffer * buffer = owner->buffer();
875 // The name of the file created by the conversion process
878 // Output to filename
879 if (format->name() == "lyx") {
880 string const latexname =
881 buffer->getLatexName(false);
882 filename = changeExtension(latexname,
883 format->extension());
884 filename = addName(buffer->temppath(), filename);
886 if (!buffer->writeFile(filename))
890 Exporter::Export(buffer, format_name, true, filename);
893 // Substitute $$FName for filename
894 if (!contains(command, "$$FName"))
895 command = "( " + command + " ) < $$FName";
896 command = subst(command, "$$FName", filename);
898 // Execute the command in the background
900 call.startscript(Systemcall::DontWait, command);
904 case LFUN_BUFFER_PRINT: {
907 string command = split(split(argument, target, ' '),
911 || target_name.empty()
912 || command.empty()) {
913 lyxerr << "Unable to parse \""
914 << argument << '"' << std::endl;
917 if (target != "printer" && target != "file") {
918 lyxerr << "Unrecognized target \""
919 << target << '"' << std::endl;
923 Buffer * buffer = owner->buffer();
925 if (!Exporter::Export(buffer, "dvi", true)) {
926 showPrintError(buffer->fileName());
930 // Push directory path.
931 string const path = buffer->temppath();
932 lyx::support::Path p(path);
934 // there are three cases here:
935 // 1. we print to a file
936 // 2. we print directly to a printer
937 // 3. we print using a spool command (print to file first)
940 string const dviname =
941 changeExtension(buffer->getLatexName(true),
944 if (target == "printer") {
945 if (!lyxrc.print_spool_command.empty()) {
946 // case 3: print using a spool
947 string const psname =
948 changeExtension(dviname,".ps");
949 command += lyxrc.print_to_file
952 + quoteName(dviname);
955 lyxrc.print_spool_command +' ';
956 if (target_name != "default") {
957 command2 += lyxrc.print_spool_printerprefix
961 command2 += quoteName(psname);
963 // If successful, then spool command
964 res = one.startscript(
969 res = one.startscript(
970 Systemcall::DontWait,
973 // case 2: print directly to a printer
974 res = one.startscript(
975 Systemcall::DontWait,
976 command + quoteName(dviname));
980 // case 1: print to a file
981 command += lyxrc.print_to_file
982 + quoteName(makeAbsPath(target_name,
985 + quoteName(dviname);
986 res = one.startscript(Systemcall::DontWait,
991 showPrintError(buffer->fileName());
995 case LFUN_BUFFER_IMPORT:
1000 if (view()->buffer()) {
1001 // save cursor Position for opened files to .lyx/session
1002 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1003 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1004 // save bookmarks to .lyx/session
1005 view()->saveSavedPositions();
1007 quitLyX(argument == "force");
1010 case LFUN_TOC_VIEW: {
1011 InsetCommandParams p("tableofcontents");
1012 string const data = InsetCommandMailer::params2string("toc", p);
1013 owner->getDialogs().show("toc", data, 0);
1017 case LFUN_BUFFER_AUTO_SAVE:
1021 case LFUN_RECONFIGURE:
1022 reconfigure(view());
1025 case LFUN_HELP_OPEN: {
1026 string const arg = argument;
1028 setErrorMessage(_("Missing argument"));
1031 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1032 if (fname.empty()) {
1033 lyxerr << "LyX: unable to find documentation file `"
1034 << arg << "'. Bad installation?" << endl;
1037 owner->message(bformat(_("Opening help file %1$s..."),
1038 makeDisplayPath(fname)));
1039 owner->loadLyXFile(fname, false);
1043 // --- version control -------------------------------
1044 case LFUN_VC_REGISTER:
1045 if (!ensureBufferClean(view()))
1047 if (!owner->buffer()->lyxvc().inUse()) {
1048 owner->buffer()->lyxvc().registrer();
1053 case LFUN_VC_CHECK_IN:
1054 if (!ensureBufferClean(view()))
1056 if (owner->buffer()->lyxvc().inUse()
1057 && !owner->buffer()->isReadonly()) {
1058 owner->buffer()->lyxvc().checkIn();
1063 case LFUN_VC_CHECK_OUT:
1064 if (!ensureBufferClean(view()))
1066 if (owner->buffer()->lyxvc().inUse()
1067 && owner->buffer()->isReadonly()) {
1068 owner->buffer()->lyxvc().checkOut();
1073 case LFUN_VC_REVERT:
1074 owner->buffer()->lyxvc().revert();
1078 case LFUN_VC_UNDO_LAST:
1079 owner->buffer()->lyxvc().undoLast();
1083 // --- buffers ----------------------------------------
1084 case LFUN_BUFFER_SWITCH:
1085 owner->setBuffer(theBufferList().getBuffer(argument));
1088 case LFUN_BUFFER_NEXT:
1089 owner->setBuffer(theBufferList().next(view()->buffer()));
1092 case LFUN_BUFFER_PREVIOUS:
1093 owner->setBuffer(theBufferList().previous(view()->buffer()));
1097 newFile(view(), argument);
1100 case LFUN_FILE_OPEN:
1104 case LFUN_DROP_LAYOUTS_CHOICE:
1105 owner->getToolbars().openLayoutList();
1108 case LFUN_MENU_OPEN:
1109 owner->getMenubar().openByName(lyx::from_utf8(argument));
1112 // --- lyxserver commands ----------------------------
1113 case LFUN_SERVER_GET_NAME:
1114 setMessage(lyx::from_utf8(owner->buffer()->fileName()));
1115 lyxerr[Debug::INFO] << "FNAME["
1116 << owner->buffer()->fileName()
1120 case LFUN_SERVER_NOTIFY:
1121 dispatch_buffer = lyx::from_utf8(keyseq.print());
1122 theApp->server().notifyClient(lyx::to_utf8(dispatch_buffer));
1125 case LFUN_SERVER_GOTO_FILE_ROW: {
1128 istringstream is(argument);
1129 is >> file_name >> row;
1130 if (prefixIs(file_name, package().temp_dir())) {
1131 // Needed by inverse dvi search. If it is a file
1132 // in tmpdir, call the apropriated function
1133 owner->setBuffer(theBufferList().getBufferFromTmp(file_name));
1135 // Must replace extension of the file to be .lyx
1136 // and get full path
1137 string const s = changeExtension(file_name, ".lyx");
1138 // Either change buffer or load the file
1139 if (theBufferList().exists(s)) {
1140 owner->setBuffer(theBufferList().getBuffer(s));
1142 owner->loadLyXFile(s);
1146 view()->setCursorFromRow(row);
1149 // see BufferView::center()
1153 case LFUN_DIALOG_SHOW: {
1154 string const name = cmd.getArg(0);
1155 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1157 if (name == "character") {
1158 data = freefont2string();
1160 owner->getDialogs().show("character", data);
1161 } else if (name == "latexlog") {
1162 pair<Buffer::LogType, string> const logfile =
1163 owner->buffer()->getLogName();
1164 switch (logfile.first) {
1165 case Buffer::latexlog:
1168 case Buffer::buildlog:
1172 data += LyXLex::quoteString(logfile.second);
1173 owner->getDialogs().show("log", data);
1174 } else if (name == "vclog") {
1175 string const data = "vc " +
1176 LyXLex::quoteString(owner->buffer()->lyxvc().getLogFile());
1177 owner->getDialogs().show("log", data);
1179 owner->getDialogs().show(name, data);
1183 case LFUN_DIALOG_SHOW_NEW_INSET: {
1184 string const name = cmd.getArg(0);
1185 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1186 if (name == "bibitem" ||
1193 InsetCommandParams p(name);
1194 data = InsetCommandMailer::params2string(name, p);
1195 } else if (name == "include") {
1196 InsetCommandParams p(data);
1197 data = InsetIncludeMailer::params2string(p);
1198 } else if (name == "box") {
1199 // \c data == "Boxed" || "Frameless" etc
1200 InsetBoxParams p(data);
1201 data = InsetBoxMailer::params2string(p);
1202 } else if (name == "branch") {
1203 InsetBranchParams p;
1204 data = InsetBranchMailer::params2string(p);
1205 } else if (name == "citation") {
1206 InsetCommandParams p("cite");
1207 data = InsetCommandMailer::params2string(name, p);
1208 } else if (name == "ert") {
1209 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1210 } else if (name == "external") {
1211 InsetExternalParams p;
1212 Buffer const & buffer = *owner->buffer();
1213 data = InsetExternalMailer::params2string(p, buffer);
1214 } else if (name == "float") {
1216 data = InsetFloatMailer::params2string(p);
1217 } else if (name == "graphics") {
1218 InsetGraphicsParams p;
1219 Buffer const & buffer = *owner->buffer();
1220 data = InsetGraphicsMailer::params2string(p, buffer);
1221 } else if (name == "note") {
1223 data = InsetNoteMailer::params2string(p);
1224 } else if (name == "vspace") {
1226 data = InsetVSpaceMailer::params2string(space);
1227 } else if (name == "wrap") {
1229 data = InsetWrapMailer::params2string(p);
1231 owner->getDialogs().show(name, data, 0);
1235 case LFUN_DIALOG_UPDATE: {
1236 string const & name = argument;
1237 // Can only update a dialog connected to an existing inset
1238 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1240 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1241 inset->dispatch(view()->cursor(), fr);
1242 } else if (name == "paragraph") {
1243 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1244 } else if (name == "prefs") {
1245 owner->getDialogs().update(name, string());
1250 case LFUN_DIALOG_HIDE:
1251 Dialogs::hide(argument, 0);
1254 case LFUN_DIALOG_DISCONNECT_INSET:
1255 owner->getDialogs().disconnect(argument);
1259 case LFUN_CITATION_INSERT: {
1260 if (!argument.empty()) {
1261 // we can have one optional argument, delimited by '|'
1262 // citation-insert <key>|<text_before>
1263 // this should be enhanced to also support text_after
1264 // and citation style
1265 string arg = argument;
1267 if (contains(argument, "|")) {
1268 arg = token(argument, '|', 0);
1269 opt1 = '[' + token(argument, '|', 1) + ']';
1271 std::ostringstream os;
1272 os << "citation LatexCommand\n"
1273 << "\\cite" << opt1 << "{" << arg << "}\n"
1275 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1278 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1282 case LFUN_BUFFER_CHILD_OPEN: {
1283 string const filename =
1284 makeAbsPath(argument, owner->buffer()->filePath());
1285 // FIXME Should use bformat
1286 setMessage(_("Opening child document ") +
1287 makeDisplayPath(filename) + "...");
1288 view()->savePosition(0);
1289 string const parentfilename = owner->buffer()->fileName();
1290 if (theBufferList().exists(filename))
1291 owner->setBuffer(theBufferList().getBuffer(filename));
1293 owner->loadLyXFile(filename);
1294 // Set the parent name of the child document.
1295 // This makes insertion of citations and references in the child work,
1296 // when the target is in the parent or another child document.
1297 owner->buffer()->setParentName(parentfilename);
1301 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1302 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1305 case LFUN_KEYMAP_OFF:
1306 owner->view()->getIntl().keyMapOn(false);
1309 case LFUN_KEYMAP_PRIMARY:
1310 owner->view()->getIntl().keyMapPrim();
1313 case LFUN_KEYMAP_SECONDARY:
1314 owner->view()->getIntl().keyMapSec();
1317 case LFUN_KEYMAP_TOGGLE:
1318 owner->view()->getIntl().toggleKeyMap();
1324 string rest = split(argument, countstr, ' ');
1325 istringstream is(countstr);
1328 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1329 for (int i = 0; i < count; ++i)
1330 dispatch(lyxaction.lookupFunc(rest));
1334 case LFUN_COMMAND_SEQUENCE: {
1335 // argument contains ';'-terminated commands
1336 string arg = argument;
1337 while (!arg.empty()) {
1339 arg = split(arg, first, ';');
1340 FuncRequest func(lyxaction.lookupFunc(first));
1341 func.origin = cmd.origin;
1347 case LFUN_PREFERENCES_SAVE: {
1348 lyx::support::Path p(package().user_support());
1349 lyxrc.write("preferences", false);
1353 case LFUN_SCREEN_FONT_UPDATE:
1354 // handle the screen font changes.
1355 lyxrc.set_font_norm_type();
1356 theFontLoader().update();
1357 // All visible buffers will need resize
1361 case LFUN_SET_COLOR: {
1363 string const x11_name = split(argument, lyx_name, ' ');
1364 if (lyx_name.empty() || x11_name.empty()) {
1365 setErrorMessage(_("Syntax: set-color <lyx_name>"
1370 bool const graphicsbg_changed =
1371 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1372 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1374 if (!lcolor.setColor(lyx_name, x11_name)) {
1376 bformat(_("Set-color \"%1$s\" failed "
1377 "- color is undefined or "
1378 "may not be redefined"),
1379 lyx::from_utf8(lyx_name)));
1383 theApp->updateColor(lcolor.getFromLyXName(lyx_name));
1385 if (graphicsbg_changed) {
1386 #ifdef WITH_WARNINGS
1387 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1390 lyx::graphics::GCache::get().changeDisplay(true);
1397 owner->message(lyx::from_utf8(argument));
1400 case LFUN_EXTERNAL_EDIT: {
1401 FuncRequest fr(action, argument);
1402 InsetExternal().dispatch(view()->cursor(), fr);
1406 case LFUN_GRAPHICS_EDIT: {
1407 FuncRequest fr(action, argument);
1408 InsetGraphics().dispatch(view()->cursor(), fr);
1412 case LFUN_INSET_APPLY: {
1413 string const name = cmd.getArg(0);
1414 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1416 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1417 inset->dispatch(view()->cursor(), fr);
1419 FuncRequest fr(LFUN_INSET_INSERT, argument);
1422 // ideally, the update flag should be set by the insets,
1423 // but this is not possible currently
1428 case LFUN_ALL_INSETS_TOGGLE: {
1430 string const name = split(argument, action, ' ');
1431 InsetBase::Code const inset_code =
1432 InsetBase::translate(name);
1434 LCursor & cur = view()->cursor();
1435 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1437 InsetBase & inset = owner->buffer()->inset();
1438 InsetIterator it = inset_iterator_begin(inset);
1439 InsetIterator const end = inset_iterator_end(inset);
1440 for (; it != end; ++it) {
1441 if (inset_code == InsetBase::NO_CODE
1442 || inset_code == it->lyxCode()) {
1443 LCursor tmpcur = cur;
1444 tmpcur.pushLeft(*it);
1445 it->dispatch(tmpcur, fr);
1452 case LFUN_BUFFER_LANGUAGE: {
1453 Buffer & buffer = *owner->buffer();
1454 Language const * oldL = buffer.params().language;
1455 Language const * newL = languages.getLanguage(argument);
1456 if (!newL || oldL == newL)
1459 if (oldL->rightToLeft() == newL->rightToLeft()
1460 && !buffer.isMultiLingual())
1461 buffer.changeLanguage(oldL, newL);
1463 buffer.updateDocLang(newL);
1467 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1468 string const fname =
1469 addName(addPath(package().user_support(), "templates/"),
1471 Buffer defaults(fname);
1473 istringstream ss(argument);
1476 int const unknown_tokens = defaults.readHeader(lex);
1478 if (unknown_tokens != 0) {
1479 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1480 << unknown_tokens << " unknown token"
1481 << (unknown_tokens == 1 ? "" : "s")
1485 if (defaults.writeFile(defaults.fileName()))
1486 // FIXME Should use bformat
1487 setMessage(_("Document defaults saved in ")
1488 + makeDisplayPath(fname));
1490 setErrorMessage(_("Unable to save document defaults"));
1494 case LFUN_BUFFER_PARAMS_APPLY: {
1495 biblio::CiteEngine const engine =
1496 owner->buffer()->params().cite_engine;
1498 istringstream ss(argument);
1501 int const unknown_tokens =
1502 owner->buffer()->readHeader(lex);
1504 if (unknown_tokens != 0) {
1505 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1506 << unknown_tokens << " unknown token"
1507 << (unknown_tokens == 1 ? "" : "s")
1510 if (engine == owner->buffer()->params().cite_engine)
1513 LCursor & cur = view()->cursor();
1514 FuncRequest fr(LFUN_INSET_REFRESH);
1516 InsetBase & inset = owner->buffer()->inset();
1517 InsetIterator it = inset_iterator_begin(inset);
1518 InsetIterator const end = inset_iterator_end(inset);
1519 for (; it != end; ++it)
1520 if (it->lyxCode() == InsetBase::CITE_CODE)
1521 it->dispatch(cur, fr);
1525 case LFUN_TEXTCLASS_APPLY: {
1526 Buffer * buffer = owner->buffer();
1528 lyx::textclass_type const old_class =
1529 buffer->params().textclass;
1531 loadTextclass(argument);
1533 std::pair<bool, lyx::textclass_type> const tc_pair =
1534 textclasslist.numberOfClass(argument);
1539 lyx::textclass_type const new_class = tc_pair.second;
1540 if (old_class == new_class)
1544 owner->message(_("Converting document to new document class..."));
1545 recordUndoFullDocument(view());
1546 buffer->params().textclass = new_class;
1547 StableDocIterator backcur(view()->cursor());
1548 ErrorList & el = buffer->errorList("Class Switch");
1549 lyx::cap::switchBetweenClasses(
1550 old_class, new_class,
1551 static_cast<InsetText &>(buffer->inset()), el);
1553 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1555 buffer->errors("Class Switch");
1556 updateLabels(*buffer);
1561 case LFUN_TEXTCLASS_LOAD:
1562 loadTextclass(argument);
1565 case LFUN_LYXRC_APPLY: {
1566 LyXRC const lyxrc_orig = lyxrc;
1568 istringstream ss(argument);
1569 bool const success = lyxrc.read(ss) == 0;
1572 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1573 << "Unable to read lyxrc data"
1578 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1583 view()->cursor().dispatch(cmd);
1584 updateforce |= view()->cursor().result().update();
1585 if (!view()->cursor().result().dispatched())
1586 updateforce |= view()->dispatch(cmd);
1591 if (view()->buffer()) {
1592 // Redraw screen unless explicitly told otherwise.
1593 // This also initializes the position cache for all insets
1594 // in (at least partially) visible top-level paragraphs.
1596 view()->update(Update::FitCursor | Update::Force);
1598 view()->update(Update::FitCursor);
1600 owner->redrawWorkArea();
1602 // if we executed a mutating lfun, mark the buffer as dirty
1604 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1605 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1606 view()->buffer()->markDirty();
1608 if (view()->cursor().inTexted()) {
1609 owner->updateLayoutChoice();
1614 // FIXME UNICODE: _() does not support anything but ascii.
1615 // Do we need a lyx::to_ascii() method?
1616 sendDispatchMessage(getMessage(), cmd);
1620 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1622 /* When an action did not originate from the UI/kbd, it makes
1623 * sense to avoid updating the GUI. It turns out that this
1624 * fixes bug 1941, for reasons that are described here:
1625 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1627 if (cmd.origin != FuncRequest::INTERNAL) {
1628 owner->updateMenubar();
1629 owner->updateToolbars();
1632 const bool verbose = (cmd.origin == FuncRequest::UI
1633 || cmd.origin == FuncRequest::COMMANDBUFFER);
1635 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1636 lyxerr[Debug::ACTION] << "dispatch msg is " << lyx::to_utf8(msg) << endl;
1638 owner->message(msg);
1642 docstring dispatch_msg = msg;
1643 if (!dispatch_msg.empty())
1644 dispatch_msg += ' ';
1646 string comname = lyxaction.getActionName(cmd.action);
1648 bool argsadded = false;
1650 if (!cmd.argument().empty()) {
1651 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1652 comname += ' ' + lyx::to_utf8(cmd.argument());
1657 string const shortcuts = toplevel_keymap->printbindings(cmd);
1659 if (!shortcuts.empty())
1660 comname += ": " + shortcuts;
1661 else if (!argsadded && !cmd.argument().empty())
1662 comname += ' ' + lyx::to_utf8(cmd.argument());
1664 if (!comname.empty()) {
1665 comname = rtrim(comname);
1666 dispatch_msg += lyx::from_utf8('(' + rtrim(comname) + ')');
1669 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1670 << lyx::to_utf8(dispatch_msg) << endl;
1671 if (!dispatch_msg.empty())
1672 owner->message(dispatch_msg);
1676 void LyXFunc::setupLocalKeymap()
1678 keyseq.stdmap = toplevel_keymap.get();
1679 keyseq.curmap = toplevel_keymap.get();
1680 cancel_meta_seq.stdmap = toplevel_keymap.get();
1681 cancel_meta_seq.curmap = toplevel_keymap.get();
1685 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1687 string initpath = lyxrc.document_path;
1688 string filename(name);
1690 if (view()->buffer()) {
1691 string const trypath = owner->buffer()->filePath();
1692 // If directory is writeable, use this as default.
1693 if (isDirWriteable(trypath))
1697 static int newfile_number;
1699 if (filename.empty()) {
1700 filename = addName(lyxrc.document_path,
1701 "newfile" + convert<string>(++newfile_number) + ".lyx");
1702 while (theBufferList().exists(filename) || fs::is_readable(filename)) {
1704 filename = addName(lyxrc.document_path,
1705 "newfile" + convert<string>(newfile_number) +
1710 // The template stuff
1713 FileDialog fileDlg(_("Select template file"),
1714 LFUN_SELECT_FILE_SYNC,
1715 make_pair(_("Documents|#o#O"), lyx::from_utf8(lyxrc.document_path)),
1716 make_pair(_("Templates|#T#t"), lyx::from_utf8(lyxrc.template_path)));
1718 FileDialog::Result result =
1719 fileDlg.open(lyx::from_utf8(lyxrc.template_path),
1720 FileFilterList(_("LyX Documents (*.lyx)")),
1723 if (result.first == FileDialog::Later)
1725 if (result.second.empty())
1727 templname = lyx::to_utf8(result.second);
1730 Buffer * const b = newFile(filename, templname, !name.empty());
1732 owner->setBuffer(b);
1736 void LyXFunc::open(string const & fname)
1738 string initpath = lyxrc.document_path;
1740 if (view()->buffer()) {
1741 string const trypath = owner->buffer()->filePath();
1742 // If directory is writeable, use this as default.
1743 if (isDirWriteable(trypath))
1749 if (fname.empty()) {
1750 FileDialog fileDlg(_("Select document to open"),
1752 make_pair(_("Documents|#o#O"), lyx::from_utf8(lyxrc.document_path)),
1753 make_pair(_("Examples|#E#e"), lyx::from_utf8(addPath(package().system_support(), "examples"))));
1755 FileDialog::Result result =
1756 fileDlg.open(lyx::from_utf8(initpath),
1757 FileFilterList(_("LyX Documents (*.lyx)")),
1760 if (result.first == FileDialog::Later)
1763 filename = lyx::to_utf8(result.second);
1765 // check selected filename
1766 if (filename.empty()) {
1767 owner->message(_("Canceled."));
1773 // get absolute path of file and add ".lyx" to the filename if
1775 string const fullpath = fileSearch(string(), filename, "lyx");
1776 if (!fullpath.empty()) {
1777 filename = fullpath;
1780 docstring const disp_fn = makeDisplayPath(filename);
1782 // if the file doesn't exist, let the user create one
1783 if (!fs::exists(filename)) {
1784 // the user specifically chose this name. Believe him.
1785 Buffer * const b = newFile(filename, string(), true);
1787 owner->setBuffer(b);
1791 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1794 if (owner->loadLyXFile(filename)) {
1795 str2 = bformat(_("Document %1$s opened."), disp_fn);
1797 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1799 owner->message(str2);
1803 void LyXFunc::doImport(string const & argument)
1806 string filename = split(argument, format, ' ');
1808 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1809 << " file: " << filename << endl;
1811 // need user interaction
1812 if (filename.empty()) {
1813 string initpath = lyxrc.document_path;
1815 if (view()->buffer()) {
1816 string const trypath = owner->buffer()->filePath();
1817 // If directory is writeable, use this as default.
1818 if (isDirWriteable(trypath))
1822 docstring const text = bformat(_("Select %1$s file to import"),
1823 formats.prettyName(format));
1825 FileDialog fileDlg(text,
1827 make_pair(_("Documents|#o#O"), lyx::from_utf8(lyxrc.document_path)),
1828 make_pair(_("Examples|#E#e"),
1829 lyx::from_utf8(addPath(package().system_support(), "examples"))));
1831 docstring filter = formats.prettyName(format);
1834 filter += lyx::from_utf8(formats.extension(format));
1837 FileDialog::Result result =
1838 fileDlg.open(lyx::from_utf8(initpath),
1839 FileFilterList(filter),
1842 if (result.first == FileDialog::Later)
1845 filename = lyx::to_utf8(result.second);
1847 // check selected filename
1848 if (filename.empty())
1849 owner->message(_("Canceled."));
1852 if (filename.empty())
1855 // get absolute path of file
1856 filename = makeAbsPath(filename);
1858 string const lyxfile = changeExtension(filename, ".lyx");
1860 // Check if the document already is open
1861 if (lyx::use_gui && theBufferList().exists(lyxfile)) {
1862 if (!theBufferList().close(theBufferList().getBuffer(lyxfile), true)) {
1863 owner->message(_("Canceled."));
1868 // if the file exists already, and we didn't do
1869 // -i lyx thefile.lyx, warn
1870 if (fs::exists(lyxfile) && filename != lyxfile) {
1871 docstring const file = makeDisplayPath(lyxfile, 30);
1873 docstring text = bformat(_("The document %1$s already exists.\n\n"
1874 "Do you want to over-write that document?"), file);
1875 int const ret = Alert::prompt(_("Over-write document?"),
1876 text, 0, 1, _("&Over-write"), _("&Cancel"));
1879 owner->message(_("Canceled."));
1884 ErrorList errorList;
1885 Importer::Import(owner, filename, format, errorList);
1886 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1890 void LyXFunc::closeBuffer()
1892 // save current cursor position
1893 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1894 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1895 if (theBufferList().close(owner->buffer(), true) && !quitting) {
1896 if (theBufferList().empty()) {
1897 // need this otherwise SEGV may occur while
1898 // trying to set variables that don't exist
1899 // since there's no current buffer
1900 owner->getDialogs().hideBufferDependent();
1902 owner->setBuffer(theBufferList().first());
1908 // Each "owner" should have it's own message method. lyxview and
1909 // the minibuffer would use the minibuffer, but lyxserver would
1910 // send an ERROR signal to its client. Alejandro 970603
1911 // This function is bit problematic when it comes to NLS, to make the
1912 // lyx servers client be language indepenent we must not translate
1913 // strings sent to this func.
1914 void LyXFunc::setErrorMessage(docstring const & m) const
1916 dispatch_buffer = m;
1921 void LyXFunc::setMessage(docstring const & m) const
1923 dispatch_buffer = m;
1927 string const LyXFunc::viewStatusMessage()
1929 // When meta-fake key is pressed, show the key sequence so far + "M-".
1931 return keyseq.print() + "M-";
1933 // Else, when a non-complete key sequence is pressed,
1934 // show the available options.
1935 if (keyseq.length() > 0 && !keyseq.deleted())
1936 return keyseq.printOptions();
1938 if (!view()->buffer())
1939 return lyx::to_utf8(_("Welcome to LyX!"));
1941 return view()->cursor().currentState();
1945 BufferView * LyXFunc::view() const
1947 BOOST_ASSERT(owner);
1948 return owner->view();
1952 bool LyXFunc::wasMetaKey() const
1954 return (meta_fake_bit != key_modifier::none);
1960 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1962 // Why the switch you might ask. It is a trick to ensure that all
1963 // the elements in the LyXRCTags enum is handled. As you can see
1964 // there are no breaks at all. So it is just a huge fall-through.
1965 // The nice thing is that we will get a warning from the compiler
1966 // if we forget an element.
1967 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1969 case LyXRC::RC_ACCEPT_COMPOUND:
1970 case LyXRC::RC_ALT_LANG:
1971 case LyXRC::RC_ASCIIROFF_COMMAND:
1972 case LyXRC::RC_ASCII_LINELEN:
1973 case LyXRC::RC_AUTOREGIONDELETE:
1974 case LyXRC::RC_AUTORESET_OPTIONS:
1975 case LyXRC::RC_AUTOSAVE:
1976 case LyXRC::RC_AUTO_NUMBER:
1977 case LyXRC::RC_BACKUPDIR_PATH:
1978 case LyXRC::RC_BIBTEX_COMMAND:
1979 case LyXRC::RC_BINDFILE:
1980 case LyXRC::RC_CHECKLASTFILES:
1981 case LyXRC::RC_USELASTFILEPOS:
1982 case LyXRC::RC_LOADSESSION:
1983 case LyXRC::RC_CHKTEX_COMMAND:
1984 case LyXRC::RC_CONVERTER:
1985 case LyXRC::RC_COPIER:
1986 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1987 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1988 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1989 case LyXRC::RC_DATE_INSERT_FORMAT:
1990 case LyXRC::RC_DEFAULT_LANGUAGE:
1991 case LyXRC::RC_DEFAULT_PAPERSIZE:
1992 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1993 case LyXRC::RC_DISPLAY_GRAPHICS:
1994 case LyXRC::RC_DOCUMENTPATH:
1995 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1996 if (fs::exists(lyxrc_new.document_path) &&
1997 fs::is_directory(lyxrc_new.document_path)) {
1998 using lyx::support::package;
1999 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 using lyx::support::prependEnvPath;
2025 prependEnvPath("PATH", lyxrc.path_prefix);
2027 case LyXRC::RC_PERS_DICT:
2028 case LyXRC::RC_POPUP_BOLD_FONT:
2029 case LyXRC::RC_POPUP_FONT_ENCODING:
2030 case LyXRC::RC_POPUP_NORMAL_FONT:
2031 case LyXRC::RC_PREVIEW:
2032 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2033 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2034 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2035 case LyXRC::RC_PRINTCOPIESFLAG:
2036 case LyXRC::RC_PRINTER:
2037 case LyXRC::RC_PRINTEVENPAGEFLAG:
2038 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2039 case LyXRC::RC_PRINTFILEEXTENSION:
2040 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2041 case LyXRC::RC_PRINTODDPAGEFLAG:
2042 case LyXRC::RC_PRINTPAGERANGEFLAG:
2043 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2044 case LyXRC::RC_PRINTPAPERFLAG:
2045 case LyXRC::RC_PRINTREVERSEFLAG:
2046 case LyXRC::RC_PRINTSPOOL_COMMAND:
2047 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2048 case LyXRC::RC_PRINTTOFILE:
2049 case LyXRC::RC_PRINTTOPRINTER:
2050 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2051 case LyXRC::RC_PRINT_COMMAND:
2052 case LyXRC::RC_RTL_SUPPORT:
2053 case LyXRC::RC_SCREEN_DPI:
2054 case LyXRC::RC_SCREEN_FONT_ENCODING:
2055 case LyXRC::RC_SCREEN_FONT_ROMAN:
2056 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2057 case LyXRC::RC_SCREEN_FONT_SANS:
2058 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2059 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2060 case LyXRC::RC_SCREEN_FONT_SIZES:
2061 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2062 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2063 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2064 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2065 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2066 case LyXRC::RC_SCREEN_ZOOM:
2067 case LyXRC::RC_SERVERPIPE:
2068 case LyXRC::RC_SET_COLOR:
2069 case LyXRC::RC_SHOW_BANNER:
2070 case LyXRC::RC_SPELL_COMMAND:
2071 case LyXRC::RC_TEMPDIRPATH:
2072 case LyXRC::RC_TEMPLATEPATH:
2073 case LyXRC::RC_TEX_ALLOWS_SPACES:
2074 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2075 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2076 namespace os = lyx::support::os;
2077 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2079 case LyXRC::RC_UIFILE:
2080 case LyXRC::RC_USER_EMAIL:
2081 case LyXRC::RC_USER_NAME:
2082 case LyXRC::RC_USETEMPDIR:
2083 case LyXRC::RC_USE_ALT_LANG:
2084 case LyXRC::RC_USE_ESC_CHARS:
2085 case LyXRC::RC_USE_INP_ENC:
2086 case LyXRC::RC_USE_PERS_DICT:
2087 case LyXRC::RC_USE_SPELL_LIB:
2088 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2089 case LyXRC::RC_VIEWER:
2090 case LyXRC::RC_LAST: