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:
376 if (flag.unknown()) {
377 flag.message(lyx::from_utf8(N_("Unknown action")));
381 if (!flag.enabled()) {
382 if (flag.message().empty())
383 flag.message(lyx::from_utf8(N_("Command disabled")));
387 // Check whether we need a buffer
388 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
390 flag.message(lyx::from_utf8(N_("Command not allowed with"
391 "out any document open")));
396 // I would really like to avoid having this switch and rather try to
397 // encode this in the function itself.
398 // -- And I'd rather let an inset decide which LFUNs it is willing
399 // to handle (Andre')
401 switch (cmd.action) {
402 case LFUN_BUFFER_TOGGLE_READ_ONLY:
403 flag.setOnOff(buf->isReadonly());
406 case LFUN_BUFFER_SWITCH:
407 // toggle on the current buffer, but do not toggle off
408 // the other ones (is that a good idea?)
409 if (lyx::to_utf8(cmd.argument()) == buf->fileName())
413 case LFUN_BUFFER_EXPORT:
414 enable = cmd.argument() == "custom"
415 || Exporter::isExportable(*buf, lyx::to_utf8(cmd.argument()));
418 case LFUN_BUFFER_CHKTEX:
419 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
422 case LFUN_BUILD_PROGRAM:
423 enable = Exporter::isExportable(*buf, "program");
426 case LFUN_LAYOUT_TABULAR:
427 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
431 case LFUN_LAYOUT_PARAGRAPH:
432 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
435 case LFUN_VC_REGISTER:
436 enable = !buf->lyxvc().inUse();
438 case LFUN_VC_CHECK_IN:
439 enable = buf->lyxvc().inUse() && !buf->isReadonly();
441 case LFUN_VC_CHECK_OUT:
442 enable = buf->lyxvc().inUse() && buf->isReadonly();
445 case LFUN_VC_UNDO_LAST:
446 enable = buf->lyxvc().inUse();
448 case LFUN_BUFFER_RELOAD:
449 enable = !buf->isUnnamed() && !buf->isClean();
452 case LFUN_INSET_SETTINGS: {
456 InsetBase::Code code = cur.inset().lyxCode();
458 case InsetBase::TABULAR_CODE:
459 enable = cmd.argument() == "tabular";
461 case InsetBase::ERT_CODE:
462 enable = cmd.argument() == "ert";
464 case InsetBase::FLOAT_CODE:
465 enable = cmd.argument() == "float";
467 case InsetBase::WRAP_CODE:
468 enable = cmd.argument() == "wrap";
470 case InsetBase::NOTE_CODE:
471 enable = cmd.argument() == "note";
473 case InsetBase::BRANCH_CODE:
474 enable = cmd.argument() == "branch";
476 case InsetBase::BOX_CODE:
477 enable = cmd.argument() == "box";
485 case LFUN_INSET_APPLY: {
486 string const name = cmd.getArg(0);
487 InsetBase * inset = owner->getDialogs().getOpenInset(name);
489 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
491 bool const success = inset->getStatus(cur, fr, fs);
492 // Every inset is supposed to handle this
493 BOOST_ASSERT(success);
496 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
497 flag |= getStatus(fr);
499 enable = flag.enabled();
503 case LFUN_DIALOG_SHOW: {
504 string const name = cmd.getArg(0);
506 enable = name == "aboutlyx"
510 || name == "texinfo";
511 else if (name == "print")
512 enable = Exporter::isExportable(*buf, "dvi")
513 && lyxrc.print_command != "none";
514 else if (name == "character" || name == "mathpanel")
515 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
516 else if (name == "latexlog")
517 enable = isFileReadable(buf->getLogName().second);
518 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
519 else if (name == "spellchecker")
522 else if (name == "vclog")
523 enable = buf->lyxvc().inUse();
524 else if (name == "view-source")
529 case LFUN_DIALOG_SHOW_NEW_INSET:
530 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
533 case LFUN_DIALOG_UPDATE: {
534 string const name = cmd.getArg(0);
536 enable = name == "prefs";
540 case LFUN_CITATION_INSERT: {
541 FuncRequest fr(LFUN_INSET_INSERT, "citation");
542 enable = getStatus(fr).enabled();
546 case LFUN_BUFFER_WRITE: {
547 enable = view()->buffer()->isUnnamed()
548 || !view()->buffer()->isClean();
552 // this one is difficult to get right. As a half-baked
553 // solution, we consider only the first action of the sequence
554 case LFUN_COMMAND_SEQUENCE: {
555 // argument contains ';'-terminated commands
556 string const firstcmd = token(lyx::to_utf8(cmd.argument()), ';', 0);
557 FuncRequest func(lyxaction.lookupFunc(firstcmd));
558 func.origin = cmd.origin;
559 flag = getStatus(func);
562 case LFUN_BUFFER_NEW:
563 case LFUN_BUFFER_NEW_TEMPLATE:
564 case LFUN_WORD_FIND_FORWARD:
565 case LFUN_WORD_FIND_BACKWARD:
566 case LFUN_COMMAND_PREFIX:
567 case LFUN_COMMAND_EXECUTE:
569 case LFUN_META_PREFIX:
570 case LFUN_BUFFER_CLOSE:
571 case LFUN_BUFFER_WRITE_AS:
572 case LFUN_BUFFER_UPDATE:
573 case LFUN_BUFFER_VIEW:
574 case LFUN_BUFFER_IMPORT:
577 case LFUN_BUFFER_AUTO_SAVE:
578 case LFUN_RECONFIGURE:
582 case LFUN_DROP_LAYOUTS_CHOICE:
584 case LFUN_SERVER_GET_NAME:
585 case LFUN_SERVER_NOTIFY:
586 case LFUN_SERVER_GOTO_FILE_ROW:
587 case LFUN_DIALOG_HIDE:
588 case LFUN_DIALOG_DISCONNECT_INSET:
589 case LFUN_BUFFER_CHILD_OPEN:
590 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
591 case LFUN_KEYMAP_OFF:
592 case LFUN_KEYMAP_PRIMARY:
593 case LFUN_KEYMAP_SECONDARY:
594 case LFUN_KEYMAP_TOGGLE:
596 case LFUN_BUFFER_EXPORT_CUSTOM:
597 case LFUN_BUFFER_PRINT:
598 case LFUN_PREFERENCES_SAVE:
599 case LFUN_SCREEN_FONT_UPDATE:
602 case LFUN_EXTERNAL_EDIT:
603 case LFUN_GRAPHICS_EDIT:
604 case LFUN_ALL_INSETS_TOGGLE:
605 case LFUN_BUFFER_LANGUAGE:
606 case LFUN_TEXTCLASS_APPLY:
607 case LFUN_TEXTCLASS_LOAD:
608 case LFUN_BUFFER_SAVE_AS_DEFAULT:
609 case LFUN_BUFFER_PARAMS_APPLY:
610 case LFUN_LYXRC_APPLY:
611 case LFUN_BUFFER_NEXT:
612 case LFUN_BUFFER_PREVIOUS:
613 // these are handled in our dispatch()
618 if (!::getStatus(cur, cmd, flag))
619 flag = view()->getStatus(cmd);
625 // Can we use a readonly buffer?
626 if (buf && buf->isReadonly()
627 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
628 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
629 flag.message(lyx::from_utf8(N_("Document is read-only")));
633 // Are we in a DELETED change-tracking region?
634 if (buf && lookupChangeType(cur, true) == Change::DELETED
635 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
636 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
637 flag.message(lyx::from_utf8(N_("This portion of the document is deleted.")));
641 // the default error message if we disable the command
642 if (!flag.enabled() && flag.message().empty())
643 flag.message(lyx::from_utf8(N_("Command disabled")));
649 bool LyXFunc::ensureBufferClean(BufferView * bv)
651 Buffer & buf = *bv->buffer();
655 docstring const file = makeDisplayPath(buf.fileName(), 30);
656 docstring text = bformat(_("The document %1$s has unsaved "
657 "changes.\n\nDo you want to save "
658 "the document?"), file);
659 int const ret = Alert::prompt(_("Save changed document?"),
660 text, 0, 1, _("&Save"),
664 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
666 return buf.isClean();
672 void showPrintError(string const & name)
674 docstring str = bformat(_("Could not print the document %1$s.\n"
675 "Check that your printer is set up correctly."),
676 makeDisplayPath(name, 50));
677 Alert::error(_("Print document failed"), str);
681 void loadTextclass(string const & name)
683 std::pair<bool, lyx::textclass_type> const tc_pair =
684 textclasslist.numberOfClass(name);
686 if (!tc_pair.first) {
687 lyxerr << "Document class \"" << name
688 << "\" does not exist."
693 lyx::textclass_type const tc = tc_pair.second;
695 if (!textclasslist[tc].load()) {
696 docstring s = bformat(_("The document could not be converted\n"
697 "into the document class %1$s."),
698 lyx::from_utf8(textclasslist[tc].name()));
699 Alert::error(_("Could not change class"), s);
704 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
709 void LyXFunc::dispatch(FuncRequest const & cmd)
711 BOOST_ASSERT(view());
712 string const argument = lyx::to_utf8(cmd.argument());
713 kb_action const action = cmd.action;
715 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
716 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
718 // we have not done anything wrong yet.
720 dispatch_buffer.erase();
722 // redraw the screen at the end (first of the two drawing steps).
723 //This is done unless explicitely requested otherwise
725 // also do the second redrawing step. Only done if requested.
726 bool updateforce = false;
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());
739 case LFUN_WORD_FIND_FORWARD:
740 case LFUN_WORD_FIND_BACKWARD: {
741 static string last_search;
742 string searched_string;
744 if (!argument.empty()) {
745 last_search = argument;
746 searched_string = argument;
748 searched_string = last_search;
751 if (searched_string.empty())
754 bool const fw = action == LFUN_WORD_FIND_FORWARD;
756 lyx::find::find2string(searched_string, true, false, fw);
757 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
761 case LFUN_COMMAND_PREFIX:
762 owner->message(lyx::from_utf8(keyseq.printOptions()));
765 case LFUN_COMMAND_EXECUTE:
766 owner->getToolbars().display("minibuffer", true);
767 owner->focus_command_buffer();
772 meta_fake_bit = key_modifier::none;
773 if (view()->buffer())
774 // cancel any selection
775 dispatch(FuncRequest(LFUN_MARK_OFF));
776 setMessage(_("Cancel"));
779 case LFUN_META_PREFIX:
780 meta_fake_bit = key_modifier::alt;
781 setMessage(lyx::from_utf8(keyseq.print()));
784 case LFUN_BUFFER_TOGGLE_READ_ONLY:
785 if (owner->buffer()->lyxvc().inUse())
786 owner->buffer()->lyxvc().toggleReadOnly();
788 owner->buffer()->setReadonly(
789 !owner->buffer()->isReadonly());
792 // --- Menus -----------------------------------------------
793 case LFUN_BUFFER_NEW:
794 menuNew(argument, false);
797 case LFUN_BUFFER_NEW_TEMPLATE:
798 menuNew(argument, true);
801 case LFUN_BUFFER_CLOSE:
805 case LFUN_BUFFER_WRITE:
806 if (!owner->buffer()->isUnnamed()) {
807 docstring const str = bformat(_("Saving document %1$s..."),
808 makeDisplayPath(owner->buffer()->fileName()));
810 menuWrite(owner->buffer());
811 owner->message(str + _(" done."));
813 writeAs(owner->buffer());
817 case LFUN_BUFFER_WRITE_AS:
818 writeAs(owner->buffer(), argument);
822 case LFUN_BUFFER_RELOAD: {
823 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
824 docstring text = bformat(_("Any changes will be lost. Are you sure "
825 "you want to revert to the saved version of the document %1$s?"), file);
826 int const ret = Alert::prompt(_("Revert to saved document?"),
827 text, 0, 1, _("&Revert"), _("&Cancel"));
834 case LFUN_BUFFER_UPDATE:
835 Exporter::Export(owner->buffer(), argument, true);
838 case LFUN_BUFFER_VIEW:
839 Exporter::preview(owner->buffer(), argument);
842 case LFUN_BUILD_PROGRAM:
843 Exporter::Export(owner->buffer(), "program", true);
846 case LFUN_BUFFER_CHKTEX:
847 owner->buffer()->runChktex();
850 case LFUN_BUFFER_EXPORT:
851 if (argument == "custom")
852 owner->getDialogs().show("sendto");
854 Exporter::Export(owner->buffer(), argument, false);
858 case LFUN_BUFFER_EXPORT_CUSTOM: {
860 string command = split(argument, format_name, ' ');
861 Format const * format = formats.getFormat(format_name);
863 lyxerr << "Format \"" << format_name
864 << "\" not recognized!"
869 Buffer * buffer = owner->buffer();
871 // The name of the file created by the conversion process
874 // Output to filename
875 if (format->name() == "lyx") {
876 string const latexname =
877 buffer->getLatexName(false);
878 filename = changeExtension(latexname,
879 format->extension());
880 filename = addName(buffer->temppath(), filename);
882 if (!buffer->writeFile(filename))
886 Exporter::Export(buffer, format_name, true, filename);
889 // Substitute $$FName for filename
890 if (!contains(command, "$$FName"))
891 command = "( " + command + " ) < $$FName";
892 command = subst(command, "$$FName", filename);
894 // Execute the command in the background
896 call.startscript(Systemcall::DontWait, command);
900 case LFUN_BUFFER_PRINT: {
903 string command = split(split(argument, target, ' '),
907 || target_name.empty()
908 || command.empty()) {
909 lyxerr << "Unable to parse \""
910 << argument << '"' << std::endl;
913 if (target != "printer" && target != "file") {
914 lyxerr << "Unrecognized target \""
915 << target << '"' << std::endl;
919 Buffer * buffer = owner->buffer();
921 if (!Exporter::Export(buffer, "dvi", true)) {
922 showPrintError(buffer->fileName());
926 // Push directory path.
927 string const path = buffer->temppath();
928 lyx::support::Path p(path);
930 // there are three cases here:
931 // 1. we print to a file
932 // 2. we print directly to a printer
933 // 3. we print using a spool command (print to file first)
936 string const dviname =
937 changeExtension(buffer->getLatexName(true),
940 if (target == "printer") {
941 if (!lyxrc.print_spool_command.empty()) {
942 // case 3: print using a spool
943 string const psname =
944 changeExtension(dviname,".ps");
945 command += lyxrc.print_to_file
948 + quoteName(dviname);
951 lyxrc.print_spool_command +' ';
952 if (target_name != "default") {
953 command2 += lyxrc.print_spool_printerprefix
957 command2 += quoteName(psname);
959 // If successful, then spool command
960 res = one.startscript(
965 res = one.startscript(
966 Systemcall::DontWait,
969 // case 2: print directly to a printer
970 res = one.startscript(
971 Systemcall::DontWait,
972 command + quoteName(dviname));
976 // case 1: print to a file
977 command += lyxrc.print_to_file
978 + quoteName(makeAbsPath(target_name,
981 + quoteName(dviname);
982 res = one.startscript(Systemcall::DontWait,
987 showPrintError(buffer->fileName());
991 case LFUN_BUFFER_IMPORT:
996 if (view()->buffer()) {
997 // save cursor Position for opened files to .lyx/session
998 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
999 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1000 // save bookmarks to .lyx/session
1001 view()->saveSavedPositions();
1003 quitLyX(argument == "force");
1006 case LFUN_TOC_VIEW: {
1007 InsetCommandParams p("tableofcontents");
1008 string const data = InsetCommandMailer::params2string("toc", p);
1009 owner->getDialogs().show("toc", data, 0);
1013 case LFUN_BUFFER_AUTO_SAVE:
1017 case LFUN_RECONFIGURE:
1018 reconfigure(view());
1021 case LFUN_HELP_OPEN: {
1022 string const arg = argument;
1024 setErrorMessage(_("Missing argument"));
1027 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1028 if (fname.empty()) {
1029 lyxerr << "LyX: unable to find documentation file `"
1030 << arg << "'. Bad installation?" << endl;
1033 owner->message(bformat(_("Opening help file %1$s..."),
1034 makeDisplayPath(fname)));
1035 owner->loadLyXFile(fname, false);
1039 // --- version control -------------------------------
1040 case LFUN_VC_REGISTER:
1041 if (!ensureBufferClean(view()))
1043 if (!owner->buffer()->lyxvc().inUse()) {
1044 owner->buffer()->lyxvc().registrer();
1049 case LFUN_VC_CHECK_IN:
1050 if (!ensureBufferClean(view()))
1052 if (owner->buffer()->lyxvc().inUse()
1053 && !owner->buffer()->isReadonly()) {
1054 owner->buffer()->lyxvc().checkIn();
1059 case LFUN_VC_CHECK_OUT:
1060 if (!ensureBufferClean(view()))
1062 if (owner->buffer()->lyxvc().inUse()
1063 && owner->buffer()->isReadonly()) {
1064 owner->buffer()->lyxvc().checkOut();
1069 case LFUN_VC_REVERT:
1070 owner->buffer()->lyxvc().revert();
1074 case LFUN_VC_UNDO_LAST:
1075 owner->buffer()->lyxvc().undoLast();
1079 // --- buffers ----------------------------------------
1080 case LFUN_BUFFER_SWITCH:
1081 owner->setBuffer(theApp->bufferList().getBuffer(argument));
1084 case LFUN_BUFFER_NEXT:
1085 owner->setBuffer(theApp->bufferList().next(view()->buffer()));
1088 case LFUN_BUFFER_PREVIOUS:
1089 owner->setBuffer(theApp->bufferList().previous(view()->buffer()));
1093 newFile(view(), argument);
1096 case LFUN_FILE_OPEN:
1100 case LFUN_DROP_LAYOUTS_CHOICE:
1101 owner->getToolbars().openLayoutList();
1104 case LFUN_MENU_OPEN:
1105 owner->getMenubar().openByName(lyx::from_utf8(argument));
1108 // --- lyxserver commands ----------------------------
1109 case LFUN_SERVER_GET_NAME:
1110 setMessage(lyx::from_utf8(owner->buffer()->fileName()));
1111 lyxerr[Debug::INFO] << "FNAME["
1112 << owner->buffer()->fileName()
1116 case LFUN_SERVER_NOTIFY:
1117 dispatch_buffer = lyx::from_utf8(keyseq.print());
1118 theApp->server().notifyClient(lyx::to_utf8(dispatch_buffer));
1121 case LFUN_SERVER_GOTO_FILE_ROW: {
1124 istringstream is(argument);
1125 is >> file_name >> row;
1126 if (prefixIs(file_name, package().temp_dir())) {
1127 // Needed by inverse dvi search. If it is a file
1128 // in tmpdir, call the apropriated function
1129 owner->setBuffer(theApp->bufferList().getBufferFromTmp(file_name));
1131 // Must replace extension of the file to be .lyx
1132 // and get full path
1133 string const s = changeExtension(file_name, ".lyx");
1134 // Either change buffer or load the file
1135 if (theApp->bufferList().exists(s)) {
1136 owner->setBuffer(theApp->bufferList().getBuffer(s));
1138 owner->loadLyXFile(s);
1142 view()->setCursorFromRow(row);
1145 // see BufferView::center()
1149 case LFUN_DIALOG_SHOW: {
1150 string const name = cmd.getArg(0);
1151 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1153 if (name == "character") {
1154 data = freefont2string();
1156 owner->getDialogs().show("character", data);
1157 } else if (name == "latexlog") {
1158 pair<Buffer::LogType, string> const logfile =
1159 owner->buffer()->getLogName();
1160 switch (logfile.first) {
1161 case Buffer::latexlog:
1164 case Buffer::buildlog:
1168 data += LyXLex::quoteString(logfile.second);
1169 owner->getDialogs().show("log", data);
1170 } else if (name == "vclog") {
1171 string const data = "vc " +
1172 LyXLex::quoteString(owner->buffer()->lyxvc().getLogFile());
1173 owner->getDialogs().show("log", data);
1175 owner->getDialogs().show(name, data);
1179 case LFUN_DIALOG_SHOW_NEW_INSET: {
1180 string const name = cmd.getArg(0);
1181 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1182 if (name == "bibitem" ||
1189 InsetCommandParams p(name);
1190 data = InsetCommandMailer::params2string(name, p);
1191 } else if (name == "include") {
1192 InsetCommandParams p(data);
1193 data = InsetIncludeMailer::params2string(p);
1194 } else if (name == "box") {
1195 // \c data == "Boxed" || "Frameless" etc
1196 InsetBoxParams p(data);
1197 data = InsetBoxMailer::params2string(p);
1198 } else if (name == "branch") {
1199 InsetBranchParams p;
1200 data = InsetBranchMailer::params2string(p);
1201 } else if (name == "citation") {
1202 InsetCommandParams p("cite");
1203 data = InsetCommandMailer::params2string(name, p);
1204 } else if (name == "ert") {
1205 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1206 } else if (name == "external") {
1207 InsetExternalParams p;
1208 Buffer const & buffer = *owner->buffer();
1209 data = InsetExternalMailer::params2string(p, buffer);
1210 } else if (name == "float") {
1212 data = InsetFloatMailer::params2string(p);
1213 } else if (name == "graphics") {
1214 InsetGraphicsParams p;
1215 Buffer const & buffer = *owner->buffer();
1216 data = InsetGraphicsMailer::params2string(p, buffer);
1217 } else if (name == "note") {
1219 data = InsetNoteMailer::params2string(p);
1220 } else if (name == "vspace") {
1222 data = InsetVSpaceMailer::params2string(space);
1223 } else if (name == "wrap") {
1225 data = InsetWrapMailer::params2string(p);
1227 owner->getDialogs().show(name, data, 0);
1231 case LFUN_DIALOG_UPDATE: {
1232 string const & name = argument;
1233 // Can only update a dialog connected to an existing inset
1234 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1236 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1237 inset->dispatch(view()->cursor(), fr);
1238 } else if (name == "paragraph") {
1239 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1240 } else if (name == "prefs") {
1241 owner->getDialogs().update(name, string());
1246 case LFUN_DIALOG_HIDE:
1247 Dialogs::hide(argument, 0);
1250 case LFUN_DIALOG_DISCONNECT_INSET:
1251 owner->getDialogs().disconnect(argument);
1255 case LFUN_CITATION_INSERT: {
1256 if (!argument.empty()) {
1257 // we can have one optional argument, delimited by '|'
1258 // citation-insert <key>|<text_before>
1259 // this should be enhanced to also support text_after
1260 // and citation style
1261 string arg = argument;
1263 if (contains(argument, "|")) {
1264 arg = token(argument, '|', 0);
1265 opt1 = '[' + token(argument, '|', 1) + ']';
1267 std::ostringstream os;
1268 os << "citation LatexCommand\n"
1269 << "\\cite" << opt1 << "{" << arg << "}\n"
1271 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1274 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1278 case LFUN_BUFFER_CHILD_OPEN: {
1279 string const filename =
1280 makeAbsPath(argument, owner->buffer()->filePath());
1281 // FIXME Should use bformat
1282 setMessage(_("Opening child document ") +
1283 makeDisplayPath(filename) + lyx::from_ascii("..."));
1284 view()->savePosition(0);
1285 string const parentfilename = owner->buffer()->fileName();
1286 if (theApp->bufferList().exists(filename))
1287 owner->setBuffer(theApp->bufferList().getBuffer(filename));
1289 owner->loadLyXFile(filename);
1290 // Set the parent name of the child document.
1291 // This makes insertion of citations and references in the child work,
1292 // when the target is in the parent or another child document.
1293 owner->buffer()->setParentName(parentfilename);
1297 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1298 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1301 case LFUN_KEYMAP_OFF:
1302 owner->view()->getIntl().keyMapOn(false);
1305 case LFUN_KEYMAP_PRIMARY:
1306 owner->view()->getIntl().keyMapPrim();
1309 case LFUN_KEYMAP_SECONDARY:
1310 owner->view()->getIntl().keyMapSec();
1313 case LFUN_KEYMAP_TOGGLE:
1314 owner->view()->getIntl().toggleKeyMap();
1320 string rest = split(argument, countstr, ' ');
1321 istringstream is(countstr);
1324 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1325 for (int i = 0; i < count; ++i)
1326 dispatch(lyxaction.lookupFunc(rest));
1330 case LFUN_COMMAND_SEQUENCE: {
1331 // argument contains ';'-terminated commands
1332 string arg = argument;
1333 while (!arg.empty()) {
1335 arg = split(arg, first, ';');
1336 FuncRequest func(lyxaction.lookupFunc(first));
1337 func.origin = cmd.origin;
1343 case LFUN_PREFERENCES_SAVE: {
1344 lyx::support::Path p(package().user_support());
1345 lyxrc.write("preferences", false);
1349 case LFUN_SCREEN_FONT_UPDATE:
1350 // handle the screen font changes.
1351 lyxrc.set_font_norm_type();
1352 theApp->fontLoader().update();
1353 // All visible buffers will need resize
1357 case LFUN_SET_COLOR: {
1359 string const x11_name = split(argument, lyx_name, ' ');
1360 if (lyx_name.empty() || x11_name.empty()) {
1361 setErrorMessage(_("Syntax: set-color <lyx_name>"
1366 bool const graphicsbg_changed =
1367 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1368 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1370 if (!lcolor.setColor(lyx_name, x11_name)) {
1372 bformat(_("Set-color \"%1$s\" failed "
1373 "- color is undefined or "
1374 "may not be redefined"),
1375 lyx::from_utf8(lyx_name)));
1379 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1381 if (graphicsbg_changed) {
1382 #ifdef WITH_WARNINGS
1383 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1386 lyx::graphics::GCache::get().changeDisplay(true);
1393 owner->message(lyx::from_utf8(argument));
1396 case LFUN_EXTERNAL_EDIT: {
1397 FuncRequest fr(action, argument);
1398 InsetExternal().dispatch(view()->cursor(), fr);
1402 case LFUN_GRAPHICS_EDIT: {
1403 FuncRequest fr(action, argument);
1404 InsetGraphics().dispatch(view()->cursor(), fr);
1408 case LFUN_INSET_APPLY: {
1409 string const name = cmd.getArg(0);
1410 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1412 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1413 inset->dispatch(view()->cursor(), fr);
1415 FuncRequest fr(LFUN_INSET_INSERT, argument);
1418 // ideally, the update flag should be set by the insets,
1419 // but this is not possible currently
1424 case LFUN_ALL_INSETS_TOGGLE: {
1426 string const name = split(argument, action, ' ');
1427 InsetBase::Code const inset_code =
1428 InsetBase::translate(name);
1430 LCursor & cur = view()->cursor();
1431 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1433 InsetBase & inset = owner->buffer()->inset();
1434 InsetIterator it = inset_iterator_begin(inset);
1435 InsetIterator const end = inset_iterator_end(inset);
1436 for (; it != end; ++it) {
1437 if (inset_code == InsetBase::NO_CODE
1438 || inset_code == it->lyxCode()) {
1439 LCursor tmpcur = cur;
1440 tmpcur.pushLeft(*it);
1441 it->dispatch(tmpcur, fr);
1448 case LFUN_BUFFER_LANGUAGE: {
1449 Buffer & buffer = *owner->buffer();
1450 Language const * oldL = buffer.params().language;
1451 Language const * newL = languages.getLanguage(argument);
1452 if (!newL || oldL == newL)
1455 if (oldL->rightToLeft() == newL->rightToLeft()
1456 && !buffer.isMultiLingual())
1457 buffer.changeLanguage(oldL, newL);
1459 buffer.updateDocLang(newL);
1463 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1464 string const fname =
1465 addName(addPath(package().user_support(), "templates/"),
1467 Buffer defaults(fname);
1469 istringstream ss(argument);
1472 int const unknown_tokens = defaults.readHeader(lex);
1474 if (unknown_tokens != 0) {
1475 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1476 << unknown_tokens << " unknown token"
1477 << (unknown_tokens == 1 ? "" : "s")
1481 if (defaults.writeFile(defaults.fileName()))
1482 // FIXME Should use bformat
1483 setMessage(_("Document defaults saved in ")
1484 + makeDisplayPath(fname));
1486 setErrorMessage(_("Unable to save document defaults"));
1490 case LFUN_BUFFER_PARAMS_APPLY: {
1491 biblio::CiteEngine const engine =
1492 owner->buffer()->params().cite_engine;
1494 istringstream ss(argument);
1497 int const unknown_tokens =
1498 owner->buffer()->readHeader(lex);
1500 if (unknown_tokens != 0) {
1501 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1502 << unknown_tokens << " unknown token"
1503 << (unknown_tokens == 1 ? "" : "s")
1506 if (engine == owner->buffer()->params().cite_engine)
1509 LCursor & cur = view()->cursor();
1510 FuncRequest fr(LFUN_INSET_REFRESH);
1512 InsetBase & inset = owner->buffer()->inset();
1513 InsetIterator it = inset_iterator_begin(inset);
1514 InsetIterator const end = inset_iterator_end(inset);
1515 for (; it != end; ++it)
1516 if (it->lyxCode() == InsetBase::CITE_CODE)
1517 it->dispatch(cur, fr);
1521 case LFUN_TEXTCLASS_APPLY: {
1522 Buffer * buffer = owner->buffer();
1524 lyx::textclass_type const old_class =
1525 buffer->params().textclass;
1527 loadTextclass(argument);
1529 std::pair<bool, lyx::textclass_type> const tc_pair =
1530 textclasslist.numberOfClass(argument);
1535 lyx::textclass_type const new_class = tc_pair.second;
1536 if (old_class == new_class)
1540 owner->message(_("Converting document to new document class..."));
1541 recordUndoFullDocument(view());
1542 buffer->params().textclass = new_class;
1543 StableDocIterator backcur(view()->cursor());
1544 ErrorList & el = buffer->errorList("Class Switch");
1545 lyx::cap::switchBetweenClasses(
1546 old_class, new_class,
1547 static_cast<InsetText &>(buffer->inset()), el);
1549 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1551 buffer->errors("Class Switch");
1552 updateLabels(*buffer);
1557 case LFUN_TEXTCLASS_LOAD:
1558 loadTextclass(argument);
1561 case LFUN_LYXRC_APPLY: {
1562 LyXRC const lyxrc_orig = lyxrc;
1564 istringstream ss(argument);
1565 bool const success = lyxrc.read(ss) == 0;
1568 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1569 << "Unable to read lyxrc data"
1574 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1579 view()->cursor().dispatch(cmd);
1580 updateforce |= view()->cursor().result().update();
1581 if (!view()->cursor().result().dispatched())
1582 updateforce |= view()->dispatch(cmd);
1587 if (view()->buffer()) {
1588 // Redraw screen unless explicitly told otherwise.
1589 // This also initializes the position cache for all insets
1590 // in (at least partially) visible top-level paragraphs.
1592 view()->update(Update::FitCursor | Update::Force);
1594 view()->update(Update::FitCursor);
1596 owner->redrawWorkArea();
1598 // if we executed a mutating lfun, mark the buffer as dirty
1600 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1601 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1602 view()->buffer()->markDirty();
1604 if (view()->cursor().inTexted()) {
1605 owner->updateLayoutChoice();
1610 // FIXME UNICODE: _() does not support anything but ascii.
1611 // Do we need a lyx::to_ascii() method?
1612 sendDispatchMessage(getMessage(), cmd);
1616 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1618 /* When an action did not originate from the UI/kbd, it makes
1619 * sense to avoid updating the GUI. It turns out that this
1620 * fixes bug 1941, for reasons that are described here:
1621 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1623 if (cmd.origin != FuncRequest::INTERNAL) {
1624 owner->updateMenubar();
1625 owner->updateToolbars();
1628 const bool verbose = (cmd.origin == FuncRequest::UI
1629 || cmd.origin == FuncRequest::COMMANDBUFFER);
1631 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1632 lyxerr[Debug::ACTION] << "dispatch msg is " << lyx::to_utf8(msg) << endl;
1634 owner->message(msg);
1638 docstring dispatch_msg = msg;
1639 if (!dispatch_msg.empty())
1640 dispatch_msg += ' ';
1642 string comname = lyxaction.getActionName(cmd.action);
1644 bool argsadded = false;
1646 if (!cmd.argument().empty()) {
1647 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1648 comname += ' ' + lyx::to_utf8(cmd.argument());
1653 string const shortcuts = toplevel_keymap->printbindings(cmd);
1655 if (!shortcuts.empty())
1656 comname += ": " + shortcuts;
1657 else if (!argsadded && !cmd.argument().empty())
1658 comname += ' ' + lyx::to_utf8(cmd.argument());
1660 if (!comname.empty()) {
1661 comname = rtrim(comname);
1662 dispatch_msg += lyx::from_utf8('(' + rtrim(comname) + ')');
1665 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1666 << lyx::to_utf8(dispatch_msg) << endl;
1667 if (!dispatch_msg.empty())
1668 owner->message(dispatch_msg);
1672 void LyXFunc::setupLocalKeymap()
1674 keyseq.stdmap = toplevel_keymap.get();
1675 keyseq.curmap = toplevel_keymap.get();
1676 cancel_meta_seq.stdmap = toplevel_keymap.get();
1677 cancel_meta_seq.curmap = toplevel_keymap.get();
1681 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1683 string initpath = lyxrc.document_path;
1684 string filename(name);
1686 if (view()->buffer()) {
1687 string const trypath = owner->buffer()->filePath();
1688 // If directory is writeable, use this as default.
1689 if (isDirWriteable(trypath))
1693 static int newfile_number;
1695 if (filename.empty()) {
1696 filename = addName(lyxrc.document_path,
1697 "newfile" + convert<string>(++newfile_number) + ".lyx");
1698 while (theApp->bufferList().exists(filename) || fs::is_readable(filename)) {
1700 filename = addName(lyxrc.document_path,
1701 "newfile" + convert<string>(newfile_number) +
1706 // The template stuff
1709 FileDialog fileDlg(lyx::to_utf8(_("Select template file")),
1710 LFUN_SELECT_FILE_SYNC,
1711 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1712 string(lyxrc.document_path)),
1713 make_pair(string(lyx::to_utf8(_("Templates|#T#t"))),
1714 string(lyxrc.template_path)));
1716 FileDialog::Result result =
1717 fileDlg.open(lyxrc.template_path,
1718 FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1721 if (result.first == FileDialog::Later)
1723 if (result.second.empty())
1725 templname = result.second;
1728 Buffer * const b = newFile(filename, templname, !name.empty());
1730 owner->setBuffer(b);
1734 void LyXFunc::open(string const & fname)
1736 string initpath = lyxrc.document_path;
1738 if (view()->buffer()) {
1739 string const trypath = owner->buffer()->filePath();
1740 // If directory is writeable, use this as default.
1741 if (isDirWriteable(trypath))
1747 if (fname.empty()) {
1748 FileDialog fileDlg(lyx::to_utf8(_("Select document to open")),
1750 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1751 string(lyxrc.document_path)),
1752 make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1753 string(addPath(package().system_support(), "examples"))));
1755 FileDialog::Result result =
1756 fileDlg.open(initpath,
1757 FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1760 if (result.first == FileDialog::Later)
1763 filename = result.second;
1765 // check selected filename
1766 if (filename.empty()) {
1767 owner->message(_("Canceled."));
1773 // get absolute path of file and add ".lyx" to the filename if
1775 string const fullpath = fileSearch(string(), filename, "lyx");
1776 if (!fullpath.empty()) {
1777 filename = fullpath;
1780 docstring const disp_fn = makeDisplayPath(filename);
1782 // if the file doesn't exist, let the user create one
1783 if (!fs::exists(filename)) {
1784 // the user specifically chose this name. Believe him.
1785 Buffer * const b = newFile(filename, string(), true);
1787 owner->setBuffer(b);
1791 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1794 if (owner->loadLyXFile(filename)) {
1795 str2 = bformat(_("Document %1$s opened."), disp_fn);
1797 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1799 owner->message(str2);
1803 void LyXFunc::doImport(string const & argument)
1806 string filename = split(argument, format, ' ');
1808 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1809 << " file: " << filename << endl;
1811 // need user interaction
1812 if (filename.empty()) {
1813 string initpath = lyxrc.document_path;
1815 if (view()->buffer()) {
1816 string const trypath = owner->buffer()->filePath();
1817 // If directory is writeable, use this as default.
1818 if (isDirWriteable(trypath))
1822 docstring const text = bformat(_("Select %1$s file to import"),
1823 formats.prettyName(format));
1825 FileDialog fileDlg(lyx::to_utf8(text),
1827 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1828 string(lyxrc.document_path)),
1829 make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1830 string(addPath(package().system_support(), "examples"))));
1832 string const filter = lyx::to_utf8(formats.prettyName(format))
1833 + " (*." + formats.extension(format) + ')';
1835 FileDialog::Result result =
1836 fileDlg.open(initpath,
1837 FileFilterList(filter),
1840 if (result.first == FileDialog::Later)
1843 filename = result.second;
1845 // check selected filename
1846 if (filename.empty())
1847 owner->message(_("Canceled."));
1850 if (filename.empty())
1853 // get absolute path of file
1854 filename = makeAbsPath(filename);
1856 string const lyxfile = changeExtension(filename, ".lyx");
1858 // Check if the document already is open
1859 if (lyx_gui::use_gui && theApp->bufferList().exists(lyxfile)) {
1860 if (!theApp->bufferList().close(theApp->bufferList().getBuffer(lyxfile), true)) {
1861 owner->message(_("Canceled."));
1866 // if the file exists already, and we didn't do
1867 // -i lyx thefile.lyx, warn
1868 if (fs::exists(lyxfile) && filename != lyxfile) {
1869 docstring const file = makeDisplayPath(lyxfile, 30);
1871 docstring text = bformat(_("The document %1$s already exists.\n\n"
1872 "Do you want to over-write that document?"), file);
1873 int const ret = Alert::prompt(_("Over-write document?"),
1874 text, 0, 1, _("&Over-write"), _("&Cancel"));
1877 owner->message(_("Canceled."));
1882 ErrorList errorList;
1883 Importer::Import(owner, filename, format, errorList);
1884 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1888 void LyXFunc::closeBuffer()
1890 // save current cursor position
1891 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1892 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1893 if (theApp->bufferList().close(owner->buffer(), true) && !quitting) {
1894 if (theApp->bufferList().empty()) {
1895 // need this otherwise SEGV may occur while
1896 // trying to set variables that don't exist
1897 // since there's no current buffer
1898 owner->getDialogs().hideBufferDependent();
1900 owner->setBuffer(theApp->bufferList().first());
1906 // Each "owner" should have it's own message method. lyxview and
1907 // the minibuffer would use the minibuffer, but lyxserver would
1908 // send an ERROR signal to its client. Alejandro 970603
1909 // This function is bit problematic when it comes to NLS, to make the
1910 // lyx servers client be language indepenent we must not translate
1911 // strings sent to this func.
1912 void LyXFunc::setErrorMessage(docstring const & m) const
1914 dispatch_buffer = m;
1919 void LyXFunc::setMessage(docstring const & m) const
1921 dispatch_buffer = m;
1925 string const LyXFunc::viewStatusMessage()
1927 // When meta-fake key is pressed, show the key sequence so far + "M-".
1929 return keyseq.print() + "M-";
1931 // Else, when a non-complete key sequence is pressed,
1932 // show the available options.
1933 if (keyseq.length() > 0 && !keyseq.deleted())
1934 return keyseq.printOptions();
1936 if (!view()->buffer())
1937 return lyx::to_utf8(_("Welcome to LyX!"));
1939 return view()->cursor().currentState();
1943 BufferView * LyXFunc::view() const
1945 BOOST_ASSERT(owner);
1946 return owner->view();
1950 bool LyXFunc::wasMetaKey() const
1952 return (meta_fake_bit != key_modifier::none);
1958 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1960 // Why the switch you might ask. It is a trick to ensure that all
1961 // the elements in the LyXRCTags enum is handled. As you can see
1962 // there are no breaks at all. So it is just a huge fall-through.
1963 // The nice thing is that we will get a warning from the compiler
1964 // if we forget an element.
1965 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1967 case LyXRC::RC_ACCEPT_COMPOUND:
1968 case LyXRC::RC_ALT_LANG:
1969 case LyXRC::RC_ASCIIROFF_COMMAND:
1970 case LyXRC::RC_ASCII_LINELEN:
1971 case LyXRC::RC_AUTOREGIONDELETE:
1972 case LyXRC::RC_AUTORESET_OPTIONS:
1973 case LyXRC::RC_AUTOSAVE:
1974 case LyXRC::RC_AUTO_NUMBER:
1975 case LyXRC::RC_BACKUPDIR_PATH:
1976 case LyXRC::RC_BIBTEX_COMMAND:
1977 case LyXRC::RC_BINDFILE:
1978 case LyXRC::RC_CHECKLASTFILES:
1979 case LyXRC::RC_USELASTFILEPOS:
1980 case LyXRC::RC_LOADSESSION:
1981 case LyXRC::RC_CHKTEX_COMMAND:
1982 case LyXRC::RC_CONVERTER:
1983 case LyXRC::RC_COPIER:
1984 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1985 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1986 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1987 case LyXRC::RC_DATE_INSERT_FORMAT:
1988 case LyXRC::RC_DEFAULT_LANGUAGE:
1989 case LyXRC::RC_DEFAULT_PAPERSIZE:
1990 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1991 case LyXRC::RC_DISPLAY_GRAPHICS:
1992 case LyXRC::RC_DOCUMENTPATH:
1993 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1994 if (fs::exists(lyxrc_new.document_path) &&
1995 fs::is_directory(lyxrc_new.document_path)) {
1996 using lyx::support::package;
1997 package().document_dir() = lyxrc.document_path;
2000 case LyXRC::RC_ESC_CHARS:
2001 case LyXRC::RC_FONT_ENCODING:
2002 case LyXRC::RC_FORMAT:
2003 case LyXRC::RC_INDEX_COMMAND:
2004 case LyXRC::RC_INPUT:
2005 case LyXRC::RC_KBMAP:
2006 case LyXRC::RC_KBMAP_PRIMARY:
2007 case LyXRC::RC_KBMAP_SECONDARY:
2008 case LyXRC::RC_LABEL_INIT_LENGTH:
2009 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2010 case LyXRC::RC_LANGUAGE_AUTO_END:
2011 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2012 case LyXRC::RC_LANGUAGE_COMMAND_END:
2013 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2014 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2015 case LyXRC::RC_LANGUAGE_PACKAGE:
2016 case LyXRC::RC_LANGUAGE_USE_BABEL:
2017 case LyXRC::RC_MAKE_BACKUP:
2018 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2019 case LyXRC::RC_NUMLASTFILES:
2020 case LyXRC::RC_PATH_PREFIX:
2021 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2022 using lyx::support::prependEnvPath;
2023 prependEnvPath("PATH", lyxrc.path_prefix);
2025 case LyXRC::RC_PERS_DICT:
2026 case LyXRC::RC_POPUP_BOLD_FONT:
2027 case LyXRC::RC_POPUP_FONT_ENCODING:
2028 case LyXRC::RC_POPUP_NORMAL_FONT:
2029 case LyXRC::RC_PREVIEW:
2030 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2031 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2032 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2033 case LyXRC::RC_PRINTCOPIESFLAG:
2034 case LyXRC::RC_PRINTER:
2035 case LyXRC::RC_PRINTEVENPAGEFLAG:
2036 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2037 case LyXRC::RC_PRINTFILEEXTENSION:
2038 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2039 case LyXRC::RC_PRINTODDPAGEFLAG:
2040 case LyXRC::RC_PRINTPAGERANGEFLAG:
2041 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2042 case LyXRC::RC_PRINTPAPERFLAG:
2043 case LyXRC::RC_PRINTREVERSEFLAG:
2044 case LyXRC::RC_PRINTSPOOL_COMMAND:
2045 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2046 case LyXRC::RC_PRINTTOFILE:
2047 case LyXRC::RC_PRINTTOPRINTER:
2048 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2049 case LyXRC::RC_PRINT_COMMAND:
2050 case LyXRC::RC_RTL_SUPPORT:
2051 case LyXRC::RC_SCREEN_DPI:
2052 case LyXRC::RC_SCREEN_FONT_ENCODING:
2053 case LyXRC::RC_SCREEN_FONT_ROMAN:
2054 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2055 case LyXRC::RC_SCREEN_FONT_SANS:
2056 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2057 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2058 case LyXRC::RC_SCREEN_FONT_SIZES:
2059 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2060 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2061 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2062 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2063 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2064 case LyXRC::RC_SCREEN_ZOOM:
2065 case LyXRC::RC_SERVERPIPE:
2066 case LyXRC::RC_SET_COLOR:
2067 case LyXRC::RC_SHOW_BANNER:
2068 case LyXRC::RC_SPELL_COMMAND:
2069 case LyXRC::RC_TEMPDIRPATH:
2070 case LyXRC::RC_TEMPLATEPATH:
2071 case LyXRC::RC_TEX_ALLOWS_SPACES:
2072 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2073 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2074 namespace os = lyx::support::os;
2075 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2077 case LyXRC::RC_UIFILE:
2078 case LyXRC::RC_USER_EMAIL:
2079 case LyXRC::RC_USER_NAME:
2080 case LyXRC::RC_USETEMPDIR:
2081 case LyXRC::RC_USE_ALT_LANG:
2082 case LyXRC::RC_USE_ESC_CHARS:
2083 case LyXRC::RC_USE_INP_ENC:
2084 case LyXRC::RC_USE_PERS_DICT:
2085 case LyXRC::RC_USE_SPELL_LIB:
2086 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2087 case LyXRC::RC_VIEWER:
2088 case LyXRC::RC_LAST: