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 lookupChange(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());
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_FAKE)) {
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_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_SELFINSERT,
314 FuncRequest::KEYBOARD);
316 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
317 owner->message(_("Unknown function."));
322 if (func.action == LFUN_SELFINSERT) {
323 if (encoded_last_key != 0) {
324 string const arg(1, encoded_last_key);
325 dispatch(FuncRequest(LFUN_SELFINSERT, 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_READ_ONLY_TOGGLE:
405 flag.setOnOff(buf->isReadonly());
408 case LFUN_SWITCHBUFFER:
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())
416 enable = cmd.argument == "custom"
417 || Exporter::isExportable(*buf, cmd.argument);
421 enable = buf->isLatex() && lyxrc.chktex_command != "none";
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_CHECKIN:
441 enable = buf->lyxvc().inUse() && !buf->isReadonly();
443 case LFUN_VC_CHECKOUT:
444 enable = buf->lyxvc().inUse() && buf->isReadonly();
448 enable = buf->lyxvc().inUse();
450 case LFUN_MENURELOAD:
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_INSERT_CITATION: {
543 FuncRequest fr(LFUN_INSET_INSERT, "citation");
544 enable = getStatus(fr).enabled();
548 case LFUN_MENUWRITE: {
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_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);
565 case LFUN_MENUNEWTMPLT:
566 case LFUN_WORDFINDFORWARD:
567 case LFUN_WORDFINDBACKWARD:
569 case LFUN_EXEC_COMMAND:
572 case LFUN_CLOSEBUFFER:
580 case LFUN_RECONFIGURE:
584 case LFUN_DROP_LAYOUTS_CHOICE:
585 case LFUN_MENU_OPEN_BY_NAME:
588 case LFUN_GOTOFILEROW:
589 case LFUN_DIALOG_SHOW_NEXT_INSET:
590 case LFUN_DIALOG_HIDE:
591 case LFUN_DIALOG_DISCONNECT_INSET:
593 case LFUN_TOGGLECURSORFOLLOW:
597 case LFUN_KMAP_TOGGLE:
599 case LFUN_EXPORT_CUSTOM:
601 case LFUN_SAVEPREFERENCES:
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_LANGUAGE_BUFFER:
609 case LFUN_TEXTCLASS_APPLY:
610 case LFUN_TEXTCLASS_LOAD:
611 case LFUN_SAVE_AS_DEFAULT:
612 case LFUN_BUFFERPARAMS_APPLY:
613 case LFUN_LYXRC_APPLY:
614 case LFUN_NEXTBUFFER:
615 case LFUN_PREVIOUSBUFFER:
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 && lookupChange(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_MENUWRITE));
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_WORDFINDFORWARD:
748 case LFUN_WORDFINDBACKWARD: {
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_WORDFINDFORWARD;
764 lyx::find::find2string(searched_string, true, false, fw);
765 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
770 owner->message(keyseq.printOptions());
773 case LFUN_EXEC_COMMAND:
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"));
788 meta_fake_bit = key_modifier::alt;
789 setMessage(keyseq.print());
792 case LFUN_READ_ONLY_TOGGLE:
793 if (owner->buffer()->lyxvc().inUse())
794 owner->buffer()->lyxvc().toggleReadOnly();
796 owner->buffer()->setReadonly(
797 !owner->buffer()->isReadonly());
800 // --- Menus -----------------------------------------------
802 menuNew(argument, false);
805 case LFUN_MENUNEWTMPLT:
806 menuNew(argument, true);
809 case LFUN_CLOSEBUFFER:
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());
826 writeAs(owner->buffer(), argument);
830 case LFUN_MENURELOAD: {
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"));
843 Exporter::Export(owner->buffer(), argument, true);
844 view()->showErrorList(bufferFormat(*owner->buffer()));
848 Exporter::preview(owner->buffer(), argument);
849 view()->showErrorList(bufferFormat(*owner->buffer()));
853 Exporter::Export(owner->buffer(), "program", true);
854 view()->showErrorList(_("Build"));
858 owner->buffer()->runChktex();
859 view()->showErrorList(_("ChkTeX"));
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_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);
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());
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 opened file name to .lyx/session
1015 LyX::ref().session().setLastOpenedFiles( bufferlist.getFileNames());
1016 // save bookmarks to .lyx/session
1017 view()->saveSavedPositions();
1019 quitLyX(argument == "force");
1022 case LFUN_TOCVIEW: {
1023 InsetCommandParams p("tableofcontents");
1024 string const data = InsetCommandMailer::params2string("toc", p);
1025 owner->getDialogs().show("toc", data, 0);
1033 case LFUN_RECONFIGURE:
1034 reconfigure(view());
1037 case LFUN_HELP_OPEN: {
1038 string const arg = argument;
1040 setErrorMessage(N_("Missing argument"));
1043 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1044 if (fname.empty()) {
1045 lyxerr << "LyX: unable to find documentation file `"
1046 << arg << "'. Bad installation?" << endl;
1049 owner->message(bformat(_("Opening help file %1$s..."),
1050 makeDisplayPath(fname)));
1051 view()->loadLyXFile(fname, false);
1055 // --- version control -------------------------------
1056 case LFUN_VC_REGISTER:
1057 if (!ensureBufferClean(view()))
1059 if (!owner->buffer()->lyxvc().inUse()) {
1060 owner->buffer()->lyxvc().registrer();
1065 case LFUN_VC_CHECKIN:
1066 if (!ensureBufferClean(view()))
1068 if (owner->buffer()->lyxvc().inUse()
1069 && !owner->buffer()->isReadonly()) {
1070 owner->buffer()->lyxvc().checkIn();
1075 case LFUN_VC_CHECKOUT:
1076 if (!ensureBufferClean(view()))
1078 if (owner->buffer()->lyxvc().inUse()
1079 && owner->buffer()->isReadonly()) {
1080 owner->buffer()->lyxvc().checkOut();
1085 case LFUN_VC_REVERT:
1086 owner->buffer()->lyxvc().revert();
1091 owner->buffer()->lyxvc().undoLast();
1095 // --- buffers ----------------------------------------
1096 case LFUN_SWITCHBUFFER:
1097 view()->setBuffer(bufferlist.getBuffer(argument));
1100 case LFUN_NEXTBUFFER:
1101 view()->setBuffer(bufferlist.next(view()->buffer()));
1104 case LFUN_PREVIOUSBUFFER:
1105 view()->setBuffer(bufferlist.previous(view()->buffer()));
1109 newFile(view(), argument);
1112 case LFUN_FILE_OPEN:
1116 case LFUN_DROP_LAYOUTS_CHOICE:
1117 owner->getToolbars().openLayoutList();
1120 case LFUN_MENU_OPEN_BY_NAME:
1121 owner->getMenubar().openByName(argument);
1124 // --- lyxserver commands ----------------------------
1126 setMessage(owner->buffer()->fileName());
1127 lyxerr[Debug::INFO] << "FNAME["
1128 << owner->buffer()->fileName()
1133 dispatch_buffer = keyseq.print();
1134 lyxserver->notifyClient(dispatch_buffer);
1137 case LFUN_GOTOFILEROW: {
1140 istringstream is(argument);
1141 is >> file_name >> row;
1142 if (prefixIs(file_name, package().temp_dir())) {
1143 // Needed by inverse dvi search. If it is a file
1144 // in tmpdir, call the apropriated function
1145 view()->setBuffer(bufferlist.getBufferFromTmp(file_name));
1147 // Must replace extension of the file to be .lyx
1148 // and get full path
1149 string const s = changeExtension(file_name, ".lyx");
1150 // Either change buffer or load the file
1151 if (bufferlist.exists(s)) {
1152 view()->setBuffer(bufferlist.getBuffer(s));
1154 view()->loadLyXFile(s);
1158 view()->setCursorFromRow(row);
1161 // see BufferView_pimpl::center()
1162 view()->updateScrollbar();
1166 case LFUN_DIALOG_SHOW: {
1167 string const name = cmd.getArg(0);
1168 string data = trim(cmd.argument.substr(name.size()));
1170 if (name == "character") {
1171 data = freefont2string();
1173 owner->getDialogs().show("character", data);
1174 } else if (name == "latexlog") {
1175 pair<Buffer::LogType, string> const logfile =
1176 owner->buffer()->getLogName();
1177 switch (logfile.first) {
1178 case Buffer::latexlog:
1181 case Buffer::buildlog:
1185 data += logfile.second;
1186 owner->getDialogs().show("log", data);
1187 } else if (name == "vclog") {
1188 string const data = "vc " +
1189 owner->buffer()->lyxvc().getLogFile();
1190 owner->getDialogs().show("log", data);
1192 owner->getDialogs().show(name, data);
1196 case LFUN_DIALOG_SHOW_NEW_INSET: {
1197 string const name = cmd.getArg(0);
1198 string data = trim(cmd.argument.substr(name.size()));
1199 if (name == "bibitem" ||
1206 InsetCommandParams p(name);
1207 data = InsetCommandMailer::params2string(name, p);
1208 } else if (name == "include") {
1209 InsetCommandParams p(data);
1210 data = InsetIncludeMailer::params2string(p);
1211 } else if (name == "box") {
1212 // \c data == "Boxed" || "Frameless" etc
1213 InsetBoxParams p(data);
1214 data = InsetBoxMailer::params2string(p);
1215 } else if (name == "branch") {
1216 InsetBranchParams p;
1217 data = InsetBranchMailer::params2string(p);
1218 } else if (name == "citation") {
1219 InsetCommandParams p("cite");
1220 data = InsetCommandMailer::params2string(name, p);
1221 } else if (name == "ert") {
1222 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1223 } else if (name == "external") {
1224 InsetExternalParams p;
1225 Buffer const & buffer = *owner->buffer();
1226 data = InsetExternalMailer::params2string(p, buffer);
1227 } else if (name == "float") {
1229 data = InsetFloatMailer::params2string(p);
1230 } else if (name == "graphics") {
1231 InsetGraphicsParams p;
1232 Buffer const & buffer = *owner->buffer();
1233 data = InsetGraphicsMailer::params2string(p, buffer);
1234 } else if (name == "note") {
1236 data = InsetNoteMailer::params2string(p);
1237 } else if (name == "vspace") {
1239 data = InsetVSpaceMailer::params2string(space);
1240 } else if (name == "wrap") {
1242 data = InsetWrapMailer::params2string(p);
1244 owner->getDialogs().show(name, data, 0);
1248 case LFUN_DIALOG_SHOW_NEXT_INSET:
1251 case LFUN_DIALOG_UPDATE: {
1252 string const & name = argument;
1253 // Can only update a dialog connected to an existing inset
1254 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1256 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument);
1257 inset->dispatch(view()->cursor(), fr);
1258 } else if (name == "paragraph") {
1259 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1260 } else if (name == "prefs") {
1261 owner->getDialogs().update(name, string());
1266 case LFUN_DIALOG_HIDE:
1267 Dialogs::hide(argument, 0);
1270 case LFUN_DIALOG_DISCONNECT_INSET:
1271 owner->getDialogs().disconnect(argument);
1275 case LFUN_INSERT_CITATION: {
1276 if (!argument.empty()) {
1277 // we can have one optional argument, delimited by '|'
1278 // citation-insert <key>|<text_before>
1279 // this should be enhanced to also support text_after
1280 // and citation style
1281 string arg = argument;
1283 if (contains(argument, "|")) {
1284 arg = token(argument, '|', 0);
1285 opt1 = '[' + token(argument, '|', 1) + ']';
1287 std::ostringstream os;
1288 os << "citation LatexCommand\n"
1289 << "\\cite" << opt1 << "{" << arg << "}\n"
1291 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1294 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1298 case LFUN_CHILDOPEN: {
1299 string const filename =
1300 makeAbsPath(argument, owner->buffer()->filePath());
1301 setMessage(N_("Opening child document ") +
1302 makeDisplayPath(filename) + "...");
1303 view()->savePosition(0);
1304 string const parentfilename = owner->buffer()->fileName();
1305 if (bufferlist.exists(filename))
1306 view()->setBuffer(bufferlist.getBuffer(filename));
1308 view()->loadLyXFile(filename);
1309 // Set the parent name of the child document.
1310 // This makes insertion of citations and references in the child work,
1311 // when the target is in the parent or another child document.
1312 owner->buffer()->setParentName(parentfilename);
1316 case LFUN_TOGGLECURSORFOLLOW:
1317 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1321 owner->getIntl().keyMapOn(false);
1324 case LFUN_KMAP_PRIM:
1325 owner->getIntl().keyMapPrim();
1329 owner->getIntl().keyMapSec();
1332 case LFUN_KMAP_TOGGLE:
1333 owner->getIntl().toggleKeyMap();
1339 string rest = split(argument, countstr, ' ');
1340 istringstream is(countstr);
1343 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1344 for (int i = 0; i < count; ++i)
1345 dispatch(lyxaction.lookupFunc(rest));
1349 case LFUN_SEQUENCE: {
1350 // argument contains ';'-terminated commands
1351 string arg = argument;
1352 while (!arg.empty()) {
1354 arg = split(arg, first, ';');
1355 FuncRequest func(lyxaction.lookupFunc(first));
1356 func.origin = cmd.origin;
1362 case LFUN_SAVEPREFERENCES: {
1363 Path p(package().user_support());
1364 lyxrc.write("preferences", false);
1368 case LFUN_SCREEN_FONT_UPDATE:
1369 // handle the screen font changes.
1370 lyxrc.set_font_norm_type();
1371 lyx_gui::update_fonts();
1372 // All visible buffers will need resize
1376 case LFUN_SET_COLOR: {
1378 string const x11_name = split(argument, lyx_name, ' ');
1379 if (lyx_name.empty() || x11_name.empty()) {
1380 setErrorMessage(N_("Syntax: set-color <lyx_name>"
1385 bool const graphicsbg_changed =
1386 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1387 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1389 if (!lcolor.setColor(lyx_name, x11_name)) {
1391 bformat(_("Set-color \"%1$s\" failed "
1392 "- color is undefined or "
1393 "may not be redefined"), lyx_name));
1397 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1399 if (graphicsbg_changed) {
1400 #ifdef WITH_WARNINGS
1401 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1404 lyx::graphics::GCache::get().changeDisplay(true);
1411 owner->message(argument);
1414 case LFUN_TOOLTIPS_TOGGLE:
1415 owner->getDialogs().toggleTooltips();
1418 case LFUN_EXTERNAL_EDIT: {
1419 FuncRequest fr(action, argument);
1420 InsetExternal().dispatch(view()->cursor(), fr);
1424 case LFUN_GRAPHICS_EDIT: {
1425 FuncRequest fr(action, argument);
1426 InsetGraphics().dispatch(view()->cursor(), fr);
1430 case LFUN_INSET_APPLY: {
1431 string const name = cmd.getArg(0);
1432 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1434 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1435 inset->dispatch(view()->cursor(), fr);
1437 FuncRequest fr(LFUN_INSET_INSERT, argument);
1440 // ideally, the update flag should be set by the insets,
1441 // but this is not possible currently
1446 case LFUN_ALL_INSETS_TOGGLE: {
1448 string const name = split(argument, action, ' ');
1449 InsetBase::Code const inset_code =
1450 InsetBase::translate(name);
1452 LCursor & cur = view()->cursor();
1453 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1455 InsetBase & inset = owner->buffer()->inset();
1456 InsetIterator it = inset_iterator_begin(inset);
1457 InsetIterator const end = inset_iterator_end(inset);
1458 for (; it != end; ++it) {
1459 if (inset_code == InsetBase::NO_CODE
1460 || inset_code == it->lyxCode()) {
1461 LCursor tmpcur = cur;
1462 tmpcur.pushLeft(*it);
1463 it->dispatch(tmpcur, fr);
1470 case LFUN_LANGUAGE_BUFFER: {
1471 Buffer & buffer = *owner->buffer();
1472 Language const * oldL = buffer.params().language;
1473 Language const * newL = languages.getLanguage(argument);
1474 if (!newL || oldL == newL)
1477 if (oldL->rightToLeft() == newL->rightToLeft()
1478 && !buffer.isMultiLingual())
1479 buffer.changeLanguage(oldL, newL);
1481 buffer.updateDocLang(newL);
1485 case LFUN_SAVE_AS_DEFAULT: {
1486 string const fname =
1487 addName(addPath(package().user_support(), "templates/"),
1489 Buffer defaults(fname);
1491 istringstream ss(argument);
1494 int const unknown_tokens = defaults.readHeader(lex);
1496 if (unknown_tokens != 0) {
1497 lyxerr << "Warning in LFUN_SAVE_AS_DEFAULT!\n"
1498 << unknown_tokens << " unknown token"
1499 << (unknown_tokens == 1 ? "" : "s")
1503 if (defaults.writeFile(defaults.fileName()))
1504 setMessage(_("Document defaults saved in ")
1505 + makeDisplayPath(fname));
1507 setErrorMessage(_("Unable to save document defaults"));
1511 case LFUN_BUFFERPARAMS_APPLY: {
1512 biblio::CiteEngine const engine =
1513 owner->buffer()->params().cite_engine;
1515 istringstream ss(argument);
1518 int const unknown_tokens =
1519 owner->buffer()->readHeader(lex);
1521 if (unknown_tokens != 0) {
1522 lyxerr << "Warning in LFUN_BUFFERPARAMS_APPLY!\n"
1523 << unknown_tokens << " unknown token"
1524 << (unknown_tokens == 1 ? "" : "s")
1527 if (engine == owner->buffer()->params().cite_engine)
1530 LCursor & cur = view()->cursor();
1531 FuncRequest fr(LFUN_INSET_REFRESH);
1533 InsetBase & inset = owner->buffer()->inset();
1534 InsetIterator it = inset_iterator_begin(inset);
1535 InsetIterator const end = inset_iterator_end(inset);
1536 for (; it != end; ++it)
1537 if (it->lyxCode() == InsetBase::CITE_CODE)
1538 it->dispatch(cur, fr);
1542 case LFUN_TEXTCLASS_APPLY: {
1543 Buffer * buffer = owner->buffer();
1545 lyx::textclass_type const old_class =
1546 buffer->params().textclass;
1548 loadTextclass(argument);
1550 std::pair<bool, lyx::textclass_type> const tc_pair =
1551 textclasslist.numberOfClass(argument);
1556 lyx::textclass_type const new_class = tc_pair.second;
1557 if (old_class == new_class)
1561 owner->message(_("Converting document to new document class..."));
1562 recordUndoFullDocument(view());
1563 buffer->params().textclass = new_class;
1564 StableDocIterator backcur(view()->cursor());
1566 lyx::cap::switchBetweenClasses(
1567 old_class, new_class,
1568 buffer->paragraphs(), el);
1570 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1571 bufferErrors(*buffer, el);
1572 view()->showErrorList(_("Class switch"));
1573 updateLabels(*buffer);
1578 case LFUN_TEXTCLASS_LOAD:
1579 loadTextclass(argument);
1582 case LFUN_LYXRC_APPLY: {
1583 LyXRC const lyxrc_orig = lyxrc;
1585 istringstream ss(argument);
1586 bool const success = lyxrc.read(ss) == 0;
1589 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1590 << "Unable to read lyxrc data"
1595 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1600 view()->cursor().dispatch(cmd);
1601 updateforce |= view()->cursor().result().update();
1602 if (!view()->cursor().result().dispatched())
1603 updateforce |= view()->dispatch(cmd);
1608 if (view()->available()) {
1609 // Redraw screen unless explicitly told otherwise.
1610 // This also initializes the position cache for all insets
1611 // in (at least partially) visible top-level paragraphs.
1613 view()->update(Update::FitCursor | Update::Force);
1615 view()->update(Update::FitCursor);
1617 // if we executed a mutating lfun, mark the buffer as dirty
1619 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1620 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1621 view()->buffer()->markDirty();
1624 if (view()->cursor().inTexted()) {
1625 view()->owner()->updateLayoutChoice();
1628 sendDispatchMessage(_(getMessage()), cmd);
1632 void LyXFunc::sendDispatchMessage(string const & msg, FuncRequest const & cmd)
1634 /* When an action did not originate from the UI/kbd, it makes
1635 * sense to avoid updating the GUI. It turns out that this
1636 * fixes bug 1941, for reasons that are described here:
1637 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1639 if (cmd.origin != FuncRequest::INTERNAL) {
1640 owner->updateMenubar();
1641 owner->updateToolbars();
1644 const bool verbose = (cmd.origin == FuncRequest::UI
1645 || cmd.origin == FuncRequest::COMMANDBUFFER);
1647 if (cmd.action == LFUN_SELFINSERT || !verbose) {
1648 lyxerr[Debug::ACTION] << "dispatch msg is " << msg << endl;
1650 owner->message(msg);
1654 string dispatch_msg = msg;
1655 if (!dispatch_msg.empty())
1656 dispatch_msg += ' ';
1658 string comname = lyxaction.getActionName(cmd.action);
1660 bool argsadded = false;
1662 if (!cmd.argument.empty()) {
1663 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1664 comname += ' ' + cmd.argument;
1669 string const shortcuts = toplevel_keymap->printbindings(cmd);
1671 if (!shortcuts.empty())
1672 comname += ": " + shortcuts;
1673 else if (!argsadded && !cmd.argument.empty())
1674 comname += ' ' + cmd.argument;
1676 if (!comname.empty()) {
1677 comname = rtrim(comname);
1678 dispatch_msg += '(' + rtrim(comname) + ')';
1681 lyxerr[Debug::ACTION] << "verbose dispatch msg " << dispatch_msg << endl;
1682 if (!dispatch_msg.empty())
1683 owner->message(dispatch_msg);
1687 void LyXFunc::setupLocalKeymap()
1689 keyseq.stdmap = toplevel_keymap.get();
1690 keyseq.curmap = toplevel_keymap.get();
1691 cancel_meta_seq.stdmap = toplevel_keymap.get();
1692 cancel_meta_seq.curmap = toplevel_keymap.get();
1696 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1698 string initpath = lyxrc.document_path;
1699 string filename(name);
1701 if (view()->available()) {
1702 string const trypath = owner->buffer()->filePath();
1703 // If directory is writeable, use this as default.
1704 if (isDirWriteable(trypath))
1708 static int newfile_number;
1710 if (filename.empty()) {
1711 filename = addName(lyxrc.document_path,
1712 "newfile" + convert<string>(++newfile_number) + ".lyx");
1713 while (bufferlist.exists(filename) || fs::is_readable(filename)) {
1715 filename = addName(lyxrc.document_path,
1716 "newfile" + convert<string>(newfile_number) +
1721 // The template stuff
1724 FileDialog fileDlg(_("Select template file"),
1725 LFUN_SELECT_FILE_SYNC,
1726 make_pair(string(_("Documents|#o#O")),
1727 string(lyxrc.document_path)),
1728 make_pair(string(_("Templates|#T#t")),
1729 string(lyxrc.template_path)));
1731 FileDialog::Result result =
1732 fileDlg.open(lyxrc.template_path,
1733 FileFilterList(_("LyX Documents (*.lyx)")),
1736 if (result.first == FileDialog::Later)
1738 if (result.second.empty())
1740 templname = result.second;
1743 view()->newFile(filename, templname, !name.empty());
1747 void LyXFunc::open(string const & fname)
1749 string initpath = lyxrc.document_path;
1751 if (view()->available()) {
1752 string const trypath = owner->buffer()->filePath();
1753 // If directory is writeable, use this as default.
1754 if (isDirWriteable(trypath))
1760 if (fname.empty()) {
1761 FileDialog fileDlg(_("Select document to open"),
1763 make_pair(string(_("Documents|#o#O")),
1764 string(lyxrc.document_path)),
1765 make_pair(string(_("Examples|#E#e")),
1766 string(addPath(package().system_support(), "examples"))));
1768 FileDialog::Result result =
1769 fileDlg.open(initpath,
1770 FileFilterList(_("LyX Documents (*.lyx)")),
1773 if (result.first == FileDialog::Later)
1776 filename = result.second;
1778 // check selected filename
1779 if (filename.empty()) {
1780 owner->message(_("Canceled."));
1786 // get absolute path of file and add ".lyx" to the filename if
1788 string const fullpath = fileSearch(string(), filename, "lyx");
1789 if (!fullpath.empty()) {
1790 filename = fullpath;
1793 string const disp_fn(makeDisplayPath(filename));
1795 // if the file doesn't exist, let the user create one
1796 if (!fs::exists(filename)) {
1797 // the user specifically chose this name. Believe them.
1798 view()->newFile(filename, "", true);
1802 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1805 if (view()->loadLyXFile(filename)) {
1806 str2 = bformat(_("Document %1$s opened."), disp_fn);
1808 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1810 owner->message(str2);
1814 void LyXFunc::doImport(string const & argument)
1817 string filename = split(argument, format, ' ');
1819 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1820 << " file: " << filename << endl;
1822 // need user interaction
1823 if (filename.empty()) {
1824 string initpath = lyxrc.document_path;
1826 if (view()->available()) {
1827 string const trypath = owner->buffer()->filePath();
1828 // If directory is writeable, use this as default.
1829 if (isDirWriteable(trypath))
1833 string const text = bformat(_("Select %1$s file to import"),
1834 formats.prettyName(format));
1836 FileDialog fileDlg(text,
1838 make_pair(string(_("Documents|#o#O")),
1839 string(lyxrc.document_path)),
1840 make_pair(string(_("Examples|#E#e")),
1841 string(addPath(package().system_support(), "examples"))));
1843 string const filter = formats.prettyName(format)
1844 + " (*." + formats.extension(format) + ')';
1846 FileDialog::Result result =
1847 fileDlg.open(initpath,
1848 FileFilterList(filter),
1851 if (result.first == FileDialog::Later)
1854 filename = result.second;
1856 // check selected filename
1857 if (filename.empty())
1858 owner->message(_("Canceled."));
1861 if (filename.empty())
1864 // get absolute path of file
1865 filename = makeAbsPath(filename);
1867 string const lyxfile = changeExtension(filename, ".lyx");
1869 // Check if the document already is open
1870 if (lyx_gui::use_gui && bufferlist.exists(lyxfile)) {
1871 if (!bufferlist.close(bufferlist.getBuffer(lyxfile), true)) {
1872 owner->message(_("Canceled."));
1877 // if the file exists already, and we didn't do
1878 // -i lyx thefile.lyx, warn
1879 if (fs::exists(lyxfile) && filename != lyxfile) {
1880 string const file = makeDisplayPath(lyxfile, 30);
1882 string text = bformat(_("The document %1$s already exists.\n\n"
1883 "Do you want to over-write that document?"), file);
1884 int const ret = Alert::prompt(_("Over-write document?"),
1885 text, 0, 1, _("&Over-write"), _("&Cancel"));
1888 owner->message(_("Canceled."));
1893 Importer::Import(owner, filename, format);
1897 void LyXFunc::closeBuffer()
1899 // save current cursor position
1900 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1901 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1902 if (bufferlist.close(owner->buffer(), true) && !quitting) {
1903 if (bufferlist.empty()) {
1904 // need this otherwise SEGV may occur while
1905 // trying to set variables that don't exist
1906 // since there's no current buffer
1907 owner->getDialogs().hideBufferDependent();
1909 view()->setBuffer(bufferlist.first());
1915 // Each "owner" should have it's own message method. lyxview and
1916 // the minibuffer would use the minibuffer, but lyxserver would
1917 // send an ERROR signal to its client. Alejandro 970603
1918 // This function is bit problematic when it comes to NLS, to make the
1919 // lyx servers client be language indepenent we must not translate
1920 // strings sent to this func.
1921 void LyXFunc::setErrorMessage(string const & m) const
1923 dispatch_buffer = m;
1928 void LyXFunc::setMessage(string const & m) const
1930 dispatch_buffer = m;
1934 string const LyXFunc::viewStatusMessage()
1936 // When meta-fake key is pressed, show the key sequence so far + "M-".
1938 return keyseq.print() + "M-";
1940 // Else, when a non-complete key sequence is pressed,
1941 // show the available options.
1942 if (keyseq.length() > 0 && !keyseq.deleted())
1943 return keyseq.printOptions();
1945 if (!view()->available())
1946 return _("Welcome to LyX!");
1948 return view()->cursor().currentState();
1952 BufferView * LyXFunc::view() const
1954 BOOST_ASSERT(owner);
1955 return owner->view().get();
1959 bool LyXFunc::wasMetaKey() const
1961 return (meta_fake_bit != key_modifier::none);
1967 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1969 // Why the switch you might ask. It is a trick to ensure that all
1970 // the elements in the LyXRCTags enum is handled. As you can see
1971 // there are no breaks at all. So it is just a huge fall-through.
1972 // The nice thing is that we will get a warning from the compiler
1973 // if we forget an element.
1974 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1976 case LyXRC::RC_ACCEPT_COMPOUND:
1977 case LyXRC::RC_ALT_LANG:
1978 case LyXRC::RC_ASCIIROFF_COMMAND:
1979 case LyXRC::RC_ASCII_LINELEN:
1980 case LyXRC::RC_AUTOREGIONDELETE:
1981 case LyXRC::RC_AUTORESET_OPTIONS:
1982 case LyXRC::RC_AUTOSAVE:
1983 case LyXRC::RC_AUTO_NUMBER:
1984 case LyXRC::RC_BACKUPDIR_PATH:
1985 case LyXRC::RC_BIBTEX_COMMAND:
1986 case LyXRC::RC_BINDFILE:
1987 case LyXRC::RC_CHECKLASTFILES:
1988 case LyXRC::RC_USELASTFILEPOS:
1989 case LyXRC::RC_LOADSESSION:
1990 case LyXRC::RC_CHKTEX_COMMAND:
1991 case LyXRC::RC_CONVERTER:
1992 case LyXRC::RC_COPIER:
1993 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1994 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1995 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1996 case LyXRC::RC_CYGWIN_PATH_FIX:
1997 if (lyxrc_orig.cygwin_path_fix != lyxrc_new.cygwin_path_fix) {
1998 namespace os = lyx::support::os;
1999 os::cygwin_path_fix(lyxrc_new.cygwin_path_fix);
2001 case LyXRC::RC_DATE_INSERT_FORMAT:
2002 case LyXRC::RC_DEFAULT_LANGUAGE:
2003 case LyXRC::RC_DEFAULT_PAPERSIZE:
2004 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2005 case LyXRC::RC_DISPLAY_GRAPHICS:
2006 case LyXRC::RC_DOCUMENTPATH:
2007 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2008 if (fs::exists(lyxrc_new.document_path) &&
2009 fs::is_directory(lyxrc_new.document_path)) {
2010 using lyx::support::package;
2011 package().document_dir() = lyxrc.document_path;
2014 case LyXRC::RC_ESC_CHARS:
2015 case LyXRC::RC_FONT_ENCODING:
2016 case LyXRC::RC_FORMAT:
2017 case LyXRC::RC_INDEX_COMMAND:
2018 case LyXRC::RC_INPUT:
2019 case LyXRC::RC_KBMAP:
2020 case LyXRC::RC_KBMAP_PRIMARY:
2021 case LyXRC::RC_KBMAP_SECONDARY:
2022 case LyXRC::RC_LABEL_INIT_LENGTH:
2023 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2024 case LyXRC::RC_LANGUAGE_AUTO_END:
2025 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2026 case LyXRC::RC_LANGUAGE_COMMAND_END:
2027 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2028 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2029 case LyXRC::RC_LANGUAGE_PACKAGE:
2030 case LyXRC::RC_LANGUAGE_USE_BABEL:
2031 case LyXRC::RC_MAKE_BACKUP:
2032 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2033 case LyXRC::RC_NUMLASTFILES:
2034 case LyXRC::RC_PATH_PREFIX:
2035 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2036 using lyx::support::prependEnvPath;
2037 prependEnvPath("PATH", lyxrc.path_prefix);
2039 case LyXRC::RC_PERS_DICT:
2040 case LyXRC::RC_POPUP_BOLD_FONT:
2041 case LyXRC::RC_POPUP_FONT_ENCODING:
2042 case LyXRC::RC_POPUP_NORMAL_FONT:
2043 case LyXRC::RC_PREVIEW:
2044 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2045 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2046 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2047 case LyXRC::RC_PRINTCOPIESFLAG:
2048 case LyXRC::RC_PRINTER:
2049 case LyXRC::RC_PRINTEVENPAGEFLAG:
2050 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2051 case LyXRC::RC_PRINTFILEEXTENSION:
2052 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2053 case LyXRC::RC_PRINTODDPAGEFLAG:
2054 case LyXRC::RC_PRINTPAGERANGEFLAG:
2055 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2056 case LyXRC::RC_PRINTPAPERFLAG:
2057 case LyXRC::RC_PRINTREVERSEFLAG:
2058 case LyXRC::RC_PRINTSPOOL_COMMAND:
2059 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2060 case LyXRC::RC_PRINTTOFILE:
2061 case LyXRC::RC_PRINTTOPRINTER:
2062 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2063 case LyXRC::RC_PRINT_COMMAND:
2064 case LyXRC::RC_RTL_SUPPORT:
2065 case LyXRC::RC_SCREEN_DPI:
2066 case LyXRC::RC_SCREEN_FONT_ENCODING:
2067 case LyXRC::RC_SCREEN_FONT_ROMAN:
2068 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2069 case LyXRC::RC_SCREEN_FONT_SANS:
2070 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2071 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2072 case LyXRC::RC_SCREEN_FONT_SIZES:
2073 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2074 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2075 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2076 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2077 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2078 case LyXRC::RC_SCREEN_ZOOM:
2079 case LyXRC::RC_SERVERPIPE:
2080 case LyXRC::RC_SET_COLOR:
2081 case LyXRC::RC_SHOW_BANNER:
2082 case LyXRC::RC_SPELL_COMMAND:
2083 case LyXRC::RC_TEMPDIRPATH:
2084 case LyXRC::RC_TEMPLATEPATH:
2085 case LyXRC::RC_TEX_ALLOWS_SPACES:
2086 case LyXRC::RC_UIFILE:
2087 case LyXRC::RC_USER_EMAIL:
2088 case LyXRC::RC_USER_NAME:
2089 case LyXRC::RC_USETEMPDIR:
2090 case LyXRC::RC_USE_ALT_LANG:
2091 case LyXRC::RC_USE_ESC_CHARS:
2092 case LyXRC::RC_USE_INP_ENC:
2093 case LyXRC::RC_USE_PERS_DICT:
2094 case LyXRC::RC_USE_SPELL_LIB:
2095 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2096 case LyXRC::RC_VIEWER:
2097 case LyXRC::RC_WHEEL_JUMP:
2098 case LyXRC::RC_LAST: