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::quoteName;
122 using lyx::support::rtrim;
123 using lyx::support::split;
124 using lyx::support::subst;
125 using lyx::support::Systemcall;
126 using lyx::support::token;
127 using lyx::support::trim;
128 using lyx::support::prefixIs;
131 using std::make_pair;
134 using std::istringstream;
135 using std::ostringstream;
137 namespace biblio = lyx::biblio;
138 namespace fs = boost::filesystem;
141 extern BufferList bufferlist;
142 extern LyXServer * lyxserver;
144 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
147 extern tex_accent_struct get_accent(kb_action action);
152 bool getStatus(LCursor cursor,
153 FuncRequest const & cmd, FuncStatus & status)
155 // Try to fix cursor in case it is broken.
156 cursor.fixIfBroken();
158 // This is, of course, a mess. Better create a new doc iterator and use
159 // this in Inset::getStatus. This might require an additional
160 // BufferView * arg, though (which should be avoided)
161 //LCursor safe = *this;
163 for ( ; cursor.depth(); cursor.pop()) {
164 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
165 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
166 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
167 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
169 // The inset's getStatus() will return 'true' if it made
170 // a definitive decision on whether it want to handle the
171 // request or not. The result of this decision is put into
172 // the 'status' parameter.
173 if (cursor.inset().getStatus(cursor, cmd, status)) {
182 /** Return the change status at cursor position, taking in account the
183 * status at each level of the document iterator (a table in a deleted
184 * footnote is deleted).
185 * When \param outer is true, the top slice is not looked at.
187 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
189 size_t const depth = dit.depth() - (outer ? 1 : 0);
191 for (size_t i = 0 ; i < depth ; ++i) {
192 CursorSlice const & slice = dit[i];
193 if (!slice.inset().inMathed()
194 && slice.pos() < slice.paragraph().size()) {
195 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
196 if (ch != Change::UNCHANGED)
200 return Change::UNCHANGED;
205 LyXFunc::LyXFunc(LyXView * lv)
208 keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
209 cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
210 meta_fake_bit(key_modifier::none)
215 void LyXFunc::handleKeyFunc(kb_action action)
217 char c = encoded_last_key;
219 if (keyseq.length()) {
223 owner->view()->getIntl().getTransManager()
224 .deadkey(c, get_accent(action).accent, view()->getLyXText());
225 // Need to clear, in case the minibuffer calls these
228 // copied verbatim from do_accent_char
229 view()->cursor().resetAnchor();
234 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
236 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
238 // Do nothing if we have nothing (JMarc)
239 if (!keysym->isOK()) {
240 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
245 if (keysym->isModifier()) {
246 lyxerr[Debug::KEY] << "isModifier true" << endl;
250 Encoding const * encoding = view()->cursor().getEncoding();
252 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
253 size_t encoded_last_key = keysym->getUCSEncoded();
255 // Do a one-deep top-level lookup for
256 // cancel and meta-fake keys. RVDK_PATCH_5
257 cancel_meta_seq.reset();
259 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
260 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
261 << " action first set to [" << func.action << ']'
264 // When not cancel or meta-fake, do the normal lookup.
265 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
266 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
267 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
268 // remove Caps Lock and Mod2 as a modifiers
269 func = keyseq.addkey(keysym, (state | meta_fake_bit));
270 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
271 << "action now set to ["
272 << func.action << ']' << endl;
275 // Dont remove this unless you know what you are doing.
276 meta_fake_bit = key_modifier::none;
278 // Can this happen now ?
279 if (func.action == LFUN_NOACTION) {
280 func = FuncRequest(LFUN_COMMAND_PREFIX);
283 if (lyxerr.debugging(Debug::KEY)) {
284 lyxerr << BOOST_CURRENT_FUNCTION
286 << func.action << "]["
287 << keyseq.print() << ']'
291 // already here we know if it any point in going further
292 // why not return already here if action == -1 and
293 // num_bytes == 0? (Lgb)
295 if (keyseq.length() > 1) {
296 owner->message(lyx::from_utf8(keyseq.print()));
300 // Maybe user can only reach the key via holding down shift.
301 // Let's see. But only if shift is the only modifier
302 if (func.action == LFUN_UNKNOWN_ACTION &&
303 state == key_modifier::shift) {
304 lyxerr[Debug::KEY] << "Trying without shift" << endl;
305 func = keyseq.addkey(keysym, key_modifier::none);
306 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
309 if (func.action == LFUN_UNKNOWN_ACTION) {
310 // Hmm, we didn't match any of the keysequences. See
311 // if it's normal insertable text not already covered
313 if (keysym->isText() && keyseq.length() == 1) {
314 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
315 func = FuncRequest(LFUN_SELF_INSERT,
316 FuncRequest::KEYBOARD);
318 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
319 owner->message(_("Unknown function."));
324 if (func.action == LFUN_SELF_INSERT) {
325 if (encoded_last_key != 0) {
326 docstring const arg(1, encoded_last_key);
327 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
328 FuncRequest::KEYBOARD));
330 << "SelfInsert arg[`" << lyx::to_utf8(arg) << "']" << endl;
338 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
340 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
342 LCursor & cur = view()->cursor();
344 /* In LyX/Mac, when a dialog is open, the menus of the
345 application can still be accessed without giving focus to
346 the main window. In this case, we want to disable the menu
347 entries that are buffer-related.
349 Note that this code is not perfect, as bug 1941 attests:
350 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
353 if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
356 buf = owner->buffer();
358 if (cmd.action == LFUN_NOACTION) {
359 flag.message(lyx::from_utf8(N_("Nothing to do")));
364 switch (cmd.action) {
365 case LFUN_UNKNOWN_ACTION:
366 #ifndef HAVE_LIBAIKSAURUS
367 case LFUN_THESAURUS_ENTRY:
373 flag |= lyx_gui::getStatus(cmd);
376 if (flag.unknown()) {
377 flag.message(lyx::from_utf8(N_("Unknown action")));
381 if (!flag.enabled()) {
382 if (flag.message().empty())
383 flag.message(lyx::from_utf8(N_("Command disabled")));
387 // Check whether we need a buffer
388 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
390 flag.message(lyx::from_utf8(N_("Command not allowed with"
391 "out any document open")));
396 // I would really like to avoid having this switch and rather try to
397 // encode this in the function itself.
398 // -- And I'd rather let an inset decide which LFUNs it is willing
399 // to handle (Andre')
401 switch (cmd.action) {
402 case LFUN_TOOLTIPS_TOGGLE:
403 flag.setOnOff(owner->getDialogs().tooltipsEnabled());
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 != "none";
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 && buf->params().tracking_changes
639 && lookupChangeType(cur, true) == Change::DELETED
640 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
641 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
642 flag.message(lyx::from_utf8(N_("This portion of the document is deleted.")));
646 // the default error message if we disable the command
647 if (!flag.enabled() && flag.message().empty())
648 flag.message(lyx::from_utf8(N_("Command disabled")));
656 bool ensureBufferClean(BufferView * bv)
658 Buffer & buf = *bv->buffer();
662 docstring const file = makeDisplayPath(buf.fileName(), 30);
663 docstring text = bformat(_("The document %1$s has unsaved "
664 "changes.\n\nDo you want to save "
665 "the document?"), file);
666 int const ret = Alert::prompt(_("Save changed document?"),
667 text, 0, 1, _("&Save"),
671 bv->owner()->dispatch(FuncRequest(LFUN_BUFFER_WRITE));
673 return buf.isClean();
677 void showPrintError(string const & name)
679 docstring str = bformat(_("Could not print the document %1$s.\n"
680 "Check that your printer is set up correctly."),
681 makeDisplayPath(name, 50));
682 Alert::error(_("Print document failed"), str);
686 void loadTextclass(string const & name)
688 std::pair<bool, lyx::textclass_type> const tc_pair =
689 textclasslist.numberOfClass(name);
691 if (!tc_pair.first) {
692 lyxerr << "Document class \"" << name
693 << "\" does not exist."
698 lyx::textclass_type const tc = tc_pair.second;
700 if (!textclasslist[tc].load()) {
701 docstring s = bformat(_("The document could not be converted\n"
702 "into the document class %1$s."),
703 lyx::from_utf8(textclasslist[tc].name()));
704 Alert::error(_("Could not change class"), s);
709 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
714 void LyXFunc::dispatch(FuncRequest const & cmd)
716 BOOST_ASSERT(view());
717 string const argument = lyx::to_utf8(cmd.argument());
718 kb_action const action = cmd.action;
720 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
721 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
723 // we have not done anything wrong yet.
725 dispatch_buffer.erase();
727 // redraw the screen at the end (first of the two drawing steps).
728 //This is done unless explicitely requested otherwise
730 // also do the second redrawing step. Only done if requested.
731 bool updateforce = false;
733 FuncStatus const flag = getStatus(cmd);
734 if (!flag.enabled()) {
735 // We cannot use this function here
736 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
737 << lyxaction.getActionName(action)
738 << " [" << action << "] is disabled at this location"
740 setErrorMessage(flag.message());
744 case LFUN_WORD_FIND_FORWARD:
745 case LFUN_WORD_FIND_BACKWARD: {
746 static string last_search;
747 string searched_string;
749 if (!argument.empty()) {
750 last_search = argument;
751 searched_string = argument;
753 searched_string = last_search;
756 if (searched_string.empty())
759 bool const fw = action == LFUN_WORD_FIND_FORWARD;
761 lyx::find::find2string(searched_string, true, false, fw);
762 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
766 case LFUN_COMMAND_PREFIX:
767 owner->message(lyx::from_utf8(keyseq.printOptions()));
770 case LFUN_COMMAND_EXECUTE:
771 owner->getToolbars().display("minibuffer", true);
772 owner->focus_command_buffer();
777 meta_fake_bit = key_modifier::none;
778 if (view()->buffer())
779 // cancel any selection
780 dispatch(FuncRequest(LFUN_MARK_OFF));
781 setMessage(_("Cancel"));
784 case LFUN_META_PREFIX:
785 meta_fake_bit = key_modifier::alt;
786 setMessage(lyx::from_utf8(keyseq.print()));
789 case LFUN_BUFFER_TOGGLE_READ_ONLY:
790 if (owner->buffer()->lyxvc().inUse())
791 owner->buffer()->lyxvc().toggleReadOnly();
793 owner->buffer()->setReadonly(
794 !owner->buffer()->isReadonly());
797 // --- Menus -----------------------------------------------
798 case LFUN_BUFFER_NEW:
799 menuNew(argument, false);
802 case LFUN_BUFFER_NEW_TEMPLATE:
803 menuNew(argument, true);
806 case LFUN_BUFFER_CLOSE:
810 case LFUN_BUFFER_WRITE:
811 if (!owner->buffer()->isUnnamed()) {
812 docstring const str = bformat(_("Saving document %1$s..."),
813 makeDisplayPath(owner->buffer()->fileName()));
815 menuWrite(owner->buffer());
816 owner->message(str + _(" done."));
818 writeAs(owner->buffer());
822 case LFUN_BUFFER_WRITE_AS:
823 writeAs(owner->buffer(), argument);
827 case LFUN_BUFFER_RELOAD: {
828 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
829 docstring text = bformat(_("Any changes will be lost. Are you sure "
830 "you want to revert to the saved version of the document %1$s?"), file);
831 int const ret = Alert::prompt(_("Revert to saved document?"),
832 text, 0, 1, _("&Revert"), _("&Cancel"));
839 case LFUN_BUFFER_UPDATE:
840 Exporter::Export(owner->buffer(), argument, true);
843 case LFUN_BUFFER_VIEW:
844 Exporter::preview(owner->buffer(), argument);
847 case LFUN_BUILD_PROGRAM:
848 Exporter::Export(owner->buffer(), "program", true);
851 case LFUN_BUFFER_CHKTEX:
852 owner->buffer()->runChktex();
855 case LFUN_BUFFER_EXPORT:
856 if (argument == "custom")
857 owner->getDialogs().show("sendto");
859 Exporter::Export(owner->buffer(), argument, false);
863 case LFUN_BUFFER_EXPORT_CUSTOM: {
865 string command = split(argument, format_name, ' ');
866 Format const * format = formats.getFormat(format_name);
868 lyxerr << "Format \"" << format_name
869 << "\" not recognized!"
874 Buffer * buffer = owner->buffer();
876 // The name of the file created by the conversion process
879 // Output to filename
880 if (format->name() == "lyx") {
881 string const latexname =
882 buffer->getLatexName(false);
883 filename = changeExtension(latexname,
884 format->extension());
885 filename = addName(buffer->temppath(), filename);
887 if (!buffer->writeFile(filename))
891 Exporter::Export(buffer, format_name, true, filename);
894 // Substitute $$FName for filename
895 if (!contains(command, "$$FName"))
896 command = "( " + command + " ) < $$FName";
897 command = subst(command, "$$FName", filename);
899 // Execute the command in the background
901 call.startscript(Systemcall::DontWait, command);
905 case LFUN_BUFFER_PRINT: {
908 string command = split(split(argument, target, ' '),
912 || target_name.empty()
913 || command.empty()) {
914 lyxerr << "Unable to parse \""
915 << argument << '"' << std::endl;
918 if (target != "printer" && target != "file") {
919 lyxerr << "Unrecognized target \""
920 << target << '"' << std::endl;
924 Buffer * buffer = owner->buffer();
926 if (!Exporter::Export(buffer, "dvi", true)) {
927 showPrintError(buffer->fileName());
931 // Push directory path.
932 string const path = buffer->temppath();
933 lyx::support::Path p(path);
935 // there are three cases here:
936 // 1. we print to a file
937 // 2. we print directly to a printer
938 // 3. we print using a spool command (print to file first)
941 string const dviname =
942 changeExtension(buffer->getLatexName(true),
945 if (target == "printer") {
946 if (!lyxrc.print_spool_command.empty()) {
947 // case 3: print using a spool
948 string const psname =
949 changeExtension(dviname,".ps");
950 command += lyxrc.print_to_file
953 + quoteName(dviname);
956 lyxrc.print_spool_command +' ';
957 if (target_name != "default") {
958 command2 += lyxrc.print_spool_printerprefix
962 command2 += quoteName(psname);
964 // If successful, then spool command
965 res = one.startscript(
970 res = one.startscript(
971 Systemcall::DontWait,
974 // case 2: print directly to a printer
975 res = one.startscript(
976 Systemcall::DontWait,
977 command + quoteName(dviname));
981 // case 1: print to a file
982 command += lyxrc.print_to_file
983 + quoteName(makeAbsPath(target_name,
986 + quoteName(dviname);
987 res = one.startscript(Systemcall::DontWait,
992 showPrintError(buffer->fileName());
996 case LFUN_BUFFER_IMPORT:
1001 if (view()->buffer()) {
1002 // save cursor Position for opened files to .lyx/session
1003 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1004 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1005 // save bookmarks to .lyx/session
1006 view()->saveSavedPositions();
1008 quitLyX(argument == "force");
1011 case LFUN_TOC_VIEW: {
1012 InsetCommandParams p("tableofcontents");
1013 string const data = InsetCommandMailer::params2string("toc", p);
1014 owner->getDialogs().show("toc", data, 0);
1018 case LFUN_BUFFER_AUTO_SAVE:
1022 case LFUN_RECONFIGURE:
1023 reconfigure(view());
1026 case LFUN_HELP_OPEN: {
1027 string const arg = argument;
1029 setErrorMessage(_("Missing argument"));
1032 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1033 if (fname.empty()) {
1034 lyxerr << "LyX: unable to find documentation file `"
1035 << arg << "'. Bad installation?" << endl;
1038 owner->message(bformat(_("Opening help file %1$s..."),
1039 makeDisplayPath(fname)));
1040 owner->loadLyXFile(fname, false);
1044 // --- version control -------------------------------
1045 case LFUN_VC_REGISTER:
1046 if (!ensureBufferClean(view()))
1048 if (!owner->buffer()->lyxvc().inUse()) {
1049 owner->buffer()->lyxvc().registrer();
1054 case LFUN_VC_CHECK_IN:
1055 if (!ensureBufferClean(view()))
1057 if (owner->buffer()->lyxvc().inUse()
1058 && !owner->buffer()->isReadonly()) {
1059 owner->buffer()->lyxvc().checkIn();
1064 case LFUN_VC_CHECK_OUT:
1065 if (!ensureBufferClean(view()))
1067 if (owner->buffer()->lyxvc().inUse()
1068 && owner->buffer()->isReadonly()) {
1069 owner->buffer()->lyxvc().checkOut();
1074 case LFUN_VC_REVERT:
1075 owner->buffer()->lyxvc().revert();
1079 case LFUN_VC_UNDO_LAST:
1080 owner->buffer()->lyxvc().undoLast();
1084 // --- buffers ----------------------------------------
1085 case LFUN_BUFFER_SWITCH:
1086 owner->setBuffer(bufferlist.getBuffer(argument));
1089 case LFUN_BUFFER_NEXT:
1090 owner->setBuffer(bufferlist.next(view()->buffer()));
1093 case LFUN_BUFFER_PREVIOUS:
1094 owner->setBuffer(bufferlist.previous(view()->buffer()));
1098 newFile(view(), argument);
1101 case LFUN_FILE_OPEN:
1105 case LFUN_DROP_LAYOUTS_CHOICE:
1106 owner->getToolbars().openLayoutList();
1109 case LFUN_MENU_OPEN:
1110 owner->getMenubar().openByName(lyx::from_utf8(argument));
1113 // --- lyxserver commands ----------------------------
1114 case LFUN_SERVER_GET_NAME:
1115 setMessage(lyx::from_utf8(owner->buffer()->fileName()));
1116 lyxerr[Debug::INFO] << "FNAME["
1117 << owner->buffer()->fileName()
1121 case LFUN_SERVER_NOTIFY:
1122 dispatch_buffer = lyx::from_utf8(keyseq.print());
1123 lyxserver->notifyClient(lyx::to_utf8(dispatch_buffer));
1126 case LFUN_SERVER_GOTO_FILE_ROW: {
1129 istringstream is(argument);
1130 is >> file_name >> row;
1131 if (prefixIs(file_name, package().temp_dir())) {
1132 // Needed by inverse dvi search. If it is a file
1133 // in tmpdir, call the apropriated function
1134 owner->setBuffer(bufferlist.getBufferFromTmp(file_name));
1136 // Must replace extension of the file to be .lyx
1137 // and get full path
1138 string const s = changeExtension(file_name, ".lyx");
1139 // Either change buffer or load the file
1140 if (bufferlist.exists(s)) {
1141 owner->setBuffer(bufferlist.getBuffer(s));
1143 owner->loadLyXFile(s);
1147 view()->setCursorFromRow(row);
1150 // see BufferView::center()
1154 case LFUN_DIALOG_SHOW: {
1155 string const name = cmd.getArg(0);
1156 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1158 if (name == "character") {
1159 data = freefont2string();
1161 owner->getDialogs().show("character", data);
1162 } else if (name == "latexlog") {
1163 pair<Buffer::LogType, string> const logfile =
1164 owner->buffer()->getLogName();
1165 switch (logfile.first) {
1166 case Buffer::latexlog:
1169 case Buffer::buildlog:
1173 data += LyXLex::quoteString(logfile.second);
1174 owner->getDialogs().show("log", data);
1175 } else if (name == "vclog") {
1176 string const data = "vc " +
1177 LyXLex::quoteString(owner->buffer()->lyxvc().getLogFile());
1178 owner->getDialogs().show("log", data);
1180 owner->getDialogs().show(name, data);
1184 case LFUN_DIALOG_SHOW_NEW_INSET: {
1185 string const name = cmd.getArg(0);
1186 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1187 if (name == "bibitem" ||
1194 InsetCommandParams p(name);
1195 data = InsetCommandMailer::params2string(name, p);
1196 } else if (name == "include") {
1197 InsetCommandParams p(data);
1198 data = InsetIncludeMailer::params2string(p);
1199 } else if (name == "box") {
1200 // \c data == "Boxed" || "Frameless" etc
1201 InsetBoxParams p(data);
1202 data = InsetBoxMailer::params2string(p);
1203 } else if (name == "branch") {
1204 InsetBranchParams p;
1205 data = InsetBranchMailer::params2string(p);
1206 } else if (name == "citation") {
1207 InsetCommandParams p("cite");
1208 data = InsetCommandMailer::params2string(name, p);
1209 } else if (name == "ert") {
1210 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1211 } else if (name == "external") {
1212 InsetExternalParams p;
1213 Buffer const & buffer = *owner->buffer();
1214 data = InsetExternalMailer::params2string(p, buffer);
1215 } else if (name == "float") {
1217 data = InsetFloatMailer::params2string(p);
1218 } else if (name == "graphics") {
1219 InsetGraphicsParams p;
1220 Buffer const & buffer = *owner->buffer();
1221 data = InsetGraphicsMailer::params2string(p, buffer);
1222 } else if (name == "note") {
1224 data = InsetNoteMailer::params2string(p);
1225 } else if (name == "vspace") {
1227 data = InsetVSpaceMailer::params2string(space);
1228 } else if (name == "wrap") {
1230 data = InsetWrapMailer::params2string(p);
1232 owner->getDialogs().show(name, data, 0);
1236 case LFUN_DIALOG_UPDATE: {
1237 string const & name = argument;
1238 // Can only update a dialog connected to an existing inset
1239 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1241 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1242 inset->dispatch(view()->cursor(), fr);
1243 } else if (name == "paragraph") {
1244 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1245 } else if (name == "prefs") {
1246 owner->getDialogs().update(name, string());
1251 case LFUN_DIALOG_HIDE:
1252 Dialogs::hide(argument, 0);
1255 case LFUN_DIALOG_DISCONNECT_INSET:
1256 owner->getDialogs().disconnect(argument);
1260 case LFUN_CITATION_INSERT: {
1261 if (!argument.empty()) {
1262 // we can have one optional argument, delimited by '|'
1263 // citation-insert <key>|<text_before>
1264 // this should be enhanced to also support text_after
1265 // and citation style
1266 string arg = argument;
1268 if (contains(argument, "|")) {
1269 arg = token(argument, '|', 0);
1270 opt1 = '[' + token(argument, '|', 1) + ']';
1272 std::ostringstream os;
1273 os << "citation LatexCommand\n"
1274 << "\\cite" << opt1 << "{" << arg << "}\n"
1276 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1279 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1283 case LFUN_BUFFER_CHILD_OPEN: {
1284 string const filename =
1285 makeAbsPath(argument, owner->buffer()->filePath());
1286 // FIXME Should use bformat
1287 setMessage(_("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->view()->getIntl().keyMapOn(false);
1310 case LFUN_KEYMAP_PRIMARY:
1311 owner->view()->getIntl().keyMapPrim();
1314 case LFUN_KEYMAP_SECONDARY:
1315 owner->view()->getIntl().keyMapSec();
1318 case LFUN_KEYMAP_TOGGLE:
1319 owner->view()->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 lyx::support::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(_("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 // FIXME Should use bformat
1492 setMessage(_("Document defaults saved in ")
1493 + makeDisplayPath(fname));
1495 setErrorMessage(_("Unable to save document defaults"));
1499 case LFUN_BUFFER_PARAMS_APPLY: {
1500 biblio::CiteEngine const engine =
1501 owner->buffer()->params().cite_engine;
1503 istringstream ss(argument);
1506 int const unknown_tokens =
1507 owner->buffer()->readHeader(lex);
1509 if (unknown_tokens != 0) {
1510 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1511 << unknown_tokens << " unknown token"
1512 << (unknown_tokens == 1 ? "" : "s")
1515 if (engine == owner->buffer()->params().cite_engine)
1518 LCursor & cur = view()->cursor();
1519 FuncRequest fr(LFUN_INSET_REFRESH);
1521 InsetBase & inset = owner->buffer()->inset();
1522 InsetIterator it = inset_iterator_begin(inset);
1523 InsetIterator const end = inset_iterator_end(inset);
1524 for (; it != end; ++it)
1525 if (it->lyxCode() == InsetBase::CITE_CODE)
1526 it->dispatch(cur, fr);
1530 case LFUN_TEXTCLASS_APPLY: {
1531 Buffer * buffer = owner->buffer();
1533 lyx::textclass_type const old_class =
1534 buffer->params().textclass;
1536 loadTextclass(argument);
1538 std::pair<bool, lyx::textclass_type> const tc_pair =
1539 textclasslist.numberOfClass(argument);
1544 lyx::textclass_type const new_class = tc_pair.second;
1545 if (old_class == new_class)
1549 owner->message(_("Converting document to new document class..."));
1550 recordUndoFullDocument(view());
1551 buffer->params().textclass = new_class;
1552 StableDocIterator backcur(view()->cursor());
1553 ErrorList & el = buffer->errorList("Class Switch");
1554 lyx::cap::switchBetweenClasses(
1555 old_class, new_class,
1556 static_cast<InsetText &>(buffer->inset()), el);
1558 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1560 buffer->errors("Class Switch");
1561 updateLabels(*buffer);
1566 case LFUN_TEXTCLASS_LOAD:
1567 loadTextclass(argument);
1570 case LFUN_LYXRC_APPLY: {
1571 LyXRC const lyxrc_orig = lyxrc;
1573 istringstream ss(argument);
1574 bool const success = lyxrc.read(ss) == 0;
1577 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1578 << "Unable to read lyxrc data"
1583 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1588 view()->cursor().dispatch(cmd);
1589 updateforce |= view()->cursor().result().update();
1590 if (!view()->cursor().result().dispatched())
1591 updateforce |= view()->dispatch(cmd);
1596 if (view()->buffer()) {
1597 // Redraw screen unless explicitly told otherwise.
1598 // This also initializes the position cache for all insets
1599 // in (at least partially) visible top-level paragraphs.
1601 view()->update(Update::FitCursor | Update::Force);
1603 view()->update(Update::FitCursor);
1605 owner->redrawWorkArea();
1607 // if we executed a mutating lfun, mark the buffer as dirty
1609 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1610 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1611 view()->buffer()->markDirty();
1613 if (view()->cursor().inTexted()) {
1614 owner->updateLayoutChoice();
1619 // FIXME UNICODE: _() does not support anything but ascii.
1620 // Do we need a lyx::to_ascii() method?
1621 sendDispatchMessage(getMessage(), cmd);
1625 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1627 /* When an action did not originate from the UI/kbd, it makes
1628 * sense to avoid updating the GUI. It turns out that this
1629 * fixes bug 1941, for reasons that are described here:
1630 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1632 if (cmd.origin != FuncRequest::INTERNAL) {
1633 owner->updateMenubar();
1634 owner->updateToolbars();
1637 const bool verbose = (cmd.origin == FuncRequest::UI
1638 || cmd.origin == FuncRequest::COMMANDBUFFER);
1640 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1641 lyxerr[Debug::ACTION] << "dispatch msg is " << lyx::to_utf8(msg) << endl;
1643 owner->message(msg);
1647 docstring dispatch_msg = msg;
1648 if (!dispatch_msg.empty())
1649 dispatch_msg += ' ';
1651 string comname = lyxaction.getActionName(cmd.action);
1653 bool argsadded = false;
1655 if (!cmd.argument().empty()) {
1656 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1657 comname += ' ' + lyx::to_utf8(cmd.argument());
1662 string const shortcuts = toplevel_keymap->printbindings(cmd);
1664 if (!shortcuts.empty())
1665 comname += ": " + shortcuts;
1666 else if (!argsadded && !cmd.argument().empty())
1667 comname += ' ' + lyx::to_utf8(cmd.argument());
1669 if (!comname.empty()) {
1670 comname = rtrim(comname);
1671 dispatch_msg += lyx::from_utf8('(' + rtrim(comname) + ')');
1674 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1675 << lyx::to_utf8(dispatch_msg) << endl;
1676 if (!dispatch_msg.empty())
1677 owner->message(dispatch_msg);
1681 void LyXFunc::setupLocalKeymap()
1683 keyseq.stdmap = toplevel_keymap.get();
1684 keyseq.curmap = toplevel_keymap.get();
1685 cancel_meta_seq.stdmap = toplevel_keymap.get();
1686 cancel_meta_seq.curmap = toplevel_keymap.get();
1690 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1692 string initpath = lyxrc.document_path;
1693 string filename(name);
1695 if (view()->buffer()) {
1696 string const trypath = owner->buffer()->filePath();
1697 // If directory is writeable, use this as default.
1698 if (isDirWriteable(trypath))
1702 static int newfile_number;
1704 if (filename.empty()) {
1705 filename = addName(lyxrc.document_path,
1706 "newfile" + convert<string>(++newfile_number) + ".lyx");
1707 while (bufferlist.exists(filename) || fs::is_readable(filename)) {
1709 filename = addName(lyxrc.document_path,
1710 "newfile" + convert<string>(newfile_number) +
1715 // The template stuff
1718 FileDialog fileDlg(lyx::to_utf8(_("Select template file")),
1719 LFUN_SELECT_FILE_SYNC,
1720 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1721 string(lyxrc.document_path)),
1722 make_pair(string(lyx::to_utf8(_("Templates|#T#t"))),
1723 string(lyxrc.template_path)));
1725 FileDialog::Result result =
1726 fileDlg.open(lyxrc.template_path,
1727 FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1730 if (result.first == FileDialog::Later)
1732 if (result.second.empty())
1734 templname = result.second;
1737 Buffer * const b = newFile(filename, templname, !name.empty());
1739 owner->setBuffer(b);
1743 void LyXFunc::open(string const & fname)
1745 string initpath = lyxrc.document_path;
1747 if (view()->buffer()) {
1748 string const trypath = owner->buffer()->filePath();
1749 // If directory is writeable, use this as default.
1750 if (isDirWriteable(trypath))
1756 if (fname.empty()) {
1757 FileDialog fileDlg(lyx::to_utf8(_("Select document to open")),
1759 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1760 string(lyxrc.document_path)),
1761 make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1762 string(addPath(package().system_support(), "examples"))));
1764 FileDialog::Result result =
1765 fileDlg.open(initpath,
1766 FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1769 if (result.first == FileDialog::Later)
1772 filename = result.second;
1774 // check selected filename
1775 if (filename.empty()) {
1776 owner->message(_("Canceled."));
1782 // get absolute path of file and add ".lyx" to the filename if
1784 string const fullpath = fileSearch(string(), filename, "lyx");
1785 if (!fullpath.empty()) {
1786 filename = fullpath;
1789 docstring const disp_fn = makeDisplayPath(filename);
1791 // if the file doesn't exist, let the user create one
1792 if (!fs::exists(filename)) {
1793 // the user specifically chose this name. Believe him.
1794 Buffer * const b = newFile(filename, string(), true);
1796 owner->setBuffer(b);
1800 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1803 if (owner->loadLyXFile(filename)) {
1804 str2 = bformat(_("Document %1$s opened."), disp_fn);
1806 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1808 owner->message(str2);
1812 void LyXFunc::doImport(string const & argument)
1815 string filename = split(argument, format, ' ');
1817 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1818 << " file: " << filename << endl;
1820 // need user interaction
1821 if (filename.empty()) {
1822 string initpath = lyxrc.document_path;
1824 if (view()->buffer()) {
1825 string const trypath = owner->buffer()->filePath();
1826 // If directory is writeable, use this as default.
1827 if (isDirWriteable(trypath))
1831 docstring const text = bformat(_("Select %1$s file to import"),
1832 formats.prettyName(format));
1834 FileDialog fileDlg(lyx::to_utf8(text),
1836 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1837 string(lyxrc.document_path)),
1838 make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1839 string(addPath(package().system_support(), "examples"))));
1841 string const filter = lyx::to_utf8(formats.prettyName(format))
1842 + " (*." + formats.extension(format) + ')';
1844 FileDialog::Result result =
1845 fileDlg.open(initpath,
1846 FileFilterList(filter),
1849 if (result.first == FileDialog::Later)
1852 filename = result.second;
1854 // check selected filename
1855 if (filename.empty())
1856 owner->message(_("Canceled."));
1859 if (filename.empty())
1862 // get absolute path of file
1863 filename = makeAbsPath(filename);
1865 string const lyxfile = changeExtension(filename, ".lyx");
1867 // Check if the document already is open
1868 if (lyx_gui::use_gui && bufferlist.exists(lyxfile)) {
1869 if (!bufferlist.close(bufferlist.getBuffer(lyxfile), true)) {
1870 owner->message(_("Canceled."));
1875 // if the file exists already, and we didn't do
1876 // -i lyx thefile.lyx, warn
1877 if (fs::exists(lyxfile) && filename != lyxfile) {
1878 docstring const file = makeDisplayPath(lyxfile, 30);
1880 docstring text = bformat(_("The document %1$s already exists.\n\n"
1881 "Do you want to over-write that document?"), file);
1882 int const ret = Alert::prompt(_("Over-write document?"),
1883 text, 0, 1, _("&Over-write"), _("&Cancel"));
1886 owner->message(_("Canceled."));
1891 ErrorList errorList;
1892 Importer::Import(owner, filename, format, errorList);
1893 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1897 void LyXFunc::closeBuffer()
1899 // save current cursor position
1900 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1901 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1902 if (bufferlist.close(owner->buffer(), true) && !quitting) {
1903 if (bufferlist.empty()) {
1904 // need this otherwise SEGV may occur while
1905 // trying to set variables that don't exist
1906 // since there's no current buffer
1907 owner->getDialogs().hideBufferDependent();
1909 owner->setBuffer(bufferlist.first());
1915 // Each "owner" should have it's own message method. lyxview and
1916 // the minibuffer would use the minibuffer, but lyxserver would
1917 // send an ERROR signal to its client. Alejandro 970603
1918 // This function is bit problematic when it comes to NLS, to make the
1919 // lyx servers client be language indepenent we must not translate
1920 // strings sent to this func.
1921 void LyXFunc::setErrorMessage(docstring const & m) const
1923 dispatch_buffer = m;
1928 void LyXFunc::setMessage(docstring const & m) const
1930 dispatch_buffer = m;
1934 string const LyXFunc::viewStatusMessage()
1936 // When meta-fake key is pressed, show the key sequence so far + "M-".
1938 return keyseq.print() + "M-";
1940 // Else, when a non-complete key sequence is pressed,
1941 // show the available options.
1942 if (keyseq.length() > 0 && !keyseq.deleted())
1943 return keyseq.printOptions();
1945 if (!view()->buffer())
1946 return lyx::to_utf8(_("Welcome to LyX!"));
1948 return view()->cursor().currentState();
1952 BufferView * LyXFunc::view() const
1954 BOOST_ASSERT(owner);
1955 return owner->view();
1959 bool LyXFunc::wasMetaKey() const
1961 return (meta_fake_bit != key_modifier::none);
1967 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1969 // Why the switch you might ask. It is a trick to ensure that all
1970 // the elements in the LyXRCTags enum is handled. As you can see
1971 // there are no breaks at all. So it is just a huge fall-through.
1972 // The nice thing is that we will get a warning from the compiler
1973 // if we forget an element.
1974 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1976 case LyXRC::RC_ACCEPT_COMPOUND:
1977 case LyXRC::RC_ALT_LANG:
1978 case LyXRC::RC_ASCIIROFF_COMMAND:
1979 case LyXRC::RC_ASCII_LINELEN:
1980 case LyXRC::RC_AUTOREGIONDELETE:
1981 case LyXRC::RC_AUTORESET_OPTIONS:
1982 case LyXRC::RC_AUTOSAVE:
1983 case LyXRC::RC_AUTO_NUMBER:
1984 case LyXRC::RC_BACKUPDIR_PATH:
1985 case LyXRC::RC_BIBTEX_COMMAND:
1986 case LyXRC::RC_BINDFILE:
1987 case LyXRC::RC_CHECKLASTFILES:
1988 case LyXRC::RC_USELASTFILEPOS:
1989 case LyXRC::RC_LOADSESSION:
1990 case LyXRC::RC_CHKTEX_COMMAND:
1991 case LyXRC::RC_CONVERTER:
1992 case LyXRC::RC_COPIER:
1993 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1994 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1995 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1996 case LyXRC::RC_DATE_INSERT_FORMAT:
1997 case LyXRC::RC_DEFAULT_LANGUAGE:
1998 case LyXRC::RC_DEFAULT_PAPERSIZE:
1999 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2000 case LyXRC::RC_DISPLAY_GRAPHICS:
2001 case LyXRC::RC_DOCUMENTPATH:
2002 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2003 if (fs::exists(lyxrc_new.document_path) &&
2004 fs::is_directory(lyxrc_new.document_path)) {
2005 using lyx::support::package;
2006 package().document_dir() = lyxrc.document_path;
2009 case LyXRC::RC_ESC_CHARS:
2010 case LyXRC::RC_FONT_ENCODING:
2011 case LyXRC::RC_FORMAT:
2012 case LyXRC::RC_INDEX_COMMAND:
2013 case LyXRC::RC_INPUT:
2014 case LyXRC::RC_KBMAP:
2015 case LyXRC::RC_KBMAP_PRIMARY:
2016 case LyXRC::RC_KBMAP_SECONDARY:
2017 case LyXRC::RC_LABEL_INIT_LENGTH:
2018 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2019 case LyXRC::RC_LANGUAGE_AUTO_END:
2020 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2021 case LyXRC::RC_LANGUAGE_COMMAND_END:
2022 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2023 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2024 case LyXRC::RC_LANGUAGE_PACKAGE:
2025 case LyXRC::RC_LANGUAGE_USE_BABEL:
2026 case LyXRC::RC_MAKE_BACKUP:
2027 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2028 case LyXRC::RC_NUMLASTFILES:
2029 case LyXRC::RC_PATH_PREFIX:
2030 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2031 using lyx::support::prependEnvPath;
2032 prependEnvPath("PATH", lyxrc.path_prefix);
2034 case LyXRC::RC_PERS_DICT:
2035 case LyXRC::RC_POPUP_BOLD_FONT:
2036 case LyXRC::RC_POPUP_FONT_ENCODING:
2037 case LyXRC::RC_POPUP_NORMAL_FONT:
2038 case LyXRC::RC_PREVIEW:
2039 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2040 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2041 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2042 case LyXRC::RC_PRINTCOPIESFLAG:
2043 case LyXRC::RC_PRINTER:
2044 case LyXRC::RC_PRINTEVENPAGEFLAG:
2045 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2046 case LyXRC::RC_PRINTFILEEXTENSION:
2047 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2048 case LyXRC::RC_PRINTODDPAGEFLAG:
2049 case LyXRC::RC_PRINTPAGERANGEFLAG:
2050 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2051 case LyXRC::RC_PRINTPAPERFLAG:
2052 case LyXRC::RC_PRINTREVERSEFLAG:
2053 case LyXRC::RC_PRINTSPOOL_COMMAND:
2054 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2055 case LyXRC::RC_PRINTTOFILE:
2056 case LyXRC::RC_PRINTTOPRINTER:
2057 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2058 case LyXRC::RC_PRINT_COMMAND:
2059 case LyXRC::RC_RTL_SUPPORT:
2060 case LyXRC::RC_SCREEN_DPI:
2061 case LyXRC::RC_SCREEN_FONT_ENCODING:
2062 case LyXRC::RC_SCREEN_FONT_ROMAN:
2063 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2064 case LyXRC::RC_SCREEN_FONT_SANS:
2065 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2066 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2067 case LyXRC::RC_SCREEN_FONT_SIZES:
2068 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2069 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2070 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2071 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2072 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2073 case LyXRC::RC_SCREEN_ZOOM:
2074 case LyXRC::RC_SERVERPIPE:
2075 case LyXRC::RC_SET_COLOR:
2076 case LyXRC::RC_SHOW_BANNER:
2077 case LyXRC::RC_SPELL_COMMAND:
2078 case LyXRC::RC_TEMPDIRPATH:
2079 case LyXRC::RC_TEMPLATEPATH:
2080 case LyXRC::RC_TEX_ALLOWS_SPACES:
2081 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2082 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2083 namespace os = lyx::support::os;
2084 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2086 case LyXRC::RC_UIFILE:
2087 case LyXRC::RC_USER_EMAIL:
2088 case LyXRC::RC_USER_NAME:
2089 case LyXRC::RC_USETEMPDIR:
2090 case LyXRC::RC_USE_ALT_LANG:
2091 case LyXRC::RC_USE_ESC_CHARS:
2092 case LyXRC::RC_USE_INP_ENC:
2093 case LyXRC::RC_USE_PERS_DICT:
2094 case LyXRC::RC_USE_SPELL_LIB:
2095 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2096 case LyXRC::RC_VIEWER:
2097 case LyXRC::RC_LAST: