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().lookupChange(slice.pos()).type;
195 if (ch != Change::UNCHANGED)
199 return Change::UNCHANGED;
204 LyXFunc::LyXFunc(LyXView * lv)
207 keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
208 cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
209 meta_fake_bit(key_modifier::none)
214 void LyXFunc::handleKeyFunc(kb_action action)
216 char c = encoded_last_key;
218 if (keyseq.length()) {
222 owner->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_TOOLTIPS_TOGGLE:
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_NEW_TEMPLATE:
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_BUFFER_NEXT:
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_NEW_TEMPLATE:
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_BUFFER_NEXT:
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()
1163 case LFUN_DIALOG_SHOW: {
1164 string const name = cmd.getArg(0);
1165 string data = trim(cmd.argument.substr(name.size()));
1167 if (name == "character") {
1168 data = freefont2string();
1170 owner->getDialogs().show("character", data);
1171 } else if (name == "latexlog") {
1172 pair<Buffer::LogType, string> const logfile =
1173 owner->buffer()->getLogName();
1174 switch (logfile.first) {
1175 case Buffer::latexlog:
1178 case Buffer::buildlog:
1182 data += logfile.second;
1183 owner->getDialogs().show("log", data);
1184 } else if (name == "vclog") {
1185 string const data = "vc " +
1186 owner->buffer()->lyxvc().getLogFile();
1187 owner->getDialogs().show("log", data);
1189 owner->getDialogs().show(name, data);
1193 case LFUN_DIALOG_SHOW_NEW_INSET: {
1194 string const name = cmd.getArg(0);
1195 string data = trim(cmd.argument.substr(name.size()));
1196 if (name == "bibitem" ||
1203 InsetCommandParams p(name);
1204 data = InsetCommandMailer::params2string(name, p);
1205 } else if (name == "include") {
1206 InsetCommandParams p(data);
1207 data = InsetIncludeMailer::params2string(p);
1208 } else if (name == "box") {
1209 // \c data == "Boxed" || "Frameless" etc
1210 InsetBoxParams p(data);
1211 data = InsetBoxMailer::params2string(p);
1212 } else if (name == "branch") {
1213 InsetBranchParams p;
1214 data = InsetBranchMailer::params2string(p);
1215 } else if (name == "citation") {
1216 InsetCommandParams p("cite");
1217 data = InsetCommandMailer::params2string(name, p);
1218 } else if (name == "ert") {
1219 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1220 } else if (name == "external") {
1221 InsetExternalParams p;
1222 Buffer const & buffer = *owner->buffer();
1223 data = InsetExternalMailer::params2string(p, buffer);
1224 } else if (name == "float") {
1226 data = InsetFloatMailer::params2string(p);
1227 } else if (name == "graphics") {
1228 InsetGraphicsParams p;
1229 Buffer const & buffer = *owner->buffer();
1230 data = InsetGraphicsMailer::params2string(p, buffer);
1231 } else if (name == "note") {
1233 data = InsetNoteMailer::params2string(p);
1234 } else if (name == "vspace") {
1236 data = InsetVSpaceMailer::params2string(space);
1237 } else if (name == "wrap") {
1239 data = InsetWrapMailer::params2string(p);
1241 owner->getDialogs().show(name, data, 0);
1245 case LFUN_DIALOG_SHOW_NEXT_INSET:
1248 case LFUN_DIALOG_UPDATE: {
1249 string const & name = argument;
1250 // Can only update a dialog connected to an existing inset
1251 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1253 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument);
1254 inset->dispatch(view()->cursor(), fr);
1255 } else if (name == "paragraph") {
1256 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1257 } else if (name == "prefs") {
1258 owner->getDialogs().update(name, string());
1263 case LFUN_DIALOG_HIDE:
1264 Dialogs::hide(argument, 0);
1267 case LFUN_DIALOG_DISCONNECT_INSET:
1268 owner->getDialogs().disconnect(argument);
1272 case LFUN_CITATION_INSERT: {
1273 if (!argument.empty()) {
1274 // we can have one optional argument, delimited by '|'
1275 // citation-insert <key>|<text_before>
1276 // this should be enhanced to also support text_after
1277 // and citation style
1278 string arg = argument;
1280 if (contains(argument, "|")) {
1281 arg = token(argument, '|', 0);
1282 opt1 = '[' + token(argument, '|', 1) + ']';
1284 std::ostringstream os;
1285 os << "citation LatexCommand\n"
1286 << "\\cite" << opt1 << "{" << arg << "}\n"
1288 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1291 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1295 case LFUN_BUFFER_CHILD_OPEN: {
1296 string const filename =
1297 makeAbsPath(argument, owner->buffer()->filePath());
1298 setMessage(N_("Opening child document ") +
1299 makeDisplayPath(filename) + "...");
1300 view()->savePosition(0);
1301 string const parentfilename = owner->buffer()->fileName();
1302 if (bufferlist.exists(filename))
1303 view()->setBuffer(bufferlist.getBuffer(filename));
1305 view()->loadLyXFile(filename);
1306 // Set the parent name of the child document.
1307 // This makes insertion of citations and references in the child work,
1308 // when the target is in the parent or another child document.
1309 owner->buffer()->setParentName(parentfilename);
1313 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1314 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1317 case LFUN_KEYMAP_OFF:
1318 owner->getIntl().keyMapOn(false);
1321 case LFUN_KEYMAP_PRIMARY:
1322 owner->getIntl().keyMapPrim();
1325 case LFUN_KEYMAP_SECONDARY:
1326 owner->getIntl().keyMapSec();
1329 case LFUN_KEYMAP_TOGGLE:
1330 owner->getIntl().toggleKeyMap();
1336 string rest = split(argument, countstr, ' ');
1337 istringstream is(countstr);
1340 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1341 for (int i = 0; i < count; ++i)
1342 dispatch(lyxaction.lookupFunc(rest));
1346 case LFUN_COMMAND_SEQUENCE: {
1347 // argument contains ';'-terminated commands
1348 string arg = argument;
1349 while (!arg.empty()) {
1351 arg = split(arg, first, ';');
1352 FuncRequest func(lyxaction.lookupFunc(first));
1353 func.origin = cmd.origin;
1359 case LFUN_PREFERENCES_SAVE: {
1360 Path p(package().user_support());
1361 lyxrc.write("preferences", false);
1365 case LFUN_SCREEN_FONT_UPDATE:
1366 // handle the screen font changes.
1367 lyxrc.set_font_norm_type();
1368 lyx_gui::update_fonts();
1369 // All visible buffers will need resize
1373 case LFUN_SET_COLOR: {
1375 string const x11_name = split(argument, lyx_name, ' ');
1376 if (lyx_name.empty() || x11_name.empty()) {
1377 setErrorMessage(N_("Syntax: set-color <lyx_name>"
1382 bool const graphicsbg_changed =
1383 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1384 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1386 if (!lcolor.setColor(lyx_name, x11_name)) {
1388 bformat(_("Set-color \"%1$s\" failed "
1389 "- color is undefined or "
1390 "may not be redefined"), lyx_name));
1394 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1396 if (graphicsbg_changed) {
1397 #ifdef WITH_WARNINGS
1398 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1401 lyx::graphics::GCache::get().changeDisplay(true);
1408 owner->message(argument);
1411 case LFUN_TOOLTIPS_TOGGLE:
1412 owner->getDialogs().toggleTooltips();
1415 case LFUN_EXTERNAL_EDIT: {
1416 FuncRequest fr(action, argument);
1417 InsetExternal().dispatch(view()->cursor(), fr);
1421 case LFUN_GRAPHICS_EDIT: {
1422 FuncRequest fr(action, argument);
1423 InsetGraphics().dispatch(view()->cursor(), fr);
1427 case LFUN_INSET_APPLY: {
1428 string const name = cmd.getArg(0);
1429 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1431 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1432 inset->dispatch(view()->cursor(), fr);
1434 FuncRequest fr(LFUN_INSET_INSERT, argument);
1437 // ideally, the update flag should be set by the insets,
1438 // but this is not possible currently
1443 case LFUN_ALL_INSETS_TOGGLE: {
1445 string const name = split(argument, action, ' ');
1446 InsetBase::Code const inset_code =
1447 InsetBase::translate(name);
1449 LCursor & cur = view()->cursor();
1450 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1452 InsetBase & inset = owner->buffer()->inset();
1453 InsetIterator it = inset_iterator_begin(inset);
1454 InsetIterator const end = inset_iterator_end(inset);
1455 for (; it != end; ++it) {
1456 if (inset_code == InsetBase::NO_CODE
1457 || inset_code == it->lyxCode()) {
1458 LCursor tmpcur = cur;
1459 tmpcur.pushLeft(*it);
1460 it->dispatch(tmpcur, fr);
1467 case LFUN_BUFFER_LANGUAGE: {
1468 Buffer & buffer = *owner->buffer();
1469 Language const * oldL = buffer.params().language;
1470 Language const * newL = languages.getLanguage(argument);
1471 if (!newL || oldL == newL)
1474 if (oldL->rightToLeft() == newL->rightToLeft()
1475 && !buffer.isMultiLingual())
1476 buffer.changeLanguage(oldL, newL);
1478 buffer.updateDocLang(newL);
1482 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1483 string const fname =
1484 addName(addPath(package().user_support(), "templates/"),
1486 Buffer defaults(fname);
1488 istringstream ss(argument);
1491 int const unknown_tokens = defaults.readHeader(lex);
1493 if (unknown_tokens != 0) {
1494 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1495 << unknown_tokens << " unknown token"
1496 << (unknown_tokens == 1 ? "" : "s")
1500 if (defaults.writeFile(defaults.fileName()))
1501 setMessage(_("Document defaults saved in ")
1502 + makeDisplayPath(fname));
1504 setErrorMessage(_("Unable to save document defaults"));
1508 case LFUN_BUFFER_PARAMS_APPLY: {
1509 biblio::CiteEngine const engine =
1510 owner->buffer()->params().cite_engine;
1512 istringstream ss(argument);
1515 int const unknown_tokens =
1516 owner->buffer()->readHeader(lex);
1518 if (unknown_tokens != 0) {
1519 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1520 << unknown_tokens << " unknown token"
1521 << (unknown_tokens == 1 ? "" : "s")
1524 if (engine == owner->buffer()->params().cite_engine)
1527 LCursor & cur = view()->cursor();
1528 FuncRequest fr(LFUN_INSET_REFRESH);
1530 InsetBase & inset = owner->buffer()->inset();
1531 InsetIterator it = inset_iterator_begin(inset);
1532 InsetIterator const end = inset_iterator_end(inset);
1533 for (; it != end; ++it)
1534 if (it->lyxCode() == InsetBase::CITE_CODE)
1535 it->dispatch(cur, fr);
1539 case LFUN_TEXTCLASS_APPLY: {
1540 Buffer * buffer = owner->buffer();
1542 lyx::textclass_type const old_class =
1543 buffer->params().textclass;
1545 loadTextclass(argument);
1547 std::pair<bool, lyx::textclass_type> const tc_pair =
1548 textclasslist.numberOfClass(argument);
1553 lyx::textclass_type const new_class = tc_pair.second;
1554 if (old_class == new_class)
1558 owner->message(_("Converting document to new document class..."));
1559 recordUndoFullDocument(view());
1560 buffer->params().textclass = new_class;
1561 StableDocIterator backcur(view()->cursor());
1563 lyx::cap::switchBetweenClasses(
1564 old_class, new_class,
1565 static_cast<InsetText &>(buffer->inset()), el);
1567 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1568 bufferErrors(*buffer, el);
1569 view()->showErrorList(_("Class switch"));
1570 updateLabels(*buffer);
1575 case LFUN_TEXTCLASS_LOAD:
1576 loadTextclass(argument);
1579 case LFUN_LYXRC_APPLY: {
1580 LyXRC const lyxrc_orig = lyxrc;
1582 istringstream ss(argument);
1583 bool const success = lyxrc.read(ss) == 0;
1586 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1587 << "Unable to read lyxrc data"
1592 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1597 view()->cursor().dispatch(cmd);
1598 updateforce |= view()->cursor().result().update();
1599 if (!view()->cursor().result().dispatched())
1600 updateforce |= view()->dispatch(cmd);
1605 if (view()->available()) {
1606 // Redraw screen unless explicitly told otherwise.
1607 // This also initializes the position cache for all insets
1608 // in (at least partially) visible top-level paragraphs.
1610 view()->update(Update::FitCursor | Update::Force);
1612 view()->update(Update::FitCursor);
1614 // if we executed a mutating lfun, mark the buffer as dirty
1616 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1617 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1618 view()->buffer()->markDirty();
1621 if (view()->cursor().inTexted()) {
1622 view()->owner()->updateLayoutChoice();
1625 sendDispatchMessage(_(getMessage()), cmd);
1629 void LyXFunc::sendDispatchMessage(string const & msg, FuncRequest const & cmd)
1631 /* When an action did not originate from the UI/kbd, it makes
1632 * sense to avoid updating the GUI. It turns out that this
1633 * fixes bug 1941, for reasons that are described here:
1634 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1636 if (cmd.origin != FuncRequest::INTERNAL) {
1637 owner->updateMenubar();
1638 owner->updateToolbars();
1641 const bool verbose = (cmd.origin == FuncRequest::UI
1642 || cmd.origin == FuncRequest::COMMANDBUFFER);
1644 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1645 lyxerr[Debug::ACTION] << "dispatch msg is " << msg << endl;
1647 owner->message(msg);
1651 string dispatch_msg = msg;
1652 if (!dispatch_msg.empty())
1653 dispatch_msg += ' ';
1655 string comname = lyxaction.getActionName(cmd.action);
1657 bool argsadded = false;
1659 if (!cmd.argument.empty()) {
1660 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1661 comname += ' ' + cmd.argument;
1666 string const shortcuts = toplevel_keymap->printbindings(cmd);
1668 if (!shortcuts.empty())
1669 comname += ": " + shortcuts;
1670 else if (!argsadded && !cmd.argument.empty())
1671 comname += ' ' + cmd.argument;
1673 if (!comname.empty()) {
1674 comname = rtrim(comname);
1675 dispatch_msg += '(' + rtrim(comname) + ')';
1678 lyxerr[Debug::ACTION] << "verbose dispatch msg " << dispatch_msg << endl;
1679 if (!dispatch_msg.empty())
1680 owner->message(dispatch_msg);
1684 void LyXFunc::setupLocalKeymap()
1686 keyseq.stdmap = toplevel_keymap.get();
1687 keyseq.curmap = toplevel_keymap.get();
1688 cancel_meta_seq.stdmap = toplevel_keymap.get();
1689 cancel_meta_seq.curmap = toplevel_keymap.get();
1693 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1695 string initpath = lyxrc.document_path;
1696 string filename(name);
1698 if (view()->available()) {
1699 string const trypath = owner->buffer()->filePath();
1700 // If directory is writeable, use this as default.
1701 if (isDirWriteable(trypath))
1705 static int newfile_number;
1707 if (filename.empty()) {
1708 filename = addName(lyxrc.document_path,
1709 "newfile" + convert<string>(++newfile_number) + ".lyx");
1710 while (bufferlist.exists(filename) || fs::is_readable(filename)) {
1712 filename = addName(lyxrc.document_path,
1713 "newfile" + convert<string>(newfile_number) +
1718 // The template stuff
1721 FileDialog fileDlg(_("Select template file"),
1722 LFUN_SELECT_FILE_SYNC,
1723 make_pair(string(_("Documents|#o#O")),
1724 string(lyxrc.document_path)),
1725 make_pair(string(_("Templates|#T#t")),
1726 string(lyxrc.template_path)));
1728 FileDialog::Result result =
1729 fileDlg.open(lyxrc.template_path,
1730 FileFilterList(_("LyX Documents (*.lyx)")),
1733 if (result.first == FileDialog::Later)
1735 if (result.second.empty())
1737 templname = result.second;
1740 view()->newFile(filename, templname, !name.empty());
1744 void LyXFunc::open(string const & fname)
1746 string initpath = lyxrc.document_path;
1748 if (view()->available()) {
1749 string const trypath = owner->buffer()->filePath();
1750 // If directory is writeable, use this as default.
1751 if (isDirWriteable(trypath))
1757 if (fname.empty()) {
1758 FileDialog fileDlg(_("Select document to open"),
1760 make_pair(string(_("Documents|#o#O")),
1761 string(lyxrc.document_path)),
1762 make_pair(string(_("Examples|#E#e")),
1763 string(addPath(package().system_support(), "examples"))));
1765 FileDialog::Result result =
1766 fileDlg.open(initpath,
1767 FileFilterList(_("LyX Documents (*.lyx)")),
1770 if (result.first == FileDialog::Later)
1773 filename = result.second;
1775 // check selected filename
1776 if (filename.empty()) {
1777 owner->message(_("Canceled."));
1783 // get absolute path of file and add ".lyx" to the filename if
1785 string const fullpath = fileSearch(string(), filename, "lyx");
1786 if (!fullpath.empty()) {
1787 filename = fullpath;
1790 string const disp_fn(makeDisplayPath(filename));
1792 // if the file doesn't exist, let the user create one
1793 if (!fs::exists(filename)) {
1794 // the user specifically chose this name. Believe them.
1795 view()->newFile(filename, "", true);
1799 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1802 if (view()->loadLyXFile(filename)) {
1803 str2 = bformat(_("Document %1$s opened."), disp_fn);
1805 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1807 owner->message(str2);
1811 void LyXFunc::doImport(string const & argument)
1814 string filename = split(argument, format, ' ');
1816 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1817 << " file: " << filename << endl;
1819 // need user interaction
1820 if (filename.empty()) {
1821 string initpath = lyxrc.document_path;
1823 if (view()->available()) {
1824 string const trypath = owner->buffer()->filePath();
1825 // If directory is writeable, use this as default.
1826 if (isDirWriteable(trypath))
1830 string const text = bformat(_("Select %1$s file to import"),
1831 formats.prettyName(format));
1833 FileDialog fileDlg(text,
1835 make_pair(string(_("Documents|#o#O")),
1836 string(lyxrc.document_path)),
1837 make_pair(string(_("Examples|#E#e")),
1838 string(addPath(package().system_support(), "examples"))));
1840 string const filter = formats.prettyName(format)
1841 + " (*." + formats.extension(format) + ')';
1843 FileDialog::Result result =
1844 fileDlg.open(initpath,
1845 FileFilterList(filter),
1848 if (result.first == FileDialog::Later)
1851 filename = result.second;
1853 // check selected filename
1854 if (filename.empty())
1855 owner->message(_("Canceled."));
1858 if (filename.empty())
1861 // get absolute path of file
1862 filename = makeAbsPath(filename);
1864 string const lyxfile = changeExtension(filename, ".lyx");
1866 // Check if the document already is open
1867 if (lyx_gui::use_gui && bufferlist.exists(lyxfile)) {
1868 if (!bufferlist.close(bufferlist.getBuffer(lyxfile), true)) {
1869 owner->message(_("Canceled."));
1874 // if the file exists already, and we didn't do
1875 // -i lyx thefile.lyx, warn
1876 if (fs::exists(lyxfile) && filename != lyxfile) {
1877 string const file = makeDisplayPath(lyxfile, 30);
1879 string text = bformat(_("The document %1$s already exists.\n\n"
1880 "Do you want to over-write that document?"), file);
1881 int const ret = Alert::prompt(_("Over-write document?"),
1882 text, 0, 1, _("&Over-write"), _("&Cancel"));
1885 owner->message(_("Canceled."));
1890 Importer::Import(owner, filename, format);
1894 void LyXFunc::closeBuffer()
1896 // save current cursor position
1897 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1898 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1899 if (bufferlist.close(owner->buffer(), true) && !quitting) {
1900 if (bufferlist.empty()) {
1901 // need this otherwise SEGV may occur while
1902 // trying to set variables that don't exist
1903 // since there's no current buffer
1904 owner->getDialogs().hideBufferDependent();
1906 view()->setBuffer(bufferlist.first());
1912 // Each "owner" should have it's own message method. lyxview and
1913 // the minibuffer would use the minibuffer, but lyxserver would
1914 // send an ERROR signal to its client. Alejandro 970603
1915 // This function is bit problematic when it comes to NLS, to make the
1916 // lyx servers client be language indepenent we must not translate
1917 // strings sent to this func.
1918 void LyXFunc::setErrorMessage(string const & m) const
1920 dispatch_buffer = m;
1925 void LyXFunc::setMessage(string const & m) const
1927 dispatch_buffer = m;
1931 string const LyXFunc::viewStatusMessage()
1933 // When meta-fake key is pressed, show the key sequence so far + "M-".
1935 return keyseq.print() + "M-";
1937 // Else, when a non-complete key sequence is pressed,
1938 // show the available options.
1939 if (keyseq.length() > 0 && !keyseq.deleted())
1940 return keyseq.printOptions();
1942 if (!view()->available())
1943 return _("Welcome to LyX!");
1945 return view()->cursor().currentState();
1949 BufferView * LyXFunc::view() const
1951 BOOST_ASSERT(owner);
1952 return owner->view();
1956 bool LyXFunc::wasMetaKey() const
1958 return (meta_fake_bit != key_modifier::none);
1964 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1966 // Why the switch you might ask. It is a trick to ensure that all
1967 // the elements in the LyXRCTags enum is handled. As you can see
1968 // there are no breaks at all. So it is just a huge fall-through.
1969 // The nice thing is that we will get a warning from the compiler
1970 // if we forget an element.
1971 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1973 case LyXRC::RC_ACCEPT_COMPOUND:
1974 case LyXRC::RC_ALT_LANG:
1975 case LyXRC::RC_ASCIIROFF_COMMAND:
1976 case LyXRC::RC_ASCII_LINELEN:
1977 case LyXRC::RC_AUTOREGIONDELETE:
1978 case LyXRC::RC_AUTORESET_OPTIONS:
1979 case LyXRC::RC_AUTOSAVE:
1980 case LyXRC::RC_AUTO_NUMBER:
1981 case LyXRC::RC_BACKUPDIR_PATH:
1982 case LyXRC::RC_BIBTEX_COMMAND:
1983 case LyXRC::RC_BINDFILE:
1984 case LyXRC::RC_CHECKLASTFILES:
1985 case LyXRC::RC_USELASTFILEPOS:
1986 case LyXRC::RC_LOADSESSION:
1987 case LyXRC::RC_CHKTEX_COMMAND:
1988 case LyXRC::RC_CONVERTER:
1989 case LyXRC::RC_COPIER:
1990 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1991 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1992 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1993 case LyXRC::RC_CYGWIN_PATH_FIX:
1994 if (lyxrc_orig.cygwin_path_fix != lyxrc_new.cygwin_path_fix) {
1995 namespace os = lyx::support::os;
1996 os::cygwin_path_fix(lyxrc_new.cygwin_path_fix);
1998 case LyXRC::RC_DATE_INSERT_FORMAT:
1999 case LyXRC::RC_DEFAULT_LANGUAGE:
2000 case LyXRC::RC_DEFAULT_PAPERSIZE:
2001 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2002 case LyXRC::RC_DISPLAY_GRAPHICS:
2003 case LyXRC::RC_DOCUMENTPATH:
2004 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2005 if (fs::exists(lyxrc_new.document_path) &&
2006 fs::is_directory(lyxrc_new.document_path)) {
2007 using lyx::support::package;
2008 package().document_dir() = lyxrc.document_path;
2011 case LyXRC::RC_ESC_CHARS:
2012 case LyXRC::RC_FONT_ENCODING:
2013 case LyXRC::RC_FORMAT:
2014 case LyXRC::RC_INDEX_COMMAND:
2015 case LyXRC::RC_INPUT:
2016 case LyXRC::RC_KBMAP:
2017 case LyXRC::RC_KBMAP_PRIMARY:
2018 case LyXRC::RC_KBMAP_SECONDARY:
2019 case LyXRC::RC_LABEL_INIT_LENGTH:
2020 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2021 case LyXRC::RC_LANGUAGE_AUTO_END:
2022 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2023 case LyXRC::RC_LANGUAGE_COMMAND_END:
2024 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2025 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2026 case LyXRC::RC_LANGUAGE_PACKAGE:
2027 case LyXRC::RC_LANGUAGE_USE_BABEL:
2028 case LyXRC::RC_MAKE_BACKUP:
2029 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2030 case LyXRC::RC_NUMLASTFILES:
2031 case LyXRC::RC_PATH_PREFIX:
2032 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2033 using lyx::support::prependEnvPath;
2034 prependEnvPath("PATH", lyxrc.path_prefix);
2036 case LyXRC::RC_PERS_DICT:
2037 case LyXRC::RC_POPUP_BOLD_FONT:
2038 case LyXRC::RC_POPUP_FONT_ENCODING:
2039 case LyXRC::RC_POPUP_NORMAL_FONT:
2040 case LyXRC::RC_PREVIEW:
2041 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2042 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2043 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2044 case LyXRC::RC_PRINTCOPIESFLAG:
2045 case LyXRC::RC_PRINTER:
2046 case LyXRC::RC_PRINTEVENPAGEFLAG:
2047 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2048 case LyXRC::RC_PRINTFILEEXTENSION:
2049 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2050 case LyXRC::RC_PRINTODDPAGEFLAG:
2051 case LyXRC::RC_PRINTPAGERANGEFLAG:
2052 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2053 case LyXRC::RC_PRINTPAPERFLAG:
2054 case LyXRC::RC_PRINTREVERSEFLAG:
2055 case LyXRC::RC_PRINTSPOOL_COMMAND:
2056 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2057 case LyXRC::RC_PRINTTOFILE:
2058 case LyXRC::RC_PRINTTOPRINTER:
2059 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2060 case LyXRC::RC_PRINT_COMMAND:
2061 case LyXRC::RC_RTL_SUPPORT:
2062 case LyXRC::RC_SCREEN_DPI:
2063 case LyXRC::RC_SCREEN_FONT_ENCODING:
2064 case LyXRC::RC_SCREEN_FONT_ROMAN:
2065 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2066 case LyXRC::RC_SCREEN_FONT_SANS:
2067 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2068 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2069 case LyXRC::RC_SCREEN_FONT_SIZES:
2070 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2071 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2072 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2073 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2074 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2075 case LyXRC::RC_SCREEN_ZOOM:
2076 case LyXRC::RC_SERVERPIPE:
2077 case LyXRC::RC_SET_COLOR:
2078 case LyXRC::RC_SHOW_BANNER:
2079 case LyXRC::RC_SPELL_COMMAND:
2080 case LyXRC::RC_TEMPDIRPATH:
2081 case LyXRC::RC_TEMPLATEPATH:
2082 case LyXRC::RC_TEX_ALLOWS_SPACES:
2083 case LyXRC::RC_UIFILE:
2084 case LyXRC::RC_USER_EMAIL:
2085 case LyXRC::RC_USER_NAME:
2086 case LyXRC::RC_USETEMPDIR:
2087 case LyXRC::RC_USE_ALT_LANG:
2088 case LyXRC::RC_USE_ESC_CHARS:
2089 case LyXRC::RC_USE_INP_ENC:
2090 case LyXRC::RC_USE_PERS_DICT:
2091 case LyXRC::RC_USE_SPELL_LIB:
2092 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2093 case LyXRC::RC_VIEWER:
2094 case LyXRC::RC_WHEEL_JUMP:
2095 case LyXRC::RC_LAST: