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"
95 #include "support/unicode.h"
97 #include <boost/current_function.hpp>
98 #include <boost/filesystem/operations.hpp>
102 using bv_funcs::freefont2string;
104 using lyx::support::absolutePath;
105 using lyx::support::addName;
106 using lyx::support::addPath;
107 using lyx::support::bformat;
108 using lyx::support::changeExtension;
109 using lyx::support::contains;
110 using lyx::support::FileFilterList;
111 using lyx::support::fileSearch;
112 using lyx::support::ForkedcallsController;
113 using lyx::support::i18nLibFileSearch;
114 using lyx::support::isDirWriteable;
115 using lyx::support::isFileReadable;
116 using lyx::support::isStrInt;
117 using lyx::support::makeAbsPath;
118 using lyx::support::makeDisplayPath;
119 using lyx::support::package;
120 using lyx::support::Path;
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(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 std::vector<char> tmp = ucs4_to_utf8(encoded_last_key);
327 string const arg(tmp.begin(), tmp.end());
328 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
329 FuncRequest::KEYBOARD));
331 << "SelfInsert arg[`" << 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 (cmd.argument == buf->fileName())
418 case LFUN_BUFFER_EXPORT:
419 enable = cmd.argument == "custom"
420 || Exporter::isExportable(*buf, 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(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_SHOW_NEXT_INSET:
593 case LFUN_DIALOG_HIDE:
594 case LFUN_DIALOG_DISCONNECT_INSET:
595 case LFUN_BUFFER_CHILD_OPEN:
596 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
597 case LFUN_KEYMAP_OFF:
598 case LFUN_KEYMAP_PRIMARY:
599 case LFUN_KEYMAP_SECONDARY:
600 case LFUN_KEYMAP_TOGGLE:
602 case LFUN_BUFFER_EXPORT_CUSTOM:
603 case LFUN_BUFFER_PRINT:
604 case LFUN_PREFERENCES_SAVE:
605 case LFUN_SCREEN_FONT_UPDATE:
608 case LFUN_EXTERNAL_EDIT:
609 case LFUN_GRAPHICS_EDIT:
610 case LFUN_ALL_INSETS_TOGGLE:
611 case LFUN_BUFFER_LANGUAGE:
612 case LFUN_TEXTCLASS_APPLY:
613 case LFUN_TEXTCLASS_LOAD:
614 case LFUN_BUFFER_SAVE_AS_DEFAULT:
615 case LFUN_BUFFER_PARAMS_APPLY:
616 case LFUN_LYXRC_APPLY:
617 case LFUN_BUFFER_NEXT:
618 case LFUN_BUFFER_PREVIOUS:
619 // these are handled in our dispatch()
624 if (!::getStatus(cur, cmd, flag))
625 flag = view()->getStatus(cmd);
631 // Can we use a readonly buffer?
632 if (buf && buf->isReadonly()
633 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
634 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
635 flag.message(N_("Document is read-only"));
639 // Are we in a DELETED change-tracking region?
640 if (buf && buf->params().tracking_changes
641 && lookupChangeType(cur, true) == Change::DELETED
642 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
643 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
644 flag.message(N_("This portion of the document is deleted."));
648 // the default error message if we disable the command
649 if (!flag.enabled() && flag.message().empty())
650 flag.message(N_("Command disabled"));
658 bool ensureBufferClean(BufferView * bv)
660 Buffer & buf = *bv->buffer();
664 string const file = makeDisplayPath(buf.fileName(), 30);
665 string text = bformat(_("The document %1$s has unsaved "
666 "changes.\n\nDo you want to save "
667 "the document?"), file);
668 int const ret = Alert::prompt(_("Save changed document?"),
669 text, 0, 1, _("&Save"),
673 bv->owner()->dispatch(FuncRequest(LFUN_BUFFER_WRITE));
675 return buf.isClean();
679 void showPrintError(string const & name)
681 string str = bformat(_("Could not print the document %1$s.\n"
682 "Check that your printer is set up correctly."),
683 makeDisplayPath(name, 50));
684 Alert::error(_("Print document failed"), str);
688 void loadTextclass(string const & name)
690 std::pair<bool, lyx::textclass_type> const tc_pair =
691 textclasslist.numberOfClass(name);
693 if (!tc_pair.first) {
694 lyxerr << "Document class \"" << name
695 << "\" does not exist."
700 lyx::textclass_type const tc = tc_pair.second;
702 if (!textclasslist[tc].load()) {
703 string s = bformat(_("The document could not be converted\n"
704 "into the document class %1$s."),
705 textclasslist[tc].name());
706 Alert::error(_("Could not change class"), s);
711 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
716 void LyXFunc::dispatch(FuncRequest const & cmd)
718 BOOST_ASSERT(view());
719 string const argument = cmd.argument;
720 kb_action const action = cmd.action;
722 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
723 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
725 // we have not done anything wrong yet.
727 dispatch_buffer.erase();
729 // redraw the screen at the end (first of the two drawing steps).
730 //This is done unless explicitely requested otherwise
732 // also do the second redrawing step. Only done if requested.
733 bool updateforce = false;
735 FuncStatus const flag = getStatus(cmd);
736 if (!flag.enabled()) {
737 // We cannot use this function here
738 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
739 << lyxaction.getActionName(action)
740 << " [" << action << "] is disabled at this location"
742 setErrorMessage(flag.message());
746 case LFUN_WORD_FIND_FORWARD:
747 case LFUN_WORD_FIND_BACKWARD: {
748 static string last_search;
749 string searched_string;
751 if (!argument.empty()) {
752 last_search = argument;
753 searched_string = argument;
755 searched_string = last_search;
758 if (searched_string.empty())
761 bool const fw = action == LFUN_WORD_FIND_FORWARD;
763 lyx::find::find2string(searched_string, true, false, fw);
764 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
768 case LFUN_COMMAND_PREFIX:
769 owner->message(keyseq.printOptions());
772 case LFUN_COMMAND_EXECUTE:
773 owner->getToolbars().display("minibuffer", true);
774 owner->focus_command_buffer();
779 meta_fake_bit = key_modifier::none;
780 if (view()->available())
781 // cancel any selection
782 dispatch(FuncRequest(LFUN_MARK_OFF));
783 setMessage(N_("Cancel"));
786 case LFUN_META_PREFIX:
787 meta_fake_bit = key_modifier::alt;
788 setMessage(keyseq.print());
791 case LFUN_BUFFER_TOGGLE_READ_ONLY:
792 if (owner->buffer()->lyxvc().inUse())
793 owner->buffer()->lyxvc().toggleReadOnly();
795 owner->buffer()->setReadonly(
796 !owner->buffer()->isReadonly());
799 // --- Menus -----------------------------------------------
800 case LFUN_BUFFER_NEW:
801 menuNew(argument, false);
804 case LFUN_BUFFER_NEW_TEMPLATE:
805 menuNew(argument, true);
808 case LFUN_BUFFER_CLOSE:
812 case LFUN_BUFFER_WRITE:
813 if (!owner->buffer()->isUnnamed()) {
814 string const str = bformat(_("Saving document %1$s..."),
815 makeDisplayPath(owner->buffer()->fileName()));
817 menuWrite(owner->buffer());
818 owner->message(str + _(" done."));
820 writeAs(owner->buffer());
824 case LFUN_BUFFER_WRITE_AS:
825 writeAs(owner->buffer(), argument);
829 case LFUN_BUFFER_RELOAD: {
830 string const file = makeDisplayPath(view()->buffer()->fileName(), 20);
831 string text = bformat(_("Any changes will be lost. Are you sure "
832 "you want to revert to the saved version of the document %1$s?"), file);
833 int const ret = Alert::prompt(_("Revert to saved document?"),
834 text, 0, 1, _("&Revert"), _("&Cancel"));
841 case LFUN_BUFFER_UPDATE:
842 Exporter::Export(owner->buffer(), argument, true);
845 case LFUN_BUFFER_VIEW:
846 Exporter::preview(owner->buffer(), argument);
849 case LFUN_BUILD_PROGRAM:
850 Exporter::Export(owner->buffer(), "program", true);
853 case LFUN_BUFFER_CHKTEX:
854 owner->buffer()->runChktex();
857 case LFUN_BUFFER_EXPORT:
858 if (argument == "custom")
859 owner->getDialogs().show("sendto");
861 Exporter::Export(owner->buffer(), argument, false);
865 case LFUN_BUFFER_EXPORT_CUSTOM: {
867 string command = split(argument, format_name, ' ');
868 Format const * format = formats.getFormat(format_name);
870 lyxerr << "Format \"" << format_name
871 << "\" not recognized!"
876 Buffer * buffer = owner->buffer();
878 // The name of the file created by the conversion process
881 // Output to filename
882 if (format->name() == "lyx") {
883 string const latexname =
884 buffer->getLatexName(false);
885 filename = changeExtension(latexname,
886 format->extension());
887 filename = addName(buffer->temppath(), filename);
889 if (!buffer->writeFile(filename))
893 Exporter::Export(buffer, format_name, true, filename);
896 // Substitute $$FName for filename
897 if (!contains(command, "$$FName"))
898 command = "( " + command + " ) < $$FName";
899 command = subst(command, "$$FName", filename);
901 // Execute the command in the background
903 call.startscript(Systemcall::DontWait, command);
907 case LFUN_BUFFER_PRINT: {
910 string command = split(split(argument, target, ' '),
914 || target_name.empty()
915 || command.empty()) {
916 lyxerr << "Unable to parse \""
917 << argument << '"' << std::endl;
920 if (target != "printer" && target != "file") {
921 lyxerr << "Unrecognized target \""
922 << target << '"' << std::endl;
926 Buffer * buffer = owner->buffer();
928 if (!Exporter::Export(buffer, "dvi", true)) {
929 showPrintError(buffer->fileName());
933 // Push directory path.
934 string const path = buffer->temppath();
937 // there are three cases here:
938 // 1. we print to a file
939 // 2. we print directly to a printer
940 // 3. we print using a spool command (print to file first)
943 string const dviname =
944 changeExtension(buffer->getLatexName(true),
947 if (target == "printer") {
948 if (!lyxrc.print_spool_command.empty()) {
949 // case 3: print using a spool
950 string const psname =
951 changeExtension(dviname,".ps");
952 command += lyxrc.print_to_file
955 + quoteName(dviname);
958 lyxrc.print_spool_command +' ';
959 if (target_name != "default") {
960 command2 += lyxrc.print_spool_printerprefix
964 command2 += quoteName(psname);
966 // If successful, then spool command
967 res = one.startscript(
972 res = one.startscript(
973 Systemcall::DontWait,
976 // case 2: print directly to a printer
977 res = one.startscript(
978 Systemcall::DontWait,
979 command + quoteName(dviname));
983 // case 1: print to a file
984 command += lyxrc.print_to_file
985 + quoteName(makeAbsPath(target_name,
988 + quoteName(dviname);
989 res = one.startscript(Systemcall::DontWait,
994 showPrintError(buffer->fileName());
998 case LFUN_BUFFER_IMPORT:
1003 if (view()->available()) {
1004 // save cursor Position for opened files to .lyx/session
1005 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1006 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1007 // save bookmarks to .lyx/session
1008 view()->saveSavedPositions();
1010 quitLyX(argument == "force");
1013 case LFUN_TOC_VIEW: {
1014 InsetCommandParams p("tableofcontents");
1015 string const data = InsetCommandMailer::params2string("toc", p);
1016 owner->getDialogs().show("toc", data, 0);
1020 case LFUN_BUFFER_AUTO_SAVE:
1024 case LFUN_RECONFIGURE:
1025 reconfigure(view());
1028 case LFUN_HELP_OPEN: {
1029 string const arg = argument;
1031 setErrorMessage(N_("Missing argument"));
1034 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1035 if (fname.empty()) {
1036 lyxerr << "LyX: unable to find documentation file `"
1037 << arg << "'. Bad installation?" << endl;
1040 owner->message(bformat(_("Opening help file %1$s..."),
1041 makeDisplayPath(fname)));
1042 owner->loadLyXFile(fname, false);
1046 // --- version control -------------------------------
1047 case LFUN_VC_REGISTER:
1048 if (!ensureBufferClean(view()))
1050 if (!owner->buffer()->lyxvc().inUse()) {
1051 owner->buffer()->lyxvc().registrer();
1056 case LFUN_VC_CHECK_IN:
1057 if (!ensureBufferClean(view()))
1059 if (owner->buffer()->lyxvc().inUse()
1060 && !owner->buffer()->isReadonly()) {
1061 owner->buffer()->lyxvc().checkIn();
1066 case LFUN_VC_CHECK_OUT:
1067 if (!ensureBufferClean(view()))
1069 if (owner->buffer()->lyxvc().inUse()
1070 && owner->buffer()->isReadonly()) {
1071 owner->buffer()->lyxvc().checkOut();
1076 case LFUN_VC_REVERT:
1077 owner->buffer()->lyxvc().revert();
1081 case LFUN_VC_UNDO_LAST:
1082 owner->buffer()->lyxvc().undoLast();
1086 // --- buffers ----------------------------------------
1087 case LFUN_BUFFER_SWITCH:
1088 owner->setBuffer(bufferlist.getBuffer(argument));
1091 case LFUN_BUFFER_NEXT:
1092 owner->setBuffer(bufferlist.next(view()->buffer()));
1095 case LFUN_BUFFER_PREVIOUS:
1096 owner->setBuffer(bufferlist.previous(view()->buffer()));
1100 newFile(view(), argument);
1103 case LFUN_FILE_OPEN:
1107 case LFUN_DROP_LAYOUTS_CHOICE:
1108 owner->getToolbars().openLayoutList();
1111 case LFUN_MENU_OPEN:
1112 owner->getMenubar().openByName(argument);
1115 // --- lyxserver commands ----------------------------
1116 case LFUN_SERVER_GET_NAME:
1117 setMessage(owner->buffer()->fileName());
1118 lyxerr[Debug::INFO] << "FNAME["
1119 << owner->buffer()->fileName()
1123 case LFUN_SERVER_NOTIFY:
1124 dispatch_buffer = keyseq.print();
1125 lyxserver->notifyClient(dispatch_buffer);
1128 case LFUN_SERVER_GOTO_FILE_ROW: {
1131 istringstream is(argument);
1132 is >> file_name >> row;
1133 if (prefixIs(file_name, package().temp_dir())) {
1134 // Needed by inverse dvi search. If it is a file
1135 // in tmpdir, call the apropriated function
1136 owner->setBuffer(bufferlist.getBufferFromTmp(file_name));
1138 // Must replace extension of the file to be .lyx
1139 // and get full path
1140 string const s = changeExtension(file_name, ".lyx");
1141 // Either change buffer or load the file
1142 if (bufferlist.exists(s)) {
1143 owner->setBuffer(bufferlist.getBuffer(s));
1145 owner->loadLyXFile(s);
1149 view()->setCursorFromRow(row);
1152 // see BufferView_pimpl::center()
1156 case LFUN_DIALOG_SHOW: {
1157 string const name = cmd.getArg(0);
1158 string data = trim(cmd.argument.substr(name.size()));
1160 if (name == "character") {
1161 data = freefont2string();
1163 owner->getDialogs().show("character", data);
1164 } else if (name == "latexlog") {
1165 pair<Buffer::LogType, string> const logfile =
1166 owner->buffer()->getLogName();
1167 switch (logfile.first) {
1168 case Buffer::latexlog:
1171 case Buffer::buildlog:
1175 data += LyXLex::quoteString(logfile.second);
1176 owner->getDialogs().show("log", data);
1177 } else if (name == "vclog") {
1178 string const data = "vc " +
1179 LyXLex::quoteString(owner->buffer()->lyxvc().getLogFile());
1180 owner->getDialogs().show("log", data);
1182 owner->getDialogs().show(name, data);
1186 case LFUN_DIALOG_SHOW_NEW_INSET: {
1187 string const name = cmd.getArg(0);
1188 string data = trim(cmd.argument.substr(name.size()));
1189 if (name == "bibitem" ||
1196 InsetCommandParams p(name);
1197 data = InsetCommandMailer::params2string(name, p);
1198 } else if (name == "include") {
1199 InsetCommandParams p(data);
1200 data = InsetIncludeMailer::params2string(p);
1201 } else if (name == "box") {
1202 // \c data == "Boxed" || "Frameless" etc
1203 InsetBoxParams p(data);
1204 data = InsetBoxMailer::params2string(p);
1205 } else if (name == "branch") {
1206 InsetBranchParams p;
1207 data = InsetBranchMailer::params2string(p);
1208 } else if (name == "citation") {
1209 InsetCommandParams p("cite");
1210 data = InsetCommandMailer::params2string(name, p);
1211 } else if (name == "ert") {
1212 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1213 } else if (name == "external") {
1214 InsetExternalParams p;
1215 Buffer const & buffer = *owner->buffer();
1216 data = InsetExternalMailer::params2string(p, buffer);
1217 } else if (name == "float") {
1219 data = InsetFloatMailer::params2string(p);
1220 } else if (name == "graphics") {
1221 InsetGraphicsParams p;
1222 Buffer const & buffer = *owner->buffer();
1223 data = InsetGraphicsMailer::params2string(p, buffer);
1224 } else if (name == "note") {
1226 data = InsetNoteMailer::params2string(p);
1227 } else if (name == "vspace") {
1229 data = InsetVSpaceMailer::params2string(space);
1230 } else if (name == "wrap") {
1232 data = InsetWrapMailer::params2string(p);
1234 owner->getDialogs().show(name, data, 0);
1238 case LFUN_DIALOG_SHOW_NEXT_INSET:
1241 case LFUN_DIALOG_UPDATE: {
1242 string const & name = argument;
1243 // Can only update a dialog connected to an existing inset
1244 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1246 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument);
1247 inset->dispatch(view()->cursor(), fr);
1248 } else if (name == "paragraph") {
1249 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1250 } else if (name == "prefs") {
1251 owner->getDialogs().update(name, string());
1256 case LFUN_DIALOG_HIDE:
1257 Dialogs::hide(argument, 0);
1260 case LFUN_DIALOG_DISCONNECT_INSET:
1261 owner->getDialogs().disconnect(argument);
1265 case LFUN_CITATION_INSERT: {
1266 if (!argument.empty()) {
1267 // we can have one optional argument, delimited by '|'
1268 // citation-insert <key>|<text_before>
1269 // this should be enhanced to also support text_after
1270 // and citation style
1271 string arg = argument;
1273 if (contains(argument, "|")) {
1274 arg = token(argument, '|', 0);
1275 opt1 = '[' + token(argument, '|', 1) + ']';
1277 std::ostringstream os;
1278 os << "citation LatexCommand\n"
1279 << "\\cite" << opt1 << "{" << arg << "}\n"
1281 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1284 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1288 case LFUN_BUFFER_CHILD_OPEN: {
1289 string const filename =
1290 makeAbsPath(argument, owner->buffer()->filePath());
1291 setMessage(N_("Opening child document ") +
1292 makeDisplayPath(filename) + "...");
1293 view()->savePosition(0);
1294 string const parentfilename = owner->buffer()->fileName();
1295 if (bufferlist.exists(filename))
1296 owner->setBuffer(bufferlist.getBuffer(filename));
1298 owner->loadLyXFile(filename);
1299 // Set the parent name of the child document.
1300 // This makes insertion of citations and references in the child work,
1301 // when the target is in the parent or another child document.
1302 owner->buffer()->setParentName(parentfilename);
1306 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1307 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1310 case LFUN_KEYMAP_OFF:
1311 owner->getIntl().keyMapOn(false);
1314 case LFUN_KEYMAP_PRIMARY:
1315 owner->getIntl().keyMapPrim();
1318 case LFUN_KEYMAP_SECONDARY:
1319 owner->getIntl().keyMapSec();
1322 case LFUN_KEYMAP_TOGGLE:
1323 owner->getIntl().toggleKeyMap();
1329 string rest = split(argument, countstr, ' ');
1330 istringstream is(countstr);
1333 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1334 for (int i = 0; i < count; ++i)
1335 dispatch(lyxaction.lookupFunc(rest));
1339 case LFUN_COMMAND_SEQUENCE: {
1340 // argument contains ';'-terminated commands
1341 string arg = argument;
1342 while (!arg.empty()) {
1344 arg = split(arg, first, ';');
1345 FuncRequest func(lyxaction.lookupFunc(first));
1346 func.origin = cmd.origin;
1352 case LFUN_PREFERENCES_SAVE: {
1353 Path p(package().user_support());
1354 lyxrc.write("preferences", false);
1358 case LFUN_SCREEN_FONT_UPDATE:
1359 // handle the screen font changes.
1360 lyxrc.set_font_norm_type();
1361 lyx_gui::update_fonts();
1362 // All visible buffers will need resize
1366 case LFUN_SET_COLOR: {
1368 string const x11_name = split(argument, lyx_name, ' ');
1369 if (lyx_name.empty() || x11_name.empty()) {
1370 setErrorMessage(N_("Syntax: set-color <lyx_name>"
1375 bool const graphicsbg_changed =
1376 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1377 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1379 if (!lcolor.setColor(lyx_name, x11_name)) {
1381 bformat(_("Set-color \"%1$s\" failed "
1382 "- color is undefined or "
1383 "may not be redefined"), lyx_name));
1387 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1389 if (graphicsbg_changed) {
1390 #ifdef WITH_WARNINGS
1391 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1394 lyx::graphics::GCache::get().changeDisplay(true);
1401 owner->message(argument);
1404 case LFUN_TOOLTIPS_TOGGLE:
1405 owner->getDialogs().toggleTooltips();
1408 case LFUN_EXTERNAL_EDIT: {
1409 FuncRequest fr(action, argument);
1410 InsetExternal().dispatch(view()->cursor(), fr);
1414 case LFUN_GRAPHICS_EDIT: {
1415 FuncRequest fr(action, argument);
1416 InsetGraphics().dispatch(view()->cursor(), fr);
1420 case LFUN_INSET_APPLY: {
1421 string const name = cmd.getArg(0);
1422 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1424 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1425 inset->dispatch(view()->cursor(), fr);
1427 FuncRequest fr(LFUN_INSET_INSERT, argument);
1430 // ideally, the update flag should be set by the insets,
1431 // but this is not possible currently
1436 case LFUN_ALL_INSETS_TOGGLE: {
1438 string const name = split(argument, action, ' ');
1439 InsetBase::Code const inset_code =
1440 InsetBase::translate(name);
1442 LCursor & cur = view()->cursor();
1443 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1445 InsetBase & inset = owner->buffer()->inset();
1446 InsetIterator it = inset_iterator_begin(inset);
1447 InsetIterator const end = inset_iterator_end(inset);
1448 for (; it != end; ++it) {
1449 if (inset_code == InsetBase::NO_CODE
1450 || inset_code == it->lyxCode()) {
1451 LCursor tmpcur = cur;
1452 tmpcur.pushLeft(*it);
1453 it->dispatch(tmpcur, fr);
1460 case LFUN_BUFFER_LANGUAGE: {
1461 Buffer & buffer = *owner->buffer();
1462 Language const * oldL = buffer.params().language;
1463 Language const * newL = languages.getLanguage(argument);
1464 if (!newL || oldL == newL)
1467 if (oldL->rightToLeft() == newL->rightToLeft()
1468 && !buffer.isMultiLingual())
1469 buffer.changeLanguage(oldL, newL);
1471 buffer.updateDocLang(newL);
1475 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1476 string const fname =
1477 addName(addPath(package().user_support(), "templates/"),
1479 Buffer defaults(fname);
1481 istringstream ss(argument);
1484 int const unknown_tokens = defaults.readHeader(lex);
1486 if (unknown_tokens != 0) {
1487 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1488 << unknown_tokens << " unknown token"
1489 << (unknown_tokens == 1 ? "" : "s")
1493 if (defaults.writeFile(defaults.fileName()))
1494 setMessage(_("Document defaults saved in ")
1495 + makeDisplayPath(fname));
1497 setErrorMessage(_("Unable to save document defaults"));
1501 case LFUN_BUFFER_PARAMS_APPLY: {
1502 biblio::CiteEngine const engine =
1503 owner->buffer()->params().cite_engine;
1505 istringstream ss(argument);
1508 int const unknown_tokens =
1509 owner->buffer()->readHeader(lex);
1511 if (unknown_tokens != 0) {
1512 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1513 << unknown_tokens << " unknown token"
1514 << (unknown_tokens == 1 ? "" : "s")
1517 if (engine == owner->buffer()->params().cite_engine)
1520 LCursor & cur = view()->cursor();
1521 FuncRequest fr(LFUN_INSET_REFRESH);
1523 InsetBase & inset = owner->buffer()->inset();
1524 InsetIterator it = inset_iterator_begin(inset);
1525 InsetIterator const end = inset_iterator_end(inset);
1526 for (; it != end; ++it)
1527 if (it->lyxCode() == InsetBase::CITE_CODE)
1528 it->dispatch(cur, fr);
1532 case LFUN_TEXTCLASS_APPLY: {
1533 Buffer * buffer = owner->buffer();
1535 lyx::textclass_type const old_class =
1536 buffer->params().textclass;
1538 loadTextclass(argument);
1540 std::pair<bool, lyx::textclass_type> const tc_pair =
1541 textclasslist.numberOfClass(argument);
1546 lyx::textclass_type const new_class = tc_pair.second;
1547 if (old_class == new_class)
1551 owner->message(_("Converting document to new document class..."));
1552 recordUndoFullDocument(view());
1553 buffer->params().textclass = new_class;
1554 StableDocIterator backcur(view()->cursor());
1555 ErrorList & el = buffer->errorList("Class Switch");
1556 lyx::cap::switchBetweenClasses(
1557 old_class, new_class,
1558 static_cast<InsetText &>(buffer->inset()), el);
1560 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1562 buffer->errors("Class Switch");
1563 updateLabels(*buffer);
1568 case LFUN_TEXTCLASS_LOAD:
1569 loadTextclass(argument);
1572 case LFUN_LYXRC_APPLY: {
1573 LyXRC const lyxrc_orig = lyxrc;
1575 istringstream ss(argument);
1576 bool const success = lyxrc.read(ss) == 0;
1579 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1580 << "Unable to read lyxrc data"
1585 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1590 view()->cursor().dispatch(cmd);
1591 updateforce |= view()->cursor().result().update();
1592 if (!view()->cursor().result().dispatched())
1593 updateforce |= view()->dispatch(cmd);
1598 if (view()->available()) {
1599 // Redraw screen unless explicitly told otherwise.
1600 // This also initializes the position cache for all insets
1601 // in (at least partially) visible top-level paragraphs.
1603 view()->update(Update::FitCursor | Update::Force);
1605 view()->update(Update::FitCursor);
1607 owner->redrawWorkArea();
1609 // if we executed a mutating lfun, mark the buffer as dirty
1611 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1612 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1613 view()->buffer()->markDirty();
1616 if (view()->cursor().inTexted()) {
1617 view()->owner()->updateLayoutChoice();
1620 sendDispatchMessage(_(getMessage()), cmd);
1624 void LyXFunc::sendDispatchMessage(string const & msg, FuncRequest const & cmd)
1626 /* When an action did not originate from the UI/kbd, it makes
1627 * sense to avoid updating the GUI. It turns out that this
1628 * fixes bug 1941, for reasons that are described here:
1629 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1631 if (cmd.origin != FuncRequest::INTERNAL) {
1632 owner->updateMenubar();
1633 owner->updateToolbars();
1636 const bool verbose = (cmd.origin == FuncRequest::UI
1637 || cmd.origin == FuncRequest::COMMANDBUFFER);
1639 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1640 lyxerr[Debug::ACTION] << "dispatch msg is " << msg << endl;
1642 owner->message(msg);
1646 string dispatch_msg = msg;
1647 if (!dispatch_msg.empty())
1648 dispatch_msg += ' ';
1650 string comname = lyxaction.getActionName(cmd.action);
1652 bool argsadded = false;
1654 if (!cmd.argument.empty()) {
1655 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1656 comname += ' ' + cmd.argument;
1661 string const shortcuts = toplevel_keymap->printbindings(cmd);
1663 if (!shortcuts.empty())
1664 comname += ": " + shortcuts;
1665 else if (!argsadded && !cmd.argument.empty())
1666 comname += ' ' + cmd.argument;
1668 if (!comname.empty()) {
1669 comname = rtrim(comname);
1670 dispatch_msg += '(' + rtrim(comname) + ')';
1673 lyxerr[Debug::ACTION] << "verbose dispatch msg " << 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(_("Select template file"),
1717 LFUN_SELECT_FILE_SYNC,
1718 make_pair(string(_("Documents|#o#O")),
1719 string(lyxrc.document_path)),
1720 make_pair(string(_("Templates|#T#t")),
1721 string(lyxrc.template_path)));
1723 FileDialog::Result result =
1724 fileDlg.open(lyxrc.template_path,
1725 FileFilterList(_("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(_("Select document to open"),
1757 make_pair(string(_("Documents|#o#O")),
1758 string(lyxrc.document_path)),
1759 make_pair(string(_("Examples|#E#e")),
1760 string(addPath(package().system_support(), "examples"))));
1762 FileDialog::Result result =
1763 fileDlg.open(initpath,
1764 FileFilterList(_("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 string 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 string const text = bformat(_("Select %1$s file to import"),
1830 formats.prettyName(format));
1832 FileDialog fileDlg(text,
1834 make_pair(string(_("Documents|#o#O")),
1835 string(lyxrc.document_path)),
1836 make_pair(string(_("Examples|#E#e")),
1837 string(addPath(package().system_support(), "examples"))));
1839 string const filter = 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 string const file = makeDisplayPath(lyxfile, 30);
1878 string 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(string const & m) const
1921 dispatch_buffer = m;
1926 void LyXFunc::setMessage(string 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 _("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: