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->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()->available())
779 // cancel any selection
780 dispatch(FuncRequest(LFUN_MARK_OFF));
781 setMessage(lyx::from_utf8(N_("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()->available()) {
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(lyx::from_utf8(N_("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 setMessage(lyx::from_utf8(N_("Opening child document ")) +
1287 makeDisplayPath(filename) + lyx::from_ascii("..."));
1288 view()->savePosition(0);
1289 string const parentfilename = owner->buffer()->fileName();
1290 if (bufferlist.exists(filename))
1291 owner->setBuffer(bufferlist.getBuffer(filename));
1293 owner->loadLyXFile(filename);
1294 // Set the parent name of the child document.
1295 // This makes insertion of citations and references in the child work,
1296 // when the target is in the parent or another child document.
1297 owner->buffer()->setParentName(parentfilename);
1301 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1302 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1305 case LFUN_KEYMAP_OFF:
1306 owner->getIntl().keyMapOn(false);
1309 case LFUN_KEYMAP_PRIMARY:
1310 owner->getIntl().keyMapPrim();
1313 case LFUN_KEYMAP_SECONDARY:
1314 owner->getIntl().keyMapSec();
1317 case LFUN_KEYMAP_TOGGLE:
1318 owner->getIntl().toggleKeyMap();
1324 string rest = split(argument, countstr, ' ');
1325 istringstream is(countstr);
1328 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1329 for (int i = 0; i < count; ++i)
1330 dispatch(lyxaction.lookupFunc(rest));
1334 case LFUN_COMMAND_SEQUENCE: {
1335 // argument contains ';'-terminated commands
1336 string arg = argument;
1337 while (!arg.empty()) {
1339 arg = split(arg, first, ';');
1340 FuncRequest func(lyxaction.lookupFunc(first));
1341 func.origin = cmd.origin;
1347 case LFUN_PREFERENCES_SAVE: {
1348 lyx::support::Path p(package().user_support());
1349 lyxrc.write("preferences", false);
1353 case LFUN_SCREEN_FONT_UPDATE:
1354 // handle the screen font changes.
1355 lyxrc.set_font_norm_type();
1356 lyx_gui::update_fonts();
1357 // All visible buffers will need resize
1361 case LFUN_SET_COLOR: {
1363 string const x11_name = split(argument, lyx_name, ' ');
1364 if (lyx_name.empty() || x11_name.empty()) {
1365 setErrorMessage(lyx::from_utf8(N_("Syntax: set-color <lyx_name>"
1370 bool const graphicsbg_changed =
1371 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1372 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1374 if (!lcolor.setColor(lyx_name, x11_name)) {
1376 bformat(_("Set-color \"%1$s\" failed "
1377 "- color is undefined or "
1378 "may not be redefined"),
1379 lyx::from_utf8(lyx_name)));
1383 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1385 if (graphicsbg_changed) {
1386 #ifdef WITH_WARNINGS
1387 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1390 lyx::graphics::GCache::get().changeDisplay(true);
1397 owner->message(lyx::from_utf8(argument));
1400 case LFUN_TOOLTIPS_TOGGLE:
1401 owner->getDialogs().toggleTooltips();
1404 case LFUN_EXTERNAL_EDIT: {
1405 FuncRequest fr(action, argument);
1406 InsetExternal().dispatch(view()->cursor(), fr);
1410 case LFUN_GRAPHICS_EDIT: {
1411 FuncRequest fr(action, argument);
1412 InsetGraphics().dispatch(view()->cursor(), fr);
1416 case LFUN_INSET_APPLY: {
1417 string const name = cmd.getArg(0);
1418 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1420 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1421 inset->dispatch(view()->cursor(), fr);
1423 FuncRequest fr(LFUN_INSET_INSERT, argument);
1426 // ideally, the update flag should be set by the insets,
1427 // but this is not possible currently
1432 case LFUN_ALL_INSETS_TOGGLE: {
1434 string const name = split(argument, action, ' ');
1435 InsetBase::Code const inset_code =
1436 InsetBase::translate(name);
1438 LCursor & cur = view()->cursor();
1439 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1441 InsetBase & inset = owner->buffer()->inset();
1442 InsetIterator it = inset_iterator_begin(inset);
1443 InsetIterator const end = inset_iterator_end(inset);
1444 for (; it != end; ++it) {
1445 if (inset_code == InsetBase::NO_CODE
1446 || inset_code == it->lyxCode()) {
1447 LCursor tmpcur = cur;
1448 tmpcur.pushLeft(*it);
1449 it->dispatch(tmpcur, fr);
1456 case LFUN_BUFFER_LANGUAGE: {
1457 Buffer & buffer = *owner->buffer();
1458 Language const * oldL = buffer.params().language;
1459 Language const * newL = languages.getLanguage(argument);
1460 if (!newL || oldL == newL)
1463 if (oldL->rightToLeft() == newL->rightToLeft()
1464 && !buffer.isMultiLingual())
1465 buffer.changeLanguage(oldL, newL);
1467 buffer.updateDocLang(newL);
1471 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1472 string const fname =
1473 addName(addPath(package().user_support(), "templates/"),
1475 Buffer defaults(fname);
1477 istringstream ss(argument);
1480 int const unknown_tokens = defaults.readHeader(lex);
1482 if (unknown_tokens != 0) {
1483 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1484 << unknown_tokens << " unknown token"
1485 << (unknown_tokens == 1 ? "" : "s")
1489 if (defaults.writeFile(defaults.fileName()))
1490 setMessage(_("Document defaults saved in ")
1491 + makeDisplayPath(fname));
1493 setErrorMessage(_("Unable to save document defaults"));
1497 case LFUN_BUFFER_PARAMS_APPLY: {
1498 biblio::CiteEngine const engine =
1499 owner->buffer()->params().cite_engine;
1501 istringstream ss(argument);
1504 int const unknown_tokens =
1505 owner->buffer()->readHeader(lex);
1507 if (unknown_tokens != 0) {
1508 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1509 << unknown_tokens << " unknown token"
1510 << (unknown_tokens == 1 ? "" : "s")
1513 if (engine == owner->buffer()->params().cite_engine)
1516 LCursor & cur = view()->cursor();
1517 FuncRequest fr(LFUN_INSET_REFRESH);
1519 InsetBase & inset = owner->buffer()->inset();
1520 InsetIterator it = inset_iterator_begin(inset);
1521 InsetIterator const end = inset_iterator_end(inset);
1522 for (; it != end; ++it)
1523 if (it->lyxCode() == InsetBase::CITE_CODE)
1524 it->dispatch(cur, fr);
1528 case LFUN_TEXTCLASS_APPLY: {
1529 Buffer * buffer = owner->buffer();
1531 lyx::textclass_type const old_class =
1532 buffer->params().textclass;
1534 loadTextclass(argument);
1536 std::pair<bool, lyx::textclass_type> const tc_pair =
1537 textclasslist.numberOfClass(argument);
1542 lyx::textclass_type const new_class = tc_pair.second;
1543 if (old_class == new_class)
1547 owner->message(_("Converting document to new document class..."));
1548 recordUndoFullDocument(view());
1549 buffer->params().textclass = new_class;
1550 StableDocIterator backcur(view()->cursor());
1551 ErrorList & el = buffer->errorList("Class Switch");
1552 lyx::cap::switchBetweenClasses(
1553 old_class, new_class,
1554 static_cast<InsetText &>(buffer->inset()), el);
1556 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1558 buffer->errors("Class Switch");
1559 updateLabels(*buffer);
1564 case LFUN_TEXTCLASS_LOAD:
1565 loadTextclass(argument);
1568 case LFUN_LYXRC_APPLY: {
1569 LyXRC const lyxrc_orig = lyxrc;
1571 istringstream ss(argument);
1572 bool const success = lyxrc.read(ss) == 0;
1575 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1576 << "Unable to read lyxrc data"
1581 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1586 view()->cursor().dispatch(cmd);
1587 updateforce |= view()->cursor().result().update();
1588 if (!view()->cursor().result().dispatched())
1589 updateforce |= view()->dispatch(cmd);
1594 if (view()->available()) {
1595 // Redraw screen unless explicitly told otherwise.
1596 // This also initializes the position cache for all insets
1597 // in (at least partially) visible top-level paragraphs.
1599 view()->update(Update::FitCursor | Update::Force);
1601 view()->update(Update::FitCursor);
1603 owner->redrawWorkArea();
1605 // if we executed a mutating lfun, mark the buffer as dirty
1607 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1608 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1609 view()->buffer()->markDirty();
1611 if (view()->cursor().inTexted()) {
1612 view()->owner()->updateLayoutChoice();
1617 // FIXME UNICODE: _() does not support anything but ascii.
1618 // Do we need a lyx::to_ascii() method?
1619 sendDispatchMessage(_(lyx::to_utf8(getMessage())), cmd);
1623 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1625 /* When an action did not originate from the UI/kbd, it makes
1626 * sense to avoid updating the GUI. It turns out that this
1627 * fixes bug 1941, for reasons that are described here:
1628 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1630 if (cmd.origin != FuncRequest::INTERNAL) {
1631 owner->updateMenubar();
1632 owner->updateToolbars();
1635 const bool verbose = (cmd.origin == FuncRequest::UI
1636 || cmd.origin == FuncRequest::COMMANDBUFFER);
1638 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1639 lyxerr[Debug::ACTION] << "dispatch msg is " << lyx::to_utf8(msg) << endl;
1641 owner->message(msg);
1645 docstring dispatch_msg = msg;
1646 if (!dispatch_msg.empty())
1647 dispatch_msg += ' ';
1649 string comname = lyxaction.getActionName(cmd.action);
1651 bool argsadded = false;
1653 if (!cmd.argument().empty()) {
1654 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1655 comname += ' ' + lyx::to_utf8(cmd.argument());
1660 string const shortcuts = toplevel_keymap->printbindings(cmd);
1662 if (!shortcuts.empty())
1663 comname += ": " + shortcuts;
1664 else if (!argsadded && !cmd.argument().empty())
1665 comname += ' ' + lyx::to_utf8(cmd.argument());
1667 if (!comname.empty()) {
1668 comname = rtrim(comname);
1669 dispatch_msg += lyx::from_utf8('(' + rtrim(comname) + ')');
1672 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1673 << lyx::to_utf8(dispatch_msg) << endl;
1674 if (!dispatch_msg.empty())
1675 owner->message(dispatch_msg);
1679 void LyXFunc::setupLocalKeymap()
1681 keyseq.stdmap = toplevel_keymap.get();
1682 keyseq.curmap = toplevel_keymap.get();
1683 cancel_meta_seq.stdmap = toplevel_keymap.get();
1684 cancel_meta_seq.curmap = toplevel_keymap.get();
1688 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1690 string initpath = lyxrc.document_path;
1691 string filename(name);
1693 if (view()->available()) {
1694 string const trypath = owner->buffer()->filePath();
1695 // If directory is writeable, use this as default.
1696 if (isDirWriteable(trypath))
1700 static int newfile_number;
1702 if (filename.empty()) {
1703 filename = addName(lyxrc.document_path,
1704 "newfile" + convert<string>(++newfile_number) + ".lyx");
1705 while (bufferlist.exists(filename) || fs::is_readable(filename)) {
1707 filename = addName(lyxrc.document_path,
1708 "newfile" + convert<string>(newfile_number) +
1713 // The template stuff
1716 FileDialog fileDlg(lyx::to_utf8(_("Select template file")),
1717 LFUN_SELECT_FILE_SYNC,
1718 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1719 string(lyxrc.document_path)),
1720 make_pair(string(lyx::to_utf8(_("Templates|#T#t"))),
1721 string(lyxrc.template_path)));
1723 FileDialog::Result result =
1724 fileDlg.open(lyxrc.template_path,
1725 FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1728 if (result.first == FileDialog::Later)
1730 if (result.second.empty())
1732 templname = result.second;
1735 Buffer * const b = newFile(filename, templname, !name.empty());
1737 owner->setBuffer(b);
1741 void LyXFunc::open(string const & fname)
1743 string initpath = lyxrc.document_path;
1745 if (view()->available()) {
1746 string const trypath = owner->buffer()->filePath();
1747 // If directory is writeable, use this as default.
1748 if (isDirWriteable(trypath))
1754 if (fname.empty()) {
1755 FileDialog fileDlg(lyx::to_utf8(_("Select document to open")),
1757 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1758 string(lyxrc.document_path)),
1759 make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1760 string(addPath(package().system_support(), "examples"))));
1762 FileDialog::Result result =
1763 fileDlg.open(initpath,
1764 FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1767 if (result.first == FileDialog::Later)
1770 filename = result.second;
1772 // check selected filename
1773 if (filename.empty()) {
1774 owner->message(_("Canceled."));
1780 // get absolute path of file and add ".lyx" to the filename if
1782 string const fullpath = fileSearch(string(), filename, "lyx");
1783 if (!fullpath.empty()) {
1784 filename = fullpath;
1787 docstring const disp_fn = makeDisplayPath(filename);
1789 // if the file doesn't exist, let the user create one
1790 if (!fs::exists(filename)) {
1791 // the user specifically chose this name. Believe him.
1792 Buffer * const b = newFile(filename, string(), true);
1794 owner->setBuffer(b);
1798 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1801 if (owner->loadLyXFile(filename)) {
1802 str2 = bformat(_("Document %1$s opened."), disp_fn);
1804 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1806 owner->message(str2);
1810 void LyXFunc::doImport(string const & argument)
1813 string filename = split(argument, format, ' ');
1815 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1816 << " file: " << filename << endl;
1818 // need user interaction
1819 if (filename.empty()) {
1820 string initpath = lyxrc.document_path;
1822 if (view()->available()) {
1823 string const trypath = owner->buffer()->filePath();
1824 // If directory is writeable, use this as default.
1825 if (isDirWriteable(trypath))
1829 docstring const text = bformat(_("Select %1$s file to import"),
1830 formats.prettyName(format));
1832 FileDialog fileDlg(lyx::to_utf8(text),
1834 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1835 string(lyxrc.document_path)),
1836 make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1837 string(addPath(package().system_support(), "examples"))));
1839 string const filter = lyx::to_utf8(formats.prettyName(format))
1840 + " (*." + formats.extension(format) + ')';
1842 FileDialog::Result result =
1843 fileDlg.open(initpath,
1844 FileFilterList(filter),
1847 if (result.first == FileDialog::Later)
1850 filename = result.second;
1852 // check selected filename
1853 if (filename.empty())
1854 owner->message(_("Canceled."));
1857 if (filename.empty())
1860 // get absolute path of file
1861 filename = makeAbsPath(filename);
1863 string const lyxfile = changeExtension(filename, ".lyx");
1865 // Check if the document already is open
1866 if (lyx_gui::use_gui && bufferlist.exists(lyxfile)) {
1867 if (!bufferlist.close(bufferlist.getBuffer(lyxfile), true)) {
1868 owner->message(_("Canceled."));
1873 // if the file exists already, and we didn't do
1874 // -i lyx thefile.lyx, warn
1875 if (fs::exists(lyxfile) && filename != lyxfile) {
1876 docstring const file = makeDisplayPath(lyxfile, 30);
1878 docstring text = bformat(_("The document %1$s already exists.\n\n"
1879 "Do you want to over-write that document?"), file);
1880 int const ret = Alert::prompt(_("Over-write document?"),
1881 text, 0, 1, _("&Over-write"), _("&Cancel"));
1884 owner->message(_("Canceled."));
1889 ErrorList errorList;
1890 Importer::Import(owner, filename, format, errorList);
1891 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1895 void LyXFunc::closeBuffer()
1897 // save current cursor position
1898 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1899 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1900 if (bufferlist.close(owner->buffer(), true) && !quitting) {
1901 if (bufferlist.empty()) {
1902 // need this otherwise SEGV may occur while
1903 // trying to set variables that don't exist
1904 // since there's no current buffer
1905 owner->getDialogs().hideBufferDependent();
1907 owner->setBuffer(bufferlist.first());
1913 // Each "owner" should have it's own message method. lyxview and
1914 // the minibuffer would use the minibuffer, but lyxserver would
1915 // send an ERROR signal to its client. Alejandro 970603
1916 // This function is bit problematic when it comes to NLS, to make the
1917 // lyx servers client be language indepenent we must not translate
1918 // strings sent to this func.
1919 void LyXFunc::setErrorMessage(docstring const & m) const
1921 dispatch_buffer = m;
1926 void LyXFunc::setMessage(docstring const & m) const
1928 dispatch_buffer = m;
1932 string const LyXFunc::viewStatusMessage()
1934 // When meta-fake key is pressed, show the key sequence so far + "M-".
1936 return keyseq.print() + "M-";
1938 // Else, when a non-complete key sequence is pressed,
1939 // show the available options.
1940 if (keyseq.length() > 0 && !keyseq.deleted())
1941 return keyseq.printOptions();
1943 if (!view()->available())
1944 return lyx::to_utf8(_("Welcome to LyX!"));
1946 return view()->cursor().currentState();
1950 BufferView * LyXFunc::view() const
1952 BOOST_ASSERT(owner);
1953 return owner->view();
1957 bool LyXFunc::wasMetaKey() const
1959 return (meta_fake_bit != key_modifier::none);
1965 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1967 // Why the switch you might ask. It is a trick to ensure that all
1968 // the elements in the LyXRCTags enum is handled. As you can see
1969 // there are no breaks at all. So it is just a huge fall-through.
1970 // The nice thing is that we will get a warning from the compiler
1971 // if we forget an element.
1972 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1974 case LyXRC::RC_ACCEPT_COMPOUND:
1975 case LyXRC::RC_ALT_LANG:
1976 case LyXRC::RC_ASCIIROFF_COMMAND:
1977 case LyXRC::RC_ASCII_LINELEN:
1978 case LyXRC::RC_AUTOREGIONDELETE:
1979 case LyXRC::RC_AUTORESET_OPTIONS:
1980 case LyXRC::RC_AUTOSAVE:
1981 case LyXRC::RC_AUTO_NUMBER:
1982 case LyXRC::RC_BACKUPDIR_PATH:
1983 case LyXRC::RC_BIBTEX_COMMAND:
1984 case LyXRC::RC_BINDFILE:
1985 case LyXRC::RC_CHECKLASTFILES:
1986 case LyXRC::RC_USELASTFILEPOS:
1987 case LyXRC::RC_LOADSESSION:
1988 case LyXRC::RC_CHKTEX_COMMAND:
1989 case LyXRC::RC_CONVERTER:
1990 case LyXRC::RC_COPIER:
1991 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1992 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1993 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1994 case LyXRC::RC_DATE_INSERT_FORMAT:
1995 case LyXRC::RC_DEFAULT_LANGUAGE:
1996 case LyXRC::RC_DEFAULT_PAPERSIZE:
1997 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1998 case LyXRC::RC_DISPLAY_GRAPHICS:
1999 case LyXRC::RC_DOCUMENTPATH:
2000 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2001 if (fs::exists(lyxrc_new.document_path) &&
2002 fs::is_directory(lyxrc_new.document_path)) {
2003 using lyx::support::package;
2004 package().document_dir() = lyxrc.document_path;
2007 case LyXRC::RC_ESC_CHARS:
2008 case LyXRC::RC_FONT_ENCODING:
2009 case LyXRC::RC_FORMAT:
2010 case LyXRC::RC_INDEX_COMMAND:
2011 case LyXRC::RC_INPUT:
2012 case LyXRC::RC_KBMAP:
2013 case LyXRC::RC_KBMAP_PRIMARY:
2014 case LyXRC::RC_KBMAP_SECONDARY:
2015 case LyXRC::RC_LABEL_INIT_LENGTH:
2016 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2017 case LyXRC::RC_LANGUAGE_AUTO_END:
2018 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2019 case LyXRC::RC_LANGUAGE_COMMAND_END:
2020 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2021 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2022 case LyXRC::RC_LANGUAGE_PACKAGE:
2023 case LyXRC::RC_LANGUAGE_USE_BABEL:
2024 case LyXRC::RC_MAKE_BACKUP:
2025 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2026 case LyXRC::RC_NUMLASTFILES:
2027 case LyXRC::RC_PATH_PREFIX:
2028 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2029 using lyx::support::prependEnvPath;
2030 prependEnvPath("PATH", lyxrc.path_prefix);
2032 case LyXRC::RC_PERS_DICT:
2033 case LyXRC::RC_POPUP_BOLD_FONT:
2034 case LyXRC::RC_POPUP_FONT_ENCODING:
2035 case LyXRC::RC_POPUP_NORMAL_FONT:
2036 case LyXRC::RC_PREVIEW:
2037 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2038 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2039 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2040 case LyXRC::RC_PRINTCOPIESFLAG:
2041 case LyXRC::RC_PRINTER:
2042 case LyXRC::RC_PRINTEVENPAGEFLAG:
2043 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2044 case LyXRC::RC_PRINTFILEEXTENSION:
2045 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2046 case LyXRC::RC_PRINTODDPAGEFLAG:
2047 case LyXRC::RC_PRINTPAGERANGEFLAG:
2048 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2049 case LyXRC::RC_PRINTPAPERFLAG:
2050 case LyXRC::RC_PRINTREVERSEFLAG:
2051 case LyXRC::RC_PRINTSPOOL_COMMAND:
2052 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2053 case LyXRC::RC_PRINTTOFILE:
2054 case LyXRC::RC_PRINTTOPRINTER:
2055 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2056 case LyXRC::RC_PRINT_COMMAND:
2057 case LyXRC::RC_RTL_SUPPORT:
2058 case LyXRC::RC_SCREEN_DPI:
2059 case LyXRC::RC_SCREEN_FONT_ENCODING:
2060 case LyXRC::RC_SCREEN_FONT_ROMAN:
2061 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2062 case LyXRC::RC_SCREEN_FONT_SANS:
2063 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2064 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2065 case LyXRC::RC_SCREEN_FONT_SIZES:
2066 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2067 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2068 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2069 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2070 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2071 case LyXRC::RC_SCREEN_ZOOM:
2072 case LyXRC::RC_SERVERPIPE:
2073 case LyXRC::RC_SET_COLOR:
2074 case LyXRC::RC_SHOW_BANNER:
2075 case LyXRC::RC_SPELL_COMMAND:
2076 case LyXRC::RC_TEMPDIRPATH:
2077 case LyXRC::RC_TEMPLATEPATH:
2078 case LyXRC::RC_TEX_ALLOWS_SPACES:
2079 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2080 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2081 namespace os = lyx::support::os;
2082 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2084 case LyXRC::RC_UIFILE:
2085 case LyXRC::RC_USER_EMAIL:
2086 case LyXRC::RC_USER_NAME:
2087 case LyXRC::RC_USETEMPDIR:
2088 case LyXRC::RC_USE_ALT_LANG:
2089 case LyXRC::RC_USE_ESC_CHARS:
2090 case LyXRC::RC_USE_INP_ENC:
2091 case LyXRC::RC_USE_PERS_DICT:
2092 case LyXRC::RC_USE_SPELL_LIB:
2093 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2094 case LyXRC::RC_VIEWER:
2095 case LyXRC::RC_LAST: