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();
728 FuncStatus const flag = getStatus(cmd);
729 if (!flag.enabled()) {
730 // We cannot use this function here
731 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
732 << lyxaction.getActionName(action)
733 << " [" << action << "] is disabled at this location"
735 setErrorMessage(flag.message());
738 if (view()->available())
739 view()->hideCursor();
743 case LFUN_WORDFINDFORWARD:
744 case LFUN_WORDFINDBACKWARD: {
745 static string last_search;
746 string searched_string;
748 if (!argument.empty()) {
749 last_search = argument;
750 searched_string = argument;
752 searched_string = last_search;
755 if (searched_string.empty())
758 bool const fw = action == LFUN_WORDFINDFORWARD;
760 lyx::find::find2string(searched_string, true, false, fw);
761 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
766 owner->message(keyseq.printOptions());
769 case LFUN_EXEC_COMMAND:
770 owner->getToolbars().display("minibuffer", true);
771 owner->focus_command_buffer();
776 meta_fake_bit = key_modifier::none;
777 if (view()->available())
778 // cancel any selection
779 dispatch(FuncRequest(LFUN_MARK_OFF));
780 setMessage(N_("Cancel"));
784 meta_fake_bit = key_modifier::alt;
785 setMessage(keyseq.print());
788 case LFUN_READ_ONLY_TOGGLE:
789 if (owner->buffer()->lyxvc().inUse())
790 owner->buffer()->lyxvc().toggleReadOnly();
792 owner->buffer()->setReadonly(
793 !owner->buffer()->isReadonly());
796 // --- Menus -----------------------------------------------
798 menuNew(argument, false);
801 case LFUN_MENUNEWTMPLT:
802 menuNew(argument, true);
805 case LFUN_CLOSEBUFFER:
810 if (!owner->buffer()->isUnnamed()) {
811 string const str = bformat(_("Saving document %1$s..."),
812 makeDisplayPath(owner->buffer()->fileName()));
814 menuWrite(owner->buffer());
815 owner->message(str + _(" done."));
817 writeAs(owner->buffer());
821 writeAs(owner->buffer(), argument);
824 case LFUN_MENURELOAD: {
825 string const file = makeDisplayPath(view()->buffer()->fileName(), 20);
826 string text = bformat(_("Any changes will be lost. Are you sure "
827 "you want to revert to the saved version of the document %1$s?"), file);
828 int const ret = Alert::prompt(_("Revert to saved document?"),
829 text, 0, 1, _("&Revert"), _("&Cancel"));
837 Exporter::Export(owner->buffer(), argument, true);
838 view()->showErrorList(bufferFormat(*owner->buffer()));
842 Exporter::preview(owner->buffer(), argument);
843 view()->showErrorList(bufferFormat(*owner->buffer()));
847 Exporter::Export(owner->buffer(), "program", true);
848 view()->showErrorList(_("Build"));
852 owner->buffer()->runChktex();
853 view()->showErrorList(_("ChkTeX"));
857 if (argument == "custom")
858 owner->getDialogs().show("sendto");
860 Exporter::Export(owner->buffer(), argument, false);
861 view()->showErrorList(bufferFormat(*owner->buffer()));
865 case LFUN_EXPORT_CUSTOM: {
867 string command = split(argument, format_name, ' ');
868 Format const * format = formats.getFormat(format_name);
870 lyxerr << "Format \"" << format_name
871 << "\" not recognized!"
876 Buffer * buffer = owner->buffer();
878 // The name of the file created by the conversion process
881 // Output to filename
882 if (format->name() == "lyx") {
883 string const latexname =
884 buffer->getLatexName(false);
885 filename = changeExtension(latexname,
886 format->extension());
887 filename = addName(buffer->temppath(), filename);
889 if (!buffer->writeFile(filename))
893 Exporter::Export(buffer, format_name, true,
897 // Substitute $$FName for filename
898 if (!contains(command, "$$FName"))
899 command = "( " + command + " ) < $$FName";
900 command = subst(command, "$$FName", filename);
902 // Execute the command in the background
904 call.startscript(Systemcall::DontWait, command);
911 string command = split(split(argument, target, ' '),
915 || target_name.empty()
916 || command.empty()) {
917 lyxerr << "Unable to parse \""
918 << argument << '"' << std::endl;
921 if (target != "printer" && target != "file") {
922 lyxerr << "Unrecognized target \""
923 << target << '"' << std::endl;
927 Buffer * buffer = owner->buffer();
929 if (!Exporter::Export(buffer, "dvi", true)) {
930 showPrintError(buffer->fileName());
934 // Push directory path.
935 string const path = buffer->temppath();
938 // there are three cases here:
939 // 1. we print to a file
940 // 2. we print directly to a printer
941 // 3. we print using a spool command (print to file first)
944 string const dviname =
945 changeExtension(buffer->getLatexName(true),
948 if (target == "printer") {
949 if (!lyxrc.print_spool_command.empty()) {
950 // case 3: print using a spool
951 string const psname =
952 changeExtension(dviname,".ps");
953 command += lyxrc.print_to_file
956 + quoteName(dviname);
959 lyxrc.print_spool_command +' ';
960 if (target_name != "default") {
961 command2 += lyxrc.print_spool_printerprefix
965 command2 += quoteName(psname);
967 // If successful, then spool command
968 res = one.startscript(
973 res = one.startscript(
974 Systemcall::DontWait,
977 // case 2: print directly to a printer
978 res = one.startscript(
979 Systemcall::DontWait,
980 command + quoteName(dviname));
984 // case 1: print to a file
985 command += lyxrc.print_to_file
986 + quoteName(makeAbsPath(target_name,
989 + quoteName(dviname);
990 res = one.startscript(Systemcall::DontWait,
995 showPrintError(buffer->fileName());
1004 if (view()->available()) {
1005 // save cursor Position for opened files to .lyx/session
1006 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1007 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1008 // save opened file name to .lyx/session
1009 LyX::ref().session().setLastOpenedFiles( bufferlist.getFileNames());
1010 // save bookmarks to .lyx/session
1011 view()->saveSavedPositions();
1013 quitLyX(argument == "force");
1016 case LFUN_TOCVIEW: {
1017 InsetCommandParams p("tableofcontents");
1018 string const data = InsetCommandMailer::params2string("toc", p);
1019 owner->getDialogs().show("toc", data, 0);
1027 case LFUN_RECONFIGURE:
1028 reconfigure(view());
1031 case LFUN_HELP_OPEN: {
1032 string const arg = argument;
1034 setErrorMessage(N_("Missing argument"));
1037 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1038 if (fname.empty()) {
1039 lyxerr << "LyX: unable to find documentation file `"
1040 << arg << "'. Bad installation?" << endl;
1043 owner->message(bformat(_("Opening help file %1$s..."),
1044 makeDisplayPath(fname)));
1045 view()->loadLyXFile(fname, false);
1049 // --- version control -------------------------------
1050 case LFUN_VC_REGISTER:
1051 if (!ensureBufferClean(view()))
1053 if (!owner->buffer()->lyxvc().inUse()) {
1054 owner->buffer()->lyxvc().registrer();
1059 case LFUN_VC_CHECKIN:
1060 if (!ensureBufferClean(view()))
1062 if (owner->buffer()->lyxvc().inUse()
1063 && !owner->buffer()->isReadonly()) {
1064 owner->buffer()->lyxvc().checkIn();
1069 case LFUN_VC_CHECKOUT:
1070 if (!ensureBufferClean(view()))
1072 if (owner->buffer()->lyxvc().inUse()
1073 && owner->buffer()->isReadonly()) {
1074 owner->buffer()->lyxvc().checkOut();
1079 case LFUN_VC_REVERT:
1080 owner->buffer()->lyxvc().revert();
1085 owner->buffer()->lyxvc().undoLast();
1089 // --- buffers ----------------------------------------
1090 case LFUN_SWITCHBUFFER:
1091 view()->setBuffer(bufferlist.getBuffer(argument));
1094 case LFUN_NEXTBUFFER:
1095 view()->setBuffer(bufferlist.next(view()->buffer()));
1098 case LFUN_PREVIOUSBUFFER:
1099 view()->setBuffer(bufferlist.previous(view()->buffer()));
1103 newFile(view(), argument);
1106 case LFUN_FILE_OPEN:
1110 case LFUN_DROP_LAYOUTS_CHOICE:
1111 owner->getToolbars().openLayoutList();
1114 case LFUN_MENU_OPEN_BY_NAME:
1115 owner->getMenubar().openByName(argument);
1118 // --- lyxserver commands ----------------------------
1120 setMessage(owner->buffer()->fileName());
1121 lyxerr[Debug::INFO] << "FNAME["
1122 << owner->buffer()->fileName()
1127 dispatch_buffer = keyseq.print();
1128 lyxserver->notifyClient(dispatch_buffer);
1131 case LFUN_GOTOFILEROW: {
1134 istringstream is(argument);
1135 is >> file_name >> row;
1136 if (prefixIs(file_name, package().temp_dir())) {
1137 // Needed by inverse dvi search. If it is a file
1138 // in tmpdir, call the apropriated function
1139 view()->setBuffer(bufferlist.getBufferFromTmp(file_name));
1141 // Must replace extension of the file to be .lyx
1142 // and get full path
1143 string const s = changeExtension(file_name, ".lyx");
1144 // Either change buffer or load the file
1145 if (bufferlist.exists(s)) {
1146 view()->setBuffer(bufferlist.getBuffer(s));
1148 view()->loadLyXFile(s);
1152 view()->setCursorFromRow(row);
1155 // see BufferView_pimpl::center()
1156 view()->updateScrollbar();
1160 case LFUN_DIALOG_SHOW: {
1161 string const name = cmd.getArg(0);
1162 string data = trim(cmd.argument.substr(name.size()));
1164 if (name == "character") {
1165 data = freefont2string();
1167 owner->getDialogs().show("character", data);
1170 else if (name == "latexlog") {
1171 pair<Buffer::LogType, string> const logfile =
1172 owner->buffer()->getLogName();
1173 switch (logfile.first) {
1174 case Buffer::latexlog:
1177 case Buffer::buildlog:
1181 data += logfile.second;
1182 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 else if (name == "view-source") {
1190 // get the *top* level paragraphs that contain the cursor,
1191 // or the selected text
1192 lyx::pit_type par_begin;
1193 lyx::pit_type par_end;
1194 if (!view()->cursor().selection()) {
1195 par_begin = view()->cursor().bottom().pit();
1196 par_end = par_begin;
1198 par_begin = view()->cursor().selectionBegin().bottom().pit();
1199 par_end = view()->cursor().selectionEnd().bottom().pit();
1201 if (par_begin > par_end)
1202 std::swap(par_begin, par_end);
1204 view()->buffer()->getSourceCode(ostr, par_begin, par_end + 1);
1205 // display the dialog and show source code
1206 owner->getDialogs().show("view-source", ostr.str());
1209 owner->getDialogs().show(name, data);
1213 case LFUN_DIALOG_SHOW_NEW_INSET: {
1214 string const name = cmd.getArg(0);
1215 string data = trim(cmd.argument.substr(name.size()));
1216 if (name == "bibitem" ||
1223 InsetCommandParams p(name);
1224 data = InsetCommandMailer::params2string(name, p);
1225 } else if (name == "include") {
1226 InsetCommandParams p(data);
1227 data = InsetIncludeMailer::params2string(p);
1228 } else if (name == "box") {
1229 // \c data == "Boxed" || "Frameless" etc
1230 InsetBoxParams p(data);
1231 data = InsetBoxMailer::params2string(p);
1232 } else if (name == "branch") {
1233 InsetBranchParams p;
1234 data = InsetBranchMailer::params2string(p);
1235 } else if (name == "citation") {
1236 InsetCommandParams p("cite");
1237 data = InsetCommandMailer::params2string(name, p);
1238 } else if (name == "ert") {
1239 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1240 } else if (name == "external") {
1241 InsetExternalParams p;
1242 Buffer const & buffer = *owner->buffer();
1243 data = InsetExternalMailer::params2string(p, buffer);
1244 } else if (name == "float") {
1246 data = InsetFloatMailer::params2string(p);
1247 } else if (name == "graphics") {
1248 InsetGraphicsParams p;
1249 Buffer const & buffer = *owner->buffer();
1250 data = InsetGraphicsMailer::params2string(p, buffer);
1251 } else if (name == "note") {
1253 data = InsetNoteMailer::params2string(p);
1254 } else if (name == "vspace") {
1256 data = InsetVSpaceMailer::params2string(space);
1257 } else if (name == "wrap") {
1259 data = InsetWrapMailer::params2string(p);
1261 owner->getDialogs().show(name, data, 0);
1265 case LFUN_DIALOG_SHOW_NEXT_INSET:
1268 case LFUN_DIALOG_UPDATE: {
1269 string const & name = argument;
1270 // Can only update a dialog connected to an existing inset
1271 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1273 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument);
1274 inset->dispatch(view()->cursor(), fr);
1275 } else if (name == "paragraph") {
1276 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1277 } else if (name == "prefs") {
1278 owner->getDialogs().update(name, string());
1283 case LFUN_DIALOG_HIDE:
1284 Dialogs::hide(argument, 0);
1287 case LFUN_DIALOG_DISCONNECT_INSET:
1288 owner->getDialogs().disconnect(argument);
1292 case LFUN_INSERT_CITATION: {
1293 if (!argument.empty()) {
1294 // we can have one optional argument, delimited by '|'
1295 // citation-insert <key>|<text_before>
1296 // this should be enhanced to also support text_after
1297 // and citation style
1298 string arg = argument;
1300 if (contains(argument, "|")) {
1301 arg = token(argument, '|', 0);
1302 opt1 = '[' + token(argument, '|', 1) + ']';
1304 std::ostringstream os;
1305 os << "citation LatexCommand\n"
1306 << "\\cite" << opt1 << "{" << arg << "}\n"
1308 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1311 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1315 case LFUN_CHILDOPEN: {
1316 string const filename =
1317 makeAbsPath(argument, owner->buffer()->filePath());
1318 setMessage(N_("Opening child document ") +
1319 makeDisplayPath(filename) + "...");
1320 view()->savePosition(0);
1321 string const parentfilename = owner->buffer()->fileName();
1322 if (bufferlist.exists(filename))
1323 view()->setBuffer(bufferlist.getBuffer(filename));
1325 view()->loadLyXFile(filename);
1326 // Set the parent name of the child document.
1327 // This makes insertion of citations and references in the child work,
1328 // when the target is in the parent or another child document.
1329 owner->buffer()->setParentName(parentfilename);
1333 case LFUN_TOGGLECURSORFOLLOW:
1334 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1338 owner->getIntl().keyMapOn(false);
1341 case LFUN_KMAP_PRIM:
1342 owner->getIntl().keyMapPrim();
1346 owner->getIntl().keyMapSec();
1349 case LFUN_KMAP_TOGGLE:
1350 owner->getIntl().toggleKeyMap();
1356 string rest = split(argument, countstr, ' ');
1357 istringstream is(countstr);
1360 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1361 for (int i = 0; i < count; ++i)
1362 dispatch(lyxaction.lookupFunc(rest));
1366 case LFUN_SEQUENCE: {
1367 // argument contains ';'-terminated commands
1368 string arg = argument;
1369 while (!arg.empty()) {
1371 arg = split(arg, first, ';');
1372 FuncRequest func(lyxaction.lookupFunc(first));
1373 func.origin = cmd.origin;
1379 case LFUN_SAVEPREFERENCES: {
1380 Path p(package().user_support());
1381 lyxrc.write("preferences", false);
1385 case LFUN_SCREEN_FONT_UPDATE:
1386 // handle the screen font changes.
1387 lyxrc.set_font_norm_type();
1388 lyx_gui::update_fonts();
1389 // All visible buffers will need resize
1393 case LFUN_SET_COLOR: {
1395 string const x11_name = split(argument, lyx_name, ' ');
1396 if (lyx_name.empty() || x11_name.empty()) {
1397 setErrorMessage(N_("Syntax: set-color <lyx_name>"
1402 bool const graphicsbg_changed =
1403 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1404 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1406 if (!lcolor.setColor(lyx_name, x11_name)) {
1408 bformat(_("Set-color \"%1$s\" failed "
1409 "- color is undefined or "
1410 "may not be redefined"), lyx_name));
1414 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1416 if (graphicsbg_changed) {
1417 #ifdef WITH_WARNINGS
1418 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1421 lyx::graphics::GCache::get().changeDisplay(true);
1428 owner->message(argument);
1431 case LFUN_TOOLTIPS_TOGGLE:
1432 owner->getDialogs().toggleTooltips();
1435 case LFUN_EXTERNAL_EDIT: {
1436 FuncRequest fr(action, argument);
1437 InsetExternal().dispatch(view()->cursor(), fr);
1441 case LFUN_GRAPHICS_EDIT: {
1442 FuncRequest fr(action, argument);
1443 InsetGraphics().dispatch(view()->cursor(), fr);
1447 case LFUN_INSET_APPLY: {
1448 string const name = cmd.getArg(0);
1449 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1451 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1452 inset->dispatch(view()->cursor(), fr);
1454 FuncRequest fr(LFUN_INSET_INSERT, argument);
1457 // ideally, the update flag should be set by the insets,
1458 // but this is not possible currently
1463 case LFUN_ALL_INSETS_TOGGLE: {
1465 string const name = split(argument, action, ' ');
1466 InsetBase::Code const inset_code =
1467 InsetBase::translate(name);
1469 LCursor & cur = view()->cursor();
1470 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1472 InsetBase & inset = owner->buffer()->inset();
1473 InsetIterator it = inset_iterator_begin(inset);
1474 InsetIterator const end = inset_iterator_end(inset);
1475 for (; it != end; ++it) {
1476 if (inset_code == InsetBase::NO_CODE
1477 || inset_code == it->lyxCode()) {
1478 LCursor tmpcur = cur;
1479 tmpcur.pushLeft(*it);
1480 it->dispatch(tmpcur, fr);
1487 case LFUN_LANGUAGE_BUFFER: {
1488 Buffer & buffer = *owner->buffer();
1489 Language const * oldL = buffer.params().language;
1490 Language const * newL = languages.getLanguage(argument);
1491 if (!newL || oldL == newL)
1494 if (oldL->rightToLeft() == newL->rightToLeft()
1495 && !buffer.isMultiLingual())
1496 buffer.changeLanguage(oldL, newL);
1498 buffer.updateDocLang(newL);
1502 case LFUN_SAVE_AS_DEFAULT: {
1503 string const fname =
1504 addName(addPath(package().user_support(), "templates/"),
1506 Buffer defaults(fname);
1508 istringstream ss(argument);
1511 int const unknown_tokens = defaults.readHeader(lex);
1513 if (unknown_tokens != 0) {
1514 lyxerr << "Warning in LFUN_SAVE_AS_DEFAULT!\n"
1515 << unknown_tokens << " unknown token"
1516 << (unknown_tokens == 1 ? "" : "s")
1520 if (defaults.writeFile(defaults.fileName()))
1521 setMessage(_("Document defaults saved in ")
1522 + makeDisplayPath(fname));
1524 setErrorMessage(_("Unable to save document defaults"));
1528 case LFUN_BUFFERPARAMS_APPLY: {
1529 biblio::CiteEngine const engine =
1530 owner->buffer()->params().cite_engine;
1532 istringstream ss(argument);
1535 int const unknown_tokens =
1536 owner->buffer()->readHeader(lex);
1538 if (unknown_tokens != 0) {
1539 lyxerr << "Warning in LFUN_BUFFERPARAMS_APPLY!\n"
1540 << unknown_tokens << " unknown token"
1541 << (unknown_tokens == 1 ? "" : "s")
1544 if (engine == owner->buffer()->params().cite_engine)
1547 LCursor & cur = view()->cursor();
1548 FuncRequest fr(LFUN_INSET_REFRESH);
1550 InsetBase & inset = owner->buffer()->inset();
1551 InsetIterator it = inset_iterator_begin(inset);
1552 InsetIterator const end = inset_iterator_end(inset);
1553 for (; it != end; ++it)
1554 if (it->lyxCode() == InsetBase::CITE_CODE)
1555 it->dispatch(cur, fr);
1559 case LFUN_TEXTCLASS_APPLY: {
1560 Buffer * buffer = owner->buffer();
1562 lyx::textclass_type const old_class =
1563 buffer->params().textclass;
1565 loadTextclass(argument);
1567 std::pair<bool, lyx::textclass_type> const tc_pair =
1568 textclasslist.numberOfClass(argument);
1573 lyx::textclass_type const new_class = tc_pair.second;
1574 if (old_class == new_class)
1578 owner->message(_("Converting document to new document class..."));
1579 recordUndoFullDocument(view());
1580 buffer->params().textclass = new_class;
1581 StableDocIterator backcur(view()->cursor());
1583 lyx::cap::switchBetweenClasses(
1584 old_class, new_class,
1585 buffer->paragraphs(), el);
1587 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1588 bufferErrors(*buffer, el);
1589 view()->showErrorList(_("Class switch"));
1590 updateCounters(*buffer);
1595 case LFUN_TEXTCLASS_LOAD:
1596 loadTextclass(argument);
1599 case LFUN_LYXRC_APPLY: {
1600 LyXRC const lyxrc_orig = lyxrc;
1602 istringstream ss(argument);
1603 bool const success = lyxrc.read(ss) == 0;
1606 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1607 << "Unable to read lyxrc data"
1612 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1617 view()->cursor().dispatch(cmd);
1618 update |= view()->cursor().result().update();
1619 if (!view()->cursor().result().dispatched())
1620 update |= view()->dispatch(cmd);
1625 if (view()->available()) {
1626 // Redraw screen unless explicitly told otherwise.
1627 // This also initializes the position cache for all insets
1628 // in (at least partially) visible top-level paragraphs.
1630 view()->update(Update::FitCursor | Update::Force);
1632 view()->update(Update::FitCursor);
1634 // if we executed a mutating lfun, mark the buffer as dirty
1636 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1637 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1638 view()->buffer()->markDirty();
1641 if (view()->cursor().inTexted()) {
1642 view()->owner()->updateLayoutChoice();
1645 sendDispatchMessage(_(getMessage()), cmd);
1649 void LyXFunc::sendDispatchMessage(string const & msg, FuncRequest const & cmd)
1651 /* When an action did not originate from the UI/kbd, it makes
1652 * sense to avoid updating the GUI. It turns out that this
1653 * fixes bug 1941, for reasons that are described here:
1654 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1656 if (cmd.origin != FuncRequest::INTERNAL) {
1657 owner->updateMenubar();
1658 owner->updateToolbars();
1661 const bool verbose = (cmd.origin == FuncRequest::UI
1662 || cmd.origin == FuncRequest::COMMANDBUFFER);
1664 if (cmd.action == LFUN_SELFINSERT || !verbose) {
1665 lyxerr[Debug::ACTION] << "dispatch msg is " << msg << endl;
1667 owner->message(msg);
1671 string dispatch_msg = msg;
1672 if (!dispatch_msg.empty())
1673 dispatch_msg += ' ';
1675 string comname = lyxaction.getActionName(cmd.action);
1677 bool argsadded = false;
1679 if (!cmd.argument.empty()) {
1680 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1681 comname += ' ' + cmd.argument;
1686 string const shortcuts = toplevel_keymap->printbindings(cmd);
1688 if (!shortcuts.empty())
1689 comname += ": " + shortcuts;
1690 else if (!argsadded && !cmd.argument.empty())
1691 comname += ' ' + cmd.argument;
1693 if (!comname.empty()) {
1694 comname = rtrim(comname);
1695 dispatch_msg += '(' + rtrim(comname) + ')';
1698 lyxerr[Debug::ACTION] << "verbose dispatch msg " << dispatch_msg << endl;
1699 if (!dispatch_msg.empty())
1700 owner->message(dispatch_msg);
1704 void LyXFunc::setupLocalKeymap()
1706 keyseq.stdmap = toplevel_keymap.get();
1707 keyseq.curmap = toplevel_keymap.get();
1708 cancel_meta_seq.stdmap = toplevel_keymap.get();
1709 cancel_meta_seq.curmap = toplevel_keymap.get();
1713 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1715 string initpath = lyxrc.document_path;
1716 string filename(name);
1718 if (view()->available()) {
1719 string const trypath = owner->buffer()->filePath();
1720 // If directory is writeable, use this as default.
1721 if (isDirWriteable(trypath))
1725 static int newfile_number;
1727 if (filename.empty()) {
1728 filename = addName(lyxrc.document_path,
1729 "newfile" + convert<string>(++newfile_number) + ".lyx");
1730 while (bufferlist.exists(filename) || fs::is_readable(filename)) {
1732 filename = addName(lyxrc.document_path,
1733 "newfile" + convert<string>(newfile_number) +
1738 // The template stuff
1741 FileDialog fileDlg(_("Select template file"),
1742 LFUN_SELECT_FILE_SYNC,
1743 make_pair(string(_("Documents|#o#O")),
1744 string(lyxrc.document_path)),
1745 make_pair(string(_("Templates|#T#t")),
1746 string(lyxrc.template_path)));
1748 FileDialog::Result result =
1749 fileDlg.open(lyxrc.template_path,
1750 FileFilterList(_("LyX Documents (*.lyx)")),
1753 if (result.first == FileDialog::Later)
1755 if (result.second.empty())
1757 templname = result.second;
1760 view()->newFile(filename, templname, !name.empty());
1764 void LyXFunc::open(string const & fname)
1766 string initpath = lyxrc.document_path;
1768 if (view()->available()) {
1769 string const trypath = owner->buffer()->filePath();
1770 // If directory is writeable, use this as default.
1771 if (isDirWriteable(trypath))
1777 if (fname.empty()) {
1778 FileDialog fileDlg(_("Select document to open"),
1780 make_pair(string(_("Documents|#o#O")),
1781 string(lyxrc.document_path)),
1782 make_pair(string(_("Examples|#E#e")),
1783 string(addPath(package().system_support(), "examples"))));
1785 FileDialog::Result result =
1786 fileDlg.open(initpath,
1787 FileFilterList(_("LyX Documents (*.lyx)")),
1790 if (result.first == FileDialog::Later)
1793 filename = result.second;
1795 // check selected filename
1796 if (filename.empty()) {
1797 owner->message(_("Canceled."));
1803 // get absolute path of file and add ".lyx" to the filename if
1805 string const fullpath = fileSearch(string(), filename, "lyx");
1806 if (!fullpath.empty()) {
1807 filename = fullpath;
1810 string const disp_fn(makeDisplayPath(filename));
1812 // if the file doesn't exist, let the user create one
1813 if (!fs::exists(filename)) {
1814 // the user specifically chose this name. Believe them.
1815 view()->newFile(filename, "", true);
1819 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1822 if (view()->loadLyXFile(filename)) {
1823 str2 = bformat(_("Document %1$s opened."), disp_fn);
1825 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1827 owner->message(str2);
1831 void LyXFunc::doImport(string const & argument)
1834 string filename = split(argument, format, ' ');
1836 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1837 << " file: " << filename << endl;
1839 // need user interaction
1840 if (filename.empty()) {
1841 string initpath = lyxrc.document_path;
1843 if (view()->available()) {
1844 string const trypath = owner->buffer()->filePath();
1845 // If directory is writeable, use this as default.
1846 if (isDirWriteable(trypath))
1850 string const text = bformat(_("Select %1$s file to import"),
1851 formats.prettyName(format));
1853 FileDialog fileDlg(text,
1855 make_pair(string(_("Documents|#o#O")),
1856 string(lyxrc.document_path)),
1857 make_pair(string(_("Examples|#E#e")),
1858 string(addPath(package().system_support(), "examples"))));
1860 string const filter = formats.prettyName(format)
1861 + " (*." + formats.extension(format) + ')';
1863 FileDialog::Result result =
1864 fileDlg.open(initpath,
1865 FileFilterList(filter),
1868 if (result.first == FileDialog::Later)
1871 filename = result.second;
1873 // check selected filename
1874 if (filename.empty())
1875 owner->message(_("Canceled."));
1878 if (filename.empty())
1881 // get absolute path of file
1882 filename = makeAbsPath(filename);
1884 string const lyxfile = changeExtension(filename, ".lyx");
1886 // Check if the document already is open
1887 if (lyx_gui::use_gui && bufferlist.exists(lyxfile)) {
1888 if (!bufferlist.close(bufferlist.getBuffer(lyxfile), true)) {
1889 owner->message(_("Canceled."));
1894 // if the file exists already, and we didn't do
1895 // -i lyx thefile.lyx, warn
1896 if (fs::exists(lyxfile) && filename != lyxfile) {
1897 string const file = makeDisplayPath(lyxfile, 30);
1899 string text = bformat(_("The document %1$s already exists.\n\n"
1900 "Do you want to over-write that document?"), file);
1901 int const ret = Alert::prompt(_("Over-write document?"),
1902 text, 0, 1, _("&Over-write"), _("&Cancel"));
1905 owner->message(_("Canceled."));
1910 Importer::Import(owner, filename, format);
1914 void LyXFunc::closeBuffer()
1916 // save current cursor position
1917 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1918 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1919 if (bufferlist.close(owner->buffer(), true) && !quitting) {
1920 if (bufferlist.empty()) {
1921 // need this otherwise SEGV may occur while
1922 // trying to set variables that don't exist
1923 // since there's no current buffer
1924 owner->getDialogs().hideBufferDependent();
1926 view()->setBuffer(bufferlist.first());
1932 // Each "owner" should have it's own message method. lyxview and
1933 // the minibuffer would use the minibuffer, but lyxserver would
1934 // send an ERROR signal to its client. Alejandro 970603
1935 // This function is bit problematic when it comes to NLS, to make the
1936 // lyx servers client be language indepenent we must not translate
1937 // strings sent to this func.
1938 void LyXFunc::setErrorMessage(string const & m) const
1940 dispatch_buffer = m;
1945 void LyXFunc::setMessage(string const & m) const
1947 dispatch_buffer = m;
1951 string const LyXFunc::viewStatusMessage()
1953 // When meta-fake key is pressed, show the key sequence so far + "M-".
1955 return keyseq.print() + "M-";
1957 // Else, when a non-complete key sequence is pressed,
1958 // show the available options.
1959 if (keyseq.length() > 0 && !keyseq.deleted())
1960 return keyseq.printOptions();
1962 if (!view()->available())
1963 return _("Welcome to LyX!");
1965 return view()->cursor().currentState();
1969 BufferView * LyXFunc::view() const
1971 BOOST_ASSERT(owner);
1972 return owner->view().get();
1976 bool LyXFunc::wasMetaKey() const
1978 return (meta_fake_bit != key_modifier::none);
1984 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1986 // Why the switch you might ask. It is a trick to ensure that all
1987 // the elements in the LyXRCTags enum is handled. As you can see
1988 // there are no breaks at all. So it is just a huge fall-through.
1989 // The nice thing is that we will get a warning from the compiler
1990 // if we forget an element.
1991 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1993 case LyXRC::RC_ACCEPT_COMPOUND:
1994 case LyXRC::RC_ALT_LANG:
1995 case LyXRC::RC_ASCIIROFF_COMMAND:
1996 case LyXRC::RC_ASCII_LINELEN:
1997 case LyXRC::RC_AUTOREGIONDELETE:
1998 case LyXRC::RC_AUTORESET_OPTIONS:
1999 case LyXRC::RC_AUTOSAVE:
2000 case LyXRC::RC_AUTO_NUMBER:
2001 case LyXRC::RC_BACKUPDIR_PATH:
2002 case LyXRC::RC_BIBTEX_COMMAND:
2003 case LyXRC::RC_BINDFILE:
2004 case LyXRC::RC_CHECKLASTFILES:
2005 case LyXRC::RC_USELASTFILEPOS:
2006 case LyXRC::RC_LOADSESSION:
2007 case LyXRC::RC_CHKTEX_COMMAND:
2008 case LyXRC::RC_CONVERTER:
2009 case LyXRC::RC_COPIER:
2010 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2011 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2012 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2013 case LyXRC::RC_CYGWIN_PATH_FIX:
2014 if (lyxrc_orig.cygwin_path_fix != lyxrc_new.cygwin_path_fix) {
2015 namespace os = lyx::support::os;
2016 os::cygwin_path_fix(lyxrc_new.cygwin_path_fix);
2018 case LyXRC::RC_DATE_INSERT_FORMAT:
2019 case LyXRC::RC_DEFAULT_LANGUAGE:
2020 case LyXRC::RC_DEFAULT_PAPERSIZE:
2021 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2022 case LyXRC::RC_DISPLAY_GRAPHICS:
2023 case LyXRC::RC_DOCUMENTPATH:
2024 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2025 if (fs::exists(lyxrc_new.document_path) &&
2026 fs::is_directory(lyxrc_new.document_path)) {
2027 using lyx::support::package;
2028 package().document_dir() = lyxrc.document_path;
2031 case LyXRC::RC_ESC_CHARS:
2032 case LyXRC::RC_FONT_ENCODING:
2033 case LyXRC::RC_FORMAT:
2034 case LyXRC::RC_INDEX_COMMAND:
2035 case LyXRC::RC_INPUT:
2036 case LyXRC::RC_KBMAP:
2037 case LyXRC::RC_KBMAP_PRIMARY:
2038 case LyXRC::RC_KBMAP_SECONDARY:
2039 case LyXRC::RC_LABEL_INIT_LENGTH:
2040 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2041 case LyXRC::RC_LANGUAGE_AUTO_END:
2042 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2043 case LyXRC::RC_LANGUAGE_COMMAND_END:
2044 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2045 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2046 case LyXRC::RC_LANGUAGE_PACKAGE:
2047 case LyXRC::RC_LANGUAGE_USE_BABEL:
2048 case LyXRC::RC_MAKE_BACKUP:
2049 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2050 case LyXRC::RC_NUMLASTFILES:
2051 case LyXRC::RC_PATH_PREFIX:
2052 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2053 using lyx::support::prependEnvPath;
2054 prependEnvPath("PATH", lyxrc.path_prefix);
2056 case LyXRC::RC_PERS_DICT:
2057 case LyXRC::RC_POPUP_BOLD_FONT:
2058 case LyXRC::RC_POPUP_FONT_ENCODING:
2059 case LyXRC::RC_POPUP_NORMAL_FONT:
2060 case LyXRC::RC_PREVIEW:
2061 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2062 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2063 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2064 case LyXRC::RC_PRINTCOPIESFLAG:
2065 case LyXRC::RC_PRINTER:
2066 case LyXRC::RC_PRINTEVENPAGEFLAG:
2067 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2068 case LyXRC::RC_PRINTFILEEXTENSION:
2069 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2070 case LyXRC::RC_PRINTODDPAGEFLAG:
2071 case LyXRC::RC_PRINTPAGERANGEFLAG:
2072 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2073 case LyXRC::RC_PRINTPAPERFLAG:
2074 case LyXRC::RC_PRINTREVERSEFLAG:
2075 case LyXRC::RC_PRINTSPOOL_COMMAND:
2076 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2077 case LyXRC::RC_PRINTTOFILE:
2078 case LyXRC::RC_PRINTTOPRINTER:
2079 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2080 case LyXRC::RC_PRINT_COMMAND:
2081 case LyXRC::RC_RTL_SUPPORT:
2082 case LyXRC::RC_SCREEN_DPI:
2083 case LyXRC::RC_SCREEN_FONT_ENCODING:
2084 case LyXRC::RC_SCREEN_FONT_ROMAN:
2085 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2086 case LyXRC::RC_SCREEN_FONT_SANS:
2087 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2088 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2089 case LyXRC::RC_SCREEN_FONT_SIZES:
2090 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2091 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2092 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2093 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2094 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2095 case LyXRC::RC_SCREEN_ZOOM:
2096 case LyXRC::RC_SERVERPIPE:
2097 case LyXRC::RC_SET_COLOR:
2098 case LyXRC::RC_SHOW_BANNER:
2099 case LyXRC::RC_SPELL_COMMAND:
2100 case LyXRC::RC_TEMPDIRPATH:
2101 case LyXRC::RC_TEMPLATEPATH:
2102 case LyXRC::RC_TEX_ALLOWS_SPACES:
2103 case LyXRC::RC_UIFILE:
2104 case LyXRC::RC_USER_EMAIL:
2105 case LyXRC::RC_USER_NAME:
2106 case LyXRC::RC_USETEMPDIR:
2107 case LyXRC::RC_USE_ALT_LANG:
2108 case LyXRC::RC_USE_ESC_CHARS:
2109 case LyXRC::RC_USE_INP_ENC:
2110 case LyXRC::RC_USE_PERS_DICT:
2111 case LyXRC::RC_USE_SPELL_LIB:
2112 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2113 case LyXRC::RC_VIEWER:
2114 case LyXRC::RC_WHEEL_JUMP:
2115 case LyXRC::RC_LAST: