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