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/Application.h"
76 #include "frontends/Alert.h"
77 #include "frontends/Dialogs.h"
78 #include "frontends/FileDialog.h"
79 #include "frontends/FontLoader.h"
80 #include "frontends/lyx_gui.h"
81 #include "frontends/LyXKeySym.h"
82 #include "frontends/LyXView.h"
83 #include "frontends/Menubar.h"
84 #include "frontends/Toolbars.h"
86 #include "support/environment.h"
87 #include "support/filefilterlist.h"
88 #include "support/filetools.h"
89 #include "support/forkedcontr.h"
90 #include "support/fs_extras.h"
91 #include "support/lstrings.h"
92 #include "support/path.h"
93 #include "support/package.h"
94 #include "support/systemcall.h"
95 #include "support/convert.h"
96 #include "support/os.h"
98 #include <boost/current_function.hpp>
99 #include <boost/filesystem/operations.hpp>
103 using bv_funcs::freefont2string;
105 using lyx::docstring;
107 using lyx::support::absolutePath;
108 using lyx::support::addName;
109 using lyx::support::addPath;
110 using lyx::support::bformat;
111 using lyx::support::changeExtension;
112 using lyx::support::contains;
113 using lyx::support::FileFilterList;
114 using lyx::support::fileSearch;
115 using lyx::support::ForkedcallsController;
116 using lyx::support::i18nLibFileSearch;
117 using lyx::support::isDirWriteable;
118 using lyx::support::isFileReadable;
119 using lyx::support::isStrInt;
120 using lyx::support::makeAbsPath;
121 using lyx::support::makeDisplayPath;
122 using lyx::support::package;
123 using lyx::support::quoteName;
124 using lyx::support::rtrim;
125 using lyx::support::split;
126 using lyx::support::subst;
127 using lyx::support::Systemcall;
128 using lyx::support::token;
129 using lyx::support::trim;
130 using lyx::support::prefixIs;
133 using std::make_pair;
136 using std::istringstream;
137 using std::ostringstream;
139 namespace biblio = lyx::biblio;
140 namespace fs = boost::filesystem;
143 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
146 extern tex_accent_struct get_accent(kb_action action);
151 bool getStatus(LCursor cursor,
152 FuncRequest const & cmd, FuncStatus & status)
154 // Try to fix cursor in case it is broken.
155 cursor.fixIfBroken();
157 // This is, of course, a mess. Better create a new doc iterator and use
158 // this in Inset::getStatus. This might require an additional
159 // BufferView * arg, though (which should be avoided)
160 //LCursor safe = *this;
162 for ( ; cursor.depth(); cursor.pop()) {
163 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
164 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
165 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
166 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
168 // The inset's getStatus() will return 'true' if it made
169 // a definitive decision on whether it want to handle the
170 // request or not. The result of this decision is put into
171 // the 'status' parameter.
172 if (cursor.inset().getStatus(cursor, cmd, status)) {
181 /** Return the change status at cursor position, taking in account the
182 * status at each level of the document iterator (a table in a deleted
183 * footnote is deleted).
184 * When \param outer is true, the top slice is not looked at.
186 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
188 size_t const depth = dit.depth() - (outer ? 1 : 0);
190 for (size_t i = 0 ; i < depth ; ++i) {
191 CursorSlice const & slice = dit[i];
192 if (!slice.inset().inMathed()
193 && slice.pos() < slice.paragraph().size()) {
194 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
195 if (ch != Change::UNCHANGED)
199 return Change::UNCHANGED;
204 LyXFunc::LyXFunc(LyXView * lv)
207 keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
208 cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
209 meta_fake_bit(key_modifier::none)
214 void LyXFunc::handleKeyFunc(kb_action action)
216 char c = encoded_last_key;
218 if (keyseq.length()) {
222 owner->view()->getIntl().getTransManager()
223 .deadkey(c, get_accent(action).accent, view()->getLyXText());
224 // Need to clear, in case the minibuffer calls these
227 // copied verbatim from do_accent_char
228 view()->cursor().resetAnchor();
233 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
235 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
237 // Do nothing if we have nothing (JMarc)
238 if (!keysym->isOK()) {
239 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
244 if (keysym->isModifier()) {
245 lyxerr[Debug::KEY] << "isModifier true" << endl;
249 Encoding const * encoding = view()->cursor().getEncoding();
251 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
252 size_t encoded_last_key = keysym->getUCSEncoded();
254 // Do a one-deep top-level lookup for
255 // cancel and meta-fake keys. RVDK_PATCH_5
256 cancel_meta_seq.reset();
258 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
259 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
260 << " action first set to [" << func.action << ']'
263 // When not cancel or meta-fake, do the normal lookup.
264 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
265 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
266 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
267 // remove Caps Lock and Mod2 as a modifiers
268 func = keyseq.addkey(keysym, (state | meta_fake_bit));
269 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
270 << "action now set to ["
271 << func.action << ']' << endl;
274 // Dont remove this unless you know what you are doing.
275 meta_fake_bit = key_modifier::none;
277 // Can this happen now ?
278 if (func.action == LFUN_NOACTION) {
279 func = FuncRequest(LFUN_COMMAND_PREFIX);
282 if (lyxerr.debugging(Debug::KEY)) {
283 lyxerr << BOOST_CURRENT_FUNCTION
285 << func.action << "]["
286 << keyseq.print() << ']'
290 // already here we know if it any point in going further
291 // why not return already here if action == -1 and
292 // num_bytes == 0? (Lgb)
294 if (keyseq.length() > 1) {
295 owner->message(lyx::from_utf8(keyseq.print()));
299 // Maybe user can only reach the key via holding down shift.
300 // Let's see. But only if shift is the only modifier
301 if (func.action == LFUN_UNKNOWN_ACTION &&
302 state == key_modifier::shift) {
303 lyxerr[Debug::KEY] << "Trying without shift" << endl;
304 func = keyseq.addkey(keysym, key_modifier::none);
305 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
308 if (func.action == LFUN_UNKNOWN_ACTION) {
309 // Hmm, we didn't match any of the keysequences. See
310 // if it's normal insertable text not already covered
312 if (keysym->isText() && keyseq.length() == 1) {
313 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
314 func = FuncRequest(LFUN_SELF_INSERT,
315 FuncRequest::KEYBOARD);
317 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
318 owner->message(_("Unknown function."));
323 if (func.action == LFUN_SELF_INSERT) {
324 if (encoded_last_key != 0) {
325 docstring const arg(1, encoded_last_key);
326 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
327 FuncRequest::KEYBOARD));
329 << "SelfInsert arg[`" << lyx::to_utf8(arg) << "']" << endl;
337 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
339 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
341 LCursor & cur = view()->cursor();
343 /* In LyX/Mac, when a dialog is open, the menus of the
344 application can still be accessed without giving focus to
345 the main window. In this case, we want to disable the menu
346 entries that are buffer-related.
348 Note that this code is not perfect, as bug 1941 attests:
349 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
352 if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
355 buf = owner->buffer();
357 if (cmd.action == LFUN_NOACTION) {
358 flag.message(lyx::from_utf8(N_("Nothing to do")));
363 switch (cmd.action) {
364 case LFUN_UNKNOWN_ACTION:
365 #ifndef HAVE_LIBAIKSAURUS
366 case LFUN_THESAURUS_ENTRY:
372 case LFUN_TOOLTIPS_TOGGLE:
380 if (flag.unknown()) {
381 flag.message(lyx::from_utf8(N_("Unknown action")));
385 if (!flag.enabled()) {
386 if (flag.message().empty())
387 flag.message(lyx::from_utf8(N_("Command disabled")));
391 // Check whether we need a buffer
392 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
394 flag.message(lyx::from_utf8(N_("Command not allowed with"
395 "out any document open")));
400 // I would really like to avoid having this switch and rather try to
401 // encode this in the function itself.
402 // -- And I'd rather let an inset decide which LFUNs it is willing
403 // to handle (Andre')
405 switch (cmd.action) {
406 case LFUN_TOOLTIPS_TOGGLE:
407 flag.setOnOff(owner->getDialogs().tooltipsEnabled());
410 case LFUN_BUFFER_TOGGLE_READ_ONLY:
411 flag.setOnOff(buf->isReadonly());
414 case LFUN_BUFFER_SWITCH:
415 // toggle on the current buffer, but do not toggle off
416 // the other ones (is that a good idea?)
417 if (lyx::to_utf8(cmd.argument()) == buf->fileName())
421 case LFUN_BUFFER_EXPORT:
422 enable = cmd.argument() == "custom"
423 || Exporter::isExportable(*buf, lyx::to_utf8(cmd.argument()));
426 case LFUN_BUFFER_CHKTEX:
427 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
430 case LFUN_BUILD_PROGRAM:
431 enable = Exporter::isExportable(*buf, "program");
434 case LFUN_LAYOUT_TABULAR:
435 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
439 case LFUN_LAYOUT_PARAGRAPH:
440 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
443 case LFUN_VC_REGISTER:
444 enable = !buf->lyxvc().inUse();
446 case LFUN_VC_CHECK_IN:
447 enable = buf->lyxvc().inUse() && !buf->isReadonly();
449 case LFUN_VC_CHECK_OUT:
450 enable = buf->lyxvc().inUse() && buf->isReadonly();
453 case LFUN_VC_UNDO_LAST:
454 enable = buf->lyxvc().inUse();
456 case LFUN_BUFFER_RELOAD:
457 enable = !buf->isUnnamed() && !buf->isClean();
460 case LFUN_INSET_SETTINGS: {
464 InsetBase::Code code = cur.inset().lyxCode();
466 case InsetBase::TABULAR_CODE:
467 enable = cmd.argument() == "tabular";
469 case InsetBase::ERT_CODE:
470 enable = cmd.argument() == "ert";
472 case InsetBase::FLOAT_CODE:
473 enable = cmd.argument() == "float";
475 case InsetBase::WRAP_CODE:
476 enable = cmd.argument() == "wrap";
478 case InsetBase::NOTE_CODE:
479 enable = cmd.argument() == "note";
481 case InsetBase::BRANCH_CODE:
482 enable = cmd.argument() == "branch";
484 case InsetBase::BOX_CODE:
485 enable = cmd.argument() == "box";
493 case LFUN_INSET_APPLY: {
494 string const name = cmd.getArg(0);
495 InsetBase * inset = owner->getDialogs().getOpenInset(name);
497 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
499 bool const success = inset->getStatus(cur, fr, fs);
500 // Every inset is supposed to handle this
501 BOOST_ASSERT(success);
504 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
505 flag |= getStatus(fr);
507 enable = flag.enabled();
511 case LFUN_DIALOG_SHOW: {
512 string const name = cmd.getArg(0);
514 enable = name == "aboutlyx"
518 || name == "texinfo";
519 else if (name == "print")
520 enable = Exporter::isExportable(*buf, "dvi")
521 && lyxrc.print_command != "none";
522 else if (name == "character" || name == "mathpanel")
523 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
524 else if (name == "latexlog")
525 enable = isFileReadable(buf->getLogName().second);
526 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
527 else if (name == "spellchecker")
530 else if (name == "vclog")
531 enable = buf->lyxvc().inUse();
532 else if (name == "view-source")
537 case LFUN_DIALOG_SHOW_NEW_INSET:
538 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
541 case LFUN_DIALOG_UPDATE: {
542 string const name = cmd.getArg(0);
544 enable = name == "prefs";
548 case LFUN_CITATION_INSERT: {
549 FuncRequest fr(LFUN_INSET_INSERT, "citation");
550 enable = getStatus(fr).enabled();
554 case LFUN_BUFFER_WRITE: {
555 enable = view()->buffer()->isUnnamed()
556 || !view()->buffer()->isClean();
560 // this one is difficult to get right. As a half-baked
561 // solution, we consider only the first action of the sequence
562 case LFUN_COMMAND_SEQUENCE: {
563 // argument contains ';'-terminated commands
564 string const firstcmd = token(lyx::to_utf8(cmd.argument()), ';', 0);
565 FuncRequest func(lyxaction.lookupFunc(firstcmd));
566 func.origin = cmd.origin;
567 flag = getStatus(func);
570 case LFUN_BUFFER_NEW:
571 case LFUN_BUFFER_NEW_TEMPLATE:
572 case LFUN_WORD_FIND_FORWARD:
573 case LFUN_WORD_FIND_BACKWARD:
574 case LFUN_COMMAND_PREFIX:
575 case LFUN_COMMAND_EXECUTE:
577 case LFUN_META_PREFIX:
578 case LFUN_BUFFER_CLOSE:
579 case LFUN_BUFFER_WRITE_AS:
580 case LFUN_BUFFER_UPDATE:
581 case LFUN_BUFFER_VIEW:
582 case LFUN_BUFFER_IMPORT:
585 case LFUN_BUFFER_AUTO_SAVE:
586 case LFUN_RECONFIGURE:
590 case LFUN_DROP_LAYOUTS_CHOICE:
592 case LFUN_SERVER_GET_NAME:
593 case LFUN_SERVER_NOTIFY:
594 case LFUN_SERVER_GOTO_FILE_ROW:
595 case LFUN_DIALOG_HIDE:
596 case LFUN_DIALOG_DISCONNECT_INSET:
597 case LFUN_BUFFER_CHILD_OPEN:
598 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
599 case LFUN_KEYMAP_OFF:
600 case LFUN_KEYMAP_PRIMARY:
601 case LFUN_KEYMAP_SECONDARY:
602 case LFUN_KEYMAP_TOGGLE:
604 case LFUN_BUFFER_EXPORT_CUSTOM:
605 case LFUN_BUFFER_PRINT:
606 case LFUN_PREFERENCES_SAVE:
607 case LFUN_SCREEN_FONT_UPDATE:
610 case LFUN_EXTERNAL_EDIT:
611 case LFUN_GRAPHICS_EDIT:
612 case LFUN_ALL_INSETS_TOGGLE:
613 case LFUN_BUFFER_LANGUAGE:
614 case LFUN_TEXTCLASS_APPLY:
615 case LFUN_TEXTCLASS_LOAD:
616 case LFUN_BUFFER_SAVE_AS_DEFAULT:
617 case LFUN_BUFFER_PARAMS_APPLY:
618 case LFUN_LYXRC_APPLY:
619 case LFUN_BUFFER_NEXT:
620 case LFUN_BUFFER_PREVIOUS:
621 // these are handled in our dispatch()
626 if (!::getStatus(cur, cmd, flag))
627 flag = view()->getStatus(cmd);
633 // Can we use a readonly buffer?
634 if (buf && buf->isReadonly()
635 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
636 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
637 flag.message(lyx::from_utf8(N_("Document is read-only")));
641 // Are we in a DELETED change-tracking region?
642 if (buf && lookupChangeType(cur, true) == Change::DELETED
643 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
644 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
645 flag.message(lyx::from_utf8(N_("This portion of the document is deleted.")));
649 // the default error message if we disable the command
650 if (!flag.enabled() && flag.message().empty())
651 flag.message(lyx::from_utf8(N_("Command disabled")));
657 bool LyXFunc::ensureBufferClean(BufferView * bv)
659 Buffer & buf = *bv->buffer();
663 docstring const file = makeDisplayPath(buf.fileName(), 30);
664 docstring text = bformat(_("The document %1$s has unsaved "
665 "changes.\n\nDo you want to save "
666 "the document?"), file);
667 int const ret = Alert::prompt(_("Save changed document?"),
668 text, 0, 1, _("&Save"),
672 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
674 return buf.isClean();
680 void showPrintError(string const & name)
682 docstring str = bformat(_("Could not print the document %1$s.\n"
683 "Check that your printer is set up correctly."),
684 makeDisplayPath(name, 50));
685 Alert::error(_("Print document failed"), str);
689 void loadTextclass(string const & name)
691 std::pair<bool, lyx::textclass_type> const tc_pair =
692 textclasslist.numberOfClass(name);
694 if (!tc_pair.first) {
695 lyxerr << "Document class \"" << name
696 << "\" does not exist."
701 lyx::textclass_type const tc = tc_pair.second;
703 if (!textclasslist[tc].load()) {
704 docstring s = bformat(_("The document could not be converted\n"
705 "into the document class %1$s."),
706 lyx::from_utf8(textclasslist[tc].name()));
707 Alert::error(_("Could not change class"), s);
712 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
717 void LyXFunc::dispatch(FuncRequest const & cmd)
719 BOOST_ASSERT(view());
720 string const argument = lyx::to_utf8(cmd.argument());
721 kb_action const action = cmd.action;
723 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
724 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
726 // we have not done anything wrong yet.
728 dispatch_buffer.erase();
730 // redraw the screen at the end (first of the two drawing steps).
731 //This is done unless explicitely requested otherwise
733 // also do the second redrawing step. Only done if requested.
734 bool updateforce = false;
736 FuncStatus const flag = getStatus(cmd);
737 if (!flag.enabled()) {
738 // We cannot use this function here
739 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
740 << lyxaction.getActionName(action)
741 << " [" << action << "] is disabled at this location"
743 setErrorMessage(flag.message());
747 case LFUN_WORD_FIND_FORWARD:
748 case LFUN_WORD_FIND_BACKWARD: {
749 static string last_search;
750 string searched_string;
752 if (!argument.empty()) {
753 last_search = argument;
754 searched_string = argument;
756 searched_string = last_search;
759 if (searched_string.empty())
762 bool const fw = action == LFUN_WORD_FIND_FORWARD;
764 lyx::find::find2string(searched_string, true, false, fw);
765 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
769 case LFUN_COMMAND_PREFIX:
770 owner->message(lyx::from_utf8(keyseq.printOptions()));
773 case LFUN_COMMAND_EXECUTE:
774 owner->getToolbars().display("minibuffer", true);
775 owner->focus_command_buffer();
780 meta_fake_bit = key_modifier::none;
781 if (view()->buffer())
782 // cancel any selection
783 dispatch(FuncRequest(LFUN_MARK_OFF));
784 setMessage(_("Cancel"));
787 case LFUN_META_PREFIX:
788 meta_fake_bit = key_modifier::alt;
789 setMessage(lyx::from_utf8(keyseq.print()));
792 case LFUN_BUFFER_TOGGLE_READ_ONLY:
793 if (owner->buffer()->lyxvc().inUse())
794 owner->buffer()->lyxvc().toggleReadOnly();
796 owner->buffer()->setReadonly(
797 !owner->buffer()->isReadonly());
800 // --- Menus -----------------------------------------------
801 case LFUN_BUFFER_NEW:
802 menuNew(argument, false);
805 case LFUN_BUFFER_NEW_TEMPLATE:
806 menuNew(argument, true);
809 case LFUN_BUFFER_CLOSE:
813 case LFUN_BUFFER_WRITE:
814 if (!owner->buffer()->isUnnamed()) {
815 docstring const str = bformat(_("Saving document %1$s..."),
816 makeDisplayPath(owner->buffer()->fileName()));
818 menuWrite(owner->buffer());
819 owner->message(str + _(" done."));
821 writeAs(owner->buffer());
825 case LFUN_BUFFER_WRITE_AS:
826 writeAs(owner->buffer(), argument);
830 case LFUN_BUFFER_RELOAD: {
831 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
832 docstring text = bformat(_("Any changes will be lost. Are you sure "
833 "you want to revert to the saved version of the document %1$s?"), file);
834 int const ret = Alert::prompt(_("Revert to saved document?"),
835 text, 0, 1, _("&Revert"), _("&Cancel"));
842 case LFUN_BUFFER_UPDATE:
843 Exporter::Export(owner->buffer(), argument, true);
846 case LFUN_BUFFER_VIEW:
847 Exporter::preview(owner->buffer(), argument);
850 case LFUN_BUILD_PROGRAM:
851 Exporter::Export(owner->buffer(), "program", true);
854 case LFUN_BUFFER_CHKTEX:
855 owner->buffer()->runChktex();
858 case LFUN_BUFFER_EXPORT:
859 if (argument == "custom")
860 owner->getDialogs().show("sendto");
862 Exporter::Export(owner->buffer(), argument, false);
866 case LFUN_BUFFER_EXPORT_CUSTOM: {
868 string command = split(argument, format_name, ' ');
869 Format const * format = formats.getFormat(format_name);
871 lyxerr << "Format \"" << format_name
872 << "\" not recognized!"
877 Buffer * buffer = owner->buffer();
879 // The name of the file created by the conversion process
882 // Output to filename
883 if (format->name() == "lyx") {
884 string const latexname =
885 buffer->getLatexName(false);
886 filename = changeExtension(latexname,
887 format->extension());
888 filename = addName(buffer->temppath(), filename);
890 if (!buffer->writeFile(filename))
894 Exporter::Export(buffer, format_name, true, filename);
897 // Substitute $$FName for filename
898 if (!contains(command, "$$FName"))
899 command = "( " + command + " ) < $$FName";
900 command = subst(command, "$$FName", filename);
902 // Execute the command in the background
904 call.startscript(Systemcall::DontWait, command);
908 case LFUN_BUFFER_PRINT: {
911 string command = split(split(argument, target, ' '),
915 || target_name.empty()
916 || command.empty()) {
917 lyxerr << "Unable to parse \""
918 << argument << '"' << std::endl;
921 if (target != "printer" && target != "file") {
922 lyxerr << "Unrecognized target \""
923 << target << '"' << std::endl;
927 Buffer * buffer = owner->buffer();
929 if (!Exporter::Export(buffer, "dvi", true)) {
930 showPrintError(buffer->fileName());
934 // Push directory path.
935 string const path = buffer->temppath();
936 lyx::support::Path p(path);
938 // there are three cases here:
939 // 1. we print to a file
940 // 2. we print directly to a printer
941 // 3. we print using a spool command (print to file first)
944 string const dviname =
945 changeExtension(buffer->getLatexName(true),
948 if (target == "printer") {
949 if (!lyxrc.print_spool_command.empty()) {
950 // case 3: print using a spool
951 string const psname =
952 changeExtension(dviname,".ps");
953 command += lyxrc.print_to_file
956 + quoteName(dviname);
959 lyxrc.print_spool_command +' ';
960 if (target_name != "default") {
961 command2 += lyxrc.print_spool_printerprefix
965 command2 += quoteName(psname);
967 // If successful, then spool command
968 res = one.startscript(
973 res = one.startscript(
974 Systemcall::DontWait,
977 // case 2: print directly to a printer
978 res = one.startscript(
979 Systemcall::DontWait,
980 command + quoteName(dviname));
984 // case 1: print to a file
985 command += lyxrc.print_to_file
986 + quoteName(makeAbsPath(target_name,
989 + quoteName(dviname);
990 res = one.startscript(Systemcall::DontWait,
995 showPrintError(buffer->fileName());
999 case LFUN_BUFFER_IMPORT:
1004 if (view()->buffer()) {
1005 // save cursor Position for opened files to .lyx/session
1006 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1007 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1008 // save bookmarks to .lyx/session
1009 view()->saveSavedPositions();
1011 quitLyX(argument == "force");
1014 case LFUN_TOC_VIEW: {
1015 InsetCommandParams p("tableofcontents");
1016 string const data = InsetCommandMailer::params2string("toc", p);
1017 owner->getDialogs().show("toc", data, 0);
1021 case LFUN_BUFFER_AUTO_SAVE:
1025 case LFUN_RECONFIGURE:
1026 reconfigure(view());
1029 case LFUN_HELP_OPEN: {
1030 string const arg = argument;
1032 setErrorMessage(_("Missing argument"));
1035 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1036 if (fname.empty()) {
1037 lyxerr << "LyX: unable to find documentation file `"
1038 << arg << "'. Bad installation?" << endl;
1041 owner->message(bformat(_("Opening help file %1$s..."),
1042 makeDisplayPath(fname)));
1043 owner->loadLyXFile(fname, false);
1047 // --- version control -------------------------------
1048 case LFUN_VC_REGISTER:
1049 if (!ensureBufferClean(view()))
1051 if (!owner->buffer()->lyxvc().inUse()) {
1052 owner->buffer()->lyxvc().registrer();
1057 case LFUN_VC_CHECK_IN:
1058 if (!ensureBufferClean(view()))
1060 if (owner->buffer()->lyxvc().inUse()
1061 && !owner->buffer()->isReadonly()) {
1062 owner->buffer()->lyxvc().checkIn();
1067 case LFUN_VC_CHECK_OUT:
1068 if (!ensureBufferClean(view()))
1070 if (owner->buffer()->lyxvc().inUse()
1071 && owner->buffer()->isReadonly()) {
1072 owner->buffer()->lyxvc().checkOut();
1077 case LFUN_VC_REVERT:
1078 owner->buffer()->lyxvc().revert();
1082 case LFUN_VC_UNDO_LAST:
1083 owner->buffer()->lyxvc().undoLast();
1087 // --- buffers ----------------------------------------
1088 case LFUN_BUFFER_SWITCH:
1089 owner->setBuffer(theApp->bufferList().getBuffer(argument));
1092 case LFUN_BUFFER_NEXT:
1093 owner->setBuffer(theApp->bufferList().next(view()->buffer()));
1096 case LFUN_BUFFER_PREVIOUS:
1097 owner->setBuffer(theApp->bufferList().previous(view()->buffer()));
1101 newFile(view(), argument);
1104 case LFUN_FILE_OPEN:
1108 case LFUN_DROP_LAYOUTS_CHOICE:
1109 owner->getToolbars().openLayoutList();
1112 case LFUN_MENU_OPEN:
1113 owner->getMenubar().openByName(lyx::from_utf8(argument));
1116 // --- lyxserver commands ----------------------------
1117 case LFUN_SERVER_GET_NAME:
1118 setMessage(lyx::from_utf8(owner->buffer()->fileName()));
1119 lyxerr[Debug::INFO] << "FNAME["
1120 << owner->buffer()->fileName()
1124 case LFUN_SERVER_NOTIFY:
1125 dispatch_buffer = lyx::from_utf8(keyseq.print());
1126 theApp->server().notifyClient(lyx::to_utf8(dispatch_buffer));
1129 case LFUN_SERVER_GOTO_FILE_ROW: {
1132 istringstream is(argument);
1133 is >> file_name >> row;
1134 if (prefixIs(file_name, package().temp_dir())) {
1135 // Needed by inverse dvi search. If it is a file
1136 // in tmpdir, call the apropriated function
1137 owner->setBuffer(theApp->bufferList().getBufferFromTmp(file_name));
1139 // Must replace extension of the file to be .lyx
1140 // and get full path
1141 string const s = changeExtension(file_name, ".lyx");
1142 // Either change buffer or load the file
1143 if (theApp->bufferList().exists(s)) {
1144 owner->setBuffer(theApp->bufferList().getBuffer(s));
1146 owner->loadLyXFile(s);
1150 view()->setCursorFromRow(row);
1153 // see BufferView::center()
1157 case LFUN_DIALOG_SHOW: {
1158 string const name = cmd.getArg(0);
1159 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1161 if (name == "character") {
1162 data = freefont2string();
1164 owner->getDialogs().show("character", data);
1165 } else if (name == "latexlog") {
1166 pair<Buffer::LogType, string> const logfile =
1167 owner->buffer()->getLogName();
1168 switch (logfile.first) {
1169 case Buffer::latexlog:
1172 case Buffer::buildlog:
1176 data += LyXLex::quoteString(logfile.second);
1177 owner->getDialogs().show("log", data);
1178 } else if (name == "vclog") {
1179 string const data = "vc " +
1180 LyXLex::quoteString(owner->buffer()->lyxvc().getLogFile());
1181 owner->getDialogs().show("log", data);
1183 owner->getDialogs().show(name, data);
1187 case LFUN_DIALOG_SHOW_NEW_INSET: {
1188 string const name = cmd.getArg(0);
1189 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1190 if (name == "bibitem" ||
1197 InsetCommandParams p(name);
1198 data = InsetCommandMailer::params2string(name, p);
1199 } else if (name == "include") {
1200 InsetCommandParams p(data);
1201 data = InsetIncludeMailer::params2string(p);
1202 } else if (name == "box") {
1203 // \c data == "Boxed" || "Frameless" etc
1204 InsetBoxParams p(data);
1205 data = InsetBoxMailer::params2string(p);
1206 } else if (name == "branch") {
1207 InsetBranchParams p;
1208 data = InsetBranchMailer::params2string(p);
1209 } else if (name == "citation") {
1210 InsetCommandParams p("cite");
1211 data = InsetCommandMailer::params2string(name, p);
1212 } else if (name == "ert") {
1213 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1214 } else if (name == "external") {
1215 InsetExternalParams p;
1216 Buffer const & buffer = *owner->buffer();
1217 data = InsetExternalMailer::params2string(p, buffer);
1218 } else if (name == "float") {
1220 data = InsetFloatMailer::params2string(p);
1221 } else if (name == "graphics") {
1222 InsetGraphicsParams p;
1223 Buffer const & buffer = *owner->buffer();
1224 data = InsetGraphicsMailer::params2string(p, buffer);
1225 } else if (name == "note") {
1227 data = InsetNoteMailer::params2string(p);
1228 } else if (name == "vspace") {
1230 data = InsetVSpaceMailer::params2string(space);
1231 } else if (name == "wrap") {
1233 data = InsetWrapMailer::params2string(p);
1235 owner->getDialogs().show(name, data, 0);
1239 case LFUN_DIALOG_UPDATE: {
1240 string const & name = argument;
1241 // Can only update a dialog connected to an existing inset
1242 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1244 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1245 inset->dispatch(view()->cursor(), fr);
1246 } else if (name == "paragraph") {
1247 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1248 } else if (name == "prefs") {
1249 owner->getDialogs().update(name, string());
1254 case LFUN_DIALOG_HIDE:
1255 Dialogs::hide(argument, 0);
1258 case LFUN_DIALOG_DISCONNECT_INSET:
1259 owner->getDialogs().disconnect(argument);
1263 case LFUN_CITATION_INSERT: {
1264 if (!argument.empty()) {
1265 // we can have one optional argument, delimited by '|'
1266 // citation-insert <key>|<text_before>
1267 // this should be enhanced to also support text_after
1268 // and citation style
1269 string arg = argument;
1271 if (contains(argument, "|")) {
1272 arg = token(argument, '|', 0);
1273 opt1 = '[' + token(argument, '|', 1) + ']';
1275 std::ostringstream os;
1276 os << "citation LatexCommand\n"
1277 << "\\cite" << opt1 << "{" << arg << "}\n"
1279 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1282 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1286 case LFUN_BUFFER_CHILD_OPEN: {
1287 string const filename =
1288 makeAbsPath(argument, owner->buffer()->filePath());
1289 // FIXME Should use bformat
1290 setMessage(_("Opening child document ") +
1291 makeDisplayPath(filename) + lyx::from_ascii("..."));
1292 view()->savePosition(0);
1293 string const parentfilename = owner->buffer()->fileName();
1294 if (theApp->bufferList().exists(filename))
1295 owner->setBuffer(theApp->bufferList().getBuffer(filename));
1297 owner->loadLyXFile(filename);
1298 // Set the parent name of the child document.
1299 // This makes insertion of citations and references in the child work,
1300 // when the target is in the parent or another child document.
1301 owner->buffer()->setParentName(parentfilename);
1305 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1306 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1309 case LFUN_KEYMAP_OFF:
1310 owner->view()->getIntl().keyMapOn(false);
1313 case LFUN_KEYMAP_PRIMARY:
1314 owner->view()->getIntl().keyMapPrim();
1317 case LFUN_KEYMAP_SECONDARY:
1318 owner->view()->getIntl().keyMapSec();
1321 case LFUN_KEYMAP_TOGGLE:
1322 owner->view()->getIntl().toggleKeyMap();
1328 string rest = split(argument, countstr, ' ');
1329 istringstream is(countstr);
1332 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1333 for (int i = 0; i < count; ++i)
1334 dispatch(lyxaction.lookupFunc(rest));
1338 case LFUN_COMMAND_SEQUENCE: {
1339 // argument contains ';'-terminated commands
1340 string arg = argument;
1341 while (!arg.empty()) {
1343 arg = split(arg, first, ';');
1344 FuncRequest func(lyxaction.lookupFunc(first));
1345 func.origin = cmd.origin;
1351 case LFUN_PREFERENCES_SAVE: {
1352 lyx::support::Path p(package().user_support());
1353 lyxrc.write("preferences", false);
1357 case LFUN_SCREEN_FONT_UPDATE:
1358 // handle the screen font changes.
1359 lyxrc.set_font_norm_type();
1360 theApp->fontLoader().update();
1361 // All visible buffers will need resize
1365 case LFUN_SET_COLOR: {
1367 string const x11_name = split(argument, lyx_name, ' ');
1368 if (lyx_name.empty() || x11_name.empty()) {
1369 setErrorMessage(_("Syntax: set-color <lyx_name>"
1374 bool const graphicsbg_changed =
1375 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1376 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1378 if (!lcolor.setColor(lyx_name, x11_name)) {
1380 bformat(_("Set-color \"%1$s\" failed "
1381 "- color is undefined or "
1382 "may not be redefined"),
1383 lyx::from_utf8(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(lyx::from_utf8(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 // FIXME Should use bformat
1495 setMessage(_("Document defaults saved in ")
1496 + makeDisplayPath(fname));
1498 setErrorMessage(_("Unable to save document defaults"));
1502 case LFUN_BUFFER_PARAMS_APPLY: {
1503 biblio::CiteEngine const engine =
1504 owner->buffer()->params().cite_engine;
1506 istringstream ss(argument);
1509 int const unknown_tokens =
1510 owner->buffer()->readHeader(lex);
1512 if (unknown_tokens != 0) {
1513 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1514 << unknown_tokens << " unknown token"
1515 << (unknown_tokens == 1 ? "" : "s")
1518 if (engine == owner->buffer()->params().cite_engine)
1521 LCursor & cur = view()->cursor();
1522 FuncRequest fr(LFUN_INSET_REFRESH);
1524 InsetBase & inset = owner->buffer()->inset();
1525 InsetIterator it = inset_iterator_begin(inset);
1526 InsetIterator const end = inset_iterator_end(inset);
1527 for (; it != end; ++it)
1528 if (it->lyxCode() == InsetBase::CITE_CODE)
1529 it->dispatch(cur, fr);
1533 case LFUN_TEXTCLASS_APPLY: {
1534 Buffer * buffer = owner->buffer();
1536 lyx::textclass_type const old_class =
1537 buffer->params().textclass;
1539 loadTextclass(argument);
1541 std::pair<bool, lyx::textclass_type> const tc_pair =
1542 textclasslist.numberOfClass(argument);
1547 lyx::textclass_type const new_class = tc_pair.second;
1548 if (old_class == new_class)
1552 owner->message(_("Converting document to new document class..."));
1553 recordUndoFullDocument(view());
1554 buffer->params().textclass = new_class;
1555 StableDocIterator backcur(view()->cursor());
1556 ErrorList & el = buffer->errorList("Class Switch");
1557 lyx::cap::switchBetweenClasses(
1558 old_class, new_class,
1559 static_cast<InsetText &>(buffer->inset()), el);
1561 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1563 buffer->errors("Class Switch");
1564 updateLabels(*buffer);
1569 case LFUN_TEXTCLASS_LOAD:
1570 loadTextclass(argument);
1573 case LFUN_LYXRC_APPLY: {
1574 LyXRC const lyxrc_orig = lyxrc;
1576 istringstream ss(argument);
1577 bool const success = lyxrc.read(ss) == 0;
1580 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1581 << "Unable to read lyxrc data"
1586 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1591 view()->cursor().dispatch(cmd);
1592 updateforce |= view()->cursor().result().update();
1593 if (!view()->cursor().result().dispatched())
1594 updateforce |= view()->dispatch(cmd);
1599 if (view()->buffer()) {
1600 // Redraw screen unless explicitly told otherwise.
1601 // This also initializes the position cache for all insets
1602 // in (at least partially) visible top-level paragraphs.
1604 view()->update(Update::FitCursor | Update::Force);
1606 view()->update(Update::FitCursor);
1608 owner->redrawWorkArea();
1610 // if we executed a mutating lfun, mark the buffer as dirty
1612 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1613 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1614 view()->buffer()->markDirty();
1616 if (view()->cursor().inTexted()) {
1617 owner->updateLayoutChoice();
1622 // FIXME UNICODE: _() does not support anything but ascii.
1623 // Do we need a lyx::to_ascii() method?
1624 sendDispatchMessage(getMessage(), cmd);
1628 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1630 /* When an action did not originate from the UI/kbd, it makes
1631 * sense to avoid updating the GUI. It turns out that this
1632 * fixes bug 1941, for reasons that are described here:
1633 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1635 if (cmd.origin != FuncRequest::INTERNAL) {
1636 owner->updateMenubar();
1637 owner->updateToolbars();
1640 const bool verbose = (cmd.origin == FuncRequest::UI
1641 || cmd.origin == FuncRequest::COMMANDBUFFER);
1643 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1644 lyxerr[Debug::ACTION] << "dispatch msg is " << lyx::to_utf8(msg) << endl;
1646 owner->message(msg);
1650 docstring dispatch_msg = msg;
1651 if (!dispatch_msg.empty())
1652 dispatch_msg += ' ';
1654 string comname = lyxaction.getActionName(cmd.action);
1656 bool argsadded = false;
1658 if (!cmd.argument().empty()) {
1659 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1660 comname += ' ' + lyx::to_utf8(cmd.argument());
1665 string const shortcuts = toplevel_keymap->printbindings(cmd);
1667 if (!shortcuts.empty())
1668 comname += ": " + shortcuts;
1669 else if (!argsadded && !cmd.argument().empty())
1670 comname += ' ' + lyx::to_utf8(cmd.argument());
1672 if (!comname.empty()) {
1673 comname = rtrim(comname);
1674 dispatch_msg += lyx::from_utf8('(' + rtrim(comname) + ')');
1677 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1678 << lyx::to_utf8(dispatch_msg) << endl;
1679 if (!dispatch_msg.empty())
1680 owner->message(dispatch_msg);
1684 void LyXFunc::setupLocalKeymap()
1686 keyseq.stdmap = toplevel_keymap.get();
1687 keyseq.curmap = toplevel_keymap.get();
1688 cancel_meta_seq.stdmap = toplevel_keymap.get();
1689 cancel_meta_seq.curmap = toplevel_keymap.get();
1693 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1695 string initpath = lyxrc.document_path;
1696 string filename(name);
1698 if (view()->buffer()) {
1699 string const trypath = owner->buffer()->filePath();
1700 // If directory is writeable, use this as default.
1701 if (isDirWriteable(trypath))
1705 static int newfile_number;
1707 if (filename.empty()) {
1708 filename = addName(lyxrc.document_path,
1709 "newfile" + convert<string>(++newfile_number) + ".lyx");
1710 while (theApp->bufferList().exists(filename) || fs::is_readable(filename)) {
1712 filename = addName(lyxrc.document_path,
1713 "newfile" + convert<string>(newfile_number) +
1718 // The template stuff
1721 FileDialog fileDlg(lyx::to_utf8(_("Select template file")),
1722 LFUN_SELECT_FILE_SYNC,
1723 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1724 string(lyxrc.document_path)),
1725 make_pair(string(lyx::to_utf8(_("Templates|#T#t"))),
1726 string(lyxrc.template_path)));
1728 FileDialog::Result result =
1729 fileDlg.open(lyxrc.template_path,
1730 FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1733 if (result.first == FileDialog::Later)
1735 if (result.second.empty())
1737 templname = result.second;
1740 Buffer * const b = newFile(filename, templname, !name.empty());
1742 owner->setBuffer(b);
1746 void LyXFunc::open(string const & fname)
1748 string initpath = lyxrc.document_path;
1750 if (view()->buffer()) {
1751 string const trypath = owner->buffer()->filePath();
1752 // If directory is writeable, use this as default.
1753 if (isDirWriteable(trypath))
1759 if (fname.empty()) {
1760 FileDialog fileDlg(lyx::to_utf8(_("Select document to open")),
1762 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1763 string(lyxrc.document_path)),
1764 make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1765 string(addPath(package().system_support(), "examples"))));
1767 FileDialog::Result result =
1768 fileDlg.open(initpath,
1769 FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1772 if (result.first == FileDialog::Later)
1775 filename = result.second;
1777 // check selected filename
1778 if (filename.empty()) {
1779 owner->message(_("Canceled."));
1785 // get absolute path of file and add ".lyx" to the filename if
1787 string const fullpath = fileSearch(string(), filename, "lyx");
1788 if (!fullpath.empty()) {
1789 filename = fullpath;
1792 docstring const disp_fn = makeDisplayPath(filename);
1794 // if the file doesn't exist, let the user create one
1795 if (!fs::exists(filename)) {
1796 // the user specifically chose this name. Believe him.
1797 Buffer * const b = newFile(filename, string(), true);
1799 owner->setBuffer(b);
1803 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1806 if (owner->loadLyXFile(filename)) {
1807 str2 = bformat(_("Document %1$s opened."), disp_fn);
1809 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1811 owner->message(str2);
1815 void LyXFunc::doImport(string const & argument)
1818 string filename = split(argument, format, ' ');
1820 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1821 << " file: " << filename << endl;
1823 // need user interaction
1824 if (filename.empty()) {
1825 string initpath = lyxrc.document_path;
1827 if (view()->buffer()) {
1828 string const trypath = owner->buffer()->filePath();
1829 // If directory is writeable, use this as default.
1830 if (isDirWriteable(trypath))
1834 docstring const text = bformat(_("Select %1$s file to import"),
1835 formats.prettyName(format));
1837 FileDialog fileDlg(lyx::to_utf8(text),
1839 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1840 string(lyxrc.document_path)),
1841 make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1842 string(addPath(package().system_support(), "examples"))));
1844 string const filter = lyx::to_utf8(formats.prettyName(format))
1845 + " (*." + formats.extension(format) + ')';
1847 FileDialog::Result result =
1848 fileDlg.open(initpath,
1849 FileFilterList(filter),
1852 if (result.first == FileDialog::Later)
1855 filename = result.second;
1857 // check selected filename
1858 if (filename.empty())
1859 owner->message(_("Canceled."));
1862 if (filename.empty())
1865 // get absolute path of file
1866 filename = makeAbsPath(filename);
1868 string const lyxfile = changeExtension(filename, ".lyx");
1870 // Check if the document already is open
1871 if (lyx_gui::use_gui && theApp->bufferList().exists(lyxfile)) {
1872 if (!theApp->bufferList().close(theApp->bufferList().getBuffer(lyxfile), true)) {
1873 owner->message(_("Canceled."));
1878 // if the file exists already, and we didn't do
1879 // -i lyx thefile.lyx, warn
1880 if (fs::exists(lyxfile) && filename != lyxfile) {
1881 docstring const file = makeDisplayPath(lyxfile, 30);
1883 docstring text = bformat(_("The document %1$s already exists.\n\n"
1884 "Do you want to over-write that document?"), file);
1885 int const ret = Alert::prompt(_("Over-write document?"),
1886 text, 0, 1, _("&Over-write"), _("&Cancel"));
1889 owner->message(_("Canceled."));
1894 ErrorList errorList;
1895 Importer::Import(owner, filename, format, errorList);
1896 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1900 void LyXFunc::closeBuffer()
1902 // save current cursor position
1903 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1904 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1905 if (theApp->bufferList().close(owner->buffer(), true) && !quitting) {
1906 if (theApp->bufferList().empty()) {
1907 // need this otherwise SEGV may occur while
1908 // trying to set variables that don't exist
1909 // since there's no current buffer
1910 owner->getDialogs().hideBufferDependent();
1912 owner->setBuffer(theApp->bufferList().first());
1918 // Each "owner" should have it's own message method. lyxview and
1919 // the minibuffer would use the minibuffer, but lyxserver would
1920 // send an ERROR signal to its client. Alejandro 970603
1921 // This function is bit problematic when it comes to NLS, to make the
1922 // lyx servers client be language indepenent we must not translate
1923 // strings sent to this func.
1924 void LyXFunc::setErrorMessage(docstring const & m) const
1926 dispatch_buffer = m;
1931 void LyXFunc::setMessage(docstring const & m) const
1933 dispatch_buffer = m;
1937 string const LyXFunc::viewStatusMessage()
1939 // When meta-fake key is pressed, show the key sequence so far + "M-".
1941 return keyseq.print() + "M-";
1943 // Else, when a non-complete key sequence is pressed,
1944 // show the available options.
1945 if (keyseq.length() > 0 && !keyseq.deleted())
1946 return keyseq.printOptions();
1948 if (!view()->buffer())
1949 return lyx::to_utf8(_("Welcome to LyX!"));
1951 return view()->cursor().currentState();
1955 BufferView * LyXFunc::view() const
1957 BOOST_ASSERT(owner);
1958 return owner->view();
1962 bool LyXFunc::wasMetaKey() const
1964 return (meta_fake_bit != key_modifier::none);
1970 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1972 // Why the switch you might ask. It is a trick to ensure that all
1973 // the elements in the LyXRCTags enum is handled. As you can see
1974 // there are no breaks at all. So it is just a huge fall-through.
1975 // The nice thing is that we will get a warning from the compiler
1976 // if we forget an element.
1977 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1979 case LyXRC::RC_ACCEPT_COMPOUND:
1980 case LyXRC::RC_ALT_LANG:
1981 case LyXRC::RC_ASCIIROFF_COMMAND:
1982 case LyXRC::RC_ASCII_LINELEN:
1983 case LyXRC::RC_AUTOREGIONDELETE:
1984 case LyXRC::RC_AUTORESET_OPTIONS:
1985 case LyXRC::RC_AUTOSAVE:
1986 case LyXRC::RC_AUTO_NUMBER:
1987 case LyXRC::RC_BACKUPDIR_PATH:
1988 case LyXRC::RC_BIBTEX_COMMAND:
1989 case LyXRC::RC_BINDFILE:
1990 case LyXRC::RC_CHECKLASTFILES:
1991 case LyXRC::RC_USELASTFILEPOS:
1992 case LyXRC::RC_LOADSESSION:
1993 case LyXRC::RC_CHKTEX_COMMAND:
1994 case LyXRC::RC_CONVERTER:
1995 case LyXRC::RC_COPIER:
1996 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1997 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1998 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1999 case LyXRC::RC_DATE_INSERT_FORMAT:
2000 case LyXRC::RC_DEFAULT_LANGUAGE:
2001 case LyXRC::RC_DEFAULT_PAPERSIZE:
2002 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2003 case LyXRC::RC_DISPLAY_GRAPHICS:
2004 case LyXRC::RC_DOCUMENTPATH:
2005 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2006 if (fs::exists(lyxrc_new.document_path) &&
2007 fs::is_directory(lyxrc_new.document_path)) {
2008 using lyx::support::package;
2009 package().document_dir() = lyxrc.document_path;
2012 case LyXRC::RC_ESC_CHARS:
2013 case LyXRC::RC_FONT_ENCODING:
2014 case LyXRC::RC_FORMAT:
2015 case LyXRC::RC_INDEX_COMMAND:
2016 case LyXRC::RC_INPUT:
2017 case LyXRC::RC_KBMAP:
2018 case LyXRC::RC_KBMAP_PRIMARY:
2019 case LyXRC::RC_KBMAP_SECONDARY:
2020 case LyXRC::RC_LABEL_INIT_LENGTH:
2021 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2022 case LyXRC::RC_LANGUAGE_AUTO_END:
2023 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2024 case LyXRC::RC_LANGUAGE_COMMAND_END:
2025 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2026 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2027 case LyXRC::RC_LANGUAGE_PACKAGE:
2028 case LyXRC::RC_LANGUAGE_USE_BABEL:
2029 case LyXRC::RC_MAKE_BACKUP:
2030 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2031 case LyXRC::RC_NUMLASTFILES:
2032 case LyXRC::RC_PATH_PREFIX:
2033 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2034 using lyx::support::prependEnvPath;
2035 prependEnvPath("PATH", lyxrc.path_prefix);
2037 case LyXRC::RC_PERS_DICT:
2038 case LyXRC::RC_POPUP_BOLD_FONT:
2039 case LyXRC::RC_POPUP_FONT_ENCODING:
2040 case LyXRC::RC_POPUP_NORMAL_FONT:
2041 case LyXRC::RC_PREVIEW:
2042 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2043 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2044 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2045 case LyXRC::RC_PRINTCOPIESFLAG:
2046 case LyXRC::RC_PRINTER:
2047 case LyXRC::RC_PRINTEVENPAGEFLAG:
2048 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2049 case LyXRC::RC_PRINTFILEEXTENSION:
2050 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2051 case LyXRC::RC_PRINTODDPAGEFLAG:
2052 case LyXRC::RC_PRINTPAGERANGEFLAG:
2053 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2054 case LyXRC::RC_PRINTPAPERFLAG:
2055 case LyXRC::RC_PRINTREVERSEFLAG:
2056 case LyXRC::RC_PRINTSPOOL_COMMAND:
2057 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2058 case LyXRC::RC_PRINTTOFILE:
2059 case LyXRC::RC_PRINTTOPRINTER:
2060 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2061 case LyXRC::RC_PRINT_COMMAND:
2062 case LyXRC::RC_RTL_SUPPORT:
2063 case LyXRC::RC_SCREEN_DPI:
2064 case LyXRC::RC_SCREEN_FONT_ENCODING:
2065 case LyXRC::RC_SCREEN_FONT_ROMAN:
2066 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2067 case LyXRC::RC_SCREEN_FONT_SANS:
2068 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2069 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2070 case LyXRC::RC_SCREEN_FONT_SIZES:
2071 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2072 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2073 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2074 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2075 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2076 case LyXRC::RC_SCREEN_ZOOM:
2077 case LyXRC::RC_SERVERPIPE:
2078 case LyXRC::RC_SET_COLOR:
2079 case LyXRC::RC_SHOW_BANNER:
2080 case LyXRC::RC_SPELL_COMMAND:
2081 case LyXRC::RC_TEMPDIRPATH:
2082 case LyXRC::RC_TEMPLATEPATH:
2083 case LyXRC::RC_TEX_ALLOWS_SPACES:
2084 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2085 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2086 namespace os = lyx::support::os;
2087 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2089 case LyXRC::RC_UIFILE:
2090 case LyXRC::RC_USER_EMAIL:
2091 case LyXRC::RC_USER_NAME:
2092 case LyXRC::RC_USETEMPDIR:
2093 case LyXRC::RC_USE_ALT_LANG:
2094 case LyXRC::RC_USE_ESC_CHARS:
2095 case LyXRC::RC_USE_INP_ENC:
2096 case LyXRC::RC_USE_PERS_DICT:
2097 case LyXRC::RC_USE_SPELL_LIB:
2098 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2099 case LyXRC::RC_VIEWER:
2100 case LyXRC::RC_LAST: