3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
9 * \author Angus Leeming
11 * \author André Pönitz
14 * \author Martin Vermeer
15 * \author Jürgen Vigna
17 * Full author contact details are available in file CREDITS.
24 #include "BranchList.h"
26 #include "buffer_funcs.h"
27 #include "bufferlist.h"
28 #include "bufferparams.h"
29 #include "BufferView.h"
31 #include "CutAndPaste.h"
33 #include "dispatchresult.h"
35 #include "errorlist.h"
38 #include "funcrequest.h"
41 #include "insetiterator.h"
49 #include "LyXAction.h"
54 #include "lyxserver.h"
55 #include "lyxtextclasslist.h"
57 #include "paragraph.h"
58 #include "pariterator.h"
59 #include "ParagraphParameters.h"
62 #include "insets/insetbox.h"
63 #include "insets/insetbranch.h"
64 #include "insets/insetcommand.h"
65 #include "insets/insetert.h"
66 #include "insets/insetexternal.h"
67 #include "insets/insetfloat.h"
68 #include "insets/insetgraphics.h"
69 #include "insets/insetinclude.h"
70 #include "insets/insetnote.h"
71 #include "insets/insettabular.h"
72 #include "insets/insetvspace.h"
73 #include "insets/insetwrap.h"
75 #include "frontends/Alert.h"
76 #include "frontends/Dialogs.h"
77 #include "frontends/FileDialog.h"
78 #include "frontends/lyx_gui.h"
79 #include "frontends/LyXKeySym.h"
80 #include "frontends/LyXView.h"
81 #include "frontends/Menubar.h"
82 #include "frontends/Toolbars.h"
84 #include "support/environment.h"
85 #include "support/filefilterlist.h"
86 #include "support/filetools.h"
87 #include "support/forkedcontr.h"
88 #include "support/fs_extras.h"
89 #include "support/lstrings.h"
90 #include "support/path.h"
91 #include "support/package.h"
92 #include "support/systemcall.h"
93 #include "support/convert.h"
94 #include "support/os.h"
96 #include <boost/current_function.hpp>
97 #include <boost/filesystem/operations.hpp>
101 using bv_funcs::freefont2string;
103 using lyx::support::absolutePath;
104 using lyx::support::addName;
105 using lyx::support::addPath;
106 using lyx::support::bformat;
107 using lyx::support::changeExtension;
108 using lyx::support::contains;
109 using lyx::support::FileFilterList;
110 using lyx::support::fileSearch;
111 using lyx::support::ForkedcallsController;
112 using lyx::support::i18nLibFileSearch;
113 using lyx::support::isDirWriteable;
114 using lyx::support::isFileReadable;
115 using lyx::support::isStrInt;
116 using lyx::support::makeAbsPath;
117 using lyx::support::makeDisplayPath;
118 using lyx::support::package;
119 using lyx::support::Path;
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 BufferList bufferlist;
141 extern LyXServer * lyxserver;
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().lookupChangeType(slice.pos());
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->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() : "");
253 // Do a one-deep top-level lookup for
254 // cancel and meta-fake keys. RVDK_PATCH_5
255 cancel_meta_seq.reset();
257 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
258 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
259 << " action first set to [" << func.action << ']'
262 // When not cancel or meta-fake, do the normal lookup.
263 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
264 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
265 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
266 // remove Caps Lock and Mod2 as a modifiers
267 func = keyseq.addkey(keysym, (state | meta_fake_bit));
268 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
269 << "action now set to ["
270 << func.action << ']' << endl;
273 // Dont remove this unless you know what you are doing.
274 meta_fake_bit = key_modifier::none;
276 // Can this happen now ?
277 if (func.action == LFUN_NOACTION) {
278 func = FuncRequest(LFUN_COMMAND_PREFIX);
281 if (lyxerr.debugging(Debug::KEY)) {
282 lyxerr << BOOST_CURRENT_FUNCTION
284 << func.action << "]["
285 << keyseq.print() << ']'
289 // already here we know if it any point in going further
290 // why not return already here if action == -1 and
291 // num_bytes == 0? (Lgb)
293 if (keyseq.length() > 1) {
294 owner->message(keyseq.print());
298 // Maybe user can only reach the key via holding down shift.
299 // Let's see. But only if shift is the only modifier
300 if (func.action == LFUN_UNKNOWN_ACTION &&
301 state == key_modifier::shift) {
302 lyxerr[Debug::KEY] << "Trying without shift" << endl;
303 func = keyseq.addkey(keysym, key_modifier::none);
304 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
307 if (func.action == LFUN_UNKNOWN_ACTION) {
308 // Hmm, we didn't match any of the keysequences. See
309 // if it's normal insertable text not already covered
311 if (keysym->isText() && keyseq.length() == 1) {
312 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
313 func = FuncRequest(LFUN_SELF_INSERT,
314 FuncRequest::KEYBOARD);
316 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
317 owner->message(_("Unknown function."));
322 if (func.action == LFUN_SELF_INSERT) {
323 if (encoded_last_key != 0) {
324 string const arg(1, encoded_last_key);
325 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
326 FuncRequest::KEYBOARD));
328 << "SelfInsert arg[`" << arg << "']" << endl;
336 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
338 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
340 LCursor & cur = view()->cursor();
342 /* In LyX/Mac, when a dialog is open, the menus of the
343 application can still be accessed without giving focus to
344 the main window. In this case, we want to disable the menu
345 entries that are buffer-related.
347 Note that this code is not perfect, as bug 1941 attests:
348 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
351 if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
354 buf = owner->buffer();
356 if (cmd.action == LFUN_NOACTION) {
357 flag.message(N_("Nothing to do"));
362 switch (cmd.action) {
363 case LFUN_UNKNOWN_ACTION:
364 #ifndef HAVE_LIBAIKSAURUS
365 case LFUN_THESAURUS_ENTRY:
371 flag |= lyx_gui::getStatus(cmd);
374 if (flag.unknown()) {
375 flag.message(N_("Unknown action"));
379 if (!flag.enabled()) {
380 if (flag.message().empty())
381 flag.message(N_("Command disabled"));
385 // Check whether we need a buffer
386 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
388 flag.message(N_("Command not allowed with"
389 "out any document open"));
394 // I would really like to avoid having this switch and rather try to
395 // encode this in the function itself.
396 // -- And I'd rather let an inset decide which LFUNs it is willing
397 // to handle (Andre')
399 switch (cmd.action) {
400 case LFUN_TOGGLE_TOOLTIPS:
401 flag.setOnOff(owner->getDialogs().tooltipsEnabled());
404 case LFUN_BUFFER_TOGGLE_READ_ONLY:
405 flag.setOnOff(buf->isReadonly());
408 case LFUN_BUFFER_SWITCH:
409 // toggle on the current buffer, but do not toggle off
410 // the other ones (is that a good idea?)
411 if (cmd.argument == buf->fileName())
415 case LFUN_BUFFER_EXPORT:
416 enable = cmd.argument == "custom"
417 || Exporter::isExportable(*buf, cmd.argument);
420 case LFUN_BUFFER_CHKTEX:
421 enable = buf->isLatex() && lyxrc.chktex_command != "none";
424 case LFUN_BUILD_PROGRAM:
425 enable = Exporter::isExportable(*buf, "program");
428 case LFUN_LAYOUT_TABULAR:
429 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
433 case LFUN_LAYOUT_PARAGRAPH:
434 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
437 case LFUN_VC_REGISTER:
438 enable = !buf->lyxvc().inUse();
440 case LFUN_VC_CHECK_IN:
441 enable = buf->lyxvc().inUse() && !buf->isReadonly();
443 case LFUN_VC_CHECK_OUT:
444 enable = buf->lyxvc().inUse() && buf->isReadonly();
447 case LFUN_VC_UNDO_LAST:
448 enable = buf->lyxvc().inUse();
450 case LFUN_BUFFER_RELOAD:
451 enable = !buf->isUnnamed() && !buf->isClean();
454 case LFUN_INSET_SETTINGS: {
458 InsetBase::Code code = cur.inset().lyxCode();
460 case InsetBase::TABULAR_CODE:
461 enable = cmd.argument == "tabular";
463 case InsetBase::ERT_CODE:
464 enable = cmd.argument == "ert";
466 case InsetBase::FLOAT_CODE:
467 enable = cmd.argument == "float";
469 case InsetBase::WRAP_CODE:
470 enable = cmd.argument == "wrap";
472 case InsetBase::NOTE_CODE:
473 enable = cmd.argument == "note";
475 case InsetBase::BRANCH_CODE:
476 enable = cmd.argument == "branch";
478 case InsetBase::BOX_CODE:
479 enable = cmd.argument == "box";
487 case LFUN_INSET_APPLY: {
488 string const name = cmd.getArg(0);
489 InsetBase * inset = owner->getDialogs().getOpenInset(name);
491 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument);
493 bool const success = inset->getStatus(cur, fr, fs);
494 // Every inset is supposed to handle this
495 BOOST_ASSERT(success);
498 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument);
499 flag |= getStatus(fr);
501 enable = flag.enabled();
505 case LFUN_DIALOG_SHOW: {
506 string const name = cmd.getArg(0);
508 enable = name == "aboutlyx"
512 || name == "texinfo";
513 else if (name == "print")
514 enable = Exporter::isExportable(*buf, "dvi")
515 && lyxrc.print_command != "none";
516 else if (name == "character" || name == "mathpanel")
517 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
518 else if (name == "latexlog")
519 enable = isFileReadable(buf->getLogName().second);
520 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
521 else if (name == "spellchecker")
524 else if (name == "vclog")
525 enable = buf->lyxvc().inUse();
526 else if (name == "view-source")
531 case LFUN_DIALOG_SHOW_NEW_INSET:
532 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
535 case LFUN_DIALOG_UPDATE: {
536 string const name = cmd.getArg(0);
538 enable = name == "prefs";
542 case LFUN_CITATION_INSERT: {
543 FuncRequest fr(LFUN_INSET_INSERT, "citation");
544 enable = getStatus(fr).enabled();
548 case LFUN_BUFFER_WRITE: {
549 enable = view()->buffer()->isUnnamed()
550 || !view()->buffer()->isClean();
554 // this one is difficult to get right. As a half-baked
555 // solution, we consider only the first action of the sequence
556 case LFUN_COMMAND_SEQUENCE: {
557 // argument contains ';'-terminated commands
558 string const firstcmd = token(cmd.argument, ';', 0);
559 FuncRequest func(lyxaction.lookupFunc(firstcmd));
560 func.origin = cmd.origin;
561 flag = getStatus(func);
564 case LFUN_BUFFER_NEW:
565 case LFUN_BUFFER_NEWTMPLT:
566 case LFUN_WORD_FIND_FORWARD:
567 case LFUN_WORD_FIND_BACKWARD:
568 case LFUN_COMMAND_PREFIX:
569 case LFUN_COMMAND_EXECUTE:
571 case LFUN_META_PREFIX:
572 case LFUN_BUFFER_CLOSE:
573 case LFUN_BUFFER_WRITE_AS:
574 case LFUN_BUFFER_UPDATE:
575 case LFUN_BUFFER_VIEW:
576 case LFUN_BUFFER_IMPORT:
579 case LFUN_BUFFER_AUTO_SAVE:
580 case LFUN_RECONFIGURE:
584 case LFUN_DROP_LAYOUTS_CHOICE:
586 case LFUN_SERVER_GET_NAME:
587 case LFUN_SERVER_NOTIFY:
588 case LFUN_SERVER_GOTO_FILE_ROW:
589 case LFUN_DIALOG_SHOW_NEXT_INSET:
590 case LFUN_DIALOG_HIDE:
591 case LFUN_DIALOG_DISCONNECT_INSET:
592 case LFUN_BUFFER_CHILD_OPEN:
593 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
594 case LFUN_KEYMAP_OFF:
595 case LFUN_KEYMAP_PRIMARY:
596 case LFUN_KEYMAP_SECONDARY:
597 case LFUN_KEYMAP_TOGGLE:
599 case LFUN_BUFFER_EXPORT_CUSTOM:
600 case LFUN_BUFFER_PRINT:
601 case LFUN_PREFERENCES_SAVE:
602 case LFUN_SCREEN_FONT_UPDATE:
605 case LFUN_EXTERNAL_EDIT:
606 case LFUN_GRAPHICS_EDIT:
607 case LFUN_ALL_INSETS_TOGGLE:
608 case LFUN_BUFFER_LANGUAGE:
609 case LFUN_TEXTCLASS_APPLY:
610 case LFUN_TEXTCLASS_LOAD:
611 case LFUN_BUFFER_SAVE_AS_DEFAULT:
612 case LFUN_BUFFER_PARAMS_APPLY:
613 case LFUN_LYXRC_APPLY:
614 case LFUN_SCREEN_DOWNBUFFER:
615 case LFUN_BUFFER_PREVIOUS:
616 // these are handled in our dispatch()
621 if (!::getStatus(cur, cmd, flag))
622 flag = view()->getStatus(cmd);
628 // Can we use a readonly buffer?
629 if (buf && buf->isReadonly()
630 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
631 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
632 flag.message(N_("Document is read-only"));
636 // Are we in a DELETED change-tracking region?
637 if (buf && buf->params().tracking_changes
638 && lookupChangeType(cur, true) == Change::DELETED
639 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
640 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
641 flag.message(N_("This portion of the document is deleted."));
645 // the default error message if we disable the command
646 if (!flag.enabled() && flag.message().empty())
647 flag.message(N_("Command disabled"));
655 bool ensureBufferClean(BufferView * bv)
657 Buffer & buf = *bv->buffer();
661 string const file = makeDisplayPath(buf.fileName(), 30);
662 string text = bformat(_("The document %1$s has unsaved "
663 "changes.\n\nDo you want to save "
664 "the document?"), file);
665 int const ret = Alert::prompt(_("Save changed document?"),
666 text, 0, 1, _("&Save"),
670 bv->owner()->dispatch(FuncRequest(LFUN_BUFFER_WRITE));
672 return buf.isClean();
676 void showPrintError(string const & name)
678 string str = bformat(_("Could not print the document %1$s.\n"
679 "Check that your printer is set up correctly."),
680 makeDisplayPath(name, 50));
681 Alert::error(_("Print document failed"), str);
685 void loadTextclass(string const & name)
687 std::pair<bool, lyx::textclass_type> const tc_pair =
688 textclasslist.numberOfClass(name);
690 if (!tc_pair.first) {
691 lyxerr << "Document class \"" << name
692 << "\" does not exist."
697 lyx::textclass_type const tc = tc_pair.second;
699 if (!textclasslist[tc].load()) {
700 string s = bformat(_("The document could not be converted\n"
701 "into the document class %1$s."),
702 textclasslist[tc].name());
703 Alert::error(_("Could not change class"), s);
708 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
713 void LyXFunc::dispatch(FuncRequest const & cmd)
715 BOOST_ASSERT(view());
716 string const argument = cmd.argument;
717 kb_action const action = cmd.action;
719 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
720 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
722 // we have not done anything wrong yet.
724 dispatch_buffer.erase();
726 // redraw the screen at the end (first of the two drawing steps).
727 //This is done unless explicitely requested otherwise
729 // also do the second redrawing step. Only done if requested.
730 bool updateforce = false;
732 FuncStatus const flag = getStatus(cmd);
733 if (!flag.enabled()) {
734 // We cannot use this function here
735 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
736 << lyxaction.getActionName(action)
737 << " [" << action << "] is disabled at this location"
739 setErrorMessage(flag.message());
742 if (view()->available())
743 view()->hideCursor();
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(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()->available())
782 // cancel any selection
783 dispatch(FuncRequest(LFUN_MARK_OFF));
784 setMessage(N_("Cancel"));
787 case LFUN_META_PREFIX:
788 meta_fake_bit = key_modifier::alt;
789 setMessage(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_NEWTMPLT:
806 menuNew(argument, true);
809 case LFUN_BUFFER_CLOSE:
813 case LFUN_BUFFER_WRITE:
814 if (!owner->buffer()->isUnnamed()) {
815 string 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 string const file = makeDisplayPath(view()->buffer()->fileName(), 20);
832 string 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);
844 view()->showErrorList(bufferFormat(*owner->buffer()));
847 case LFUN_BUFFER_VIEW:
848 Exporter::preview(owner->buffer(), argument);
849 view()->showErrorList(bufferFormat(*owner->buffer()));
852 case LFUN_BUILD_PROGRAM:
853 Exporter::Export(owner->buffer(), "program", true);
854 view()->showErrorList(_("Build"));
857 case LFUN_BUFFER_CHKTEX:
858 owner->buffer()->runChktex();
859 view()->showErrorList(_("ChkTeX"));
862 case LFUN_BUFFER_EXPORT:
863 if (argument == "custom")
864 owner->getDialogs().show("sendto");
866 Exporter::Export(owner->buffer(), argument, false);
867 view()->showErrorList(bufferFormat(*owner->buffer()));
871 case LFUN_BUFFER_EXPORT_CUSTOM: {
873 string command = split(argument, format_name, ' ');
874 Format const * format = formats.getFormat(format_name);
876 lyxerr << "Format \"" << format_name
877 << "\" not recognized!"
882 Buffer * buffer = owner->buffer();
884 // The name of the file created by the conversion process
887 // Output to filename
888 if (format->name() == "lyx") {
889 string const latexname =
890 buffer->getLatexName(false);
891 filename = changeExtension(latexname,
892 format->extension());
893 filename = addName(buffer->temppath(), filename);
895 if (!buffer->writeFile(filename))
899 Exporter::Export(buffer, format_name, true,
903 // Substitute $$FName for filename
904 if (!contains(command, "$$FName"))
905 command = "( " + command + " ) < $$FName";
906 command = subst(command, "$$FName", filename);
908 // Execute the command in the background
910 call.startscript(Systemcall::DontWait, command);
914 case LFUN_BUFFER_PRINT: {
917 string command = split(split(argument, target, ' '),
921 || target_name.empty()
922 || command.empty()) {
923 lyxerr << "Unable to parse \""
924 << argument << '"' << std::endl;
927 if (target != "printer" && target != "file") {
928 lyxerr << "Unrecognized target \""
929 << target << '"' << std::endl;
933 Buffer * buffer = owner->buffer();
935 if (!Exporter::Export(buffer, "dvi", true)) {
936 showPrintError(buffer->fileName());
940 // Push directory path.
941 string const path = buffer->temppath();
944 // there are three cases here:
945 // 1. we print to a file
946 // 2. we print directly to a printer
947 // 3. we print using a spool command (print to file first)
950 string const dviname =
951 changeExtension(buffer->getLatexName(true),
954 if (target == "printer") {
955 if (!lyxrc.print_spool_command.empty()) {
956 // case 3: print using a spool
957 string const psname =
958 changeExtension(dviname,".ps");
959 command += lyxrc.print_to_file
962 + quoteName(dviname);
965 lyxrc.print_spool_command +' ';
966 if (target_name != "default") {
967 command2 += lyxrc.print_spool_printerprefix
971 command2 += quoteName(psname);
973 // If successful, then spool command
974 res = one.startscript(
979 res = one.startscript(
980 Systemcall::DontWait,
983 // case 2: print directly to a printer
984 res = one.startscript(
985 Systemcall::DontWait,
986 command + quoteName(dviname));
990 // case 1: print to a file
991 command += lyxrc.print_to_file
992 + quoteName(makeAbsPath(target_name,
995 + quoteName(dviname);
996 res = one.startscript(Systemcall::DontWait,
1001 showPrintError(buffer->fileName());
1005 case LFUN_BUFFER_IMPORT:
1010 if (view()->available()) {
1011 // save cursor Position for opened files to .lyx/session
1012 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1013 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1014 // save bookmarks to .lyx/session
1015 view()->saveSavedPositions();
1017 quitLyX(argument == "force");
1020 case LFUN_TOC_VIEW: {
1021 InsetCommandParams p("tableofcontents");
1022 string const data = InsetCommandMailer::params2string("toc", p);
1023 owner->getDialogs().show("toc", data, 0);
1027 case LFUN_BUFFER_AUTO_SAVE:
1031 case LFUN_RECONFIGURE:
1032 reconfigure(view());
1035 case LFUN_HELP_OPEN: {
1036 string const arg = argument;
1038 setErrorMessage(N_("Missing argument"));
1041 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1042 if (fname.empty()) {
1043 lyxerr << "LyX: unable to find documentation file `"
1044 << arg << "'. Bad installation?" << endl;
1047 owner->message(bformat(_("Opening help file %1$s..."),
1048 makeDisplayPath(fname)));
1049 view()->loadLyXFile(fname, false);
1053 // --- version control -------------------------------
1054 case LFUN_VC_REGISTER:
1055 if (!ensureBufferClean(view()))
1057 if (!owner->buffer()->lyxvc().inUse()) {
1058 owner->buffer()->lyxvc().registrer();
1063 case LFUN_VC_CHECK_IN:
1064 if (!ensureBufferClean(view()))
1066 if (owner->buffer()->lyxvc().inUse()
1067 && !owner->buffer()->isReadonly()) {
1068 owner->buffer()->lyxvc().checkIn();
1073 case LFUN_VC_CHECK_OUT:
1074 if (!ensureBufferClean(view()))
1076 if (owner->buffer()->lyxvc().inUse()
1077 && owner->buffer()->isReadonly()) {
1078 owner->buffer()->lyxvc().checkOut();
1083 case LFUN_VC_REVERT:
1084 owner->buffer()->lyxvc().revert();
1088 case LFUN_VC_UNDO_LAST:
1089 owner->buffer()->lyxvc().undoLast();
1093 // --- buffers ----------------------------------------
1094 case LFUN_BUFFER_SWITCH:
1095 view()->setBuffer(bufferlist.getBuffer(argument));
1098 case LFUN_SCREEN_DOWNBUFFER:
1099 view()->setBuffer(bufferlist.next(view()->buffer()));
1102 case LFUN_BUFFER_PREVIOUS:
1103 view()->setBuffer(bufferlist.previous(view()->buffer()));
1107 newFile(view(), argument);
1110 case LFUN_FILE_OPEN:
1114 case LFUN_DROP_LAYOUTS_CHOICE:
1115 owner->getToolbars().openLayoutList();
1118 case LFUN_MENU_OPEN:
1119 owner->getMenubar().openByName(argument);
1122 // --- lyxserver commands ----------------------------
1123 case LFUN_SERVER_GET_NAME:
1124 setMessage(owner->buffer()->fileName());
1125 lyxerr[Debug::INFO] << "FNAME["
1126 << owner->buffer()->fileName()
1130 case LFUN_SERVER_NOTIFY:
1131 dispatch_buffer = keyseq.print();
1132 lyxserver->notifyClient(dispatch_buffer);
1135 case LFUN_SERVER_GOTO_FILE_ROW: {
1138 istringstream is(argument);
1139 is >> file_name >> row;
1140 if (prefixIs(file_name, package().temp_dir())) {
1141 // Needed by inverse dvi search. If it is a file
1142 // in tmpdir, call the apropriated function
1143 view()->setBuffer(bufferlist.getBufferFromTmp(file_name));
1145 // Must replace extension of the file to be .lyx
1146 // and get full path
1147 string const s = changeExtension(file_name, ".lyx");
1148 // Either change buffer or load the file
1149 if (bufferlist.exists(s)) {
1150 view()->setBuffer(bufferlist.getBuffer(s));
1152 view()->loadLyXFile(s);
1156 view()->setCursorFromRow(row);
1159 // see BufferView_pimpl::center()
1160 view()->updateScrollbar();
1164 case LFUN_DIALOG_SHOW: {
1165 string const name = cmd.getArg(0);
1166 string data = trim(cmd.argument.substr(name.size()));
1168 if (name == "character") {
1169 data = freefont2string();
1171 owner->getDialogs().show("character", data);
1172 } else if (name == "latexlog") {
1173 pair<Buffer::LogType, string> const logfile =
1174 owner->buffer()->getLogName();
1175 switch (logfile.first) {
1176 case Buffer::latexlog:
1179 case Buffer::buildlog:
1183 data += logfile.second;
1184 owner->getDialogs().show("log", data);
1185 } else if (name == "vclog") {
1186 string const data = "vc " +
1187 owner->buffer()->lyxvc().getLogFile();
1188 owner->getDialogs().show("log", data);
1190 owner->getDialogs().show(name, data);
1194 case LFUN_DIALOG_SHOW_NEW_INSET: {
1195 string const name = cmd.getArg(0);
1196 string data = trim(cmd.argument.substr(name.size()));
1197 if (name == "bibitem" ||
1204 InsetCommandParams p(name);
1205 data = InsetCommandMailer::params2string(name, p);
1206 } else if (name == "include") {
1207 InsetCommandParams p(data);
1208 data = InsetIncludeMailer::params2string(p);
1209 } else if (name == "box") {
1210 // \c data == "Boxed" || "Frameless" etc
1211 InsetBoxParams p(data);
1212 data = InsetBoxMailer::params2string(p);
1213 } else if (name == "branch") {
1214 InsetBranchParams p;
1215 data = InsetBranchMailer::params2string(p);
1216 } else if (name == "citation") {
1217 InsetCommandParams p("cite");
1218 data = InsetCommandMailer::params2string(name, p);
1219 } else if (name == "ert") {
1220 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1221 } else if (name == "external") {
1222 InsetExternalParams p;
1223 Buffer const & buffer = *owner->buffer();
1224 data = InsetExternalMailer::params2string(p, buffer);
1225 } else if (name == "float") {
1227 data = InsetFloatMailer::params2string(p);
1228 } else if (name == "graphics") {
1229 InsetGraphicsParams p;
1230 Buffer const & buffer = *owner->buffer();
1231 data = InsetGraphicsMailer::params2string(p, buffer);
1232 } else if (name == "note") {
1234 data = InsetNoteMailer::params2string(p);
1235 } else if (name == "vspace") {
1237 data = InsetVSpaceMailer::params2string(space);
1238 } else if (name == "wrap") {
1240 data = InsetWrapMailer::params2string(p);
1242 owner->getDialogs().show(name, data, 0);
1246 case LFUN_DIALOG_SHOW_NEXT_INSET:
1249 case LFUN_DIALOG_UPDATE: {
1250 string const & name = argument;
1251 // Can only update a dialog connected to an existing inset
1252 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1254 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument);
1255 inset->dispatch(view()->cursor(), fr);
1256 } else if (name == "paragraph") {
1257 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1258 } else if (name == "prefs") {
1259 owner->getDialogs().update(name, string());
1264 case LFUN_DIALOG_HIDE:
1265 Dialogs::hide(argument, 0);
1268 case LFUN_DIALOG_DISCONNECT_INSET:
1269 owner->getDialogs().disconnect(argument);
1273 case LFUN_CITATION_INSERT: {
1274 if (!argument.empty()) {
1275 // we can have one optional argument, delimited by '|'
1276 // citation-insert <key>|<text_before>
1277 // this should be enhanced to also support text_after
1278 // and citation style
1279 string arg = argument;
1281 if (contains(argument, "|")) {
1282 arg = token(argument, '|', 0);
1283 opt1 = '[' + token(argument, '|', 1) + ']';
1285 std::ostringstream os;
1286 os << "citation LatexCommand\n"
1287 << "\\cite" << opt1 << "{" << arg << "}\n"
1289 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1292 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1296 case LFUN_BUFFER_CHILD_OPEN: {
1297 string const filename =
1298 makeAbsPath(argument, owner->buffer()->filePath());
1299 setMessage(N_("Opening child document ") +
1300 makeDisplayPath(filename) + "...");
1301 view()->savePosition(0);
1302 string const parentfilename = owner->buffer()->fileName();
1303 if (bufferlist.exists(filename))
1304 view()->setBuffer(bufferlist.getBuffer(filename));
1306 view()->loadLyXFile(filename);
1307 // Set the parent name of the child document.
1308 // This makes insertion of citations and references in the child work,
1309 // when the target is in the parent or another child document.
1310 owner->buffer()->setParentName(parentfilename);
1314 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1315 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1318 case LFUN_KEYMAP_OFF:
1319 owner->getIntl().keyMapOn(false);
1322 case LFUN_KEYMAP_PRIMARY:
1323 owner->getIntl().keyMapPrim();
1326 case LFUN_KEYMAP_SECONDARY:
1327 owner->getIntl().keyMapSec();
1330 case LFUN_KEYMAP_TOGGLE:
1331 owner->getIntl().toggleKeyMap();
1337 string rest = split(argument, countstr, ' ');
1338 istringstream is(countstr);
1341 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1342 for (int i = 0; i < count; ++i)
1343 dispatch(lyxaction.lookupFunc(rest));
1347 case LFUN_COMMAND_SEQUENCE: {
1348 // argument contains ';'-terminated commands
1349 string arg = argument;
1350 while (!arg.empty()) {
1352 arg = split(arg, first, ';');
1353 FuncRequest func(lyxaction.lookupFunc(first));
1354 func.origin = cmd.origin;
1360 case LFUN_PREFERENCES_SAVE: {
1361 Path p(package().user_support());
1362 lyxrc.write("preferences", false);
1366 case LFUN_SCREEN_FONT_UPDATE:
1367 // handle the screen font changes.
1368 lyxrc.set_font_norm_type();
1369 lyx_gui::update_fonts();
1370 // All visible buffers will need resize
1374 case LFUN_SET_COLOR: {
1376 string const x11_name = split(argument, lyx_name, ' ');
1377 if (lyx_name.empty() || x11_name.empty()) {
1378 setErrorMessage(N_("Syntax: set-color <lyx_name>"
1383 bool const graphicsbg_changed =
1384 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1385 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1387 if (!lcolor.setColor(lyx_name, x11_name)) {
1389 bformat(_("Set-color \"%1$s\" failed "
1390 "- color is undefined or "
1391 "may not be redefined"), lyx_name));
1395 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1397 if (graphicsbg_changed) {
1398 #ifdef WITH_WARNINGS
1399 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1402 lyx::graphics::GCache::get().changeDisplay(true);
1409 owner->message(argument);
1412 case LFUN_TOGGLE_TOOLTIPS:
1413 owner->getDialogs().toggleTooltips();
1416 case LFUN_EXTERNAL_EDIT: {
1417 FuncRequest fr(action, argument);
1418 InsetExternal().dispatch(view()->cursor(), fr);
1422 case LFUN_GRAPHICS_EDIT: {
1423 FuncRequest fr(action, argument);
1424 InsetGraphics().dispatch(view()->cursor(), fr);
1428 case LFUN_INSET_APPLY: {
1429 string const name = cmd.getArg(0);
1430 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1432 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1433 inset->dispatch(view()->cursor(), fr);
1435 FuncRequest fr(LFUN_INSET_INSERT, argument);
1438 // ideally, the update flag should be set by the insets,
1439 // but this is not possible currently
1444 case LFUN_ALL_INSETS_TOGGLE: {
1446 string const name = split(argument, action, ' ');
1447 InsetBase::Code const inset_code =
1448 InsetBase::translate(name);
1450 LCursor & cur = view()->cursor();
1451 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1453 InsetBase & inset = owner->buffer()->inset();
1454 InsetIterator it = inset_iterator_begin(inset);
1455 InsetIterator const end = inset_iterator_end(inset);
1456 for (; it != end; ++it) {
1457 if (inset_code == InsetBase::NO_CODE
1458 || inset_code == it->lyxCode()) {
1459 LCursor tmpcur = cur;
1460 tmpcur.pushLeft(*it);
1461 it->dispatch(tmpcur, fr);
1468 case LFUN_BUFFER_LANGUAGE: {
1469 Buffer & buffer = *owner->buffer();
1470 Language const * oldL = buffer.params().language;
1471 Language const * newL = languages.getLanguage(argument);
1472 if (!newL || oldL == newL)
1475 if (oldL->rightToLeft() == newL->rightToLeft()
1476 && !buffer.isMultiLingual())
1477 buffer.changeLanguage(oldL, newL);
1479 buffer.updateDocLang(newL);
1483 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1484 string const fname =
1485 addName(addPath(package().user_support(), "templates/"),
1487 Buffer defaults(fname);
1489 istringstream ss(argument);
1492 int const unknown_tokens = defaults.readHeader(lex);
1494 if (unknown_tokens != 0) {
1495 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1496 << unknown_tokens << " unknown token"
1497 << (unknown_tokens == 1 ? "" : "s")
1501 if (defaults.writeFile(defaults.fileName()))
1502 setMessage(_("Document defaults saved in ")
1503 + makeDisplayPath(fname));
1505 setErrorMessage(_("Unable to save document defaults"));
1509 case LFUN_BUFFER_PARAMS_APPLY: {
1510 biblio::CiteEngine const engine =
1511 owner->buffer()->params().cite_engine;
1513 istringstream ss(argument);
1516 int const unknown_tokens =
1517 owner->buffer()->readHeader(lex);
1519 if (unknown_tokens != 0) {
1520 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1521 << unknown_tokens << " unknown token"
1522 << (unknown_tokens == 1 ? "" : "s")
1525 if (engine == owner->buffer()->params().cite_engine)
1528 LCursor & cur = view()->cursor();
1529 FuncRequest fr(LFUN_INSET_REFRESH);
1531 InsetBase & inset = owner->buffer()->inset();
1532 InsetIterator it = inset_iterator_begin(inset);
1533 InsetIterator const end = inset_iterator_end(inset);
1534 for (; it != end; ++it)
1535 if (it->lyxCode() == InsetBase::CITE_CODE)
1536 it->dispatch(cur, fr);
1540 case LFUN_TEXTCLASS_APPLY: {
1541 Buffer * buffer = owner->buffer();
1543 lyx::textclass_type const old_class =
1544 buffer->params().textclass;
1546 loadTextclass(argument);
1548 std::pair<bool, lyx::textclass_type> const tc_pair =
1549 textclasslist.numberOfClass(argument);
1554 lyx::textclass_type const new_class = tc_pair.second;
1555 if (old_class == new_class)
1559 owner->message(_("Converting document to new document class..."));
1560 recordUndoFullDocument(view());
1561 buffer->params().textclass = new_class;
1562 StableDocIterator backcur(view()->cursor());
1564 lyx::cap::switchBetweenClasses(
1565 old_class, new_class,
1566 buffer->paragraphs(), el);
1568 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1569 bufferErrors(*buffer, el);
1570 view()->showErrorList(_("Class switch"));
1571 updateLabels(*buffer);
1576 case LFUN_TEXTCLASS_LOAD:
1577 loadTextclass(argument);
1580 case LFUN_LYXRC_APPLY: {
1581 LyXRC const lyxrc_orig = lyxrc;
1583 istringstream ss(argument);
1584 bool const success = lyxrc.read(ss) == 0;
1587 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1588 << "Unable to read lyxrc data"
1593 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1598 view()->cursor().dispatch(cmd);
1599 updateforce |= view()->cursor().result().update();
1600 if (!view()->cursor().result().dispatched())
1601 updateforce |= view()->dispatch(cmd);
1606 if (view()->available()) {
1607 // Redraw screen unless explicitly told otherwise.
1608 // This also initializes the position cache for all insets
1609 // in (at least partially) visible top-level paragraphs.
1611 view()->update(Update::FitCursor | Update::Force);
1613 view()->update(Update::FitCursor);
1615 // if we executed a mutating lfun, mark the buffer as dirty
1617 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1618 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1619 view()->buffer()->markDirty();
1622 if (view()->cursor().inTexted()) {
1623 view()->owner()->updateLayoutChoice();
1626 sendDispatchMessage(_(getMessage()), cmd);
1630 void LyXFunc::sendDispatchMessage(string const & msg, FuncRequest const & cmd)
1632 /* When an action did not originate from the UI/kbd, it makes
1633 * sense to avoid updating the GUI. It turns out that this
1634 * fixes bug 1941, for reasons that are described here:
1635 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1637 if (cmd.origin != FuncRequest::INTERNAL) {
1638 owner->updateMenubar();
1639 owner->updateToolbars();
1642 const bool verbose = (cmd.origin == FuncRequest::UI
1643 || cmd.origin == FuncRequest::COMMANDBUFFER);
1645 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1646 lyxerr[Debug::ACTION] << "dispatch msg is " << msg << endl;
1648 owner->message(msg);
1652 string dispatch_msg = msg;
1653 if (!dispatch_msg.empty())
1654 dispatch_msg += ' ';
1656 string comname = lyxaction.getActionName(cmd.action);
1658 bool argsadded = false;
1660 if (!cmd.argument.empty()) {
1661 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1662 comname += ' ' + cmd.argument;
1667 string const shortcuts = toplevel_keymap->printbindings(cmd);
1669 if (!shortcuts.empty())
1670 comname += ": " + shortcuts;
1671 else if (!argsadded && !cmd.argument.empty())
1672 comname += ' ' + cmd.argument;
1674 if (!comname.empty()) {
1675 comname = rtrim(comname);
1676 dispatch_msg += '(' + rtrim(comname) + ')';
1679 lyxerr[Debug::ACTION] << "verbose dispatch msg " << dispatch_msg << endl;
1680 if (!dispatch_msg.empty())
1681 owner->message(dispatch_msg);
1685 void LyXFunc::setupLocalKeymap()
1687 keyseq.stdmap = toplevel_keymap.get();
1688 keyseq.curmap = toplevel_keymap.get();
1689 cancel_meta_seq.stdmap = toplevel_keymap.get();
1690 cancel_meta_seq.curmap = toplevel_keymap.get();
1694 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1696 string initpath = lyxrc.document_path;
1697 string filename(name);
1699 if (view()->available()) {
1700 string const trypath = owner->buffer()->filePath();
1701 // If directory is writeable, use this as default.
1702 if (isDirWriteable(trypath))
1706 static int newfile_number;
1708 if (filename.empty()) {
1709 filename = addName(lyxrc.document_path,
1710 "newfile" + convert<string>(++newfile_number) + ".lyx");
1711 while (bufferlist.exists(filename) || fs::is_readable(filename)) {
1713 filename = addName(lyxrc.document_path,
1714 "newfile" + convert<string>(newfile_number) +
1719 // The template stuff
1722 FileDialog fileDlg(_("Select template file"),
1723 LFUN_SELECT_FILE_SYNC,
1724 make_pair(string(_("Documents|#o#O")),
1725 string(lyxrc.document_path)),
1726 make_pair(string(_("Templates|#T#t")),
1727 string(lyxrc.template_path)));
1729 FileDialog::Result result =
1730 fileDlg.open(lyxrc.template_path,
1731 FileFilterList(_("LyX Documents (*.lyx)")),
1734 if (result.first == FileDialog::Later)
1736 if (result.second.empty())
1738 templname = result.second;
1741 view()->newFile(filename, templname, !name.empty());
1745 void LyXFunc::open(string const & fname)
1747 string initpath = lyxrc.document_path;
1749 if (view()->available()) {
1750 string const trypath = owner->buffer()->filePath();
1751 // If directory is writeable, use this as default.
1752 if (isDirWriteable(trypath))
1758 if (fname.empty()) {
1759 FileDialog fileDlg(_("Select document to open"),
1761 make_pair(string(_("Documents|#o#O")),
1762 string(lyxrc.document_path)),
1763 make_pair(string(_("Examples|#E#e")),
1764 string(addPath(package().system_support(), "examples"))));
1766 FileDialog::Result result =
1767 fileDlg.open(initpath,
1768 FileFilterList(_("LyX Documents (*.lyx)")),
1771 if (result.first == FileDialog::Later)
1774 filename = result.second;
1776 // check selected filename
1777 if (filename.empty()) {
1778 owner->message(_("Canceled."));
1784 // get absolute path of file and add ".lyx" to the filename if
1786 string const fullpath = fileSearch(string(), filename, "lyx");
1787 if (!fullpath.empty()) {
1788 filename = fullpath;
1791 string const disp_fn(makeDisplayPath(filename));
1793 // if the file doesn't exist, let the user create one
1794 if (!fs::exists(filename)) {
1795 // the user specifically chose this name. Believe them.
1796 view()->newFile(filename, "", true);
1800 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1803 if (view()->loadLyXFile(filename)) {
1804 str2 = bformat(_("Document %1$s opened."), disp_fn);
1806 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1808 owner->message(str2);
1812 void LyXFunc::doImport(string const & argument)
1815 string filename = split(argument, format, ' ');
1817 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1818 << " file: " << filename << endl;
1820 // need user interaction
1821 if (filename.empty()) {
1822 string initpath = lyxrc.document_path;
1824 if (view()->available()) {
1825 string const trypath = owner->buffer()->filePath();
1826 // If directory is writeable, use this as default.
1827 if (isDirWriteable(trypath))
1831 string const text = bformat(_("Select %1$s file to import"),
1832 formats.prettyName(format));
1834 FileDialog fileDlg(text,
1836 make_pair(string(_("Documents|#o#O")),
1837 string(lyxrc.document_path)),
1838 make_pair(string(_("Examples|#E#e")),
1839 string(addPath(package().system_support(), "examples"))));
1841 string const filter = formats.prettyName(format)
1842 + " (*." + formats.extension(format) + ')';
1844 FileDialog::Result result =
1845 fileDlg.open(initpath,
1846 FileFilterList(filter),
1849 if (result.first == FileDialog::Later)
1852 filename = result.second;
1854 // check selected filename
1855 if (filename.empty())
1856 owner->message(_("Canceled."));
1859 if (filename.empty())
1862 // get absolute path of file
1863 filename = makeAbsPath(filename);
1865 string const lyxfile = changeExtension(filename, ".lyx");
1867 // Check if the document already is open
1868 if (lyx_gui::use_gui && bufferlist.exists(lyxfile)) {
1869 if (!bufferlist.close(bufferlist.getBuffer(lyxfile), true)) {
1870 owner->message(_("Canceled."));
1875 // if the file exists already, and we didn't do
1876 // -i lyx thefile.lyx, warn
1877 if (fs::exists(lyxfile) && filename != lyxfile) {
1878 string const file = makeDisplayPath(lyxfile, 30);
1880 string text = bformat(_("The document %1$s already exists.\n\n"
1881 "Do you want to over-write that document?"), file);
1882 int const ret = Alert::prompt(_("Over-write document?"),
1883 text, 0, 1, _("&Over-write"), _("&Cancel"));
1886 owner->message(_("Canceled."));
1891 Importer::Import(owner, filename, format);
1895 void LyXFunc::closeBuffer()
1897 // save current cursor position
1898 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1899 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1900 if (bufferlist.close(owner->buffer(), true) && !quitting) {
1901 if (bufferlist.empty()) {
1902 // need this otherwise SEGV may occur while
1903 // trying to set variables that don't exist
1904 // since there's no current buffer
1905 owner->getDialogs().hideBufferDependent();
1907 view()->setBuffer(bufferlist.first());
1913 // Each "owner" should have it's own message method. lyxview and
1914 // the minibuffer would use the minibuffer, but lyxserver would
1915 // send an ERROR signal to its client. Alejandro 970603
1916 // This function is bit problematic when it comes to NLS, to make the
1917 // lyx servers client be language indepenent we must not translate
1918 // strings sent to this func.
1919 void LyXFunc::setErrorMessage(string const & m) const
1921 dispatch_buffer = m;
1926 void LyXFunc::setMessage(string const & m) const
1928 dispatch_buffer = m;
1932 string const LyXFunc::viewStatusMessage()
1934 // When meta-fake key is pressed, show the key sequence so far + "M-".
1936 return keyseq.print() + "M-";
1938 // Else, when a non-complete key sequence is pressed,
1939 // show the available options.
1940 if (keyseq.length() > 0 && !keyseq.deleted())
1941 return keyseq.printOptions();
1943 if (!view()->available())
1944 return _("Welcome to LyX!");
1946 return view()->cursor().currentState();
1950 BufferView * LyXFunc::view() const
1952 BOOST_ASSERT(owner);
1953 return owner->view().get();
1957 bool LyXFunc::wasMetaKey() const
1959 return (meta_fake_bit != key_modifier::none);
1965 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1967 // Why the switch you might ask. It is a trick to ensure that all
1968 // the elements in the LyXRCTags enum is handled. As you can see
1969 // there are no breaks at all. So it is just a huge fall-through.
1970 // The nice thing is that we will get a warning from the compiler
1971 // if we forget an element.
1972 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1974 case LyXRC::RC_ACCEPT_COMPOUND:
1975 case LyXRC::RC_ALT_LANG:
1976 case LyXRC::RC_ASCIIROFF_COMMAND:
1977 case LyXRC::RC_ASCII_LINELEN:
1978 case LyXRC::RC_AUTOREGIONDELETE:
1979 case LyXRC::RC_AUTORESET_OPTIONS:
1980 case LyXRC::RC_AUTOSAVE:
1981 case LyXRC::RC_AUTO_NUMBER:
1982 case LyXRC::RC_BACKUPDIR_PATH:
1983 case LyXRC::RC_BIBTEX_COMMAND:
1984 case LyXRC::RC_BINDFILE:
1985 case LyXRC::RC_CHECKLASTFILES:
1986 case LyXRC::RC_USELASTFILEPOS:
1987 case LyXRC::RC_LOADSESSION:
1988 case LyXRC::RC_CHKTEX_COMMAND:
1989 case LyXRC::RC_CONVERTER:
1990 case LyXRC::RC_COPIER:
1991 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1992 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1993 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1994 case LyXRC::RC_CYGWIN_PATH_FIX:
1995 if (lyxrc_orig.cygwin_path_fix != lyxrc_new.cygwin_path_fix) {
1996 namespace os = lyx::support::os;
1997 os::cygwin_path_fix(lyxrc_new.cygwin_path_fix);
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_UIFILE:
2085 case LyXRC::RC_USER_EMAIL:
2086 case LyXRC::RC_USER_NAME:
2087 case LyXRC::RC_USETEMPDIR:
2088 case LyXRC::RC_USE_ALT_LANG:
2089 case LyXRC::RC_USE_ESC_CHARS:
2090 case LyXRC::RC_USE_INP_ENC:
2091 case LyXRC::RC_USE_PERS_DICT:
2092 case LyXRC::RC_USE_SPELL_LIB:
2093 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2094 case LyXRC::RC_VIEWER:
2095 case LyXRC::RC_WHEEL_JUMP:
2096 case LyXRC::RC_LAST: