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/Application.h"
76 #include "frontends/Alert.h"
77 #include "frontends/Dialogs.h"
78 #include "frontends/FileDialog.h"
79 #include "frontends/FontLoader.h"
80 #include "frontends/lyx_gui.h"
81 #include "frontends/LyXKeySym.h"
82 #include "frontends/LyXView.h"
83 #include "frontends/Menubar.h"
84 #include "frontends/Toolbars.h"
86 #include "support/environment.h"
87 #include "support/filefilterlist.h"
88 #include "support/filetools.h"
89 #include "support/forkedcontr.h"
90 #include "support/fs_extras.h"
91 #include "support/lstrings.h"
92 #include "support/path.h"
93 #include "support/package.h"
94 #include "support/systemcall.h"
95 #include "support/convert.h"
96 #include "support/os.h"
98 #include <boost/current_function.hpp>
99 #include <boost/filesystem/operations.hpp>
103 using bv_funcs::freefont2string;
105 using lyx::docstring;
107 using lyx::support::absolutePath;
108 using lyx::support::addName;
109 using lyx::support::addPath;
110 using lyx::support::bformat;
111 using lyx::support::changeExtension;
112 using lyx::support::contains;
113 using lyx::support::FileFilterList;
114 using lyx::support::fileSearch;
115 using lyx::support::ForkedcallsController;
116 using lyx::support::i18nLibFileSearch;
117 using lyx::support::isDirWriteable;
118 using lyx::support::isFileReadable;
119 using lyx::support::isStrInt;
120 using lyx::support::makeAbsPath;
121 using lyx::support::makeDisplayPath;
122 using lyx::support::package;
123 using lyx::support::quoteName;
124 using lyx::support::rtrim;
125 using lyx::support::split;
126 using lyx::support::subst;
127 using lyx::support::Systemcall;
128 using lyx::support::token;
129 using lyx::support::trim;
130 using lyx::support::prefixIs;
133 using std::make_pair;
136 using std::istringstream;
137 using std::ostringstream;
139 namespace biblio = lyx::biblio;
140 namespace fs = boost::filesystem;
143 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
146 extern tex_accent_struct get_accent(kb_action action);
151 bool getStatus(LCursor cursor,
152 FuncRequest const & cmd, FuncStatus & status)
154 // Try to fix cursor in case it is broken.
155 cursor.fixIfBroken();
157 // This is, of course, a mess. Better create a new doc iterator and use
158 // this in Inset::getStatus. This might require an additional
159 // BufferView * arg, though (which should be avoided)
160 //LCursor safe = *this;
162 for ( ; cursor.depth(); cursor.pop()) {
163 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
164 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
165 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
166 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
168 // The inset's getStatus() will return 'true' if it made
169 // a definitive decision on whether it want to handle the
170 // request or not. The result of this decision is put into
171 // the 'status' parameter.
172 if (cursor.inset().getStatus(cursor, cmd, status)) {
181 /** Return the change status at cursor position, taking in account the
182 * status at each level of the document iterator (a table in a deleted
183 * footnote is deleted).
184 * When \param outer is true, the top slice is not looked at.
186 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
188 size_t const depth = dit.depth() - (outer ? 1 : 0);
190 for (size_t i = 0 ; i < depth ; ++i) {
191 CursorSlice const & slice = dit[i];
192 if (!slice.inset().inMathed()
193 && slice.pos() < slice.paragraph().size()) {
194 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
195 if (ch != Change::UNCHANGED)
199 return Change::UNCHANGED;
204 LyXFunc::LyXFunc(LyXView * lv)
207 keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
208 cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
209 meta_fake_bit(key_modifier::none)
214 void LyXFunc::handleKeyFunc(kb_action action)
216 char c = encoded_last_key;
218 if (keyseq.length()) {
222 owner->view()->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() : "");
252 size_t encoded_last_key = keysym->getUCSEncoded();
254 // Do a one-deep top-level lookup for
255 // cancel and meta-fake keys. RVDK_PATCH_5
256 cancel_meta_seq.reset();
258 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
259 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
260 << " action first set to [" << func.action << ']'
263 // When not cancel or meta-fake, do the normal lookup.
264 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
265 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
266 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
267 // remove Caps Lock and Mod2 as a modifiers
268 func = keyseq.addkey(keysym, (state | meta_fake_bit));
269 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
270 << "action now set to ["
271 << func.action << ']' << endl;
274 // Dont remove this unless you know what you are doing.
275 meta_fake_bit = key_modifier::none;
277 // Can this happen now ?
278 if (func.action == LFUN_NOACTION) {
279 func = FuncRequest(LFUN_COMMAND_PREFIX);
282 if (lyxerr.debugging(Debug::KEY)) {
283 lyxerr << BOOST_CURRENT_FUNCTION
285 << func.action << "]["
286 << keyseq.print() << ']'
290 // already here we know if it any point in going further
291 // why not return already here if action == -1 and
292 // num_bytes == 0? (Lgb)
294 if (keyseq.length() > 1) {
295 owner->message(lyx::from_utf8(keyseq.print()));
299 // Maybe user can only reach the key via holding down shift.
300 // Let's see. But only if shift is the only modifier
301 if (func.action == LFUN_UNKNOWN_ACTION &&
302 state == key_modifier::shift) {
303 lyxerr[Debug::KEY] << "Trying without shift" << endl;
304 func = keyseq.addkey(keysym, key_modifier::none);
305 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
308 if (func.action == LFUN_UNKNOWN_ACTION) {
309 // Hmm, we didn't match any of the keysequences. See
310 // if it's normal insertable text not already covered
312 if (keysym->isText() && keyseq.length() == 1) {
313 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
314 func = FuncRequest(LFUN_SELF_INSERT,
315 FuncRequest::KEYBOARD);
317 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
318 owner->message(_("Unknown function."));
323 if (func.action == LFUN_SELF_INSERT) {
324 if (encoded_last_key != 0) {
325 docstring const arg(1, encoded_last_key);
326 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
327 FuncRequest::KEYBOARD));
329 << "SelfInsert arg[`" << lyx::to_utf8(arg) << "']" << endl;
337 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
339 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
341 LCursor & cur = view()->cursor();
343 /* In LyX/Mac, when a dialog is open, the menus of the
344 application can still be accessed without giving focus to
345 the main window. In this case, we want to disable the menu
346 entries that are buffer-related.
348 Note that this code is not perfect, as bug 1941 attests:
349 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
352 if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
355 buf = owner->buffer();
357 if (cmd.action == LFUN_NOACTION) {
358 flag.message(lyx::from_utf8(N_("Nothing to do")));
363 switch (cmd.action) {
364 case LFUN_UNKNOWN_ACTION:
365 #ifndef HAVE_LIBAIKSAURUS
366 case LFUN_THESAURUS_ENTRY:
372 flag |= lyx_gui::getStatus(cmd);
375 if (flag.unknown()) {
376 flag.message(lyx::from_utf8(N_("Unknown action")));
380 if (!flag.enabled()) {
381 if (flag.message().empty())
382 flag.message(lyx::from_utf8(N_("Command disabled")));
386 // Check whether we need a buffer
387 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
389 flag.message(lyx::from_utf8(N_("Command not allowed with"
390 "out any document open")));
395 // I would really like to avoid having this switch and rather try to
396 // encode this in the function itself.
397 // -- And I'd rather let an inset decide which LFUNs it is willing
398 // to handle (Andre')
400 switch (cmd.action) {
401 case LFUN_TOOLTIPS_TOGGLE:
402 flag.setOnOff(owner->getDialogs().tooltipsEnabled());
405 case LFUN_BUFFER_TOGGLE_READ_ONLY:
406 flag.setOnOff(buf->isReadonly());
409 case LFUN_BUFFER_SWITCH:
410 // toggle on the current buffer, but do not toggle off
411 // the other ones (is that a good idea?)
412 if (lyx::to_utf8(cmd.argument()) == buf->fileName())
416 case LFUN_BUFFER_EXPORT:
417 enable = cmd.argument() == "custom"
418 || Exporter::isExportable(*buf, lyx::to_utf8(cmd.argument()));
421 case LFUN_BUFFER_CHKTEX:
422 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
425 case LFUN_BUILD_PROGRAM:
426 enable = Exporter::isExportable(*buf, "program");
429 case LFUN_LAYOUT_TABULAR:
430 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
434 case LFUN_LAYOUT_PARAGRAPH:
435 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
438 case LFUN_VC_REGISTER:
439 enable = !buf->lyxvc().inUse();
441 case LFUN_VC_CHECK_IN:
442 enable = buf->lyxvc().inUse() && !buf->isReadonly();
444 case LFUN_VC_CHECK_OUT:
445 enable = buf->lyxvc().inUse() && buf->isReadonly();
448 case LFUN_VC_UNDO_LAST:
449 enable = buf->lyxvc().inUse();
451 case LFUN_BUFFER_RELOAD:
452 enable = !buf->isUnnamed() && !buf->isClean();
455 case LFUN_INSET_SETTINGS: {
459 InsetBase::Code code = cur.inset().lyxCode();
461 case InsetBase::TABULAR_CODE:
462 enable = cmd.argument() == "tabular";
464 case InsetBase::ERT_CODE:
465 enable = cmd.argument() == "ert";
467 case InsetBase::FLOAT_CODE:
468 enable = cmd.argument() == "float";
470 case InsetBase::WRAP_CODE:
471 enable = cmd.argument() == "wrap";
473 case InsetBase::NOTE_CODE:
474 enable = cmd.argument() == "note";
476 case InsetBase::BRANCH_CODE:
477 enable = cmd.argument() == "branch";
479 case InsetBase::BOX_CODE:
480 enable = cmd.argument() == "box";
488 case LFUN_INSET_APPLY: {
489 string const name = cmd.getArg(0);
490 InsetBase * inset = owner->getDialogs().getOpenInset(name);
492 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
494 bool const success = inset->getStatus(cur, fr, fs);
495 // Every inset is supposed to handle this
496 BOOST_ASSERT(success);
499 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
500 flag |= getStatus(fr);
502 enable = flag.enabled();
506 case LFUN_DIALOG_SHOW: {
507 string const name = cmd.getArg(0);
509 enable = name == "aboutlyx"
513 || name == "texinfo";
514 else if (name == "print")
515 enable = Exporter::isExportable(*buf, "dvi")
516 && lyxrc.print_command != "none";
517 else if (name == "character" || name == "mathpanel")
518 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
519 else if (name == "latexlog")
520 enable = isFileReadable(buf->getLogName().second);
521 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
522 else if (name == "spellchecker")
525 else if (name == "vclog")
526 enable = buf->lyxvc().inUse();
527 else if (name == "view-source")
532 case LFUN_DIALOG_SHOW_NEW_INSET:
533 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
536 case LFUN_DIALOG_UPDATE: {
537 string const name = cmd.getArg(0);
539 enable = name == "prefs";
543 case LFUN_CITATION_INSERT: {
544 FuncRequest fr(LFUN_INSET_INSERT, "citation");
545 enable = getStatus(fr).enabled();
549 case LFUN_BUFFER_WRITE: {
550 enable = view()->buffer()->isUnnamed()
551 || !view()->buffer()->isClean();
555 // this one is difficult to get right. As a half-baked
556 // solution, we consider only the first action of the sequence
557 case LFUN_COMMAND_SEQUENCE: {
558 // argument contains ';'-terminated commands
559 string const firstcmd = token(lyx::to_utf8(cmd.argument()), ';', 0);
560 FuncRequest func(lyxaction.lookupFunc(firstcmd));
561 func.origin = cmd.origin;
562 flag = getStatus(func);
565 case LFUN_BUFFER_NEW:
566 case LFUN_BUFFER_NEW_TEMPLATE:
567 case LFUN_WORD_FIND_FORWARD:
568 case LFUN_WORD_FIND_BACKWARD:
569 case LFUN_COMMAND_PREFIX:
570 case LFUN_COMMAND_EXECUTE:
572 case LFUN_META_PREFIX:
573 case LFUN_BUFFER_CLOSE:
574 case LFUN_BUFFER_WRITE_AS:
575 case LFUN_BUFFER_UPDATE:
576 case LFUN_BUFFER_VIEW:
577 case LFUN_BUFFER_IMPORT:
580 case LFUN_BUFFER_AUTO_SAVE:
581 case LFUN_RECONFIGURE:
585 case LFUN_DROP_LAYOUTS_CHOICE:
587 case LFUN_SERVER_GET_NAME:
588 case LFUN_SERVER_NOTIFY:
589 case LFUN_SERVER_GOTO_FILE_ROW:
590 case LFUN_DIALOG_HIDE:
591 case LFUN_DIALOG_DISCONNECT_INSET:
592 case LFUN_BUFFER_CHILD_OPEN:
593 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
594 case LFUN_KEYMAP_OFF:
595 case LFUN_KEYMAP_PRIMARY:
596 case LFUN_KEYMAP_SECONDARY:
597 case LFUN_KEYMAP_TOGGLE:
599 case LFUN_BUFFER_EXPORT_CUSTOM:
600 case LFUN_BUFFER_PRINT:
601 case LFUN_PREFERENCES_SAVE:
602 case LFUN_SCREEN_FONT_UPDATE:
605 case LFUN_EXTERNAL_EDIT:
606 case LFUN_GRAPHICS_EDIT:
607 case LFUN_ALL_INSETS_TOGGLE:
608 case LFUN_BUFFER_LANGUAGE:
609 case LFUN_TEXTCLASS_APPLY:
610 case LFUN_TEXTCLASS_LOAD:
611 case LFUN_BUFFER_SAVE_AS_DEFAULT:
612 case LFUN_BUFFER_PARAMS_APPLY:
613 case LFUN_LYXRC_APPLY:
614 case LFUN_BUFFER_NEXT:
615 case LFUN_BUFFER_PREVIOUS:
616 // these are handled in our dispatch()
621 if (!::getStatus(cur, cmd, flag))
622 flag = view()->getStatus(cmd);
628 // Can we use a readonly buffer?
629 if (buf && buf->isReadonly()
630 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
631 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
632 flag.message(lyx::from_utf8(N_("Document is read-only")));
636 // Are we in a DELETED change-tracking region?
637 if (buf && buf->params().tracking_changes
638 && lookupChangeType(cur, true) == Change::DELETED
639 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
640 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
641 flag.message(lyx::from_utf8(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(lyx::from_utf8(N_("Command disabled")));
653 bool LyXFunc::ensureBufferClean(BufferView * bv)
655 Buffer & buf = *bv->buffer();
659 docstring const file = makeDisplayPath(buf.fileName(), 30);
660 docstring text = bformat(_("The document %1$s has unsaved "
661 "changes.\n\nDo you want to save "
662 "the document?"), file);
663 int const ret = Alert::prompt(_("Save changed document?"),
664 text, 0, 1, _("&Save"),
668 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
670 return buf.isClean();
676 void showPrintError(string const & name)
678 docstring 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 docstring s = bformat(_("The document could not be converted\n"
701 "into the document class %1$s."),
702 lyx::from_utf8(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 = lyx::to_utf8(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());
743 case LFUN_WORD_FIND_FORWARD:
744 case LFUN_WORD_FIND_BACKWARD: {
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_WORD_FIND_FORWARD;
760 lyx::find::find2string(searched_string, true, false, fw);
761 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
765 case LFUN_COMMAND_PREFIX:
766 owner->message(lyx::from_utf8(keyseq.printOptions()));
769 case LFUN_COMMAND_EXECUTE:
770 owner->getToolbars().display("minibuffer", true);
771 owner->focus_command_buffer();
776 meta_fake_bit = key_modifier::none;
777 if (view()->buffer())
778 // cancel any selection
779 dispatch(FuncRequest(LFUN_MARK_OFF));
780 setMessage(_("Cancel"));
783 case LFUN_META_PREFIX:
784 meta_fake_bit = key_modifier::alt;
785 setMessage(lyx::from_utf8(keyseq.print()));
788 case LFUN_BUFFER_TOGGLE_READ_ONLY:
789 if (owner->buffer()->lyxvc().inUse())
790 owner->buffer()->lyxvc().toggleReadOnly();
792 owner->buffer()->setReadonly(
793 !owner->buffer()->isReadonly());
796 // --- Menus -----------------------------------------------
797 case LFUN_BUFFER_NEW:
798 menuNew(argument, false);
801 case LFUN_BUFFER_NEW_TEMPLATE:
802 menuNew(argument, true);
805 case LFUN_BUFFER_CLOSE:
809 case LFUN_BUFFER_WRITE:
810 if (!owner->buffer()->isUnnamed()) {
811 docstring 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 case LFUN_BUFFER_WRITE_AS:
822 writeAs(owner->buffer(), argument);
826 case LFUN_BUFFER_RELOAD: {
827 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
828 docstring text = bformat(_("Any changes will be lost. Are you sure "
829 "you want to revert to the saved version of the document %1$s?"), file);
830 int const ret = Alert::prompt(_("Revert to saved document?"),
831 text, 0, 1, _("&Revert"), _("&Cancel"));
838 case LFUN_BUFFER_UPDATE:
839 Exporter::Export(owner->buffer(), argument, true);
842 case LFUN_BUFFER_VIEW:
843 Exporter::preview(owner->buffer(), argument);
846 case LFUN_BUILD_PROGRAM:
847 Exporter::Export(owner->buffer(), "program", true);
850 case LFUN_BUFFER_CHKTEX:
851 owner->buffer()->runChktex();
854 case LFUN_BUFFER_EXPORT:
855 if (argument == "custom")
856 owner->getDialogs().show("sendto");
858 Exporter::Export(owner->buffer(), argument, false);
862 case LFUN_BUFFER_EXPORT_CUSTOM: {
864 string command = split(argument, format_name, ' ');
865 Format const * format = formats.getFormat(format_name);
867 lyxerr << "Format \"" << format_name
868 << "\" not recognized!"
873 Buffer * buffer = owner->buffer();
875 // The name of the file created by the conversion process
878 // Output to filename
879 if (format->name() == "lyx") {
880 string const latexname =
881 buffer->getLatexName(false);
882 filename = changeExtension(latexname,
883 format->extension());
884 filename = addName(buffer->temppath(), filename);
886 if (!buffer->writeFile(filename))
890 Exporter::Export(buffer, format_name, true, filename);
893 // Substitute $$FName for filename
894 if (!contains(command, "$$FName"))
895 command = "( " + command + " ) < $$FName";
896 command = subst(command, "$$FName", filename);
898 // Execute the command in the background
900 call.startscript(Systemcall::DontWait, command);
904 case LFUN_BUFFER_PRINT: {
907 string command = split(split(argument, target, ' '),
911 || target_name.empty()
912 || command.empty()) {
913 lyxerr << "Unable to parse \""
914 << argument << '"' << std::endl;
917 if (target != "printer" && target != "file") {
918 lyxerr << "Unrecognized target \""
919 << target << '"' << std::endl;
923 Buffer * buffer = owner->buffer();
925 if (!Exporter::Export(buffer, "dvi", true)) {
926 showPrintError(buffer->fileName());
930 // Push directory path.
931 string const path = buffer->temppath();
932 lyx::support::Path p(path);
934 // there are three cases here:
935 // 1. we print to a file
936 // 2. we print directly to a printer
937 // 3. we print using a spool command (print to file first)
940 string const dviname =
941 changeExtension(buffer->getLatexName(true),
944 if (target == "printer") {
945 if (!lyxrc.print_spool_command.empty()) {
946 // case 3: print using a spool
947 string const psname =
948 changeExtension(dviname,".ps");
949 command += lyxrc.print_to_file
952 + quoteName(dviname);
955 lyxrc.print_spool_command +' ';
956 if (target_name != "default") {
957 command2 += lyxrc.print_spool_printerprefix
961 command2 += quoteName(psname);
963 // If successful, then spool command
964 res = one.startscript(
969 res = one.startscript(
970 Systemcall::DontWait,
973 // case 2: print directly to a printer
974 res = one.startscript(
975 Systemcall::DontWait,
976 command + quoteName(dviname));
980 // case 1: print to a file
981 command += lyxrc.print_to_file
982 + quoteName(makeAbsPath(target_name,
985 + quoteName(dviname);
986 res = one.startscript(Systemcall::DontWait,
991 showPrintError(buffer->fileName());
995 case LFUN_BUFFER_IMPORT:
1000 if (view()->buffer()) {
1001 // save cursor Position for opened files to .lyx/session
1002 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1003 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1004 // save bookmarks to .lyx/session
1005 view()->saveSavedPositions();
1007 quitLyX(argument == "force");
1010 case LFUN_TOC_VIEW: {
1011 InsetCommandParams p("tableofcontents");
1012 string const data = InsetCommandMailer::params2string("toc", p);
1013 owner->getDialogs().show("toc", data, 0);
1017 case LFUN_BUFFER_AUTO_SAVE:
1021 case LFUN_RECONFIGURE:
1022 reconfigure(view());
1025 case LFUN_HELP_OPEN: {
1026 string const arg = argument;
1028 setErrorMessage(_("Missing argument"));
1031 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1032 if (fname.empty()) {
1033 lyxerr << "LyX: unable to find documentation file `"
1034 << arg << "'. Bad installation?" << endl;
1037 owner->message(bformat(_("Opening help file %1$s..."),
1038 makeDisplayPath(fname)));
1039 owner->loadLyXFile(fname, false);
1043 // --- version control -------------------------------
1044 case LFUN_VC_REGISTER:
1045 if (!ensureBufferClean(view()))
1047 if (!owner->buffer()->lyxvc().inUse()) {
1048 owner->buffer()->lyxvc().registrer();
1053 case LFUN_VC_CHECK_IN:
1054 if (!ensureBufferClean(view()))
1056 if (owner->buffer()->lyxvc().inUse()
1057 && !owner->buffer()->isReadonly()) {
1058 owner->buffer()->lyxvc().checkIn();
1063 case LFUN_VC_CHECK_OUT:
1064 if (!ensureBufferClean(view()))
1066 if (owner->buffer()->lyxvc().inUse()
1067 && owner->buffer()->isReadonly()) {
1068 owner->buffer()->lyxvc().checkOut();
1073 case LFUN_VC_REVERT:
1074 owner->buffer()->lyxvc().revert();
1078 case LFUN_VC_UNDO_LAST:
1079 owner->buffer()->lyxvc().undoLast();
1083 // --- buffers ----------------------------------------
1084 case LFUN_BUFFER_SWITCH:
1085 owner->setBuffer(theApp->bufferList().getBuffer(argument));
1088 case LFUN_BUFFER_NEXT:
1089 owner->setBuffer(theApp->bufferList().next(view()->buffer()));
1092 case LFUN_BUFFER_PREVIOUS:
1093 owner->setBuffer(theApp->bufferList().previous(view()->buffer()));
1097 newFile(view(), argument);
1100 case LFUN_FILE_OPEN:
1104 case LFUN_DROP_LAYOUTS_CHOICE:
1105 owner->getToolbars().openLayoutList();
1108 case LFUN_MENU_OPEN:
1109 owner->getMenubar().openByName(lyx::from_utf8(argument));
1112 // --- lyxserver commands ----------------------------
1113 case LFUN_SERVER_GET_NAME:
1114 setMessage(lyx::from_utf8(owner->buffer()->fileName()));
1115 lyxerr[Debug::INFO] << "FNAME["
1116 << owner->buffer()->fileName()
1120 case LFUN_SERVER_NOTIFY:
1121 dispatch_buffer = lyx::from_utf8(keyseq.print());
1122 theApp->server().notifyClient(lyx::to_utf8(dispatch_buffer));
1125 case LFUN_SERVER_GOTO_FILE_ROW: {
1128 istringstream is(argument);
1129 is >> file_name >> row;
1130 if (prefixIs(file_name, package().temp_dir())) {
1131 // Needed by inverse dvi search. If it is a file
1132 // in tmpdir, call the apropriated function
1133 owner->setBuffer(theApp->bufferList().getBufferFromTmp(file_name));
1135 // Must replace extension of the file to be .lyx
1136 // and get full path
1137 string const s = changeExtension(file_name, ".lyx");
1138 // Either change buffer or load the file
1139 if (theApp->bufferList().exists(s)) {
1140 owner->setBuffer(theApp->bufferList().getBuffer(s));
1142 owner->loadLyXFile(s);
1146 view()->setCursorFromRow(row);
1149 // see BufferView::center()
1153 case LFUN_DIALOG_SHOW: {
1154 string const name = cmd.getArg(0);
1155 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1157 if (name == "character") {
1158 data = freefont2string();
1160 owner->getDialogs().show("character", data);
1161 } else if (name == "latexlog") {
1162 pair<Buffer::LogType, string> const logfile =
1163 owner->buffer()->getLogName();
1164 switch (logfile.first) {
1165 case Buffer::latexlog:
1168 case Buffer::buildlog:
1172 data += LyXLex::quoteString(logfile.second);
1173 owner->getDialogs().show("log", data);
1174 } else if (name == "vclog") {
1175 string const data = "vc " +
1176 LyXLex::quoteString(owner->buffer()->lyxvc().getLogFile());
1177 owner->getDialogs().show("log", data);
1179 owner->getDialogs().show(name, data);
1183 case LFUN_DIALOG_SHOW_NEW_INSET: {
1184 string const name = cmd.getArg(0);
1185 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1186 if (name == "bibitem" ||
1193 InsetCommandParams p(name);
1194 data = InsetCommandMailer::params2string(name, p);
1195 } else if (name == "include") {
1196 InsetCommandParams p(data);
1197 data = InsetIncludeMailer::params2string(p);
1198 } else if (name == "box") {
1199 // \c data == "Boxed" || "Frameless" etc
1200 InsetBoxParams p(data);
1201 data = InsetBoxMailer::params2string(p);
1202 } else if (name == "branch") {
1203 InsetBranchParams p;
1204 data = InsetBranchMailer::params2string(p);
1205 } else if (name == "citation") {
1206 InsetCommandParams p("cite");
1207 data = InsetCommandMailer::params2string(name, p);
1208 } else if (name == "ert") {
1209 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1210 } else if (name == "external") {
1211 InsetExternalParams p;
1212 Buffer const & buffer = *owner->buffer();
1213 data = InsetExternalMailer::params2string(p, buffer);
1214 } else if (name == "float") {
1216 data = InsetFloatMailer::params2string(p);
1217 } else if (name == "graphics") {
1218 InsetGraphicsParams p;
1219 Buffer const & buffer = *owner->buffer();
1220 data = InsetGraphicsMailer::params2string(p, buffer);
1221 } else if (name == "note") {
1223 data = InsetNoteMailer::params2string(p);
1224 } else if (name == "vspace") {
1226 data = InsetVSpaceMailer::params2string(space);
1227 } else if (name == "wrap") {
1229 data = InsetWrapMailer::params2string(p);
1231 owner->getDialogs().show(name, data, 0);
1235 case LFUN_DIALOG_UPDATE: {
1236 string const & name = argument;
1237 // Can only update a dialog connected to an existing inset
1238 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1240 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1241 inset->dispatch(view()->cursor(), fr);
1242 } else if (name == "paragraph") {
1243 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1244 } else if (name == "prefs") {
1245 owner->getDialogs().update(name, string());
1250 case LFUN_DIALOG_HIDE:
1251 Dialogs::hide(argument, 0);
1254 case LFUN_DIALOG_DISCONNECT_INSET:
1255 owner->getDialogs().disconnect(argument);
1259 case LFUN_CITATION_INSERT: {
1260 if (!argument.empty()) {
1261 // we can have one optional argument, delimited by '|'
1262 // citation-insert <key>|<text_before>
1263 // this should be enhanced to also support text_after
1264 // and citation style
1265 string arg = argument;
1267 if (contains(argument, "|")) {
1268 arg = token(argument, '|', 0);
1269 opt1 = '[' + token(argument, '|', 1) + ']';
1271 std::ostringstream os;
1272 os << "citation LatexCommand\n"
1273 << "\\cite" << opt1 << "{" << arg << "}\n"
1275 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1278 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1282 case LFUN_BUFFER_CHILD_OPEN: {
1283 string const filename =
1284 makeAbsPath(argument, owner->buffer()->filePath());
1285 // FIXME Should use bformat
1286 setMessage(_("Opening child document ") +
1287 makeDisplayPath(filename) + lyx::from_ascii("..."));
1288 view()->savePosition(0);
1289 string const parentfilename = owner->buffer()->fileName();
1290 if (theApp->bufferList().exists(filename))
1291 owner->setBuffer(theApp->bufferList().getBuffer(filename));
1293 owner->loadLyXFile(filename);
1294 // Set the parent name of the child document.
1295 // This makes insertion of citations and references in the child work,
1296 // when the target is in the parent or another child document.
1297 owner->buffer()->setParentName(parentfilename);
1301 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1302 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1305 case LFUN_KEYMAP_OFF:
1306 owner->view()->getIntl().keyMapOn(false);
1309 case LFUN_KEYMAP_PRIMARY:
1310 owner->view()->getIntl().keyMapPrim();
1313 case LFUN_KEYMAP_SECONDARY:
1314 owner->view()->getIntl().keyMapSec();
1317 case LFUN_KEYMAP_TOGGLE:
1318 owner->view()->getIntl().toggleKeyMap();
1324 string rest = split(argument, countstr, ' ');
1325 istringstream is(countstr);
1328 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1329 for (int i = 0; i < count; ++i)
1330 dispatch(lyxaction.lookupFunc(rest));
1334 case LFUN_COMMAND_SEQUENCE: {
1335 // argument contains ';'-terminated commands
1336 string arg = argument;
1337 while (!arg.empty()) {
1339 arg = split(arg, first, ';');
1340 FuncRequest func(lyxaction.lookupFunc(first));
1341 func.origin = cmd.origin;
1347 case LFUN_PREFERENCES_SAVE: {
1348 lyx::support::Path p(package().user_support());
1349 lyxrc.write("preferences", false);
1353 case LFUN_SCREEN_FONT_UPDATE:
1354 // handle the screen font changes.
1355 lyxrc.set_font_norm_type();
1356 theApp->fontLoader().update();
1357 // All visible buffers will need resize
1361 case LFUN_SET_COLOR: {
1363 string const x11_name = split(argument, lyx_name, ' ');
1364 if (lyx_name.empty() || x11_name.empty()) {
1365 setErrorMessage(_("Syntax: set-color <lyx_name>"
1370 bool const graphicsbg_changed =
1371 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1372 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1374 if (!lcolor.setColor(lyx_name, x11_name)) {
1376 bformat(_("Set-color \"%1$s\" failed "
1377 "- color is undefined or "
1378 "may not be redefined"),
1379 lyx::from_utf8(lyx_name)));
1383 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1385 if (graphicsbg_changed) {
1386 #ifdef WITH_WARNINGS
1387 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1390 lyx::graphics::GCache::get().changeDisplay(true);
1397 owner->message(lyx::from_utf8(argument));
1400 case LFUN_TOOLTIPS_TOGGLE:
1401 owner->getDialogs().toggleTooltips();
1404 case LFUN_EXTERNAL_EDIT: {
1405 FuncRequest fr(action, argument);
1406 InsetExternal().dispatch(view()->cursor(), fr);
1410 case LFUN_GRAPHICS_EDIT: {
1411 FuncRequest fr(action, argument);
1412 InsetGraphics().dispatch(view()->cursor(), fr);
1416 case LFUN_INSET_APPLY: {
1417 string const name = cmd.getArg(0);
1418 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1420 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1421 inset->dispatch(view()->cursor(), fr);
1423 FuncRequest fr(LFUN_INSET_INSERT, argument);
1426 // ideally, the update flag should be set by the insets,
1427 // but this is not possible currently
1432 case LFUN_ALL_INSETS_TOGGLE: {
1434 string const name = split(argument, action, ' ');
1435 InsetBase::Code const inset_code =
1436 InsetBase::translate(name);
1438 LCursor & cur = view()->cursor();
1439 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1441 InsetBase & inset = owner->buffer()->inset();
1442 InsetIterator it = inset_iterator_begin(inset);
1443 InsetIterator const end = inset_iterator_end(inset);
1444 for (; it != end; ++it) {
1445 if (inset_code == InsetBase::NO_CODE
1446 || inset_code == it->lyxCode()) {
1447 LCursor tmpcur = cur;
1448 tmpcur.pushLeft(*it);
1449 it->dispatch(tmpcur, fr);
1456 case LFUN_BUFFER_LANGUAGE: {
1457 Buffer & buffer = *owner->buffer();
1458 Language const * oldL = buffer.params().language;
1459 Language const * newL = languages.getLanguage(argument);
1460 if (!newL || oldL == newL)
1463 if (oldL->rightToLeft() == newL->rightToLeft()
1464 && !buffer.isMultiLingual())
1465 buffer.changeLanguage(oldL, newL);
1467 buffer.updateDocLang(newL);
1471 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1472 string const fname =
1473 addName(addPath(package().user_support(), "templates/"),
1475 Buffer defaults(fname);
1477 istringstream ss(argument);
1480 int const unknown_tokens = defaults.readHeader(lex);
1482 if (unknown_tokens != 0) {
1483 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1484 << unknown_tokens << " unknown token"
1485 << (unknown_tokens == 1 ? "" : "s")
1489 if (defaults.writeFile(defaults.fileName()))
1490 // FIXME Should use bformat
1491 setMessage(_("Document defaults saved in ")
1492 + makeDisplayPath(fname));
1494 setErrorMessage(_("Unable to save document defaults"));
1498 case LFUN_BUFFER_PARAMS_APPLY: {
1499 biblio::CiteEngine const engine =
1500 owner->buffer()->params().cite_engine;
1502 istringstream ss(argument);
1505 int const unknown_tokens =
1506 owner->buffer()->readHeader(lex);
1508 if (unknown_tokens != 0) {
1509 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1510 << unknown_tokens << " unknown token"
1511 << (unknown_tokens == 1 ? "" : "s")
1514 if (engine == owner->buffer()->params().cite_engine)
1517 LCursor & cur = view()->cursor();
1518 FuncRequest fr(LFUN_INSET_REFRESH);
1520 InsetBase & inset = owner->buffer()->inset();
1521 InsetIterator it = inset_iterator_begin(inset);
1522 InsetIterator const end = inset_iterator_end(inset);
1523 for (; it != end; ++it)
1524 if (it->lyxCode() == InsetBase::CITE_CODE)
1525 it->dispatch(cur, fr);
1529 case LFUN_TEXTCLASS_APPLY: {
1530 Buffer * buffer = owner->buffer();
1532 lyx::textclass_type const old_class =
1533 buffer->params().textclass;
1535 loadTextclass(argument);
1537 std::pair<bool, lyx::textclass_type> const tc_pair =
1538 textclasslist.numberOfClass(argument);
1543 lyx::textclass_type const new_class = tc_pair.second;
1544 if (old_class == new_class)
1548 owner->message(_("Converting document to new document class..."));
1549 recordUndoFullDocument(view());
1550 buffer->params().textclass = new_class;
1551 StableDocIterator backcur(view()->cursor());
1552 ErrorList & el = buffer->errorList("Class Switch");
1553 lyx::cap::switchBetweenClasses(
1554 old_class, new_class,
1555 static_cast<InsetText &>(buffer->inset()), el);
1557 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1559 buffer->errors("Class Switch");
1560 updateLabels(*buffer);
1565 case LFUN_TEXTCLASS_LOAD:
1566 loadTextclass(argument);
1569 case LFUN_LYXRC_APPLY: {
1570 LyXRC const lyxrc_orig = lyxrc;
1572 istringstream ss(argument);
1573 bool const success = lyxrc.read(ss) == 0;
1576 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1577 << "Unable to read lyxrc data"
1582 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1587 view()->cursor().dispatch(cmd);
1588 updateforce |= view()->cursor().result().update();
1589 if (!view()->cursor().result().dispatched())
1590 updateforce |= view()->dispatch(cmd);
1595 if (view()->buffer()) {
1596 // Redraw screen unless explicitly told otherwise.
1597 // This also initializes the position cache for all insets
1598 // in (at least partially) visible top-level paragraphs.
1600 view()->update(Update::FitCursor | Update::Force);
1602 view()->update(Update::FitCursor);
1604 owner->redrawWorkArea();
1606 // if we executed a mutating lfun, mark the buffer as dirty
1608 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1609 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1610 view()->buffer()->markDirty();
1612 if (view()->cursor().inTexted()) {
1613 owner->updateLayoutChoice();
1618 // FIXME UNICODE: _() does not support anything but ascii.
1619 // Do we need a lyx::to_ascii() method?
1620 sendDispatchMessage(getMessage(), cmd);
1624 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1626 /* When an action did not originate from the UI/kbd, it makes
1627 * sense to avoid updating the GUI. It turns out that this
1628 * fixes bug 1941, for reasons that are described here:
1629 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1631 if (cmd.origin != FuncRequest::INTERNAL) {
1632 owner->updateMenubar();
1633 owner->updateToolbars();
1636 const bool verbose = (cmd.origin == FuncRequest::UI
1637 || cmd.origin == FuncRequest::COMMANDBUFFER);
1639 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1640 lyxerr[Debug::ACTION] << "dispatch msg is " << lyx::to_utf8(msg) << endl;
1642 owner->message(msg);
1646 docstring dispatch_msg = msg;
1647 if (!dispatch_msg.empty())
1648 dispatch_msg += ' ';
1650 string comname = lyxaction.getActionName(cmd.action);
1652 bool argsadded = false;
1654 if (!cmd.argument().empty()) {
1655 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1656 comname += ' ' + lyx::to_utf8(cmd.argument());
1661 string const shortcuts = toplevel_keymap->printbindings(cmd);
1663 if (!shortcuts.empty())
1664 comname += ": " + shortcuts;
1665 else if (!argsadded && !cmd.argument().empty())
1666 comname += ' ' + lyx::to_utf8(cmd.argument());
1668 if (!comname.empty()) {
1669 comname = rtrim(comname);
1670 dispatch_msg += lyx::from_utf8('(' + rtrim(comname) + ')');
1673 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1674 << lyx::to_utf8(dispatch_msg) << endl;
1675 if (!dispatch_msg.empty())
1676 owner->message(dispatch_msg);
1680 void LyXFunc::setupLocalKeymap()
1682 keyseq.stdmap = toplevel_keymap.get();
1683 keyseq.curmap = toplevel_keymap.get();
1684 cancel_meta_seq.stdmap = toplevel_keymap.get();
1685 cancel_meta_seq.curmap = toplevel_keymap.get();
1689 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1691 string initpath = lyxrc.document_path;
1692 string filename(name);
1694 if (view()->buffer()) {
1695 string const trypath = owner->buffer()->filePath();
1696 // If directory is writeable, use this as default.
1697 if (isDirWriteable(trypath))
1701 static int newfile_number;
1703 if (filename.empty()) {
1704 filename = addName(lyxrc.document_path,
1705 "newfile" + convert<string>(++newfile_number) + ".lyx");
1706 while (theApp->bufferList().exists(filename) || fs::is_readable(filename)) {
1708 filename = addName(lyxrc.document_path,
1709 "newfile" + convert<string>(newfile_number) +
1714 // The template stuff
1717 FileDialog fileDlg(lyx::to_utf8(_("Select template file")),
1718 LFUN_SELECT_FILE_SYNC,
1719 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1720 string(lyxrc.document_path)),
1721 make_pair(string(lyx::to_utf8(_("Templates|#T#t"))),
1722 string(lyxrc.template_path)));
1724 FileDialog::Result result =
1725 fileDlg.open(lyxrc.template_path,
1726 FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1729 if (result.first == FileDialog::Later)
1731 if (result.second.empty())
1733 templname = result.second;
1736 Buffer * const b = newFile(filename, templname, !name.empty());
1738 owner->setBuffer(b);
1742 void LyXFunc::open(string const & fname)
1744 string initpath = lyxrc.document_path;
1746 if (view()->buffer()) {
1747 string const trypath = owner->buffer()->filePath();
1748 // If directory is writeable, use this as default.
1749 if (isDirWriteable(trypath))
1755 if (fname.empty()) {
1756 FileDialog fileDlg(lyx::to_utf8(_("Select document to open")),
1758 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1759 string(lyxrc.document_path)),
1760 make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1761 string(addPath(package().system_support(), "examples"))));
1763 FileDialog::Result result =
1764 fileDlg.open(initpath,
1765 FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1768 if (result.first == FileDialog::Later)
1771 filename = result.second;
1773 // check selected filename
1774 if (filename.empty()) {
1775 owner->message(_("Canceled."));
1781 // get absolute path of file and add ".lyx" to the filename if
1783 string const fullpath = fileSearch(string(), filename, "lyx");
1784 if (!fullpath.empty()) {
1785 filename = fullpath;
1788 docstring const disp_fn = makeDisplayPath(filename);
1790 // if the file doesn't exist, let the user create one
1791 if (!fs::exists(filename)) {
1792 // the user specifically chose this name. Believe him.
1793 Buffer * const b = newFile(filename, string(), true);
1795 owner->setBuffer(b);
1799 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1802 if (owner->loadLyXFile(filename)) {
1803 str2 = bformat(_("Document %1$s opened."), disp_fn);
1805 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1807 owner->message(str2);
1811 void LyXFunc::doImport(string const & argument)
1814 string filename = split(argument, format, ' ');
1816 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1817 << " file: " << filename << endl;
1819 // need user interaction
1820 if (filename.empty()) {
1821 string initpath = lyxrc.document_path;
1823 if (view()->buffer()) {
1824 string const trypath = owner->buffer()->filePath();
1825 // If directory is writeable, use this as default.
1826 if (isDirWriteable(trypath))
1830 docstring const text = bformat(_("Select %1$s file to import"),
1831 formats.prettyName(format));
1833 FileDialog fileDlg(lyx::to_utf8(text),
1835 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1836 string(lyxrc.document_path)),
1837 make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1838 string(addPath(package().system_support(), "examples"))));
1840 string const filter = lyx::to_utf8(formats.prettyName(format))
1841 + " (*." + formats.extension(format) + ')';
1843 FileDialog::Result result =
1844 fileDlg.open(initpath,
1845 FileFilterList(filter),
1848 if (result.first == FileDialog::Later)
1851 filename = result.second;
1853 // check selected filename
1854 if (filename.empty())
1855 owner->message(_("Canceled."));
1858 if (filename.empty())
1861 // get absolute path of file
1862 filename = makeAbsPath(filename);
1864 string const lyxfile = changeExtension(filename, ".lyx");
1866 // Check if the document already is open
1867 if (lyx_gui::use_gui && theApp->bufferList().exists(lyxfile)) {
1868 if (!theApp->bufferList().close(theApp->bufferList().getBuffer(lyxfile), true)) {
1869 owner->message(_("Canceled."));
1874 // if the file exists already, and we didn't do
1875 // -i lyx thefile.lyx, warn
1876 if (fs::exists(lyxfile) && filename != lyxfile) {
1877 docstring const file = makeDisplayPath(lyxfile, 30);
1879 docstring text = bformat(_("The document %1$s already exists.\n\n"
1880 "Do you want to over-write that document?"), file);
1881 int const ret = Alert::prompt(_("Over-write document?"),
1882 text, 0, 1, _("&Over-write"), _("&Cancel"));
1885 owner->message(_("Canceled."));
1890 ErrorList errorList;
1891 Importer::Import(owner, filename, format, errorList);
1892 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1896 void LyXFunc::closeBuffer()
1898 // save current cursor position
1899 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1900 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1901 if (theApp->bufferList().close(owner->buffer(), true) && !quitting) {
1902 if (theApp->bufferList().empty()) {
1903 // need this otherwise SEGV may occur while
1904 // trying to set variables that don't exist
1905 // since there's no current buffer
1906 owner->getDialogs().hideBufferDependent();
1908 owner->setBuffer(theApp->bufferList().first());
1914 // Each "owner" should have it's own message method. lyxview and
1915 // the minibuffer would use the minibuffer, but lyxserver would
1916 // send an ERROR signal to its client. Alejandro 970603
1917 // This function is bit problematic when it comes to NLS, to make the
1918 // lyx servers client be language indepenent we must not translate
1919 // strings sent to this func.
1920 void LyXFunc::setErrorMessage(docstring const & m) const
1922 dispatch_buffer = m;
1927 void LyXFunc::setMessage(docstring const & m) const
1929 dispatch_buffer = m;
1933 string const LyXFunc::viewStatusMessage()
1935 // When meta-fake key is pressed, show the key sequence so far + "M-".
1937 return keyseq.print() + "M-";
1939 // Else, when a non-complete key sequence is pressed,
1940 // show the available options.
1941 if (keyseq.length() > 0 && !keyseq.deleted())
1942 return keyseq.printOptions();
1944 if (!view()->buffer())
1945 return lyx::to_utf8(_("Welcome to LyX!"));
1947 return view()->cursor().currentState();
1951 BufferView * LyXFunc::view() const
1953 BOOST_ASSERT(owner);
1954 return owner->view();
1958 bool LyXFunc::wasMetaKey() const
1960 return (meta_fake_bit != key_modifier::none);
1966 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1968 // Why the switch you might ask. It is a trick to ensure that all
1969 // the elements in the LyXRCTags enum is handled. As you can see
1970 // there are no breaks at all. So it is just a huge fall-through.
1971 // The nice thing is that we will get a warning from the compiler
1972 // if we forget an element.
1973 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1975 case LyXRC::RC_ACCEPT_COMPOUND:
1976 case LyXRC::RC_ALT_LANG:
1977 case LyXRC::RC_ASCIIROFF_COMMAND:
1978 case LyXRC::RC_ASCII_LINELEN:
1979 case LyXRC::RC_AUTOREGIONDELETE:
1980 case LyXRC::RC_AUTORESET_OPTIONS:
1981 case LyXRC::RC_AUTOSAVE:
1982 case LyXRC::RC_AUTO_NUMBER:
1983 case LyXRC::RC_BACKUPDIR_PATH:
1984 case LyXRC::RC_BIBTEX_COMMAND:
1985 case LyXRC::RC_BINDFILE:
1986 case LyXRC::RC_CHECKLASTFILES:
1987 case LyXRC::RC_USELASTFILEPOS:
1988 case LyXRC::RC_LOADSESSION:
1989 case LyXRC::RC_CHKTEX_COMMAND:
1990 case LyXRC::RC_CONVERTER:
1991 case LyXRC::RC_COPIER:
1992 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1993 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1994 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1995 case LyXRC::RC_DATE_INSERT_FORMAT:
1996 case LyXRC::RC_DEFAULT_LANGUAGE:
1997 case LyXRC::RC_DEFAULT_PAPERSIZE:
1998 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1999 case LyXRC::RC_DISPLAY_GRAPHICS:
2000 case LyXRC::RC_DOCUMENTPATH:
2001 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2002 if (fs::exists(lyxrc_new.document_path) &&
2003 fs::is_directory(lyxrc_new.document_path)) {
2004 using lyx::support::package;
2005 package().document_dir() = lyxrc.document_path;
2008 case LyXRC::RC_ESC_CHARS:
2009 case LyXRC::RC_FONT_ENCODING:
2010 case LyXRC::RC_FORMAT:
2011 case LyXRC::RC_INDEX_COMMAND:
2012 case LyXRC::RC_INPUT:
2013 case LyXRC::RC_KBMAP:
2014 case LyXRC::RC_KBMAP_PRIMARY:
2015 case LyXRC::RC_KBMAP_SECONDARY:
2016 case LyXRC::RC_LABEL_INIT_LENGTH:
2017 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2018 case LyXRC::RC_LANGUAGE_AUTO_END:
2019 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2020 case LyXRC::RC_LANGUAGE_COMMAND_END:
2021 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2022 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2023 case LyXRC::RC_LANGUAGE_PACKAGE:
2024 case LyXRC::RC_LANGUAGE_USE_BABEL:
2025 case LyXRC::RC_MAKE_BACKUP:
2026 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2027 case LyXRC::RC_NUMLASTFILES:
2028 case LyXRC::RC_PATH_PREFIX:
2029 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2030 using lyx::support::prependEnvPath;
2031 prependEnvPath("PATH", lyxrc.path_prefix);
2033 case LyXRC::RC_PERS_DICT:
2034 case LyXRC::RC_POPUP_BOLD_FONT:
2035 case LyXRC::RC_POPUP_FONT_ENCODING:
2036 case LyXRC::RC_POPUP_NORMAL_FONT:
2037 case LyXRC::RC_PREVIEW:
2038 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2039 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2040 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2041 case LyXRC::RC_PRINTCOPIESFLAG:
2042 case LyXRC::RC_PRINTER:
2043 case LyXRC::RC_PRINTEVENPAGEFLAG:
2044 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2045 case LyXRC::RC_PRINTFILEEXTENSION:
2046 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2047 case LyXRC::RC_PRINTODDPAGEFLAG:
2048 case LyXRC::RC_PRINTPAGERANGEFLAG:
2049 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2050 case LyXRC::RC_PRINTPAPERFLAG:
2051 case LyXRC::RC_PRINTREVERSEFLAG:
2052 case LyXRC::RC_PRINTSPOOL_COMMAND:
2053 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2054 case LyXRC::RC_PRINTTOFILE:
2055 case LyXRC::RC_PRINTTOPRINTER:
2056 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2057 case LyXRC::RC_PRINT_COMMAND:
2058 case LyXRC::RC_RTL_SUPPORT:
2059 case LyXRC::RC_SCREEN_DPI:
2060 case LyXRC::RC_SCREEN_FONT_ENCODING:
2061 case LyXRC::RC_SCREEN_FONT_ROMAN:
2062 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2063 case LyXRC::RC_SCREEN_FONT_SANS:
2064 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2065 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2066 case LyXRC::RC_SCREEN_FONT_SIZES:
2067 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2068 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2069 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2070 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2071 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2072 case LyXRC::RC_SCREEN_ZOOM:
2073 case LyXRC::RC_SERVERPIPE:
2074 case LyXRC::RC_SET_COLOR:
2075 case LyXRC::RC_SHOW_BANNER:
2076 case LyXRC::RC_SPELL_COMMAND:
2077 case LyXRC::RC_TEMPDIRPATH:
2078 case LyXRC::RC_TEMPLATEPATH:
2079 case LyXRC::RC_TEX_ALLOWS_SPACES:
2080 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2081 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2082 namespace os = lyx::support::os;
2083 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2085 case LyXRC::RC_UIFILE:
2086 case LyXRC::RC_USER_EMAIL:
2087 case LyXRC::RC_USER_NAME:
2088 case LyXRC::RC_USETEMPDIR:
2089 case LyXRC::RC_USE_ALT_LANG:
2090 case LyXRC::RC_USE_ESC_CHARS:
2091 case LyXRC::RC_USE_INP_ENC:
2092 case LyXRC::RC_USE_PERS_DICT:
2093 case LyXRC::RC_USE_SPELL_LIB:
2094 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2095 case LyXRC::RC_VIEWER:
2096 case LyXRC::RC_LAST: