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"
41 #include "insetiterator.h"
49 #include "LyXAction.h"
54 #include "lyxserver.h"
55 #include "lyxtextclasslist.h"
57 #include "paragraph.h"
58 #include "pariterator.h"
59 #include "ParagraphParameters.h"
62 #include "insets/insetbox.h"
63 #include "insets/insetbranch.h"
64 #include "insets/insetcommand.h"
65 #include "insets/insetert.h"
66 #include "insets/insetexternal.h"
67 #include "insets/insetfloat.h"
68 #include "insets/insetgraphics.h"
69 #include "insets/insetinclude.h"
70 #include "insets/insetnote.h"
71 #include "insets/insettabular.h"
72 #include "insets/insetvspace.h"
73 #include "insets/insetwrap.h"
75 #include "frontends/Alert.h"
76 #include "frontends/Dialogs.h"
77 #include "frontends/FileDialog.h"
78 #include "frontends/lyx_gui.h"
79 #include "frontends/LyXKeySym.h"
80 #include "frontends/LyXView.h"
81 #include "frontends/Menubar.h"
82 #include "frontends/Toolbars.h"
84 #include "support/environment.h"
85 #include "support/filefilterlist.h"
86 #include "support/filetools.h"
87 #include "support/forkedcontr.h"
88 #include "support/fs_extras.h"
89 #include "support/lstrings.h"
90 #include "support/path.h"
91 #include "support/package.h"
92 #include "support/systemcall.h"
93 #include "support/convert.h"
94 #include "support/os.h"
96 #include <boost/current_function.hpp>
97 #include <boost/filesystem/operations.hpp>
101 using bv_funcs::freefont2string;
103 using lyx::docstring;
105 using lyx::support::absolutePath;
106 using lyx::support::addName;
107 using lyx::support::addPath;
108 using lyx::support::bformat;
109 using lyx::support::changeExtension;
110 using lyx::support::contains;
111 using lyx::support::FileFilterList;
112 using lyx::support::fileSearch;
113 using lyx::support::ForkedcallsController;
114 using lyx::support::i18nLibFileSearch;
115 using lyx::support::isDirWriteable;
116 using lyx::support::isFileReadable;
117 using lyx::support::isStrInt;
118 using lyx::support::makeAbsPath;
119 using lyx::support::makeDisplayPath;
120 using lyx::support::package;
121 using lyx::support::Path;
122 using lyx::support::quoteName;
123 using lyx::support::rtrim;
124 using lyx::support::split;
125 using lyx::support::subst;
126 using lyx::support::Systemcall;
127 using lyx::support::token;
128 using lyx::support::trim;
129 using lyx::support::prefixIs;
132 using std::make_pair;
135 using std::istringstream;
136 using std::ostringstream;
138 namespace biblio = lyx::biblio;
139 namespace fs = boost::filesystem;
142 extern BufferList bufferlist;
143 extern LyXServer * lyxserver;
145 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
148 extern tex_accent_struct get_accent(kb_action action);
153 bool getStatus(LCursor cursor,
154 FuncRequest const & cmd, FuncStatus & status)
156 // Try to fix cursor in case it is broken.
157 cursor.fixIfBroken();
159 // This is, of course, a mess. Better create a new doc iterator and use
160 // this in Inset::getStatus. This might require an additional
161 // BufferView * arg, though (which should be avoided)
162 //LCursor safe = *this;
164 for ( ; cursor.depth(); cursor.pop()) {
165 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
166 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
167 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
168 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
170 // The inset's getStatus() will return 'true' if it made
171 // a definitive decision on whether it want to handle the
172 // request or not. The result of this decision is put into
173 // the 'status' parameter.
174 if (cursor.inset().getStatus(cursor, cmd, status)) {
183 /** Return the change status at cursor position, taking in account the
184 * status at each level of the document iterator (a table in a deleted
185 * footnote is deleted).
186 * When \param outer is true, the top slice is not looked at.
188 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
190 size_t const depth = dit.depth() - (outer ? 1 : 0);
192 for (size_t i = 0 ; i < depth ; ++i) {
193 CursorSlice const & slice = dit[i];
194 if (!slice.inset().inMathed()
195 && slice.pos() < slice.paragraph().size()) {
196 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
197 if (ch != Change::UNCHANGED)
201 return Change::UNCHANGED;
206 LyXFunc::LyXFunc(LyXView * lv)
209 keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
210 cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
211 meta_fake_bit(key_modifier::none)
216 void LyXFunc::handleKeyFunc(kb_action action)
218 char c = encoded_last_key;
220 if (keyseq.length()) {
224 owner->getIntl().getTransManager()
225 .deadkey(c, get_accent(action).accent, view()->getLyXText());
226 // Need to clear, in case the minibuffer calls these
229 // copied verbatim from do_accent_char
230 view()->cursor().resetAnchor();
235 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
237 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
239 // Do nothing if we have nothing (JMarc)
240 if (!keysym->isOK()) {
241 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
246 if (keysym->isModifier()) {
247 lyxerr[Debug::KEY] << "isModifier true" << endl;
251 Encoding const * encoding = view()->cursor().getEncoding();
253 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
254 size_t encoded_last_key = keysym->getUCSEncoded();
256 // Do a one-deep top-level lookup for
257 // cancel and meta-fake keys. RVDK_PATCH_5
258 cancel_meta_seq.reset();
260 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
261 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
262 << " action first set to [" << func.action << ']'
265 // When not cancel or meta-fake, do the normal lookup.
266 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
267 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
268 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
269 // remove Caps Lock and Mod2 as a modifiers
270 func = keyseq.addkey(keysym, (state | meta_fake_bit));
271 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
272 << "action now set to ["
273 << func.action << ']' << endl;
276 // Dont remove this unless you know what you are doing.
277 meta_fake_bit = key_modifier::none;
279 // Can this happen now ?
280 if (func.action == LFUN_NOACTION) {
281 func = FuncRequest(LFUN_COMMAND_PREFIX);
284 if (lyxerr.debugging(Debug::KEY)) {
285 lyxerr << BOOST_CURRENT_FUNCTION
287 << func.action << "]["
288 << keyseq.print() << ']'
292 // already here we know if it any point in going further
293 // why not return already here if action == -1 and
294 // num_bytes == 0? (Lgb)
296 if (keyseq.length() > 1) {
297 owner->message(lyx::from_utf8(keyseq.print()));
301 // Maybe user can only reach the key via holding down shift.
302 // Let's see. But only if shift is the only modifier
303 if (func.action == LFUN_UNKNOWN_ACTION &&
304 state == key_modifier::shift) {
305 lyxerr[Debug::KEY] << "Trying without shift" << endl;
306 func = keyseq.addkey(keysym, key_modifier::none);
307 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
310 if (func.action == LFUN_UNKNOWN_ACTION) {
311 // Hmm, we didn't match any of the keysequences. See
312 // if it's normal insertable text not already covered
314 if (keysym->isText() && keyseq.length() == 1) {
315 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
316 func = FuncRequest(LFUN_SELF_INSERT,
317 FuncRequest::KEYBOARD);
319 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
320 owner->message(_("Unknown function."));
325 if (func.action == LFUN_SELF_INSERT) {
326 if (encoded_last_key != 0) {
327 docstring const arg(1, encoded_last_key);
328 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
329 FuncRequest::KEYBOARD));
331 << "SelfInsert arg[`" << lyx::to_utf8(arg) << "']" << endl;
339 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
341 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
343 LCursor & cur = view()->cursor();
345 /* In LyX/Mac, when a dialog is open, the menus of the
346 application can still be accessed without giving focus to
347 the main window. In this case, we want to disable the menu
348 entries that are buffer-related.
350 Note that this code is not perfect, as bug 1941 attests:
351 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
354 if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
357 buf = owner->buffer();
359 if (cmd.action == LFUN_NOACTION) {
360 flag.message(lyx::from_utf8(N_("Nothing to do")));
365 switch (cmd.action) {
366 case LFUN_UNKNOWN_ACTION:
367 #ifndef HAVE_LIBAIKSAURUS
368 case LFUN_THESAURUS_ENTRY:
374 flag |= lyx_gui::getStatus(cmd);
377 if (flag.unknown()) {
378 flag.message(lyx::from_utf8(N_("Unknown action")));
382 if (!flag.enabled()) {
383 if (flag.message().empty())
384 flag.message(lyx::from_utf8(N_("Command disabled")));
388 // Check whether we need a buffer
389 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
391 flag.message(lyx::from_utf8(N_("Command not allowed with"
392 "out any document open")));
397 // I would really like to avoid having this switch and rather try to
398 // encode this in the function itself.
399 // -- And I'd rather let an inset decide which LFUNs it is willing
400 // to handle (Andre')
402 switch (cmd.action) {
403 case LFUN_TOOLTIPS_TOGGLE:
404 flag.setOnOff(owner->getDialogs().tooltipsEnabled());
407 case LFUN_BUFFER_TOGGLE_READ_ONLY:
408 flag.setOnOff(buf->isReadonly());
411 case LFUN_BUFFER_SWITCH:
412 // toggle on the current buffer, but do not toggle off
413 // the other ones (is that a good idea?)
414 if (lyx::to_utf8(cmd.argument()) == buf->fileName())
418 case LFUN_BUFFER_EXPORT:
419 enable = cmd.argument() == "custom"
420 || Exporter::isExportable(*buf, lyx::to_utf8(cmd.argument()));
423 case LFUN_BUFFER_CHKTEX:
424 enable = buf->isLatex() && lyxrc.chktex_command != "none";
427 case LFUN_BUILD_PROGRAM:
428 enable = Exporter::isExportable(*buf, "program");
431 case LFUN_LAYOUT_TABULAR:
432 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
436 case LFUN_LAYOUT_PARAGRAPH:
437 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
440 case LFUN_VC_REGISTER:
441 enable = !buf->lyxvc().inUse();
443 case LFUN_VC_CHECK_IN:
444 enable = buf->lyxvc().inUse() && !buf->isReadonly();
446 case LFUN_VC_CHECK_OUT:
447 enable = buf->lyxvc().inUse() && buf->isReadonly();
450 case LFUN_VC_UNDO_LAST:
451 enable = buf->lyxvc().inUse();
453 case LFUN_BUFFER_RELOAD:
454 enable = !buf->isUnnamed() && !buf->isClean();
457 case LFUN_INSET_SETTINGS: {
461 InsetBase::Code code = cur.inset().lyxCode();
463 case InsetBase::TABULAR_CODE:
464 enable = cmd.argument() == "tabular";
466 case InsetBase::ERT_CODE:
467 enable = cmd.argument() == "ert";
469 case InsetBase::FLOAT_CODE:
470 enable = cmd.argument() == "float";
472 case InsetBase::WRAP_CODE:
473 enable = cmd.argument() == "wrap";
475 case InsetBase::NOTE_CODE:
476 enable = cmd.argument() == "note";
478 case InsetBase::BRANCH_CODE:
479 enable = cmd.argument() == "branch";
481 case InsetBase::BOX_CODE:
482 enable = cmd.argument() == "box";
490 case LFUN_INSET_APPLY: {
491 string const name = cmd.getArg(0);
492 InsetBase * inset = owner->getDialogs().getOpenInset(name);
494 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
496 bool const success = inset->getStatus(cur, fr, fs);
497 // Every inset is supposed to handle this
498 BOOST_ASSERT(success);
501 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
502 flag |= getStatus(fr);
504 enable = flag.enabled();
508 case LFUN_DIALOG_SHOW: {
509 string const name = cmd.getArg(0);
511 enable = name == "aboutlyx"
515 || name == "texinfo";
516 else if (name == "print")
517 enable = Exporter::isExportable(*buf, "dvi")
518 && lyxrc.print_command != "none";
519 else if (name == "character" || name == "mathpanel")
520 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
521 else if (name == "latexlog")
522 enable = isFileReadable(buf->getLogName().second);
523 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
524 else if (name == "spellchecker")
527 else if (name == "vclog")
528 enable = buf->lyxvc().inUse();
529 else if (name == "view-source")
534 case LFUN_DIALOG_SHOW_NEW_INSET:
535 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
538 case LFUN_DIALOG_UPDATE: {
539 string const name = cmd.getArg(0);
541 enable = name == "prefs";
545 case LFUN_CITATION_INSERT: {
546 FuncRequest fr(LFUN_INSET_INSERT, "citation");
547 enable = getStatus(fr).enabled();
551 case LFUN_BUFFER_WRITE: {
552 enable = view()->buffer()->isUnnamed()
553 || !view()->buffer()->isClean();
557 // this one is difficult to get right. As a half-baked
558 // solution, we consider only the first action of the sequence
559 case LFUN_COMMAND_SEQUENCE: {
560 // argument contains ';'-terminated commands
561 string const firstcmd = token(lyx::to_utf8(cmd.argument()), ';', 0);
562 FuncRequest func(lyxaction.lookupFunc(firstcmd));
563 func.origin = cmd.origin;
564 flag = getStatus(func);
567 case LFUN_BUFFER_NEW:
568 case LFUN_BUFFER_NEW_TEMPLATE:
569 case LFUN_WORD_FIND_FORWARD:
570 case LFUN_WORD_FIND_BACKWARD:
571 case LFUN_COMMAND_PREFIX:
572 case LFUN_COMMAND_EXECUTE:
574 case LFUN_META_PREFIX:
575 case LFUN_BUFFER_CLOSE:
576 case LFUN_BUFFER_WRITE_AS:
577 case LFUN_BUFFER_UPDATE:
578 case LFUN_BUFFER_VIEW:
579 case LFUN_BUFFER_IMPORT:
582 case LFUN_BUFFER_AUTO_SAVE:
583 case LFUN_RECONFIGURE:
587 case LFUN_DROP_LAYOUTS_CHOICE:
589 case LFUN_SERVER_GET_NAME:
590 case LFUN_SERVER_NOTIFY:
591 case LFUN_SERVER_GOTO_FILE_ROW:
592 case LFUN_DIALOG_HIDE:
593 case LFUN_DIALOG_DISCONNECT_INSET:
594 case LFUN_BUFFER_CHILD_OPEN:
595 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
596 case LFUN_KEYMAP_OFF:
597 case LFUN_KEYMAP_PRIMARY:
598 case LFUN_KEYMAP_SECONDARY:
599 case LFUN_KEYMAP_TOGGLE:
601 case LFUN_BUFFER_EXPORT_CUSTOM:
602 case LFUN_BUFFER_PRINT:
603 case LFUN_PREFERENCES_SAVE:
604 case LFUN_SCREEN_FONT_UPDATE:
607 case LFUN_EXTERNAL_EDIT:
608 case LFUN_GRAPHICS_EDIT:
609 case LFUN_ALL_INSETS_TOGGLE:
610 case LFUN_BUFFER_LANGUAGE:
611 case LFUN_TEXTCLASS_APPLY:
612 case LFUN_TEXTCLASS_LOAD:
613 case LFUN_BUFFER_SAVE_AS_DEFAULT:
614 case LFUN_BUFFER_PARAMS_APPLY:
615 case LFUN_LYXRC_APPLY:
616 case LFUN_BUFFER_NEXT:
617 case LFUN_BUFFER_PREVIOUS:
618 // these are handled in our dispatch()
623 if (!::getStatus(cur, cmd, flag))
624 flag = view()->getStatus(cmd);
630 // Can we use a readonly buffer?
631 if (buf && buf->isReadonly()
632 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
633 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
634 flag.message(lyx::from_utf8(N_("Document is read-only")));
638 // Are we in a DELETED change-tracking region?
639 if (buf && buf->params().tracking_changes
640 && lookupChangeType(cur, true) == Change::DELETED
641 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
642 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
643 flag.message(lyx::from_utf8(N_("This portion of the document is deleted.")));
647 // the default error message if we disable the command
648 if (!flag.enabled() && flag.message().empty())
649 flag.message(lyx::from_utf8(N_("Command disabled")));
657 bool 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 bv->owner()->dispatch(FuncRequest(LFUN_BUFFER_WRITE));
674 return buf.isClean();
678 void showPrintError(string const & name)
680 docstring str = bformat(_("Could not print the document %1$s.\n"
681 "Check that your printer is set up correctly."),
682 makeDisplayPath(name, 50));
683 Alert::error(_("Print document failed"), str);
687 void loadTextclass(string const & name)
689 std::pair<bool, lyx::textclass_type> const tc_pair =
690 textclasslist.numberOfClass(name);
692 if (!tc_pair.first) {
693 lyxerr << "Document class \"" << name
694 << "\" does not exist."
699 lyx::textclass_type const tc = tc_pair.second;
701 if (!textclasslist[tc].load()) {
702 docstring s = bformat(_("The document could not be converted\n"
703 "into the document class %1$s."),
704 lyx::from_utf8(textclasslist[tc].name()));
705 Alert::error(_("Could not change class"), s);
710 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
715 void LyXFunc::dispatch(FuncRequest const & cmd)
717 BOOST_ASSERT(view());
718 string const argument = lyx::to_utf8(cmd.argument());
719 kb_action const action = cmd.action;
721 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
722 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
724 // we have not done anything wrong yet.
726 dispatch_buffer.erase();
728 // redraw the screen at the end (first of the two drawing steps).
729 //This is done unless explicitely requested otherwise
731 // also do the second redrawing step. Only done if requested.
732 bool updateforce = false;
734 FuncStatus const flag = getStatus(cmd);
735 if (!flag.enabled()) {
736 // We cannot use this function here
737 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
738 << lyxaction.getActionName(action)
739 << " [" << action << "] is disabled at this location"
741 setErrorMessage(flag.message());
745 case LFUN_WORD_FIND_FORWARD:
746 case LFUN_WORD_FIND_BACKWARD: {
747 static string last_search;
748 string searched_string;
750 if (!argument.empty()) {
751 last_search = argument;
752 searched_string = argument;
754 searched_string = last_search;
757 if (searched_string.empty())
760 bool const fw = action == LFUN_WORD_FIND_FORWARD;
762 lyx::find::find2string(searched_string, true, false, fw);
763 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
767 case LFUN_COMMAND_PREFIX:
768 owner->message(lyx::from_utf8(keyseq.printOptions()));
771 case LFUN_COMMAND_EXECUTE:
772 owner->getToolbars().display("minibuffer", true);
773 owner->focus_command_buffer();
778 meta_fake_bit = key_modifier::none;
779 if (view()->available())
780 // cancel any selection
781 dispatch(FuncRequest(LFUN_MARK_OFF));
782 setMessage(lyx::from_utf8(N_("Cancel")));
785 case LFUN_META_PREFIX:
786 meta_fake_bit = key_modifier::alt;
787 setMessage(lyx::from_utf8(keyseq.print()));
790 case LFUN_BUFFER_TOGGLE_READ_ONLY:
791 if (owner->buffer()->lyxvc().inUse())
792 owner->buffer()->lyxvc().toggleReadOnly();
794 owner->buffer()->setReadonly(
795 !owner->buffer()->isReadonly());
798 // --- Menus -----------------------------------------------
799 case LFUN_BUFFER_NEW:
800 menuNew(argument, false);
803 case LFUN_BUFFER_NEW_TEMPLATE:
804 menuNew(argument, true);
807 case LFUN_BUFFER_CLOSE:
811 case LFUN_BUFFER_WRITE:
812 if (!owner->buffer()->isUnnamed()) {
813 docstring const str = bformat(_("Saving document %1$s..."),
814 makeDisplayPath(owner->buffer()->fileName()));
816 menuWrite(owner->buffer());
817 owner->message(str + _(" done."));
819 writeAs(owner->buffer());
823 case LFUN_BUFFER_WRITE_AS:
824 writeAs(owner->buffer(), argument);
828 case LFUN_BUFFER_RELOAD: {
829 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
830 docstring text = bformat(_("Any changes will be lost. Are you sure "
831 "you want to revert to the saved version of the document %1$s?"), file);
832 int const ret = Alert::prompt(_("Revert to saved document?"),
833 text, 0, 1, _("&Revert"), _("&Cancel"));
840 case LFUN_BUFFER_UPDATE:
841 Exporter::Export(owner->buffer(), argument, true);
844 case LFUN_BUFFER_VIEW:
845 Exporter::preview(owner->buffer(), argument);
848 case LFUN_BUILD_PROGRAM:
849 Exporter::Export(owner->buffer(), "program", true);
852 case LFUN_BUFFER_CHKTEX:
853 owner->buffer()->runChktex();
856 case LFUN_BUFFER_EXPORT:
857 if (argument == "custom")
858 owner->getDialogs().show("sendto");
860 Exporter::Export(owner->buffer(), argument, false);
864 case LFUN_BUFFER_EXPORT_CUSTOM: {
866 string command = split(argument, format_name, ' ');
867 Format const * format = formats.getFormat(format_name);
869 lyxerr << "Format \"" << format_name
870 << "\" not recognized!"
875 Buffer * buffer = owner->buffer();
877 // The name of the file created by the conversion process
880 // Output to filename
881 if (format->name() == "lyx") {
882 string const latexname =
883 buffer->getLatexName(false);
884 filename = changeExtension(latexname,
885 format->extension());
886 filename = addName(buffer->temppath(), filename);
888 if (!buffer->writeFile(filename))
892 Exporter::Export(buffer, format_name, true, filename);
895 // Substitute $$FName for filename
896 if (!contains(command, "$$FName"))
897 command = "( " + command + " ) < $$FName";
898 command = subst(command, "$$FName", filename);
900 // Execute the command in the background
902 call.startscript(Systemcall::DontWait, command);
906 case LFUN_BUFFER_PRINT: {
909 string command = split(split(argument, target, ' '),
913 || target_name.empty()
914 || command.empty()) {
915 lyxerr << "Unable to parse \""
916 << argument << '"' << std::endl;
919 if (target != "printer" && target != "file") {
920 lyxerr << "Unrecognized target \""
921 << target << '"' << std::endl;
925 Buffer * buffer = owner->buffer();
927 if (!Exporter::Export(buffer, "dvi", true)) {
928 showPrintError(buffer->fileName());
932 // Push directory path.
933 string const path = buffer->temppath();
936 // there are three cases here:
937 // 1. we print to a file
938 // 2. we print directly to a printer
939 // 3. we print using a spool command (print to file first)
942 string const dviname =
943 changeExtension(buffer->getLatexName(true),
946 if (target == "printer") {
947 if (!lyxrc.print_spool_command.empty()) {
948 // case 3: print using a spool
949 string const psname =
950 changeExtension(dviname,".ps");
951 command += lyxrc.print_to_file
954 + quoteName(dviname);
957 lyxrc.print_spool_command +' ';
958 if (target_name != "default") {
959 command2 += lyxrc.print_spool_printerprefix
963 command2 += quoteName(psname);
965 // If successful, then spool command
966 res = one.startscript(
971 res = one.startscript(
972 Systemcall::DontWait,
975 // case 2: print directly to a printer
976 res = one.startscript(
977 Systemcall::DontWait,
978 command + quoteName(dviname));
982 // case 1: print to a file
983 command += lyxrc.print_to_file
984 + quoteName(makeAbsPath(target_name,
987 + quoteName(dviname);
988 res = one.startscript(Systemcall::DontWait,
993 showPrintError(buffer->fileName());
997 case LFUN_BUFFER_IMPORT:
1002 if (view()->available()) {
1003 // save cursor Position for opened files to .lyx/session
1004 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1005 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1006 // save bookmarks to .lyx/session
1007 view()->saveSavedPositions();
1009 quitLyX(argument == "force");
1012 case LFUN_TOC_VIEW: {
1013 InsetCommandParams p("tableofcontents");
1014 string const data = InsetCommandMailer::params2string("toc", p);
1015 owner->getDialogs().show("toc", data, 0);
1019 case LFUN_BUFFER_AUTO_SAVE:
1023 case LFUN_RECONFIGURE:
1024 reconfigure(view());
1027 case LFUN_HELP_OPEN: {
1028 string const arg = argument;
1030 setErrorMessage(lyx::from_utf8(N_("Missing argument")));
1033 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1034 if (fname.empty()) {
1035 lyxerr << "LyX: unable to find documentation file `"
1036 << arg << "'. Bad installation?" << endl;
1039 owner->message(bformat(_("Opening help file %1$s..."),
1040 makeDisplayPath(fname)));
1041 owner->loadLyXFile(fname, false);
1045 // --- version control -------------------------------
1046 case LFUN_VC_REGISTER:
1047 if (!ensureBufferClean(view()))
1049 if (!owner->buffer()->lyxvc().inUse()) {
1050 owner->buffer()->lyxvc().registrer();
1055 case LFUN_VC_CHECK_IN:
1056 if (!ensureBufferClean(view()))
1058 if (owner->buffer()->lyxvc().inUse()
1059 && !owner->buffer()->isReadonly()) {
1060 owner->buffer()->lyxvc().checkIn();
1065 case LFUN_VC_CHECK_OUT:
1066 if (!ensureBufferClean(view()))
1068 if (owner->buffer()->lyxvc().inUse()
1069 && owner->buffer()->isReadonly()) {
1070 owner->buffer()->lyxvc().checkOut();
1075 case LFUN_VC_REVERT:
1076 owner->buffer()->lyxvc().revert();
1080 case LFUN_VC_UNDO_LAST:
1081 owner->buffer()->lyxvc().undoLast();
1085 // --- buffers ----------------------------------------
1086 case LFUN_BUFFER_SWITCH:
1087 owner->setBuffer(bufferlist.getBuffer(argument));
1090 case LFUN_BUFFER_NEXT:
1091 owner->setBuffer(bufferlist.next(view()->buffer()));
1094 case LFUN_BUFFER_PREVIOUS:
1095 owner->setBuffer(bufferlist.previous(view()->buffer()));
1099 newFile(view(), argument);
1102 case LFUN_FILE_OPEN:
1106 case LFUN_DROP_LAYOUTS_CHOICE:
1107 owner->getToolbars().openLayoutList();
1110 case LFUN_MENU_OPEN:
1111 owner->getMenubar().openByName(lyx::from_utf8(argument));
1114 // --- lyxserver commands ----------------------------
1115 case LFUN_SERVER_GET_NAME:
1116 setMessage(lyx::from_utf8(owner->buffer()->fileName()));
1117 lyxerr[Debug::INFO] << "FNAME["
1118 << owner->buffer()->fileName()
1122 case LFUN_SERVER_NOTIFY:
1123 dispatch_buffer = lyx::from_utf8(keyseq.print());
1124 lyxserver->notifyClient(lyx::to_utf8(dispatch_buffer));
1127 case LFUN_SERVER_GOTO_FILE_ROW: {
1130 istringstream is(argument);
1131 is >> file_name >> row;
1132 if (prefixIs(file_name, package().temp_dir())) {
1133 // Needed by inverse dvi search. If it is a file
1134 // in tmpdir, call the apropriated function
1135 owner->setBuffer(bufferlist.getBufferFromTmp(file_name));
1137 // Must replace extension of the file to be .lyx
1138 // and get full path
1139 string const s = changeExtension(file_name, ".lyx");
1140 // Either change buffer or load the file
1141 if (bufferlist.exists(s)) {
1142 owner->setBuffer(bufferlist.getBuffer(s));
1144 owner->loadLyXFile(s);
1148 view()->setCursorFromRow(row);
1151 // see BufferView_pimpl::center()
1155 case LFUN_DIALOG_SHOW: {
1156 string const name = cmd.getArg(0);
1157 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1159 if (name == "character") {
1160 data = freefont2string();
1162 owner->getDialogs().show("character", data);
1163 } else if (name == "latexlog") {
1164 pair<Buffer::LogType, string> const logfile =
1165 owner->buffer()->getLogName();
1166 switch (logfile.first) {
1167 case Buffer::latexlog:
1170 case Buffer::buildlog:
1174 data += LyXLex::quoteString(logfile.second);
1175 owner->getDialogs().show("log", data);
1176 } else if (name == "vclog") {
1177 string const data = "vc " +
1178 LyXLex::quoteString(owner->buffer()->lyxvc().getLogFile());
1179 owner->getDialogs().show("log", data);
1181 owner->getDialogs().show(name, data);
1185 case LFUN_DIALOG_SHOW_NEW_INSET: {
1186 string const name = cmd.getArg(0);
1187 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1188 if (name == "bibitem" ||
1195 InsetCommandParams p(name);
1196 data = InsetCommandMailer::params2string(name, p);
1197 } else if (name == "include") {
1198 InsetCommandParams p(data);
1199 data = InsetIncludeMailer::params2string(p);
1200 } else if (name == "box") {
1201 // \c data == "Boxed" || "Frameless" etc
1202 InsetBoxParams p(data);
1203 data = InsetBoxMailer::params2string(p);
1204 } else if (name == "branch") {
1205 InsetBranchParams p;
1206 data = InsetBranchMailer::params2string(p);
1207 } else if (name == "citation") {
1208 InsetCommandParams p("cite");
1209 data = InsetCommandMailer::params2string(name, p);
1210 } else if (name == "ert") {
1211 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1212 } else if (name == "external") {
1213 InsetExternalParams p;
1214 Buffer const & buffer = *owner->buffer();
1215 data = InsetExternalMailer::params2string(p, buffer);
1216 } else if (name == "float") {
1218 data = InsetFloatMailer::params2string(p);
1219 } else if (name == "graphics") {
1220 InsetGraphicsParams p;
1221 Buffer const & buffer = *owner->buffer();
1222 data = InsetGraphicsMailer::params2string(p, buffer);
1223 } else if (name == "note") {
1225 data = InsetNoteMailer::params2string(p);
1226 } else if (name == "vspace") {
1228 data = InsetVSpaceMailer::params2string(space);
1229 } else if (name == "wrap") {
1231 data = InsetWrapMailer::params2string(p);
1233 owner->getDialogs().show(name, data, 0);
1237 case LFUN_DIALOG_UPDATE: {
1238 string const & name = argument;
1239 // Can only update a dialog connected to an existing inset
1240 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1242 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1243 inset->dispatch(view()->cursor(), fr);
1244 } else if (name == "paragraph") {
1245 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1246 } else if (name == "prefs") {
1247 owner->getDialogs().update(name, string());
1252 case LFUN_DIALOG_HIDE:
1253 Dialogs::hide(argument, 0);
1256 case LFUN_DIALOG_DISCONNECT_INSET:
1257 owner->getDialogs().disconnect(argument);
1261 case LFUN_CITATION_INSERT: {
1262 if (!argument.empty()) {
1263 // we can have one optional argument, delimited by '|'
1264 // citation-insert <key>|<text_before>
1265 // this should be enhanced to also support text_after
1266 // and citation style
1267 string arg = argument;
1269 if (contains(argument, "|")) {
1270 arg = token(argument, '|', 0);
1271 opt1 = '[' + token(argument, '|', 1) + ']';
1273 std::ostringstream os;
1274 os << "citation LatexCommand\n"
1275 << "\\cite" << opt1 << "{" << arg << "}\n"
1277 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1280 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1284 case LFUN_BUFFER_CHILD_OPEN: {
1285 string const filename =
1286 makeAbsPath(argument, owner->buffer()->filePath());
1287 setMessage(lyx::from_utf8(N_("Opening child document ")) +
1288 makeDisplayPath(filename) + lyx::from_ascii("..."));
1289 view()->savePosition(0);
1290 string const parentfilename = owner->buffer()->fileName();
1291 if (bufferlist.exists(filename))
1292 owner->setBuffer(bufferlist.getBuffer(filename));
1294 owner->loadLyXFile(filename);
1295 // Set the parent name of the child document.
1296 // This makes insertion of citations and references in the child work,
1297 // when the target is in the parent or another child document.
1298 owner->buffer()->setParentName(parentfilename);
1302 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1303 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1306 case LFUN_KEYMAP_OFF:
1307 owner->getIntl().keyMapOn(false);
1310 case LFUN_KEYMAP_PRIMARY:
1311 owner->getIntl().keyMapPrim();
1314 case LFUN_KEYMAP_SECONDARY:
1315 owner->getIntl().keyMapSec();
1318 case LFUN_KEYMAP_TOGGLE:
1319 owner->getIntl().toggleKeyMap();
1325 string rest = split(argument, countstr, ' ');
1326 istringstream is(countstr);
1329 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1330 for (int i = 0; i < count; ++i)
1331 dispatch(lyxaction.lookupFunc(rest));
1335 case LFUN_COMMAND_SEQUENCE: {
1336 // argument contains ';'-terminated commands
1337 string arg = argument;
1338 while (!arg.empty()) {
1340 arg = split(arg, first, ';');
1341 FuncRequest func(lyxaction.lookupFunc(first));
1342 func.origin = cmd.origin;
1348 case LFUN_PREFERENCES_SAVE: {
1349 Path p(package().user_support());
1350 lyxrc.write("preferences", false);
1354 case LFUN_SCREEN_FONT_UPDATE:
1355 // handle the screen font changes.
1356 lyxrc.set_font_norm_type();
1357 lyx_gui::update_fonts();
1358 // All visible buffers will need resize
1362 case LFUN_SET_COLOR: {
1364 string const x11_name = split(argument, lyx_name, ' ');
1365 if (lyx_name.empty() || x11_name.empty()) {
1366 setErrorMessage(lyx::from_utf8(N_("Syntax: set-color <lyx_name>"
1371 bool const graphicsbg_changed =
1372 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1373 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1375 if (!lcolor.setColor(lyx_name, x11_name)) {
1377 bformat(_("Set-color \"%1$s\" failed "
1378 "- color is undefined or "
1379 "may not be redefined"),
1380 lyx::from_utf8(lyx_name)));
1384 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1386 if (graphicsbg_changed) {
1387 #ifdef WITH_WARNINGS
1388 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1391 lyx::graphics::GCache::get().changeDisplay(true);
1398 owner->message(lyx::from_utf8(argument));
1401 case LFUN_TOOLTIPS_TOGGLE:
1402 owner->getDialogs().toggleTooltips();
1405 case LFUN_EXTERNAL_EDIT: {
1406 FuncRequest fr(action, argument);
1407 InsetExternal().dispatch(view()->cursor(), fr);
1411 case LFUN_GRAPHICS_EDIT: {
1412 FuncRequest fr(action, argument);
1413 InsetGraphics().dispatch(view()->cursor(), fr);
1417 case LFUN_INSET_APPLY: {
1418 string const name = cmd.getArg(0);
1419 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1421 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1422 inset->dispatch(view()->cursor(), fr);
1424 FuncRequest fr(LFUN_INSET_INSERT, argument);
1427 // ideally, the update flag should be set by the insets,
1428 // but this is not possible currently
1433 case LFUN_ALL_INSETS_TOGGLE: {
1435 string const name = split(argument, action, ' ');
1436 InsetBase::Code const inset_code =
1437 InsetBase::translate(name);
1439 LCursor & cur = view()->cursor();
1440 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1442 InsetBase & inset = owner->buffer()->inset();
1443 InsetIterator it = inset_iterator_begin(inset);
1444 InsetIterator const end = inset_iterator_end(inset);
1445 for (; it != end; ++it) {
1446 if (inset_code == InsetBase::NO_CODE
1447 || inset_code == it->lyxCode()) {
1448 LCursor tmpcur = cur;
1449 tmpcur.pushLeft(*it);
1450 it->dispatch(tmpcur, fr);
1457 case LFUN_BUFFER_LANGUAGE: {
1458 Buffer & buffer = *owner->buffer();
1459 Language const * oldL = buffer.params().language;
1460 Language const * newL = languages.getLanguage(argument);
1461 if (!newL || oldL == newL)
1464 if (oldL->rightToLeft() == newL->rightToLeft()
1465 && !buffer.isMultiLingual())
1466 buffer.changeLanguage(oldL, newL);
1468 buffer.updateDocLang(newL);
1472 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1473 string const fname =
1474 addName(addPath(package().user_support(), "templates/"),
1476 Buffer defaults(fname);
1478 istringstream ss(argument);
1481 int const unknown_tokens = defaults.readHeader(lex);
1483 if (unknown_tokens != 0) {
1484 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1485 << unknown_tokens << " unknown token"
1486 << (unknown_tokens == 1 ? "" : "s")
1490 if (defaults.writeFile(defaults.fileName()))
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 owner->buffer()->params().cite_engine;
1502 istringstream ss(argument);
1505 int const unknown_tokens =
1506 owner->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 == owner->buffer()->params().cite_engine)
1517 LCursor & cur = view()->cursor();
1518 FuncRequest fr(LFUN_INSET_REFRESH);
1520 InsetBase & inset = owner->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 = owner->buffer();
1532 lyx::textclass_type const old_class =
1533 buffer->params().textclass;
1535 loadTextclass(argument);
1537 std::pair<bool, lyx::textclass_type> const tc_pair =
1538 textclasslist.numberOfClass(argument);
1543 lyx::textclass_type const new_class = tc_pair.second;
1544 if (old_class == new_class)
1548 owner->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 lyx::cap::switchBetweenClasses(
1554 old_class, new_class,
1555 static_cast<InsetText &>(buffer->inset()), el);
1557 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1559 buffer->errors("Class Switch");
1560 updateLabels(*buffer);
1565 case LFUN_TEXTCLASS_LOAD:
1566 loadTextclass(argument);
1569 case LFUN_LYXRC_APPLY: {
1570 LyXRC const lyxrc_orig = lyxrc;
1572 istringstream ss(argument);
1573 bool const success = lyxrc.read(ss) == 0;
1576 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1577 << "Unable to read lyxrc data"
1582 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1587 view()->cursor().dispatch(cmd);
1588 updateforce |= view()->cursor().result().update();
1589 if (!view()->cursor().result().dispatched())
1590 updateforce |= view()->dispatch(cmd);
1595 if (view()->available()) {
1596 // Redraw screen unless explicitly told otherwise.
1597 // This also initializes the position cache for all insets
1598 // in (at least partially) visible top-level paragraphs.
1600 view()->update(Update::FitCursor | Update::Force);
1602 view()->update(Update::FitCursor);
1604 owner->redrawWorkArea();
1606 // if we executed a mutating lfun, mark the buffer as dirty
1608 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1609 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1610 view()->buffer()->markDirty();
1612 if (view()->cursor().inTexted()) {
1613 view()->owner()->updateLayoutChoice();
1618 // FIXME UNICODE: _() does not support anything but ascii.
1619 // Do we need a lyx::to_ascii() method?
1620 sendDispatchMessage(_(lyx::to_utf8(getMessage())), cmd);
1624 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1626 /* When an action did not originate from the UI/kbd, it makes
1627 * sense to avoid updating the GUI. It turns out that this
1628 * fixes bug 1941, for reasons that are described here:
1629 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1631 if (cmd.origin != FuncRequest::INTERNAL) {
1632 owner->updateMenubar();
1633 owner->updateToolbars();
1636 const bool verbose = (cmd.origin == FuncRequest::UI
1637 || cmd.origin == FuncRequest::COMMANDBUFFER);
1639 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1640 lyxerr[Debug::ACTION] << "dispatch msg is " << lyx::to_utf8(msg) << endl;
1642 owner->message(msg);
1646 docstring dispatch_msg = msg;
1647 if (!dispatch_msg.empty())
1648 dispatch_msg += ' ';
1650 string comname = lyxaction.getActionName(cmd.action);
1652 bool argsadded = false;
1654 if (!cmd.argument().empty()) {
1655 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1656 comname += ' ' + lyx::to_utf8(cmd.argument());
1661 string const shortcuts = toplevel_keymap->printbindings(cmd);
1663 if (!shortcuts.empty())
1664 comname += ": " + shortcuts;
1665 else if (!argsadded && !cmd.argument().empty())
1666 comname += ' ' + lyx::to_utf8(cmd.argument());
1668 if (!comname.empty()) {
1669 comname = rtrim(comname);
1670 dispatch_msg += lyx::from_utf8('(' + rtrim(comname) + ')');
1673 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1674 << lyx::to_utf8(dispatch_msg) << endl;
1675 if (!dispatch_msg.empty())
1676 owner->message(dispatch_msg);
1680 void LyXFunc::setupLocalKeymap()
1682 keyseq.stdmap = toplevel_keymap.get();
1683 keyseq.curmap = toplevel_keymap.get();
1684 cancel_meta_seq.stdmap = toplevel_keymap.get();
1685 cancel_meta_seq.curmap = toplevel_keymap.get();
1689 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1691 string initpath = lyxrc.document_path;
1692 string filename(name);
1694 if (view()->available()) {
1695 string const trypath = owner->buffer()->filePath();
1696 // If directory is writeable, use this as default.
1697 if (isDirWriteable(trypath))
1701 static int newfile_number;
1703 if (filename.empty()) {
1704 filename = addName(lyxrc.document_path,
1705 "newfile" + convert<string>(++newfile_number) + ".lyx");
1706 while (bufferlist.exists(filename) || fs::is_readable(filename)) {
1708 filename = addName(lyxrc.document_path,
1709 "newfile" + convert<string>(newfile_number) +
1714 // The template stuff
1717 FileDialog fileDlg(lyx::to_utf8(_("Select template file")),
1718 LFUN_SELECT_FILE_SYNC,
1719 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1720 string(lyxrc.document_path)),
1721 make_pair(string(lyx::to_utf8(_("Templates|#T#t"))),
1722 string(lyxrc.template_path)));
1724 FileDialog::Result result =
1725 fileDlg.open(lyxrc.template_path,
1726 FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1729 if (result.first == FileDialog::Later)
1731 if (result.second.empty())
1733 templname = result.second;
1736 Buffer * const b = newFile(filename, templname, !name.empty());
1738 owner->setBuffer(b);
1742 void LyXFunc::open(string const & fname)
1744 string initpath = lyxrc.document_path;
1746 if (view()->available()) {
1747 string const trypath = owner->buffer()->filePath();
1748 // If directory is writeable, use this as default.
1749 if (isDirWriteable(trypath))
1755 if (fname.empty()) {
1756 FileDialog fileDlg(lyx::to_utf8(_("Select document to open")),
1758 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1759 string(lyxrc.document_path)),
1760 make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1761 string(addPath(package().system_support(), "examples"))));
1763 FileDialog::Result result =
1764 fileDlg.open(initpath,
1765 FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1768 if (result.first == FileDialog::Later)
1771 filename = result.second;
1773 // check selected filename
1774 if (filename.empty()) {
1775 owner->message(_("Canceled."));
1781 // get absolute path of file and add ".lyx" to the filename if
1783 string const fullpath = fileSearch(string(), filename, "lyx");
1784 if (!fullpath.empty()) {
1785 filename = fullpath;
1788 docstring const disp_fn = makeDisplayPath(filename);
1790 // if the file doesn't exist, let the user create one
1791 if (!fs::exists(filename)) {
1792 // the user specifically chose this name. Believe him.
1793 Buffer * const b = newFile(filename, string(), true);
1795 owner->setBuffer(b);
1799 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1802 if (owner->loadLyXFile(filename)) {
1803 str2 = bformat(_("Document %1$s opened."), disp_fn);
1805 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1807 owner->message(str2);
1811 void LyXFunc::doImport(string const & argument)
1814 string filename = split(argument, format, ' ');
1816 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1817 << " file: " << filename << endl;
1819 // need user interaction
1820 if (filename.empty()) {
1821 string initpath = lyxrc.document_path;
1823 if (view()->available()) {
1824 string const trypath = owner->buffer()->filePath();
1825 // If directory is writeable, use this as default.
1826 if (isDirWriteable(trypath))
1830 docstring const text = bformat(_("Select %1$s file to import"),
1831 formats.prettyName(format));
1833 FileDialog fileDlg(lyx::to_utf8(text),
1835 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1836 string(lyxrc.document_path)),
1837 make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1838 string(addPath(package().system_support(), "examples"))));
1840 string const filter = lyx::to_utf8(formats.prettyName(format))
1841 + " (*." + formats.extension(format) + ')';
1843 FileDialog::Result result =
1844 fileDlg.open(initpath,
1845 FileFilterList(filter),
1848 if (result.first == FileDialog::Later)
1851 filename = result.second;
1853 // check selected filename
1854 if (filename.empty())
1855 owner->message(_("Canceled."));
1858 if (filename.empty())
1861 // get absolute path of file
1862 filename = makeAbsPath(filename);
1864 string const lyxfile = changeExtension(filename, ".lyx");
1866 // Check if the document already is open
1867 if (lyx_gui::use_gui && bufferlist.exists(lyxfile)) {
1868 if (!bufferlist.close(bufferlist.getBuffer(lyxfile), true)) {
1869 owner->message(_("Canceled."));
1874 // if the file exists already, and we didn't do
1875 // -i lyx thefile.lyx, warn
1876 if (fs::exists(lyxfile) && filename != lyxfile) {
1877 docstring const file = makeDisplayPath(lyxfile, 30);
1879 docstring text = bformat(_("The document %1$s already exists.\n\n"
1880 "Do you want to over-write that document?"), file);
1881 int const ret = Alert::prompt(_("Over-write document?"),
1882 text, 0, 1, _("&Over-write"), _("&Cancel"));
1885 owner->message(_("Canceled."));
1890 ErrorList errorList;
1891 Importer::Import(owner, filename, format, errorList);
1892 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1896 void LyXFunc::closeBuffer()
1898 // save current cursor position
1899 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1900 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1901 if (bufferlist.close(owner->buffer(), true) && !quitting) {
1902 if (bufferlist.empty()) {
1903 // need this otherwise SEGV may occur while
1904 // trying to set variables that don't exist
1905 // since there's no current buffer
1906 owner->getDialogs().hideBufferDependent();
1908 owner->setBuffer(bufferlist.first());
1914 // Each "owner" should have it's own message method. lyxview and
1915 // the minibuffer would use the minibuffer, but lyxserver would
1916 // send an ERROR signal to its client. Alejandro 970603
1917 // This function is bit problematic when it comes to NLS, to make the
1918 // lyx servers client be language indepenent we must not translate
1919 // strings sent to this func.
1920 void LyXFunc::setErrorMessage(docstring const & m) const
1922 dispatch_buffer = m;
1927 void LyXFunc::setMessage(docstring const & m) const
1929 dispatch_buffer = m;
1933 string const LyXFunc::viewStatusMessage()
1935 // When meta-fake key is pressed, show the key sequence so far + "M-".
1937 return keyseq.print() + "M-";
1939 // Else, when a non-complete key sequence is pressed,
1940 // show the available options.
1941 if (keyseq.length() > 0 && !keyseq.deleted())
1942 return keyseq.printOptions();
1944 if (!view()->available())
1945 return lyx::to_utf8(_("Welcome to LyX!"));
1947 return view()->cursor().currentState();
1951 BufferView * LyXFunc::view() const
1953 BOOST_ASSERT(owner);
1954 return owner->view();
1958 bool LyXFunc::wasMetaKey() const
1960 return (meta_fake_bit != key_modifier::none);
1966 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1968 // Why the switch you might ask. It is a trick to ensure that all
1969 // the elements in the LyXRCTags enum is handled. As you can see
1970 // there are no breaks at all. So it is just a huge fall-through.
1971 // The nice thing is that we will get a warning from the compiler
1972 // if we forget an element.
1973 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1975 case LyXRC::RC_ACCEPT_COMPOUND:
1976 case LyXRC::RC_ALT_LANG:
1977 case LyXRC::RC_ASCIIROFF_COMMAND:
1978 case LyXRC::RC_ASCII_LINELEN:
1979 case LyXRC::RC_AUTOREGIONDELETE:
1980 case LyXRC::RC_AUTORESET_OPTIONS:
1981 case LyXRC::RC_AUTOSAVE:
1982 case LyXRC::RC_AUTO_NUMBER:
1983 case LyXRC::RC_BACKUPDIR_PATH:
1984 case LyXRC::RC_BIBTEX_COMMAND:
1985 case LyXRC::RC_BINDFILE:
1986 case LyXRC::RC_CHECKLASTFILES:
1987 case LyXRC::RC_USELASTFILEPOS:
1988 case LyXRC::RC_LOADSESSION:
1989 case LyXRC::RC_CHKTEX_COMMAND:
1990 case LyXRC::RC_CONVERTER:
1991 case LyXRC::RC_COPIER:
1992 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1993 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1994 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1995 case LyXRC::RC_DATE_INSERT_FORMAT:
1996 case LyXRC::RC_DEFAULT_LANGUAGE:
1997 case LyXRC::RC_DEFAULT_PAPERSIZE:
1998 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1999 case LyXRC::RC_DISPLAY_GRAPHICS:
2000 case LyXRC::RC_DOCUMENTPATH:
2001 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2002 if (fs::exists(lyxrc_new.document_path) &&
2003 fs::is_directory(lyxrc_new.document_path)) {
2004 using lyx::support::package;
2005 package().document_dir() = lyxrc.document_path;
2008 case LyXRC::RC_ESC_CHARS:
2009 case LyXRC::RC_FONT_ENCODING:
2010 case LyXRC::RC_FORMAT:
2011 case LyXRC::RC_INDEX_COMMAND:
2012 case LyXRC::RC_INPUT:
2013 case LyXRC::RC_KBMAP:
2014 case LyXRC::RC_KBMAP_PRIMARY:
2015 case LyXRC::RC_KBMAP_SECONDARY:
2016 case LyXRC::RC_LABEL_INIT_LENGTH:
2017 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2018 case LyXRC::RC_LANGUAGE_AUTO_END:
2019 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2020 case LyXRC::RC_LANGUAGE_COMMAND_END:
2021 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2022 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2023 case LyXRC::RC_LANGUAGE_PACKAGE:
2024 case LyXRC::RC_LANGUAGE_USE_BABEL:
2025 case LyXRC::RC_MAKE_BACKUP:
2026 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2027 case LyXRC::RC_NUMLASTFILES:
2028 case LyXRC::RC_PATH_PREFIX:
2029 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2030 using lyx::support::prependEnvPath;
2031 prependEnvPath("PATH", lyxrc.path_prefix);
2033 case LyXRC::RC_PERS_DICT:
2034 case LyXRC::RC_POPUP_BOLD_FONT:
2035 case LyXRC::RC_POPUP_FONT_ENCODING:
2036 case LyXRC::RC_POPUP_NORMAL_FONT:
2037 case LyXRC::RC_PREVIEW:
2038 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2039 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2040 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2041 case LyXRC::RC_PRINTCOPIESFLAG:
2042 case LyXRC::RC_PRINTER:
2043 case LyXRC::RC_PRINTEVENPAGEFLAG:
2044 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2045 case LyXRC::RC_PRINTFILEEXTENSION:
2046 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2047 case LyXRC::RC_PRINTODDPAGEFLAG:
2048 case LyXRC::RC_PRINTPAGERANGEFLAG:
2049 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2050 case LyXRC::RC_PRINTPAPERFLAG:
2051 case LyXRC::RC_PRINTREVERSEFLAG:
2052 case LyXRC::RC_PRINTSPOOL_COMMAND:
2053 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2054 case LyXRC::RC_PRINTTOFILE:
2055 case LyXRC::RC_PRINTTOPRINTER:
2056 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2057 case LyXRC::RC_PRINT_COMMAND:
2058 case LyXRC::RC_RTL_SUPPORT:
2059 case LyXRC::RC_SCREEN_DPI:
2060 case LyXRC::RC_SCREEN_FONT_ENCODING:
2061 case LyXRC::RC_SCREEN_FONT_ROMAN:
2062 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2063 case LyXRC::RC_SCREEN_FONT_SANS:
2064 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2065 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2066 case LyXRC::RC_SCREEN_FONT_SIZES:
2067 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2068 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2069 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2070 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2071 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2072 case LyXRC::RC_SCREEN_ZOOM:
2073 case LyXRC::RC_SERVERPIPE:
2074 case LyXRC::RC_SET_COLOR:
2075 case LyXRC::RC_SHOW_BANNER:
2076 case LyXRC::RC_SPELL_COMMAND:
2077 case LyXRC::RC_TEMPDIRPATH:
2078 case LyXRC::RC_TEMPLATEPATH:
2079 case LyXRC::RC_TEX_ALLOWS_SPACES:
2080 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2081 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2082 namespace os = lyx::support::os;
2083 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2085 case LyXRC::RC_UIFILE:
2086 case LyXRC::RC_USER_EMAIL:
2087 case LyXRC::RC_USER_NAME:
2088 case LyXRC::RC_USETEMPDIR:
2089 case LyXRC::RC_USE_ALT_LANG:
2090 case LyXRC::RC_USE_ESC_CHARS:
2091 case LyXRC::RC_USE_INP_ENC:
2092 case LyXRC::RC_USE_PERS_DICT:
2093 case LyXRC::RC_USE_SPELL_LIB:
2094 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2095 case LyXRC::RC_VIEWER:
2096 case LyXRC::RC_LAST: