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 "bufferparams.h"
28 #include "BufferView.h"
30 #include "CutAndPaste.h"
32 #include "dispatchresult.h"
34 #include "errorlist.h"
37 #include "funcrequest.h"
40 #include "insetiterator.h"
48 #include "LyXAction.h"
53 #include "lyxtextclasslist.h"
55 #include "paragraph.h"
56 #include "pariterator.h"
57 #include "ParagraphParameters.h"
60 #include "insets/insetbox.h"
61 #include "insets/insetbranch.h"
62 #include "insets/insetcommand.h"
63 #include "insets/insetert.h"
64 #include "insets/insetexternal.h"
65 #include "insets/insetfloat.h"
66 #include "insets/insetgraphics.h"
67 #include "insets/insetinclude.h"
68 #include "insets/insetnote.h"
69 #include "insets/insettabular.h"
70 #include "insets/insetvspace.h"
71 #include "insets/insetwrap.h"
73 #include "frontends/Application.h"
74 #include "frontends/Alert.h"
75 #include "frontends/Dialogs.h"
76 #include "frontends/FileDialog.h"
77 #include "frontends/lyx_gui.h"
78 #include "frontends/LyXKeySym.h"
79 #include "frontends/LyXView.h"
80 #include "frontends/Menubar.h"
81 #include "frontends/Toolbars.h"
83 #include "support/environment.h"
84 #include "support/filefilterlist.h"
85 #include "support/filetools.h"
86 #include "support/forkedcontr.h"
87 #include "support/fs_extras.h"
88 #include "support/lstrings.h"
89 #include "support/path.h"
90 #include "support/package.h"
91 #include "support/systemcall.h"
92 #include "support/convert.h"
93 #include "support/os.h"
95 #include <boost/current_function.hpp>
96 #include <boost/filesystem/operations.hpp>
100 using bv_funcs::freefont2string;
102 using lyx::docstring;
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::quoteName;
121 using lyx::support::rtrim;
122 using lyx::support::split;
123 using lyx::support::subst;
124 using lyx::support::Systemcall;
125 using lyx::support::token;
126 using lyx::support::trim;
127 using lyx::support::prefixIs;
130 using std::make_pair;
133 using std::istringstream;
134 using std::ostringstream;
136 namespace biblio = lyx::biblio;
137 namespace fs = boost::filesystem;
140 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
143 extern tex_accent_struct get_accent(kb_action action);
148 bool getStatus(LCursor cursor,
149 FuncRequest const & cmd, FuncStatus & status)
151 // Try to fix cursor in case it is broken.
152 cursor.fixIfBroken();
154 // This is, of course, a mess. Better create a new doc iterator and use
155 // this in Inset::getStatus. This might require an additional
156 // BufferView * arg, though (which should be avoided)
157 //LCursor safe = *this;
159 for ( ; cursor.depth(); cursor.pop()) {
160 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
161 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
162 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
163 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
165 // The inset's getStatus() will return 'true' if it made
166 // a definitive decision on whether it want to handle the
167 // request or not. The result of this decision is put into
168 // the 'status' parameter.
169 if (cursor.inset().getStatus(cursor, cmd, status)) {
178 /** Return the change status at cursor position, taking in account the
179 * status at each level of the document iterator (a table in a deleted
180 * footnote is deleted).
181 * When \param outer is true, the top slice is not looked at.
183 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
185 size_t const depth = dit.depth() - (outer ? 1 : 0);
187 for (size_t i = 0 ; i < depth ; ++i) {
188 CursorSlice const & slice = dit[i];
189 if (!slice.inset().inMathed()
190 && slice.pos() < slice.paragraph().size()) {
191 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
192 if (ch != Change::UNCHANGED)
196 return Change::UNCHANGED;
201 LyXFunc::LyXFunc(LyXView * lv)
204 keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
205 cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
206 meta_fake_bit(key_modifier::none)
211 void LyXFunc::handleKeyFunc(kb_action action)
213 char c = encoded_last_key;
215 if (keyseq.length()) {
219 owner->view()->getIntl().getTransManager()
220 .deadkey(c, get_accent(action).accent, view()->getLyXText());
221 // Need to clear, in case the minibuffer calls these
224 // copied verbatim from do_accent_char
225 view()->cursor().resetAnchor();
230 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
232 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
234 // Do nothing if we have nothing (JMarc)
235 if (!keysym->isOK()) {
236 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
241 if (keysym->isModifier()) {
242 lyxerr[Debug::KEY] << "isModifier true" << endl;
246 Encoding const * encoding = view()->cursor().getEncoding();
248 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
249 size_t encoded_last_key = keysym->getUCSEncoded();
251 // Do a one-deep top-level lookup for
252 // cancel and meta-fake keys. RVDK_PATCH_5
253 cancel_meta_seq.reset();
255 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
256 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
257 << " action first set to [" << func.action << ']'
260 // When not cancel or meta-fake, do the normal lookup.
261 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
262 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
263 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
264 // remove Caps Lock and Mod2 as a modifiers
265 func = keyseq.addkey(keysym, (state | meta_fake_bit));
266 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
267 << "action now set to ["
268 << func.action << ']' << endl;
271 // Dont remove this unless you know what you are doing.
272 meta_fake_bit = key_modifier::none;
274 // Can this happen now ?
275 if (func.action == LFUN_NOACTION) {
276 func = FuncRequest(LFUN_COMMAND_PREFIX);
279 if (lyxerr.debugging(Debug::KEY)) {
280 lyxerr << BOOST_CURRENT_FUNCTION
282 << func.action << "]["
283 << keyseq.print() << ']'
287 // already here we know if it any point in going further
288 // why not return already here if action == -1 and
289 // num_bytes == 0? (Lgb)
291 if (keyseq.length() > 1) {
292 owner->message(lyx::from_utf8(keyseq.print()));
296 // Maybe user can only reach the key via holding down shift.
297 // Let's see. But only if shift is the only modifier
298 if (func.action == LFUN_UNKNOWN_ACTION &&
299 state == key_modifier::shift) {
300 lyxerr[Debug::KEY] << "Trying without shift" << endl;
301 func = keyseq.addkey(keysym, key_modifier::none);
302 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
305 if (func.action == LFUN_UNKNOWN_ACTION) {
306 // Hmm, we didn't match any of the keysequences. See
307 // if it's normal insertable text not already covered
309 if (keysym->isText() && keyseq.length() == 1) {
310 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
311 func = FuncRequest(LFUN_SELF_INSERT,
312 FuncRequest::KEYBOARD);
314 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
315 owner->message(_("Unknown function."));
320 if (func.action == LFUN_SELF_INSERT) {
321 if (encoded_last_key != 0) {
322 docstring const arg(1, encoded_last_key);
323 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
324 FuncRequest::KEYBOARD));
326 << "SelfInsert arg[`" << lyx::to_utf8(arg) << "']" << endl;
334 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
336 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
338 LCursor & cur = view()->cursor();
340 /* In LyX/Mac, when a dialog is open, the menus of the
341 application can still be accessed without giving focus to
342 the main window. In this case, we want to disable the menu
343 entries that are buffer-related.
345 Note that this code is not perfect, as bug 1941 attests:
346 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
349 if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
352 buf = owner->buffer();
354 if (cmd.action == LFUN_NOACTION) {
355 flag.message(lyx::from_utf8(N_("Nothing to do")));
360 switch (cmd.action) {
361 case LFUN_UNKNOWN_ACTION:
362 #ifndef HAVE_LIBAIKSAURUS
363 case LFUN_THESAURUS_ENTRY:
369 flag |= lyx_gui::getStatus(cmd);
372 if (flag.unknown()) {
373 flag.message(lyx::from_utf8(N_("Unknown action")));
377 if (!flag.enabled()) {
378 if (flag.message().empty())
379 flag.message(lyx::from_utf8(N_("Command disabled")));
383 // Check whether we need a buffer
384 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
386 flag.message(lyx::from_utf8(N_("Command not allowed with"
387 "out any document open")));
392 // I would really like to avoid having this switch and rather try to
393 // encode this in the function itself.
394 // -- And I'd rather let an inset decide which LFUNs it is willing
395 // to handle (Andre')
397 switch (cmd.action) {
398 case LFUN_TOOLTIPS_TOGGLE:
399 flag.setOnOff(owner->getDialogs().tooltipsEnabled());
402 case LFUN_BUFFER_TOGGLE_READ_ONLY:
403 flag.setOnOff(buf->isReadonly());
406 case LFUN_BUFFER_SWITCH:
407 // toggle on the current buffer, but do not toggle off
408 // the other ones (is that a good idea?)
409 if (lyx::to_utf8(cmd.argument()) == buf->fileName())
413 case LFUN_BUFFER_EXPORT:
414 enable = cmd.argument() == "custom"
415 || Exporter::isExportable(*buf, lyx::to_utf8(cmd.argument()));
418 case LFUN_BUFFER_CHKTEX:
419 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
422 case LFUN_BUILD_PROGRAM:
423 enable = Exporter::isExportable(*buf, "program");
426 case LFUN_LAYOUT_TABULAR:
427 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
431 case LFUN_LAYOUT_PARAGRAPH:
432 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
435 case LFUN_VC_REGISTER:
436 enable = !buf->lyxvc().inUse();
438 case LFUN_VC_CHECK_IN:
439 enable = buf->lyxvc().inUse() && !buf->isReadonly();
441 case LFUN_VC_CHECK_OUT:
442 enable = buf->lyxvc().inUse() && buf->isReadonly();
445 case LFUN_VC_UNDO_LAST:
446 enable = buf->lyxvc().inUse();
448 case LFUN_BUFFER_RELOAD:
449 enable = !buf->isUnnamed() && !buf->isClean();
452 case LFUN_INSET_SETTINGS: {
456 InsetBase::Code code = cur.inset().lyxCode();
458 case InsetBase::TABULAR_CODE:
459 enable = cmd.argument() == "tabular";
461 case InsetBase::ERT_CODE:
462 enable = cmd.argument() == "ert";
464 case InsetBase::FLOAT_CODE:
465 enable = cmd.argument() == "float";
467 case InsetBase::WRAP_CODE:
468 enable = cmd.argument() == "wrap";
470 case InsetBase::NOTE_CODE:
471 enable = cmd.argument() == "note";
473 case InsetBase::BRANCH_CODE:
474 enable = cmd.argument() == "branch";
476 case InsetBase::BOX_CODE:
477 enable = cmd.argument() == "box";
485 case LFUN_INSET_APPLY: {
486 string const name = cmd.getArg(0);
487 InsetBase * inset = owner->getDialogs().getOpenInset(name);
489 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
491 bool const success = inset->getStatus(cur, fr, fs);
492 // Every inset is supposed to handle this
493 BOOST_ASSERT(success);
496 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
497 flag |= getStatus(fr);
499 enable = flag.enabled();
503 case LFUN_DIALOG_SHOW: {
504 string const name = cmd.getArg(0);
506 enable = name == "aboutlyx"
510 || name == "texinfo";
511 else if (name == "print")
512 enable = Exporter::isExportable(*buf, "dvi")
513 && lyxrc.print_command != "none";
514 else if (name == "character" || name == "mathpanel")
515 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
516 else if (name == "latexlog")
517 enable = isFileReadable(buf->getLogName().second);
518 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
519 else if (name == "spellchecker")
522 else if (name == "vclog")
523 enable = buf->lyxvc().inUse();
524 else if (name == "view-source")
529 case LFUN_DIALOG_SHOW_NEW_INSET:
530 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
533 case LFUN_DIALOG_UPDATE: {
534 string const name = cmd.getArg(0);
536 enable = name == "prefs";
540 case LFUN_CITATION_INSERT: {
541 FuncRequest fr(LFUN_INSET_INSERT, "citation");
542 enable = getStatus(fr).enabled();
546 case LFUN_BUFFER_WRITE: {
547 enable = view()->buffer()->isUnnamed()
548 || !view()->buffer()->isClean();
552 // this one is difficult to get right. As a half-baked
553 // solution, we consider only the first action of the sequence
554 case LFUN_COMMAND_SEQUENCE: {
555 // argument contains ';'-terminated commands
556 string const firstcmd = token(lyx::to_utf8(cmd.argument()), ';', 0);
557 FuncRequest func(lyxaction.lookupFunc(firstcmd));
558 func.origin = cmd.origin;
559 flag = getStatus(func);
562 case LFUN_BUFFER_NEW:
563 case LFUN_BUFFER_NEW_TEMPLATE:
564 case LFUN_WORD_FIND_FORWARD:
565 case LFUN_WORD_FIND_BACKWARD:
566 case LFUN_COMMAND_PREFIX:
567 case LFUN_COMMAND_EXECUTE:
569 case LFUN_META_PREFIX:
570 case LFUN_BUFFER_CLOSE:
571 case LFUN_BUFFER_WRITE_AS:
572 case LFUN_BUFFER_UPDATE:
573 case LFUN_BUFFER_VIEW:
574 case LFUN_BUFFER_IMPORT:
577 case LFUN_BUFFER_AUTO_SAVE:
578 case LFUN_RECONFIGURE:
582 case LFUN_DROP_LAYOUTS_CHOICE:
584 case LFUN_SERVER_GET_NAME:
585 case LFUN_SERVER_NOTIFY:
586 case LFUN_SERVER_GOTO_FILE_ROW:
587 case LFUN_DIALOG_HIDE:
588 case LFUN_DIALOG_DISCONNECT_INSET:
589 case LFUN_BUFFER_CHILD_OPEN:
590 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
591 case LFUN_KEYMAP_OFF:
592 case LFUN_KEYMAP_PRIMARY:
593 case LFUN_KEYMAP_SECONDARY:
594 case LFUN_KEYMAP_TOGGLE:
596 case LFUN_BUFFER_EXPORT_CUSTOM:
597 case LFUN_BUFFER_PRINT:
598 case LFUN_PREFERENCES_SAVE:
599 case LFUN_SCREEN_FONT_UPDATE:
602 case LFUN_EXTERNAL_EDIT:
603 case LFUN_GRAPHICS_EDIT:
604 case LFUN_ALL_INSETS_TOGGLE:
605 case LFUN_BUFFER_LANGUAGE:
606 case LFUN_TEXTCLASS_APPLY:
607 case LFUN_TEXTCLASS_LOAD:
608 case LFUN_BUFFER_SAVE_AS_DEFAULT:
609 case LFUN_BUFFER_PARAMS_APPLY:
610 case LFUN_LYXRC_APPLY:
611 case LFUN_BUFFER_NEXT:
612 case LFUN_BUFFER_PREVIOUS:
613 // these are handled in our dispatch()
618 if (!::getStatus(cur, cmd, flag))
619 flag = view()->getStatus(cmd);
625 // Can we use a readonly buffer?
626 if (buf && buf->isReadonly()
627 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
628 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
629 flag.message(lyx::from_utf8(N_("Document is read-only")));
633 // Are we in a DELETED change-tracking region?
634 if (buf && buf->params().tracking_changes
635 && lookupChangeType(cur, true) == Change::DELETED
636 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
637 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
638 flag.message(lyx::from_utf8(N_("This portion of the document is deleted.")));
642 // the default error message if we disable the command
643 if (!flag.enabled() && flag.message().empty())
644 flag.message(lyx::from_utf8(N_("Command disabled")));
650 bool LyXFunc::ensureBufferClean(BufferView * bv)
652 Buffer & buf = *bv->buffer();
656 docstring const file = makeDisplayPath(buf.fileName(), 30);
657 docstring text = bformat(_("The document %1$s has unsaved "
658 "changes.\n\nDo you want to save "
659 "the document?"), file);
660 int const ret = Alert::prompt(_("Save changed document?"),
661 text, 0, 1, _("&Save"),
665 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
667 return buf.isClean();
673 void showPrintError(string const & name)
675 docstring str = bformat(_("Could not print the document %1$s.\n"
676 "Check that your printer is set up correctly."),
677 makeDisplayPath(name, 50));
678 Alert::error(_("Print document failed"), str);
682 void loadTextclass(string const & name)
684 std::pair<bool, lyx::textclass_type> const tc_pair =
685 textclasslist.numberOfClass(name);
687 if (!tc_pair.first) {
688 lyxerr << "Document class \"" << name
689 << "\" does not exist."
694 lyx::textclass_type const tc = tc_pair.second;
696 if (!textclasslist[tc].load()) {
697 docstring s = bformat(_("The document could not be converted\n"
698 "into the document class %1$s."),
699 lyx::from_utf8(textclasslist[tc].name()));
700 Alert::error(_("Could not change class"), s);
705 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
710 void LyXFunc::dispatch(FuncRequest const & cmd)
712 BOOST_ASSERT(view());
713 string const argument = lyx::to_utf8(cmd.argument());
714 kb_action const action = cmd.action;
716 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
717 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
719 // we have not done anything wrong yet.
721 dispatch_buffer.erase();
723 // redraw the screen at the end (first of the two drawing steps).
724 //This is done unless explicitely requested otherwise
726 // also do the second redrawing step. Only done if requested.
727 bool updateforce = false;
729 FuncStatus const flag = getStatus(cmd);
730 if (!flag.enabled()) {
731 // We cannot use this function here
732 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
733 << lyxaction.getActionName(action)
734 << " [" << action << "] is disabled at this location"
736 setErrorMessage(flag.message());
740 case LFUN_WORD_FIND_FORWARD:
741 case LFUN_WORD_FIND_BACKWARD: {
742 static string last_search;
743 string searched_string;
745 if (!argument.empty()) {
746 last_search = argument;
747 searched_string = argument;
749 searched_string = last_search;
752 if (searched_string.empty())
755 bool const fw = action == LFUN_WORD_FIND_FORWARD;
757 lyx::find::find2string(searched_string, true, false, fw);
758 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
762 case LFUN_COMMAND_PREFIX:
763 owner->message(lyx::from_utf8(keyseq.printOptions()));
766 case LFUN_COMMAND_EXECUTE:
767 owner->getToolbars().display("minibuffer", true);
768 owner->focus_command_buffer();
773 meta_fake_bit = key_modifier::none;
774 if (view()->buffer())
775 // cancel any selection
776 dispatch(FuncRequest(LFUN_MARK_OFF));
777 setMessage(_("Cancel"));
780 case LFUN_META_PREFIX:
781 meta_fake_bit = key_modifier::alt;
782 setMessage(lyx::from_utf8(keyseq.print()));
785 case LFUN_BUFFER_TOGGLE_READ_ONLY:
786 if (owner->buffer()->lyxvc().inUse())
787 owner->buffer()->lyxvc().toggleReadOnly();
789 owner->buffer()->setReadonly(
790 !owner->buffer()->isReadonly());
793 // --- Menus -----------------------------------------------
794 case LFUN_BUFFER_NEW:
795 menuNew(argument, false);
798 case LFUN_BUFFER_NEW_TEMPLATE:
799 menuNew(argument, true);
802 case LFUN_BUFFER_CLOSE:
806 case LFUN_BUFFER_WRITE:
807 if (!owner->buffer()->isUnnamed()) {
808 docstring const str = bformat(_("Saving document %1$s..."),
809 makeDisplayPath(owner->buffer()->fileName()));
811 menuWrite(owner->buffer());
812 owner->message(str + _(" done."));
814 writeAs(owner->buffer());
818 case LFUN_BUFFER_WRITE_AS:
819 writeAs(owner->buffer(), argument);
823 case LFUN_BUFFER_RELOAD: {
824 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
825 docstring text = bformat(_("Any changes will be lost. Are you sure "
826 "you want to revert to the saved version of the document %1$s?"), file);
827 int const ret = Alert::prompt(_("Revert to saved document?"),
828 text, 0, 1, _("&Revert"), _("&Cancel"));
835 case LFUN_BUFFER_UPDATE:
836 Exporter::Export(owner->buffer(), argument, true);
839 case LFUN_BUFFER_VIEW:
840 Exporter::preview(owner->buffer(), argument);
843 case LFUN_BUILD_PROGRAM:
844 Exporter::Export(owner->buffer(), "program", true);
847 case LFUN_BUFFER_CHKTEX:
848 owner->buffer()->runChktex();
851 case LFUN_BUFFER_EXPORT:
852 if (argument == "custom")
853 owner->getDialogs().show("sendto");
855 Exporter::Export(owner->buffer(), argument, false);
859 case LFUN_BUFFER_EXPORT_CUSTOM: {
861 string command = split(argument, format_name, ' ');
862 Format const * format = formats.getFormat(format_name);
864 lyxerr << "Format \"" << format_name
865 << "\" not recognized!"
870 Buffer * buffer = owner->buffer();
872 // The name of the file created by the conversion process
875 // Output to filename
876 if (format->name() == "lyx") {
877 string const latexname =
878 buffer->getLatexName(false);
879 filename = changeExtension(latexname,
880 format->extension());
881 filename = addName(buffer->temppath(), filename);
883 if (!buffer->writeFile(filename))
887 Exporter::Export(buffer, format_name, true, filename);
890 // Substitute $$FName for filename
891 if (!contains(command, "$$FName"))
892 command = "( " + command + " ) < $$FName";
893 command = subst(command, "$$FName", filename);
895 // Execute the command in the background
897 call.startscript(Systemcall::DontWait, command);
901 case LFUN_BUFFER_PRINT: {
904 string command = split(split(argument, target, ' '),
908 || target_name.empty()
909 || command.empty()) {
910 lyxerr << "Unable to parse \""
911 << argument << '"' << std::endl;
914 if (target != "printer" && target != "file") {
915 lyxerr << "Unrecognized target \""
916 << target << '"' << std::endl;
920 Buffer * buffer = owner->buffer();
922 if (!Exporter::Export(buffer, "dvi", true)) {
923 showPrintError(buffer->fileName());
927 // Push directory path.
928 string const path = buffer->temppath();
929 lyx::support::Path p(path);
931 // there are three cases here:
932 // 1. we print to a file
933 // 2. we print directly to a printer
934 // 3. we print using a spool command (print to file first)
937 string const dviname =
938 changeExtension(buffer->getLatexName(true),
941 if (target == "printer") {
942 if (!lyxrc.print_spool_command.empty()) {
943 // case 3: print using a spool
944 string const psname =
945 changeExtension(dviname,".ps");
946 command += lyxrc.print_to_file
949 + quoteName(dviname);
952 lyxrc.print_spool_command +' ';
953 if (target_name != "default") {
954 command2 += lyxrc.print_spool_printerprefix
958 command2 += quoteName(psname);
960 // If successful, then spool command
961 res = one.startscript(
966 res = one.startscript(
967 Systemcall::DontWait,
970 // case 2: print directly to a printer
971 res = one.startscript(
972 Systemcall::DontWait,
973 command + quoteName(dviname));
977 // case 1: print to a file
978 command += lyxrc.print_to_file
979 + quoteName(makeAbsPath(target_name,
982 + quoteName(dviname);
983 res = one.startscript(Systemcall::DontWait,
988 showPrintError(buffer->fileName());
992 case LFUN_BUFFER_IMPORT:
997 if (view()->buffer()) {
998 // save cursor Position for opened files to .lyx/session
999 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1000 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1001 // save bookmarks to .lyx/session
1002 view()->saveSavedPositions();
1004 quitLyX(argument == "force");
1007 case LFUN_TOC_VIEW: {
1008 InsetCommandParams p("tableofcontents");
1009 string const data = InsetCommandMailer::params2string("toc", p);
1010 owner->getDialogs().show("toc", data, 0);
1014 case LFUN_BUFFER_AUTO_SAVE:
1018 case LFUN_RECONFIGURE:
1019 reconfigure(view());
1022 case LFUN_HELP_OPEN: {
1023 string const arg = argument;
1025 setErrorMessage(_("Missing argument"));
1028 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1029 if (fname.empty()) {
1030 lyxerr << "LyX: unable to find documentation file `"
1031 << arg << "'. Bad installation?" << endl;
1034 owner->message(bformat(_("Opening help file %1$s..."),
1035 makeDisplayPath(fname)));
1036 owner->loadLyXFile(fname, false);
1040 // --- version control -------------------------------
1041 case LFUN_VC_REGISTER:
1042 if (!ensureBufferClean(view()))
1044 if (!owner->buffer()->lyxvc().inUse()) {
1045 owner->buffer()->lyxvc().registrer();
1050 case LFUN_VC_CHECK_IN:
1051 if (!ensureBufferClean(view()))
1053 if (owner->buffer()->lyxvc().inUse()
1054 && !owner->buffer()->isReadonly()) {
1055 owner->buffer()->lyxvc().checkIn();
1060 case LFUN_VC_CHECK_OUT:
1061 if (!ensureBufferClean(view()))
1063 if (owner->buffer()->lyxvc().inUse()
1064 && owner->buffer()->isReadonly()) {
1065 owner->buffer()->lyxvc().checkOut();
1070 case LFUN_VC_REVERT:
1071 owner->buffer()->lyxvc().revert();
1075 case LFUN_VC_UNDO_LAST:
1076 owner->buffer()->lyxvc().undoLast();
1080 // --- buffers ----------------------------------------
1081 case LFUN_BUFFER_SWITCH:
1082 owner->setBuffer(theApp->bufferList().getBuffer(argument));
1085 case LFUN_BUFFER_NEXT:
1086 owner->setBuffer(theApp->bufferList().next(view()->buffer()));
1089 case LFUN_BUFFER_PREVIOUS:
1090 owner->setBuffer(theApp->bufferList().previous(view()->buffer()));
1094 newFile(view(), argument);
1097 case LFUN_FILE_OPEN:
1101 case LFUN_DROP_LAYOUTS_CHOICE:
1102 owner->getToolbars().openLayoutList();
1105 case LFUN_MENU_OPEN:
1106 owner->getMenubar().openByName(lyx::from_utf8(argument));
1109 // --- lyxserver commands ----------------------------
1110 case LFUN_SERVER_GET_NAME:
1111 setMessage(lyx::from_utf8(owner->buffer()->fileName()));
1112 lyxerr[Debug::INFO] << "FNAME["
1113 << owner->buffer()->fileName()
1117 case LFUN_SERVER_NOTIFY:
1118 dispatch_buffer = lyx::from_utf8(keyseq.print());
1119 theApp->server().notifyClient(lyx::to_utf8(dispatch_buffer));
1122 case LFUN_SERVER_GOTO_FILE_ROW: {
1125 istringstream is(argument);
1126 is >> file_name >> row;
1127 if (prefixIs(file_name, package().temp_dir())) {
1128 // Needed by inverse dvi search. If it is a file
1129 // in tmpdir, call the apropriated function
1130 owner->setBuffer(theApp->bufferList().getBufferFromTmp(file_name));
1132 // Must replace extension of the file to be .lyx
1133 // and get full path
1134 string const s = changeExtension(file_name, ".lyx");
1135 // Either change buffer or load the file
1136 if (theApp->bufferList().exists(s)) {
1137 owner->setBuffer(theApp->bufferList().getBuffer(s));
1139 owner->loadLyXFile(s);
1143 view()->setCursorFromRow(row);
1146 // see BufferView::center()
1150 case LFUN_DIALOG_SHOW: {
1151 string const name = cmd.getArg(0);
1152 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1154 if (name == "character") {
1155 data = freefont2string();
1157 owner->getDialogs().show("character", data);
1158 } else if (name == "latexlog") {
1159 pair<Buffer::LogType, string> const logfile =
1160 owner->buffer()->getLogName();
1161 switch (logfile.first) {
1162 case Buffer::latexlog:
1165 case Buffer::buildlog:
1169 data += LyXLex::quoteString(logfile.second);
1170 owner->getDialogs().show("log", data);
1171 } else if (name == "vclog") {
1172 string const data = "vc " +
1173 LyXLex::quoteString(owner->buffer()->lyxvc().getLogFile());
1174 owner->getDialogs().show("log", data);
1176 owner->getDialogs().show(name, data);
1180 case LFUN_DIALOG_SHOW_NEW_INSET: {
1181 string const name = cmd.getArg(0);
1182 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1183 if (name == "bibitem" ||
1190 InsetCommandParams p(name);
1191 data = InsetCommandMailer::params2string(name, p);
1192 } else if (name == "include") {
1193 InsetCommandParams p(data);
1194 data = InsetIncludeMailer::params2string(p);
1195 } else if (name == "box") {
1196 // \c data == "Boxed" || "Frameless" etc
1197 InsetBoxParams p(data);
1198 data = InsetBoxMailer::params2string(p);
1199 } else if (name == "branch") {
1200 InsetBranchParams p;
1201 data = InsetBranchMailer::params2string(p);
1202 } else if (name == "citation") {
1203 InsetCommandParams p("cite");
1204 data = InsetCommandMailer::params2string(name, p);
1205 } else if (name == "ert") {
1206 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1207 } else if (name == "external") {
1208 InsetExternalParams p;
1209 Buffer const & buffer = *owner->buffer();
1210 data = InsetExternalMailer::params2string(p, buffer);
1211 } else if (name == "float") {
1213 data = InsetFloatMailer::params2string(p);
1214 } else if (name == "graphics") {
1215 InsetGraphicsParams p;
1216 Buffer const & buffer = *owner->buffer();
1217 data = InsetGraphicsMailer::params2string(p, buffer);
1218 } else if (name == "note") {
1220 data = InsetNoteMailer::params2string(p);
1221 } else if (name == "vspace") {
1223 data = InsetVSpaceMailer::params2string(space);
1224 } else if (name == "wrap") {
1226 data = InsetWrapMailer::params2string(p);
1228 owner->getDialogs().show(name, data, 0);
1232 case LFUN_DIALOG_UPDATE: {
1233 string const & name = argument;
1234 // Can only update a dialog connected to an existing inset
1235 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1237 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1238 inset->dispatch(view()->cursor(), fr);
1239 } else if (name == "paragraph") {
1240 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1241 } else if (name == "prefs") {
1242 owner->getDialogs().update(name, string());
1247 case LFUN_DIALOG_HIDE:
1248 Dialogs::hide(argument, 0);
1251 case LFUN_DIALOG_DISCONNECT_INSET:
1252 owner->getDialogs().disconnect(argument);
1256 case LFUN_CITATION_INSERT: {
1257 if (!argument.empty()) {
1258 // we can have one optional argument, delimited by '|'
1259 // citation-insert <key>|<text_before>
1260 // this should be enhanced to also support text_after
1261 // and citation style
1262 string arg = argument;
1264 if (contains(argument, "|")) {
1265 arg = token(argument, '|', 0);
1266 opt1 = '[' + token(argument, '|', 1) + ']';
1268 std::ostringstream os;
1269 os << "citation LatexCommand\n"
1270 << "\\cite" << opt1 << "{" << arg << "}\n"
1272 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1275 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1279 case LFUN_BUFFER_CHILD_OPEN: {
1280 string const filename =
1281 makeAbsPath(argument, owner->buffer()->filePath());
1282 // FIXME Should use bformat
1283 setMessage(_("Opening child document ") +
1284 makeDisplayPath(filename) + lyx::from_ascii("..."));
1285 view()->savePosition(0);
1286 string const parentfilename = owner->buffer()->fileName();
1287 if (theApp->bufferList().exists(filename))
1288 owner->setBuffer(theApp->bufferList().getBuffer(filename));
1290 owner->loadLyXFile(filename);
1291 // Set the parent name of the child document.
1292 // This makes insertion of citations and references in the child work,
1293 // when the target is in the parent or another child document.
1294 owner->buffer()->setParentName(parentfilename);
1298 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1299 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1302 case LFUN_KEYMAP_OFF:
1303 owner->view()->getIntl().keyMapOn(false);
1306 case LFUN_KEYMAP_PRIMARY:
1307 owner->view()->getIntl().keyMapPrim();
1310 case LFUN_KEYMAP_SECONDARY:
1311 owner->view()->getIntl().keyMapSec();
1314 case LFUN_KEYMAP_TOGGLE:
1315 owner->view()->getIntl().toggleKeyMap();
1321 string rest = split(argument, countstr, ' ');
1322 istringstream is(countstr);
1325 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1326 for (int i = 0; i < count; ++i)
1327 dispatch(lyxaction.lookupFunc(rest));
1331 case LFUN_COMMAND_SEQUENCE: {
1332 // argument contains ';'-terminated commands
1333 string arg = argument;
1334 while (!arg.empty()) {
1336 arg = split(arg, first, ';');
1337 FuncRequest func(lyxaction.lookupFunc(first));
1338 func.origin = cmd.origin;
1344 case LFUN_PREFERENCES_SAVE: {
1345 lyx::support::Path p(package().user_support());
1346 lyxrc.write("preferences", false);
1350 case LFUN_SCREEN_FONT_UPDATE:
1351 // handle the screen font changes.
1352 lyxrc.set_font_norm_type();
1353 lyx_gui::update_fonts();
1354 // All visible buffers will need resize
1358 case LFUN_SET_COLOR: {
1360 string const x11_name = split(argument, lyx_name, ' ');
1361 if (lyx_name.empty() || x11_name.empty()) {
1362 setErrorMessage(_("Syntax: set-color <lyx_name>"
1367 bool const graphicsbg_changed =
1368 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1369 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1371 if (!lcolor.setColor(lyx_name, x11_name)) {
1373 bformat(_("Set-color \"%1$s\" failed "
1374 "- color is undefined or "
1375 "may not be redefined"),
1376 lyx::from_utf8(lyx_name)));
1380 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1382 if (graphicsbg_changed) {
1383 #ifdef WITH_WARNINGS
1384 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1387 lyx::graphics::GCache::get().changeDisplay(true);
1394 owner->message(lyx::from_utf8(argument));
1397 case LFUN_TOOLTIPS_TOGGLE:
1398 owner->getDialogs().toggleTooltips();
1401 case LFUN_EXTERNAL_EDIT: {
1402 FuncRequest fr(action, argument);
1403 InsetExternal().dispatch(view()->cursor(), fr);
1407 case LFUN_GRAPHICS_EDIT: {
1408 FuncRequest fr(action, argument);
1409 InsetGraphics().dispatch(view()->cursor(), fr);
1413 case LFUN_INSET_APPLY: {
1414 string const name = cmd.getArg(0);
1415 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1417 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1418 inset->dispatch(view()->cursor(), fr);
1420 FuncRequest fr(LFUN_INSET_INSERT, argument);
1423 // ideally, the update flag should be set by the insets,
1424 // but this is not possible currently
1429 case LFUN_ALL_INSETS_TOGGLE: {
1431 string const name = split(argument, action, ' ');
1432 InsetBase::Code const inset_code =
1433 InsetBase::translate(name);
1435 LCursor & cur = view()->cursor();
1436 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1438 InsetBase & inset = owner->buffer()->inset();
1439 InsetIterator it = inset_iterator_begin(inset);
1440 InsetIterator const end = inset_iterator_end(inset);
1441 for (; it != end; ++it) {
1442 if (inset_code == InsetBase::NO_CODE
1443 || inset_code == it->lyxCode()) {
1444 LCursor tmpcur = cur;
1445 tmpcur.pushLeft(*it);
1446 it->dispatch(tmpcur, fr);
1453 case LFUN_BUFFER_LANGUAGE: {
1454 Buffer & buffer = *owner->buffer();
1455 Language const * oldL = buffer.params().language;
1456 Language const * newL = languages.getLanguage(argument);
1457 if (!newL || oldL == newL)
1460 if (oldL->rightToLeft() == newL->rightToLeft()
1461 && !buffer.isMultiLingual())
1462 buffer.changeLanguage(oldL, newL);
1464 buffer.updateDocLang(newL);
1468 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1469 string const fname =
1470 addName(addPath(package().user_support(), "templates/"),
1472 Buffer defaults(fname);
1474 istringstream ss(argument);
1477 int const unknown_tokens = defaults.readHeader(lex);
1479 if (unknown_tokens != 0) {
1480 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1481 << unknown_tokens << " unknown token"
1482 << (unknown_tokens == 1 ? "" : "s")
1486 if (defaults.writeFile(defaults.fileName()))
1487 // FIXME Should use bformat
1488 setMessage(_("Document defaults saved in ")
1489 + makeDisplayPath(fname));
1491 setErrorMessage(_("Unable to save document defaults"));
1495 case LFUN_BUFFER_PARAMS_APPLY: {
1496 biblio::CiteEngine const engine =
1497 owner->buffer()->params().cite_engine;
1499 istringstream ss(argument);
1502 int const unknown_tokens =
1503 owner->buffer()->readHeader(lex);
1505 if (unknown_tokens != 0) {
1506 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1507 << unknown_tokens << " unknown token"
1508 << (unknown_tokens == 1 ? "" : "s")
1511 if (engine == owner->buffer()->params().cite_engine)
1514 LCursor & cur = view()->cursor();
1515 FuncRequest fr(LFUN_INSET_REFRESH);
1517 InsetBase & inset = owner->buffer()->inset();
1518 InsetIterator it = inset_iterator_begin(inset);
1519 InsetIterator const end = inset_iterator_end(inset);
1520 for (; it != end; ++it)
1521 if (it->lyxCode() == InsetBase::CITE_CODE)
1522 it->dispatch(cur, fr);
1526 case LFUN_TEXTCLASS_APPLY: {
1527 Buffer * buffer = owner->buffer();
1529 lyx::textclass_type const old_class =
1530 buffer->params().textclass;
1532 loadTextclass(argument);
1534 std::pair<bool, lyx::textclass_type> const tc_pair =
1535 textclasslist.numberOfClass(argument);
1540 lyx::textclass_type const new_class = tc_pair.second;
1541 if (old_class == new_class)
1545 owner->message(_("Converting document to new document class..."));
1546 recordUndoFullDocument(view());
1547 buffer->params().textclass = new_class;
1548 StableDocIterator backcur(view()->cursor());
1549 ErrorList & el = buffer->errorList("Class Switch");
1550 lyx::cap::switchBetweenClasses(
1551 old_class, new_class,
1552 static_cast<InsetText &>(buffer->inset()), el);
1554 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1556 buffer->errors("Class Switch");
1557 updateLabels(*buffer);
1562 case LFUN_TEXTCLASS_LOAD:
1563 loadTextclass(argument);
1566 case LFUN_LYXRC_APPLY: {
1567 LyXRC const lyxrc_orig = lyxrc;
1569 istringstream ss(argument);
1570 bool const success = lyxrc.read(ss) == 0;
1573 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1574 << "Unable to read lyxrc data"
1579 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1584 view()->cursor().dispatch(cmd);
1585 updateforce |= view()->cursor().result().update();
1586 if (!view()->cursor().result().dispatched())
1587 updateforce |= view()->dispatch(cmd);
1592 if (view()->buffer()) {
1593 // Redraw screen unless explicitly told otherwise.
1594 // This also initializes the position cache for all insets
1595 // in (at least partially) visible top-level paragraphs.
1597 view()->update(Update::FitCursor | Update::Force);
1599 view()->update(Update::FitCursor);
1601 owner->redrawWorkArea();
1603 // if we executed a mutating lfun, mark the buffer as dirty
1605 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1606 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1607 view()->buffer()->markDirty();
1609 if (view()->cursor().inTexted()) {
1610 owner->updateLayoutChoice();
1615 // FIXME UNICODE: _() does not support anything but ascii.
1616 // Do we need a lyx::to_ascii() method?
1617 sendDispatchMessage(getMessage(), cmd);
1621 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1623 /* When an action did not originate from the UI/kbd, it makes
1624 * sense to avoid updating the GUI. It turns out that this
1625 * fixes bug 1941, for reasons that are described here:
1626 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1628 if (cmd.origin != FuncRequest::INTERNAL) {
1629 owner->updateMenubar();
1630 owner->updateToolbars();
1633 const bool verbose = (cmd.origin == FuncRequest::UI
1634 || cmd.origin == FuncRequest::COMMANDBUFFER);
1636 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1637 lyxerr[Debug::ACTION] << "dispatch msg is " << lyx::to_utf8(msg) << endl;
1639 owner->message(msg);
1643 docstring dispatch_msg = msg;
1644 if (!dispatch_msg.empty())
1645 dispatch_msg += ' ';
1647 string comname = lyxaction.getActionName(cmd.action);
1649 bool argsadded = false;
1651 if (!cmd.argument().empty()) {
1652 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1653 comname += ' ' + lyx::to_utf8(cmd.argument());
1658 string const shortcuts = toplevel_keymap->printbindings(cmd);
1660 if (!shortcuts.empty())
1661 comname += ": " + shortcuts;
1662 else if (!argsadded && !cmd.argument().empty())
1663 comname += ' ' + lyx::to_utf8(cmd.argument());
1665 if (!comname.empty()) {
1666 comname = rtrim(comname);
1667 dispatch_msg += lyx::from_utf8('(' + rtrim(comname) + ')');
1670 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1671 << lyx::to_utf8(dispatch_msg) << endl;
1672 if (!dispatch_msg.empty())
1673 owner->message(dispatch_msg);
1677 void LyXFunc::setupLocalKeymap()
1679 keyseq.stdmap = toplevel_keymap.get();
1680 keyseq.curmap = toplevel_keymap.get();
1681 cancel_meta_seq.stdmap = toplevel_keymap.get();
1682 cancel_meta_seq.curmap = toplevel_keymap.get();
1686 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1688 string initpath = lyxrc.document_path;
1689 string filename(name);
1691 if (view()->buffer()) {
1692 string const trypath = owner->buffer()->filePath();
1693 // If directory is writeable, use this as default.
1694 if (isDirWriteable(trypath))
1698 static int newfile_number;
1700 if (filename.empty()) {
1701 filename = addName(lyxrc.document_path,
1702 "newfile" + convert<string>(++newfile_number) + ".lyx");
1703 while (theApp->bufferList().exists(filename) || fs::is_readable(filename)) {
1705 filename = addName(lyxrc.document_path,
1706 "newfile" + convert<string>(newfile_number) +
1711 // The template stuff
1714 FileDialog fileDlg(lyx::to_utf8(_("Select template file")),
1715 LFUN_SELECT_FILE_SYNC,
1716 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1717 string(lyxrc.document_path)),
1718 make_pair(string(lyx::to_utf8(_("Templates|#T#t"))),
1719 string(lyxrc.template_path)));
1721 FileDialog::Result result =
1722 fileDlg.open(lyxrc.template_path,
1723 FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1726 if (result.first == FileDialog::Later)
1728 if (result.second.empty())
1730 templname = result.second;
1733 Buffer * const b = newFile(filename, templname, !name.empty());
1735 owner->setBuffer(b);
1739 void LyXFunc::open(string const & fname)
1741 string initpath = lyxrc.document_path;
1743 if (view()->buffer()) {
1744 string const trypath = owner->buffer()->filePath();
1745 // If directory is writeable, use this as default.
1746 if (isDirWriteable(trypath))
1752 if (fname.empty()) {
1753 FileDialog fileDlg(lyx::to_utf8(_("Select document to open")),
1755 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1756 string(lyxrc.document_path)),
1757 make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1758 string(addPath(package().system_support(), "examples"))));
1760 FileDialog::Result result =
1761 fileDlg.open(initpath,
1762 FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1765 if (result.first == FileDialog::Later)
1768 filename = result.second;
1770 // check selected filename
1771 if (filename.empty()) {
1772 owner->message(_("Canceled."));
1778 // get absolute path of file and add ".lyx" to the filename if
1780 string const fullpath = fileSearch(string(), filename, "lyx");
1781 if (!fullpath.empty()) {
1782 filename = fullpath;
1785 docstring const disp_fn = makeDisplayPath(filename);
1787 // if the file doesn't exist, let the user create one
1788 if (!fs::exists(filename)) {
1789 // the user specifically chose this name. Believe him.
1790 Buffer * const b = newFile(filename, string(), true);
1792 owner->setBuffer(b);
1796 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1799 if (owner->loadLyXFile(filename)) {
1800 str2 = bformat(_("Document %1$s opened."), disp_fn);
1802 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1804 owner->message(str2);
1808 void LyXFunc::doImport(string const & argument)
1811 string filename = split(argument, format, ' ');
1813 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1814 << " file: " << filename << endl;
1816 // need user interaction
1817 if (filename.empty()) {
1818 string initpath = lyxrc.document_path;
1820 if (view()->buffer()) {
1821 string const trypath = owner->buffer()->filePath();
1822 // If directory is writeable, use this as default.
1823 if (isDirWriteable(trypath))
1827 docstring const text = bformat(_("Select %1$s file to import"),
1828 formats.prettyName(format));
1830 FileDialog fileDlg(lyx::to_utf8(text),
1832 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1833 string(lyxrc.document_path)),
1834 make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1835 string(addPath(package().system_support(), "examples"))));
1837 string const filter = lyx::to_utf8(formats.prettyName(format))
1838 + " (*." + formats.extension(format) + ')';
1840 FileDialog::Result result =
1841 fileDlg.open(initpath,
1842 FileFilterList(filter),
1845 if (result.first == FileDialog::Later)
1848 filename = result.second;
1850 // check selected filename
1851 if (filename.empty())
1852 owner->message(_("Canceled."));
1855 if (filename.empty())
1858 // get absolute path of file
1859 filename = makeAbsPath(filename);
1861 string const lyxfile = changeExtension(filename, ".lyx");
1863 // Check if the document already is open
1864 if (lyx_gui::use_gui && theApp->bufferList().exists(lyxfile)) {
1865 if (!theApp->bufferList().close(theApp->bufferList().getBuffer(lyxfile), true)) {
1866 owner->message(_("Canceled."));
1871 // if the file exists already, and we didn't do
1872 // -i lyx thefile.lyx, warn
1873 if (fs::exists(lyxfile) && filename != lyxfile) {
1874 docstring const file = makeDisplayPath(lyxfile, 30);
1876 docstring text = bformat(_("The document %1$s already exists.\n\n"
1877 "Do you want to over-write that document?"), file);
1878 int const ret = Alert::prompt(_("Over-write document?"),
1879 text, 0, 1, _("&Over-write"), _("&Cancel"));
1882 owner->message(_("Canceled."));
1887 ErrorList errorList;
1888 Importer::Import(owner, filename, format, errorList);
1889 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1893 void LyXFunc::closeBuffer()
1895 // save current cursor position
1896 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1897 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1898 if (theApp->bufferList().close(owner->buffer(), true) && !quitting) {
1899 if (theApp->bufferList().empty()) {
1900 // need this otherwise SEGV may occur while
1901 // trying to set variables that don't exist
1902 // since there's no current buffer
1903 owner->getDialogs().hideBufferDependent();
1905 owner->setBuffer(theApp->bufferList().first());
1911 // Each "owner" should have it's own message method. lyxview and
1912 // the minibuffer would use the minibuffer, but lyxserver would
1913 // send an ERROR signal to its client. Alejandro 970603
1914 // This function is bit problematic when it comes to NLS, to make the
1915 // lyx servers client be language indepenent we must not translate
1916 // strings sent to this func.
1917 void LyXFunc::setErrorMessage(docstring const & m) const
1919 dispatch_buffer = m;
1924 void LyXFunc::setMessage(docstring const & m) const
1926 dispatch_buffer = m;
1930 string const LyXFunc::viewStatusMessage()
1932 // When meta-fake key is pressed, show the key sequence so far + "M-".
1934 return keyseq.print() + "M-";
1936 // Else, when a non-complete key sequence is pressed,
1937 // show the available options.
1938 if (keyseq.length() > 0 && !keyseq.deleted())
1939 return keyseq.printOptions();
1941 if (!view()->buffer())
1942 return lyx::to_utf8(_("Welcome to LyX!"));
1944 return view()->cursor().currentState();
1948 BufferView * LyXFunc::view() const
1950 BOOST_ASSERT(owner);
1951 return owner->view();
1955 bool LyXFunc::wasMetaKey() const
1957 return (meta_fake_bit != key_modifier::none);
1963 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1965 // Why the switch you might ask. It is a trick to ensure that all
1966 // the elements in the LyXRCTags enum is handled. As you can see
1967 // there are no breaks at all. So it is just a huge fall-through.
1968 // The nice thing is that we will get a warning from the compiler
1969 // if we forget an element.
1970 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1972 case LyXRC::RC_ACCEPT_COMPOUND:
1973 case LyXRC::RC_ALT_LANG:
1974 case LyXRC::RC_ASCIIROFF_COMMAND:
1975 case LyXRC::RC_ASCII_LINELEN:
1976 case LyXRC::RC_AUTOREGIONDELETE:
1977 case LyXRC::RC_AUTORESET_OPTIONS:
1978 case LyXRC::RC_AUTOSAVE:
1979 case LyXRC::RC_AUTO_NUMBER:
1980 case LyXRC::RC_BACKUPDIR_PATH:
1981 case LyXRC::RC_BIBTEX_COMMAND:
1982 case LyXRC::RC_BINDFILE:
1983 case LyXRC::RC_CHECKLASTFILES:
1984 case LyXRC::RC_USELASTFILEPOS:
1985 case LyXRC::RC_LOADSESSION:
1986 case LyXRC::RC_CHKTEX_COMMAND:
1987 case LyXRC::RC_CONVERTER:
1988 case LyXRC::RC_COPIER:
1989 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1990 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1991 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1992 case LyXRC::RC_DATE_INSERT_FORMAT:
1993 case LyXRC::RC_DEFAULT_LANGUAGE:
1994 case LyXRC::RC_DEFAULT_PAPERSIZE:
1995 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1996 case LyXRC::RC_DISPLAY_GRAPHICS:
1997 case LyXRC::RC_DOCUMENTPATH:
1998 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1999 if (fs::exists(lyxrc_new.document_path) &&
2000 fs::is_directory(lyxrc_new.document_path)) {
2001 using lyx::support::package;
2002 package().document_dir() = lyxrc.document_path;
2005 case LyXRC::RC_ESC_CHARS:
2006 case LyXRC::RC_FONT_ENCODING:
2007 case LyXRC::RC_FORMAT:
2008 case LyXRC::RC_INDEX_COMMAND:
2009 case LyXRC::RC_INPUT:
2010 case LyXRC::RC_KBMAP:
2011 case LyXRC::RC_KBMAP_PRIMARY:
2012 case LyXRC::RC_KBMAP_SECONDARY:
2013 case LyXRC::RC_LABEL_INIT_LENGTH:
2014 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2015 case LyXRC::RC_LANGUAGE_AUTO_END:
2016 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2017 case LyXRC::RC_LANGUAGE_COMMAND_END:
2018 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2019 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2020 case LyXRC::RC_LANGUAGE_PACKAGE:
2021 case LyXRC::RC_LANGUAGE_USE_BABEL:
2022 case LyXRC::RC_MAKE_BACKUP:
2023 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2024 case LyXRC::RC_NUMLASTFILES:
2025 case LyXRC::RC_PATH_PREFIX:
2026 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2027 using lyx::support::prependEnvPath;
2028 prependEnvPath("PATH", lyxrc.path_prefix);
2030 case LyXRC::RC_PERS_DICT:
2031 case LyXRC::RC_POPUP_BOLD_FONT:
2032 case LyXRC::RC_POPUP_FONT_ENCODING:
2033 case LyXRC::RC_POPUP_NORMAL_FONT:
2034 case LyXRC::RC_PREVIEW:
2035 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2036 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2037 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2038 case LyXRC::RC_PRINTCOPIESFLAG:
2039 case LyXRC::RC_PRINTER:
2040 case LyXRC::RC_PRINTEVENPAGEFLAG:
2041 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2042 case LyXRC::RC_PRINTFILEEXTENSION:
2043 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2044 case LyXRC::RC_PRINTODDPAGEFLAG:
2045 case LyXRC::RC_PRINTPAGERANGEFLAG:
2046 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2047 case LyXRC::RC_PRINTPAPERFLAG:
2048 case LyXRC::RC_PRINTREVERSEFLAG:
2049 case LyXRC::RC_PRINTSPOOL_COMMAND:
2050 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2051 case LyXRC::RC_PRINTTOFILE:
2052 case LyXRC::RC_PRINTTOPRINTER:
2053 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2054 case LyXRC::RC_PRINT_COMMAND:
2055 case LyXRC::RC_RTL_SUPPORT:
2056 case LyXRC::RC_SCREEN_DPI:
2057 case LyXRC::RC_SCREEN_FONT_ENCODING:
2058 case LyXRC::RC_SCREEN_FONT_ROMAN:
2059 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2060 case LyXRC::RC_SCREEN_FONT_SANS:
2061 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2062 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2063 case LyXRC::RC_SCREEN_FONT_SIZES:
2064 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2065 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2066 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2067 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2068 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2069 case LyXRC::RC_SCREEN_ZOOM:
2070 case LyXRC::RC_SERVERPIPE:
2071 case LyXRC::RC_SET_COLOR:
2072 case LyXRC::RC_SHOW_BANNER:
2073 case LyXRC::RC_SPELL_COMMAND:
2074 case LyXRC::RC_TEMPDIRPATH:
2075 case LyXRC::RC_TEMPLATEPATH:
2076 case LyXRC::RC_TEX_ALLOWS_SPACES:
2077 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2078 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2079 namespace os = lyx::support::os;
2080 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2082 case LyXRC::RC_UIFILE:
2083 case LyXRC::RC_USER_EMAIL:
2084 case LyXRC::RC_USER_NAME:
2085 case LyXRC::RC_USETEMPDIR:
2086 case LyXRC::RC_USE_ALT_LANG:
2087 case LyXRC::RC_USE_ESC_CHARS:
2088 case LyXRC::RC_USE_INP_ENC:
2089 case LyXRC::RC_USE_PERS_DICT:
2090 case LyXRC::RC_USE_SPELL_LIB:
2091 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2092 case LyXRC::RC_VIEWER:
2093 case LyXRC::RC_LAST: