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 Alert = lyx::frontend::Alert;
140 namespace biblio = lyx::biblio;
141 namespace fs = boost::filesystem;
144 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
147 extern tex_accent_struct get_accent(kb_action action);
152 bool getStatus(LCursor cursor,
153 FuncRequest const & cmd, FuncStatus & status)
155 // Try to fix cursor in case it is broken.
156 cursor.fixIfBroken();
158 // This is, of course, a mess. Better create a new doc iterator and use
159 // this in Inset::getStatus. This might require an additional
160 // BufferView * arg, though (which should be avoided)
161 //LCursor safe = *this;
163 for ( ; cursor.depth(); cursor.pop()) {
164 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
165 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
166 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
167 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
169 // The inset's getStatus() will return 'true' if it made
170 // a definitive decision on whether it want to handle the
171 // request or not. The result of this decision is put into
172 // the 'status' parameter.
173 if (cursor.inset().getStatus(cursor, cmd, status)) {
182 /** Return the change status at cursor position, taking in account the
183 * status at each level of the document iterator (a table in a deleted
184 * footnote is deleted).
185 * When \param outer is true, the top slice is not looked at.
187 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
189 size_t const depth = dit.depth() - (outer ? 1 : 0);
191 for (size_t i = 0 ; i < depth ; ++i) {
192 CursorSlice const & slice = dit[i];
193 if (!slice.inset().inMathed()
194 && slice.pos() < slice.paragraph().size()) {
195 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
196 if (ch != Change::UNCHANGED)
200 return Change::UNCHANGED;
205 LyXFunc::LyXFunc(LyXView * lv)
208 keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
209 cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
210 meta_fake_bit(key_modifier::none)
215 void LyXFunc::handleKeyFunc(kb_action action)
217 char c = encoded_last_key;
219 if (keyseq.length()) {
223 owner->view()->getIntl().getTransManager()
224 .deadkey(c, get_accent(action).accent, view()->getLyXText());
225 // Need to clear, in case the minibuffer calls these
228 // copied verbatim from do_accent_char
229 view()->cursor().resetAnchor();
234 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
236 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
238 // Do nothing if we have nothing (JMarc)
239 if (!keysym->isOK()) {
240 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
245 if (keysym->isModifier()) {
246 lyxerr[Debug::KEY] << "isModifier true" << endl;
250 Encoding const * encoding = view()->cursor().getEncoding();
252 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
253 size_t encoded_last_key = keysym->getUCSEncoded();
255 // Do a one-deep top-level lookup for
256 // cancel and meta-fake keys. RVDK_PATCH_5
257 cancel_meta_seq.reset();
259 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
260 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
261 << " action first set to [" << func.action << ']'
264 // When not cancel or meta-fake, do the normal lookup.
265 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
266 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
267 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
268 // remove Caps Lock and Mod2 as a modifiers
269 func = keyseq.addkey(keysym, (state | meta_fake_bit));
270 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
271 << "action now set to ["
272 << func.action << ']' << endl;
275 // Dont remove this unless you know what you are doing.
276 meta_fake_bit = key_modifier::none;
278 // Can this happen now ?
279 if (func.action == LFUN_NOACTION) {
280 func = FuncRequest(LFUN_COMMAND_PREFIX);
283 if (lyxerr.debugging(Debug::KEY)) {
284 lyxerr << BOOST_CURRENT_FUNCTION
286 << func.action << "]["
287 << keyseq.print() << ']'
291 // already here we know if it any point in going further
292 // why not return already here if action == -1 and
293 // num_bytes == 0? (Lgb)
295 if (keyseq.length() > 1) {
296 owner->message(lyx::from_utf8(keyseq.print()));
300 // Maybe user can only reach the key via holding down shift.
301 // Let's see. But only if shift is the only modifier
302 if (func.action == LFUN_UNKNOWN_ACTION &&
303 state == key_modifier::shift) {
304 lyxerr[Debug::KEY] << "Trying without shift" << endl;
305 func = keyseq.addkey(keysym, key_modifier::none);
306 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
309 if (func.action == LFUN_UNKNOWN_ACTION) {
310 // Hmm, we didn't match any of the keysequences. See
311 // if it's normal insertable text not already covered
313 if (keysym->isText() && keyseq.length() == 1) {
314 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
315 func = FuncRequest(LFUN_SELF_INSERT,
316 FuncRequest::KEYBOARD);
318 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
319 owner->message(_("Unknown function."));
324 if (func.action == LFUN_SELF_INSERT) {
325 if (encoded_last_key != 0) {
326 docstring const arg(1, encoded_last_key);
327 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
328 FuncRequest::KEYBOARD));
330 << "SelfInsert arg[`" << lyx::to_utf8(arg) << "']" << endl;
338 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
340 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
342 LCursor & cur = view()->cursor();
344 /* In LyX/Mac, when a dialog is open, the menus of the
345 application can still be accessed without giving focus to
346 the main window. In this case, we want to disable the menu
347 entries that are buffer-related.
349 Note that this code is not perfect, as bug 1941 attests:
350 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
353 if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
356 buf = owner->buffer();
358 if (cmd.action == LFUN_NOACTION) {
359 flag.message(lyx::from_utf8(N_("Nothing to do")));
364 switch (cmd.action) {
365 case LFUN_UNKNOWN_ACTION:
366 #ifndef HAVE_LIBAIKSAURUS
367 case LFUN_THESAURUS_ENTRY:
377 if (flag.unknown()) {
378 flag.message(lyx::from_utf8(N_("Unknown action")));
382 if (!flag.enabled()) {
383 if (flag.message().empty())
384 flag.message(lyx::from_utf8(N_("Command disabled")));
388 // Check whether we need a buffer
389 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
391 flag.message(lyx::from_utf8(N_("Command not allowed with"
392 "out any document open")));
397 // I would really like to avoid having this switch and rather try to
398 // encode this in the function itself.
399 // -- And I'd rather let an inset decide which LFUNs it is willing
400 // to handle (Andre')
402 switch (cmd.action) {
403 case LFUN_BUFFER_TOGGLE_READ_ONLY:
404 flag.setOnOff(buf->isReadonly());
407 case LFUN_BUFFER_SWITCH:
408 // toggle on the current buffer, but do not toggle off
409 // the other ones (is that a good idea?)
410 if (lyx::to_utf8(cmd.argument()) == buf->fileName())
414 case LFUN_BUFFER_EXPORT:
415 enable = cmd.argument() == "custom"
416 || Exporter::isExportable(*buf, lyx::to_utf8(cmd.argument()));
419 case LFUN_BUFFER_CHKTEX:
420 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
423 case LFUN_BUILD_PROGRAM:
424 enable = Exporter::isExportable(*buf, "program");
427 case LFUN_LAYOUT_TABULAR:
428 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
432 case LFUN_LAYOUT_PARAGRAPH:
433 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
436 case LFUN_VC_REGISTER:
437 enable = !buf->lyxvc().inUse();
439 case LFUN_VC_CHECK_IN:
440 enable = buf->lyxvc().inUse() && !buf->isReadonly();
442 case LFUN_VC_CHECK_OUT:
443 enable = buf->lyxvc().inUse() && buf->isReadonly();
446 case LFUN_VC_UNDO_LAST:
447 enable = buf->lyxvc().inUse();
449 case LFUN_BUFFER_RELOAD:
450 enable = !buf->isUnnamed() && !buf->isClean();
453 case LFUN_INSET_SETTINGS: {
457 InsetBase::Code code = cur.inset().lyxCode();
459 case InsetBase::TABULAR_CODE:
460 enable = cmd.argument() == "tabular";
462 case InsetBase::ERT_CODE:
463 enable = cmd.argument() == "ert";
465 case InsetBase::FLOAT_CODE:
466 enable = cmd.argument() == "float";
468 case InsetBase::WRAP_CODE:
469 enable = cmd.argument() == "wrap";
471 case InsetBase::NOTE_CODE:
472 enable = cmd.argument() == "note";
474 case InsetBase::BRANCH_CODE:
475 enable = cmd.argument() == "branch";
477 case InsetBase::BOX_CODE:
478 enable = cmd.argument() == "box";
486 case LFUN_INSET_APPLY: {
487 string const name = cmd.getArg(0);
488 InsetBase * inset = owner->getDialogs().getOpenInset(name);
490 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
492 bool const success = inset->getStatus(cur, fr, fs);
493 // Every inset is supposed to handle this
494 BOOST_ASSERT(success);
497 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
498 flag |= getStatus(fr);
500 enable = flag.enabled();
504 case LFUN_DIALOG_SHOW: {
505 string const name = cmd.getArg(0);
507 enable = name == "aboutlyx"
511 || name == "texinfo";
512 else if (name == "print")
513 enable = Exporter::isExportable(*buf, "dvi")
514 && lyxrc.print_command != "none";
515 else if (name == "character" || name == "mathpanel")
516 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
517 else if (name == "latexlog")
518 enable = isFileReadable(buf->getLogName().second);
519 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
520 else if (name == "spellchecker")
523 else if (name == "vclog")
524 enable = buf->lyxvc().inUse();
525 else if (name == "view-source")
530 case LFUN_DIALOG_SHOW_NEW_INSET:
531 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
534 case LFUN_DIALOG_UPDATE: {
535 string const name = cmd.getArg(0);
537 enable = name == "prefs";
541 case LFUN_CITATION_INSERT: {
542 FuncRequest fr(LFUN_INSET_INSERT, "citation");
543 enable = getStatus(fr).enabled();
547 case LFUN_BUFFER_WRITE: {
548 enable = view()->buffer()->isUnnamed()
549 || !view()->buffer()->isClean();
553 // this one is difficult to get right. As a half-baked
554 // solution, we consider only the first action of the sequence
555 case LFUN_COMMAND_SEQUENCE: {
556 // argument contains ';'-terminated commands
557 string const firstcmd = token(lyx::to_utf8(cmd.argument()), ';', 0);
558 FuncRequest func(lyxaction.lookupFunc(firstcmd));
559 func.origin = cmd.origin;
560 flag = getStatus(func);
563 case LFUN_BUFFER_NEW:
564 case LFUN_BUFFER_NEW_TEMPLATE:
565 case LFUN_WORD_FIND_FORWARD:
566 case LFUN_WORD_FIND_BACKWARD:
567 case LFUN_COMMAND_PREFIX:
568 case LFUN_COMMAND_EXECUTE:
570 case LFUN_META_PREFIX:
571 case LFUN_BUFFER_CLOSE:
572 case LFUN_BUFFER_WRITE_AS:
573 case LFUN_BUFFER_UPDATE:
574 case LFUN_BUFFER_VIEW:
575 case LFUN_BUFFER_IMPORT:
578 case LFUN_BUFFER_AUTO_SAVE:
579 case LFUN_RECONFIGURE:
583 case LFUN_DROP_LAYOUTS_CHOICE:
585 case LFUN_SERVER_GET_NAME:
586 case LFUN_SERVER_NOTIFY:
587 case LFUN_SERVER_GOTO_FILE_ROW:
588 case LFUN_DIALOG_HIDE:
589 case LFUN_DIALOG_DISCONNECT_INSET:
590 case LFUN_BUFFER_CHILD_OPEN:
591 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
592 case LFUN_KEYMAP_OFF:
593 case LFUN_KEYMAP_PRIMARY:
594 case LFUN_KEYMAP_SECONDARY:
595 case LFUN_KEYMAP_TOGGLE:
597 case LFUN_BUFFER_EXPORT_CUSTOM:
598 case LFUN_BUFFER_PRINT:
599 case LFUN_PREFERENCES_SAVE:
600 case LFUN_SCREEN_FONT_UPDATE:
603 case LFUN_EXTERNAL_EDIT:
604 case LFUN_GRAPHICS_EDIT:
605 case LFUN_ALL_INSETS_TOGGLE:
606 case LFUN_BUFFER_LANGUAGE:
607 case LFUN_TEXTCLASS_APPLY:
608 case LFUN_TEXTCLASS_LOAD:
609 case LFUN_BUFFER_SAVE_AS_DEFAULT:
610 case LFUN_BUFFER_PARAMS_APPLY:
611 case LFUN_LYXRC_APPLY:
612 case LFUN_BUFFER_NEXT:
613 case LFUN_BUFFER_PREVIOUS:
614 // these are handled in our dispatch()
619 if (!::getStatus(cur, cmd, flag))
620 flag = view()->getStatus(cmd);
626 // Can we use a readonly buffer?
627 if (buf && buf->isReadonly()
628 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
629 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
630 flag.message(lyx::from_utf8(N_("Document is read-only")));
634 // Are we in a DELETED change-tracking region?
635 if (buf && lookupChangeType(cur, true) == Change::DELETED
636 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
637 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
638 flag.message(lyx::from_utf8(N_("This portion of the document is deleted.")));
642 // the default error message if we disable the command
643 if (!flag.enabled() && flag.message().empty())
644 flag.message(lyx::from_utf8(N_("Command disabled")));
650 bool LyXFunc::ensureBufferClean(BufferView * bv)
652 Buffer & buf = *bv->buffer();
656 docstring const file = makeDisplayPath(buf.fileName(), 30);
657 docstring text = bformat(_("The document %1$s has unsaved "
658 "changes.\n\nDo you want to save "
659 "the document?"), file);
660 int const ret = Alert::prompt(_("Save changed document?"),
661 text, 0, 1, _("&Save"),
665 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
667 return buf.isClean();
673 void showPrintError(string const & name)
675 docstring str = bformat(_("Could not print the document %1$s.\n"
676 "Check that your printer is set up correctly."),
677 makeDisplayPath(name, 50));
678 Alert::error(_("Print document failed"), str);
682 void loadTextclass(string const & name)
684 std::pair<bool, lyx::textclass_type> const tc_pair =
685 textclasslist.numberOfClass(name);
687 if (!tc_pair.first) {
688 lyxerr << "Document class \"" << name
689 << "\" does not exist."
694 lyx::textclass_type const tc = tc_pair.second;
696 if (!textclasslist[tc].load()) {
697 docstring s = bformat(_("The document could not be converted\n"
698 "into the document class %1$s."),
699 lyx::from_utf8(textclasslist[tc].name()));
700 Alert::error(_("Could not change class"), s);
705 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
710 void LyXFunc::dispatch(FuncRequest const & cmd)
712 BOOST_ASSERT(view());
713 string const argument = lyx::to_utf8(cmd.argument());
714 kb_action const action = cmd.action;
716 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
717 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
719 // we have not done anything wrong yet.
721 dispatch_buffer.erase();
723 // redraw the screen at the end (first of the two drawing steps).
724 //This is done unless explicitely requested otherwise
726 // also do the second redrawing step. Only done if requested.
727 bool updateforce = false;
729 FuncStatus const flag = getStatus(cmd);
730 if (!flag.enabled()) {
731 // We cannot use this function here
732 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
733 << lyxaction.getActionName(action)
734 << " [" << action << "] is disabled at this location"
736 setErrorMessage(flag.message());
740 case LFUN_WORD_FIND_FORWARD:
741 case LFUN_WORD_FIND_BACKWARD: {
742 static string last_search;
743 string searched_string;
745 if (!argument.empty()) {
746 last_search = argument;
747 searched_string = argument;
749 searched_string = last_search;
752 if (searched_string.empty())
755 bool const fw = action == LFUN_WORD_FIND_FORWARD;
757 lyx::find::find2string(searched_string, true, false, fw);
758 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
762 case LFUN_COMMAND_PREFIX:
763 owner->message(lyx::from_utf8(keyseq.printOptions()));
766 case LFUN_COMMAND_EXECUTE:
767 owner->getToolbars().display("minibuffer", true);
768 owner->focus_command_buffer();
773 meta_fake_bit = key_modifier::none;
774 if (view()->buffer())
775 // cancel any selection
776 dispatch(FuncRequest(LFUN_MARK_OFF));
777 setMessage(_("Cancel"));
780 case LFUN_META_PREFIX:
781 meta_fake_bit = key_modifier::alt;
782 setMessage(lyx::from_utf8(keyseq.print()));
785 case LFUN_BUFFER_TOGGLE_READ_ONLY:
786 if (owner->buffer()->lyxvc().inUse())
787 owner->buffer()->lyxvc().toggleReadOnly();
789 owner->buffer()->setReadonly(
790 !owner->buffer()->isReadonly());
793 // --- Menus -----------------------------------------------
794 case LFUN_BUFFER_NEW:
795 menuNew(argument, false);
798 case LFUN_BUFFER_NEW_TEMPLATE:
799 menuNew(argument, true);
802 case LFUN_BUFFER_CLOSE:
806 case LFUN_BUFFER_WRITE:
807 if (!owner->buffer()->isUnnamed()) {
808 docstring const str = bformat(_("Saving document %1$s..."),
809 makeDisplayPath(owner->buffer()->fileName()));
811 menuWrite(owner->buffer());
812 owner->message(str + _(" done."));
814 writeAs(owner->buffer());
818 case LFUN_BUFFER_WRITE_AS:
819 writeAs(owner->buffer(), argument);
823 case LFUN_BUFFER_RELOAD: {
824 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
825 docstring text = bformat(_("Any changes will be lost. Are you sure "
826 "you want to revert to the saved version of the document %1$s?"), file);
827 int const ret = Alert::prompt(_("Revert to saved document?"),
828 text, 0, 1, _("&Revert"), _("&Cancel"));
835 case LFUN_BUFFER_UPDATE:
836 Exporter::Export(owner->buffer(), argument, true);
839 case LFUN_BUFFER_VIEW:
840 Exporter::preview(owner->buffer(), argument);
843 case LFUN_BUILD_PROGRAM:
844 Exporter::Export(owner->buffer(), "program", true);
847 case LFUN_BUFFER_CHKTEX:
848 owner->buffer()->runChktex();
851 case LFUN_BUFFER_EXPORT:
852 if (argument == "custom")
853 owner->getDialogs().show("sendto");
855 Exporter::Export(owner->buffer(), argument, false);
859 case LFUN_BUFFER_EXPORT_CUSTOM: {
861 string command = split(argument, format_name, ' ');
862 Format const * format = formats.getFormat(format_name);
864 lyxerr << "Format \"" << format_name
865 << "\" not recognized!"
870 Buffer * buffer = owner->buffer();
872 // The name of the file created by the conversion process
875 // Output to filename
876 if (format->name() == "lyx") {
877 string const latexname =
878 buffer->getLatexName(false);
879 filename = changeExtension(latexname,
880 format->extension());
881 filename = addName(buffer->temppath(), filename);
883 if (!buffer->writeFile(filename))
887 Exporter::Export(buffer, format_name, true, filename);
890 // Substitute $$FName for filename
891 if (!contains(command, "$$FName"))
892 command = "( " + command + " ) < $$FName";
893 command = subst(command, "$$FName", filename);
895 // Execute the command in the background
897 call.startscript(Systemcall::DontWait, command);
901 case LFUN_BUFFER_PRINT: {
904 string command = split(split(argument, target, ' '),
908 || target_name.empty()
909 || command.empty()) {
910 lyxerr << "Unable to parse \""
911 << argument << '"' << std::endl;
914 if (target != "printer" && target != "file") {
915 lyxerr << "Unrecognized target \""
916 << target << '"' << std::endl;
920 Buffer * buffer = owner->buffer();
922 if (!Exporter::Export(buffer, "dvi", true)) {
923 showPrintError(buffer->fileName());
927 // Push directory path.
928 string const path = buffer->temppath();
929 lyx::support::Path p(path);
931 // there are three cases here:
932 // 1. we print to a file
933 // 2. we print directly to a printer
934 // 3. we print using a spool command (print to file first)
937 string const dviname =
938 changeExtension(buffer->getLatexName(true),
941 if (target == "printer") {
942 if (!lyxrc.print_spool_command.empty()) {
943 // case 3: print using a spool
944 string const psname =
945 changeExtension(dviname,".ps");
946 command += lyxrc.print_to_file
949 + quoteName(dviname);
952 lyxrc.print_spool_command +' ';
953 if (target_name != "default") {
954 command2 += lyxrc.print_spool_printerprefix
958 command2 += quoteName(psname);
960 // If successful, then spool command
961 res = one.startscript(
966 res = one.startscript(
967 Systemcall::DontWait,
970 // case 2: print directly to a printer
971 res = one.startscript(
972 Systemcall::DontWait,
973 command + quoteName(dviname));
977 // case 1: print to a file
978 command += lyxrc.print_to_file
979 + quoteName(makeAbsPath(target_name,
982 + quoteName(dviname);
983 res = one.startscript(Systemcall::DontWait,
988 showPrintError(buffer->fileName());
992 case LFUN_BUFFER_IMPORT:
997 if (view()->buffer()) {
998 // save cursor Position for opened files to .lyx/session
999 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1000 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1001 // save bookmarks to .lyx/session
1002 view()->saveSavedPositions();
1004 quitLyX(argument == "force");
1007 case LFUN_TOC_VIEW: {
1008 InsetCommandParams p("tableofcontents");
1009 string const data = InsetCommandMailer::params2string("toc", p);
1010 owner->getDialogs().show("toc", data, 0);
1014 case LFUN_BUFFER_AUTO_SAVE:
1018 case LFUN_RECONFIGURE:
1019 reconfigure(view());
1022 case LFUN_HELP_OPEN: {
1023 string const arg = argument;
1025 setErrorMessage(_("Missing argument"));
1028 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1029 if (fname.empty()) {
1030 lyxerr << "LyX: unable to find documentation file `"
1031 << arg << "'. Bad installation?" << endl;
1034 owner->message(bformat(_("Opening help file %1$s..."),
1035 makeDisplayPath(fname)));
1036 owner->loadLyXFile(fname, false);
1040 // --- version control -------------------------------
1041 case LFUN_VC_REGISTER:
1042 if (!ensureBufferClean(view()))
1044 if (!owner->buffer()->lyxvc().inUse()) {
1045 owner->buffer()->lyxvc().registrer();
1050 case LFUN_VC_CHECK_IN:
1051 if (!ensureBufferClean(view()))
1053 if (owner->buffer()->lyxvc().inUse()
1054 && !owner->buffer()->isReadonly()) {
1055 owner->buffer()->lyxvc().checkIn();
1060 case LFUN_VC_CHECK_OUT:
1061 if (!ensureBufferClean(view()))
1063 if (owner->buffer()->lyxvc().inUse()
1064 && owner->buffer()->isReadonly()) {
1065 owner->buffer()->lyxvc().checkOut();
1070 case LFUN_VC_REVERT:
1071 owner->buffer()->lyxvc().revert();
1075 case LFUN_VC_UNDO_LAST:
1076 owner->buffer()->lyxvc().undoLast();
1080 // --- buffers ----------------------------------------
1081 case LFUN_BUFFER_SWITCH:
1082 owner->setBuffer(theApp->bufferList().getBuffer(argument));
1085 case LFUN_BUFFER_NEXT:
1086 owner->setBuffer(theApp->bufferList().next(view()->buffer()));
1089 case LFUN_BUFFER_PREVIOUS:
1090 owner->setBuffer(theApp->bufferList().previous(view()->buffer()));
1094 newFile(view(), argument);
1097 case LFUN_FILE_OPEN:
1101 case LFUN_DROP_LAYOUTS_CHOICE:
1102 owner->getToolbars().openLayoutList();
1105 case LFUN_MENU_OPEN:
1106 owner->getMenubar().openByName(lyx::from_utf8(argument));
1109 // --- lyxserver commands ----------------------------
1110 case LFUN_SERVER_GET_NAME:
1111 setMessage(lyx::from_utf8(owner->buffer()->fileName()));
1112 lyxerr[Debug::INFO] << "FNAME["
1113 << owner->buffer()->fileName()
1117 case LFUN_SERVER_NOTIFY:
1118 dispatch_buffer = lyx::from_utf8(keyseq.print());
1119 theApp->server().notifyClient(lyx::to_utf8(dispatch_buffer));
1122 case LFUN_SERVER_GOTO_FILE_ROW: {
1125 istringstream is(argument);
1126 is >> file_name >> row;
1127 if (prefixIs(file_name, package().temp_dir())) {
1128 // Needed by inverse dvi search. If it is a file
1129 // in tmpdir, call the apropriated function
1130 owner->setBuffer(theApp->bufferList().getBufferFromTmp(file_name));
1132 // Must replace extension of the file to be .lyx
1133 // and get full path
1134 string const s = changeExtension(file_name, ".lyx");
1135 // Either change buffer or load the file
1136 if (theApp->bufferList().exists(s)) {
1137 owner->setBuffer(theApp->bufferList().getBuffer(s));
1139 owner->loadLyXFile(s);
1143 view()->setCursorFromRow(row);
1146 // see BufferView::center()
1150 case LFUN_DIALOG_SHOW: {
1151 string const name = cmd.getArg(0);
1152 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1154 if (name == "character") {
1155 data = freefont2string();
1157 owner->getDialogs().show("character", data);
1158 } else if (name == "latexlog") {
1159 pair<Buffer::LogType, string> const logfile =
1160 owner->buffer()->getLogName();
1161 switch (logfile.first) {
1162 case Buffer::latexlog:
1165 case Buffer::buildlog:
1169 data += LyXLex::quoteString(logfile.second);
1170 owner->getDialogs().show("log", data);
1171 } else if (name == "vclog") {
1172 string const data = "vc " +
1173 LyXLex::quoteString(owner->buffer()->lyxvc().getLogFile());
1174 owner->getDialogs().show("log", data);
1176 owner->getDialogs().show(name, data);
1180 case LFUN_DIALOG_SHOW_NEW_INSET: {
1181 string const name = cmd.getArg(0);
1182 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1183 if (name == "bibitem" ||
1190 InsetCommandParams p(name);
1191 data = InsetCommandMailer::params2string(name, p);
1192 } else if (name == "include") {
1193 InsetCommandParams p(data);
1194 data = InsetIncludeMailer::params2string(p);
1195 } else if (name == "box") {
1196 // \c data == "Boxed" || "Frameless" etc
1197 InsetBoxParams p(data);
1198 data = InsetBoxMailer::params2string(p);
1199 } else if (name == "branch") {
1200 InsetBranchParams p;
1201 data = InsetBranchMailer::params2string(p);
1202 } else if (name == "citation") {
1203 InsetCommandParams p("cite");
1204 data = InsetCommandMailer::params2string(name, p);
1205 } else if (name == "ert") {
1206 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1207 } else if (name == "external") {
1208 InsetExternalParams p;
1209 Buffer const & buffer = *owner->buffer();
1210 data = InsetExternalMailer::params2string(p, buffer);
1211 } else if (name == "float") {
1213 data = InsetFloatMailer::params2string(p);
1214 } else if (name == "graphics") {
1215 InsetGraphicsParams p;
1216 Buffer const & buffer = *owner->buffer();
1217 data = InsetGraphicsMailer::params2string(p, buffer);
1218 } else if (name == "note") {
1220 data = InsetNoteMailer::params2string(p);
1221 } else if (name == "vspace") {
1223 data = InsetVSpaceMailer::params2string(space);
1224 } else if (name == "wrap") {
1226 data = InsetWrapMailer::params2string(p);
1228 owner->getDialogs().show(name, data, 0);
1232 case LFUN_DIALOG_UPDATE: {
1233 string const & name = argument;
1234 // Can only update a dialog connected to an existing inset
1235 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1237 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1238 inset->dispatch(view()->cursor(), fr);
1239 } else if (name == "paragraph") {
1240 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1241 } else if (name == "prefs") {
1242 owner->getDialogs().update(name, string());
1247 case LFUN_DIALOG_HIDE:
1248 Dialogs::hide(argument, 0);
1251 case LFUN_DIALOG_DISCONNECT_INSET:
1252 owner->getDialogs().disconnect(argument);
1256 case LFUN_CITATION_INSERT: {
1257 if (!argument.empty()) {
1258 // we can have one optional argument, delimited by '|'
1259 // citation-insert <key>|<text_before>
1260 // this should be enhanced to also support text_after
1261 // and citation style
1262 string arg = argument;
1264 if (contains(argument, "|")) {
1265 arg = token(argument, '|', 0);
1266 opt1 = '[' + token(argument, '|', 1) + ']';
1268 std::ostringstream os;
1269 os << "citation LatexCommand\n"
1270 << "\\cite" << opt1 << "{" << arg << "}\n"
1272 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1275 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1279 case LFUN_BUFFER_CHILD_OPEN: {
1280 string const filename =
1281 makeAbsPath(argument, owner->buffer()->filePath());
1282 // FIXME Should use bformat
1283 setMessage(_("Opening child document ") +
1284 makeDisplayPath(filename) + lyx::from_ascii("..."));
1285 view()->savePosition(0);
1286 string const parentfilename = owner->buffer()->fileName();
1287 if (theApp->bufferList().exists(filename))
1288 owner->setBuffer(theApp->bufferList().getBuffer(filename));
1290 owner->loadLyXFile(filename);
1291 // Set the parent name of the child document.
1292 // This makes insertion of citations and references in the child work,
1293 // when the target is in the parent or another child document.
1294 owner->buffer()->setParentName(parentfilename);
1298 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1299 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1302 case LFUN_KEYMAP_OFF:
1303 owner->view()->getIntl().keyMapOn(false);
1306 case LFUN_KEYMAP_PRIMARY:
1307 owner->view()->getIntl().keyMapPrim();
1310 case LFUN_KEYMAP_SECONDARY:
1311 owner->view()->getIntl().keyMapSec();
1314 case LFUN_KEYMAP_TOGGLE:
1315 owner->view()->getIntl().toggleKeyMap();
1321 string rest = split(argument, countstr, ' ');
1322 istringstream is(countstr);
1325 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1326 for (int i = 0; i < count; ++i)
1327 dispatch(lyxaction.lookupFunc(rest));
1331 case LFUN_COMMAND_SEQUENCE: {
1332 // argument contains ';'-terminated commands
1333 string arg = argument;
1334 while (!arg.empty()) {
1336 arg = split(arg, first, ';');
1337 FuncRequest func(lyxaction.lookupFunc(first));
1338 func.origin = cmd.origin;
1344 case LFUN_PREFERENCES_SAVE: {
1345 lyx::support::Path p(package().user_support());
1346 lyxrc.write("preferences", false);
1350 case LFUN_SCREEN_FONT_UPDATE:
1351 // handle the screen font changes.
1352 lyxrc.set_font_norm_type();
1353 theApp->fontLoader().update();
1354 // All visible buffers will need resize
1358 case LFUN_SET_COLOR: {
1360 string const x11_name = split(argument, lyx_name, ' ');
1361 if (lyx_name.empty() || x11_name.empty()) {
1362 setErrorMessage(_("Syntax: set-color <lyx_name>"
1367 bool const graphicsbg_changed =
1368 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1369 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1371 if (!lcolor.setColor(lyx_name, x11_name)) {
1373 bformat(_("Set-color \"%1$s\" failed "
1374 "- color is undefined or "
1375 "may not be redefined"),
1376 lyx::from_utf8(lyx_name)));
1380 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1382 if (graphicsbg_changed) {
1383 #ifdef WITH_WARNINGS
1384 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1387 lyx::graphics::GCache::get().changeDisplay(true);
1394 owner->message(lyx::from_utf8(argument));
1397 case LFUN_EXTERNAL_EDIT: {
1398 FuncRequest fr(action, argument);
1399 InsetExternal().dispatch(view()->cursor(), fr);
1403 case LFUN_GRAPHICS_EDIT: {
1404 FuncRequest fr(action, argument);
1405 InsetGraphics().dispatch(view()->cursor(), fr);
1409 case LFUN_INSET_APPLY: {
1410 string const name = cmd.getArg(0);
1411 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1413 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1414 inset->dispatch(view()->cursor(), fr);
1416 FuncRequest fr(LFUN_INSET_INSERT, argument);
1419 // ideally, the update flag should be set by the insets,
1420 // but this is not possible currently
1425 case LFUN_ALL_INSETS_TOGGLE: {
1427 string const name = split(argument, action, ' ');
1428 InsetBase::Code const inset_code =
1429 InsetBase::translate(name);
1431 LCursor & cur = view()->cursor();
1432 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1434 InsetBase & inset = owner->buffer()->inset();
1435 InsetIterator it = inset_iterator_begin(inset);
1436 InsetIterator const end = inset_iterator_end(inset);
1437 for (; it != end; ++it) {
1438 if (inset_code == InsetBase::NO_CODE
1439 || inset_code == it->lyxCode()) {
1440 LCursor tmpcur = cur;
1441 tmpcur.pushLeft(*it);
1442 it->dispatch(tmpcur, fr);
1449 case LFUN_BUFFER_LANGUAGE: {
1450 Buffer & buffer = *owner->buffer();
1451 Language const * oldL = buffer.params().language;
1452 Language const * newL = languages.getLanguage(argument);
1453 if (!newL || oldL == newL)
1456 if (oldL->rightToLeft() == newL->rightToLeft()
1457 && !buffer.isMultiLingual())
1458 buffer.changeLanguage(oldL, newL);
1460 buffer.updateDocLang(newL);
1464 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1465 string const fname =
1466 addName(addPath(package().user_support(), "templates/"),
1468 Buffer defaults(fname);
1470 istringstream ss(argument);
1473 int const unknown_tokens = defaults.readHeader(lex);
1475 if (unknown_tokens != 0) {
1476 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1477 << unknown_tokens << " unknown token"
1478 << (unknown_tokens == 1 ? "" : "s")
1482 if (defaults.writeFile(defaults.fileName()))
1483 // FIXME Should use bformat
1484 setMessage(_("Document defaults saved in ")
1485 + makeDisplayPath(fname));
1487 setErrorMessage(_("Unable to save document defaults"));
1491 case LFUN_BUFFER_PARAMS_APPLY: {
1492 biblio::CiteEngine const engine =
1493 owner->buffer()->params().cite_engine;
1495 istringstream ss(argument);
1498 int const unknown_tokens =
1499 owner->buffer()->readHeader(lex);
1501 if (unknown_tokens != 0) {
1502 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1503 << unknown_tokens << " unknown token"
1504 << (unknown_tokens == 1 ? "" : "s")
1507 if (engine == owner->buffer()->params().cite_engine)
1510 LCursor & cur = view()->cursor();
1511 FuncRequest fr(LFUN_INSET_REFRESH);
1513 InsetBase & inset = owner->buffer()->inset();
1514 InsetIterator it = inset_iterator_begin(inset);
1515 InsetIterator const end = inset_iterator_end(inset);
1516 for (; it != end; ++it)
1517 if (it->lyxCode() == InsetBase::CITE_CODE)
1518 it->dispatch(cur, fr);
1522 case LFUN_TEXTCLASS_APPLY: {
1523 Buffer * buffer = owner->buffer();
1525 lyx::textclass_type const old_class =
1526 buffer->params().textclass;
1528 loadTextclass(argument);
1530 std::pair<bool, lyx::textclass_type> const tc_pair =
1531 textclasslist.numberOfClass(argument);
1536 lyx::textclass_type const new_class = tc_pair.second;
1537 if (old_class == new_class)
1541 owner->message(_("Converting document to new document class..."));
1542 recordUndoFullDocument(view());
1543 buffer->params().textclass = new_class;
1544 StableDocIterator backcur(view()->cursor());
1545 ErrorList & el = buffer->errorList("Class Switch");
1546 lyx::cap::switchBetweenClasses(
1547 old_class, new_class,
1548 static_cast<InsetText &>(buffer->inset()), el);
1550 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1552 buffer->errors("Class Switch");
1553 updateLabels(*buffer);
1558 case LFUN_TEXTCLASS_LOAD:
1559 loadTextclass(argument);
1562 case LFUN_LYXRC_APPLY: {
1563 LyXRC const lyxrc_orig = lyxrc;
1565 istringstream ss(argument);
1566 bool const success = lyxrc.read(ss) == 0;
1569 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1570 << "Unable to read lyxrc data"
1575 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1580 view()->cursor().dispatch(cmd);
1581 updateforce |= view()->cursor().result().update();
1582 if (!view()->cursor().result().dispatched())
1583 updateforce |= view()->dispatch(cmd);
1588 if (view()->buffer()) {
1589 // Redraw screen unless explicitly told otherwise.
1590 // This also initializes the position cache for all insets
1591 // in (at least partially) visible top-level paragraphs.
1593 view()->update(Update::FitCursor | Update::Force);
1595 view()->update(Update::FitCursor);
1597 owner->redrawWorkArea();
1599 // if we executed a mutating lfun, mark the buffer as dirty
1601 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1602 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1603 view()->buffer()->markDirty();
1605 if (view()->cursor().inTexted()) {
1606 owner->updateLayoutChoice();
1611 // FIXME UNICODE: _() does not support anything but ascii.
1612 // Do we need a lyx::to_ascii() method?
1613 sendDispatchMessage(getMessage(), cmd);
1617 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1619 /* When an action did not originate from the UI/kbd, it makes
1620 * sense to avoid updating the GUI. It turns out that this
1621 * fixes bug 1941, for reasons that are described here:
1622 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1624 if (cmd.origin != FuncRequest::INTERNAL) {
1625 owner->updateMenubar();
1626 owner->updateToolbars();
1629 const bool verbose = (cmd.origin == FuncRequest::UI
1630 || cmd.origin == FuncRequest::COMMANDBUFFER);
1632 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1633 lyxerr[Debug::ACTION] << "dispatch msg is " << lyx::to_utf8(msg) << endl;
1635 owner->message(msg);
1639 docstring dispatch_msg = msg;
1640 if (!dispatch_msg.empty())
1641 dispatch_msg += ' ';
1643 string comname = lyxaction.getActionName(cmd.action);
1645 bool argsadded = false;
1647 if (!cmd.argument().empty()) {
1648 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1649 comname += ' ' + lyx::to_utf8(cmd.argument());
1654 string const shortcuts = toplevel_keymap->printbindings(cmd);
1656 if (!shortcuts.empty())
1657 comname += ": " + shortcuts;
1658 else if (!argsadded && !cmd.argument().empty())
1659 comname += ' ' + lyx::to_utf8(cmd.argument());
1661 if (!comname.empty()) {
1662 comname = rtrim(comname);
1663 dispatch_msg += lyx::from_utf8('(' + rtrim(comname) + ')');
1666 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1667 << lyx::to_utf8(dispatch_msg) << endl;
1668 if (!dispatch_msg.empty())
1669 owner->message(dispatch_msg);
1673 void LyXFunc::setupLocalKeymap()
1675 keyseq.stdmap = toplevel_keymap.get();
1676 keyseq.curmap = toplevel_keymap.get();
1677 cancel_meta_seq.stdmap = toplevel_keymap.get();
1678 cancel_meta_seq.curmap = toplevel_keymap.get();
1682 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1684 string initpath = lyxrc.document_path;
1685 string filename(name);
1687 if (view()->buffer()) {
1688 string const trypath = owner->buffer()->filePath();
1689 // If directory is writeable, use this as default.
1690 if (isDirWriteable(trypath))
1694 static int newfile_number;
1696 if (filename.empty()) {
1697 filename = addName(lyxrc.document_path,
1698 "newfile" + convert<string>(++newfile_number) + ".lyx");
1699 while (theApp->bufferList().exists(filename) || fs::is_readable(filename)) {
1701 filename = addName(lyxrc.document_path,
1702 "newfile" + convert<string>(newfile_number) +
1707 // The template stuff
1710 FileDialog fileDlg(_("Select template file"),
1711 LFUN_SELECT_FILE_SYNC,
1712 make_pair(_("Documents|#o#O"), lyx::from_utf8(lyxrc.document_path)),
1713 make_pair(_("Templates|#T#t"), lyx::from_utf8(lyxrc.template_path)));
1715 FileDialog::Result result =
1716 fileDlg.open(lyx::from_utf8(lyxrc.template_path),
1717 FileFilterList(_("LyX Documents (*.lyx)")),
1720 if (result.first == FileDialog::Later)
1722 if (result.second.empty())
1724 templname = lyx::to_utf8(result.second);
1727 Buffer * const b = newFile(filename, templname, !name.empty());
1729 owner->setBuffer(b);
1733 void LyXFunc::open(string const & fname)
1735 string initpath = lyxrc.document_path;
1737 if (view()->buffer()) {
1738 string const trypath = owner->buffer()->filePath();
1739 // If directory is writeable, use this as default.
1740 if (isDirWriteable(trypath))
1746 if (fname.empty()) {
1747 FileDialog fileDlg(_("Select document to open"),
1749 make_pair(_("Documents|#o#O"), lyx::from_utf8(lyxrc.document_path)),
1750 make_pair(_("Examples|#E#e"), lyx::from_utf8(addPath(package().system_support(), "examples"))));
1752 FileDialog::Result result =
1753 fileDlg.open(lyx::from_utf8(initpath),
1754 FileFilterList(_("LyX Documents (*.lyx)")),
1757 if (result.first == FileDialog::Later)
1760 filename = lyx::to_utf8(result.second);
1762 // check selected filename
1763 if (filename.empty()) {
1764 owner->message(_("Canceled."));
1770 // get absolute path of file and add ".lyx" to the filename if
1772 string const fullpath = fileSearch(string(), filename, "lyx");
1773 if (!fullpath.empty()) {
1774 filename = fullpath;
1777 docstring const disp_fn = makeDisplayPath(filename);
1779 // if the file doesn't exist, let the user create one
1780 if (!fs::exists(filename)) {
1781 // the user specifically chose this name. Believe him.
1782 Buffer * const b = newFile(filename, string(), true);
1784 owner->setBuffer(b);
1788 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1791 if (owner->loadLyXFile(filename)) {
1792 str2 = bformat(_("Document %1$s opened."), disp_fn);
1794 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1796 owner->message(str2);
1800 void LyXFunc::doImport(string const & argument)
1803 string filename = split(argument, format, ' ');
1805 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1806 << " file: " << filename << endl;
1808 // need user interaction
1809 if (filename.empty()) {
1810 string initpath = lyxrc.document_path;
1812 if (view()->buffer()) {
1813 string const trypath = owner->buffer()->filePath();
1814 // If directory is writeable, use this as default.
1815 if (isDirWriteable(trypath))
1819 docstring const text = bformat(_("Select %1$s file to import"),
1820 formats.prettyName(format));
1822 FileDialog fileDlg(text,
1824 make_pair(_("Documents|#o#O"), lyx::from_utf8(lyxrc.document_path)),
1825 make_pair(_("Examples|#E#e"),
1826 lyx::from_utf8(addPath(package().system_support(), "examples"))));
1828 docstring filter = formats.prettyName(format);
1831 filter += lyx::from_utf8(formats.extension(format));
1834 FileDialog::Result result =
1835 fileDlg.open(lyx::from_utf8(initpath),
1836 FileFilterList(filter),
1839 if (result.first == FileDialog::Later)
1842 filename = lyx::to_utf8(result.second);
1844 // check selected filename
1845 if (filename.empty())
1846 owner->message(_("Canceled."));
1849 if (filename.empty())
1852 // get absolute path of file
1853 filename = makeAbsPath(filename);
1855 string const lyxfile = changeExtension(filename, ".lyx");
1857 // Check if the document already is open
1858 if (lyx_gui::use_gui && theApp->bufferList().exists(lyxfile)) {
1859 if (!theApp->bufferList().close(theApp->bufferList().getBuffer(lyxfile), true)) {
1860 owner->message(_("Canceled."));
1865 // if the file exists already, and we didn't do
1866 // -i lyx thefile.lyx, warn
1867 if (fs::exists(lyxfile) && filename != lyxfile) {
1868 docstring const file = makeDisplayPath(lyxfile, 30);
1870 docstring text = bformat(_("The document %1$s already exists.\n\n"
1871 "Do you want to over-write that document?"), file);
1872 int const ret = Alert::prompt(_("Over-write document?"),
1873 text, 0, 1, _("&Over-write"), _("&Cancel"));
1876 owner->message(_("Canceled."));
1881 ErrorList errorList;
1882 Importer::Import(owner, filename, format, errorList);
1883 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1887 void LyXFunc::closeBuffer()
1889 // save current cursor position
1890 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1891 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1892 if (theApp->bufferList().close(owner->buffer(), true) && !quitting) {
1893 if (theApp->bufferList().empty()) {
1894 // need this otherwise SEGV may occur while
1895 // trying to set variables that don't exist
1896 // since there's no current buffer
1897 owner->getDialogs().hideBufferDependent();
1899 owner->setBuffer(theApp->bufferList().first());
1905 // Each "owner" should have it's own message method. lyxview and
1906 // the minibuffer would use the minibuffer, but lyxserver would
1907 // send an ERROR signal to its client. Alejandro 970603
1908 // This function is bit problematic when it comes to NLS, to make the
1909 // lyx servers client be language indepenent we must not translate
1910 // strings sent to this func.
1911 void LyXFunc::setErrorMessage(docstring const & m) const
1913 dispatch_buffer = m;
1918 void LyXFunc::setMessage(docstring const & m) const
1920 dispatch_buffer = m;
1924 string const LyXFunc::viewStatusMessage()
1926 // When meta-fake key is pressed, show the key sequence so far + "M-".
1928 return keyseq.print() + "M-";
1930 // Else, when a non-complete key sequence is pressed,
1931 // show the available options.
1932 if (keyseq.length() > 0 && !keyseq.deleted())
1933 return keyseq.printOptions();
1935 if (!view()->buffer())
1936 return lyx::to_utf8(_("Welcome to LyX!"));
1938 return view()->cursor().currentState();
1942 BufferView * LyXFunc::view() const
1944 BOOST_ASSERT(owner);
1945 return owner->view();
1949 bool LyXFunc::wasMetaKey() const
1951 return (meta_fake_bit != key_modifier::none);
1957 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1959 // Why the switch you might ask. It is a trick to ensure that all
1960 // the elements in the LyXRCTags enum is handled. As you can see
1961 // there are no breaks at all. So it is just a huge fall-through.
1962 // The nice thing is that we will get a warning from the compiler
1963 // if we forget an element.
1964 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1966 case LyXRC::RC_ACCEPT_COMPOUND:
1967 case LyXRC::RC_ALT_LANG:
1968 case LyXRC::RC_ASCIIROFF_COMMAND:
1969 case LyXRC::RC_ASCII_LINELEN:
1970 case LyXRC::RC_AUTOREGIONDELETE:
1971 case LyXRC::RC_AUTORESET_OPTIONS:
1972 case LyXRC::RC_AUTOSAVE:
1973 case LyXRC::RC_AUTO_NUMBER:
1974 case LyXRC::RC_BACKUPDIR_PATH:
1975 case LyXRC::RC_BIBTEX_COMMAND:
1976 case LyXRC::RC_BINDFILE:
1977 case LyXRC::RC_CHECKLASTFILES:
1978 case LyXRC::RC_USELASTFILEPOS:
1979 case LyXRC::RC_LOADSESSION:
1980 case LyXRC::RC_CHKTEX_COMMAND:
1981 case LyXRC::RC_CONVERTER:
1982 case LyXRC::RC_COPIER:
1983 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1984 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1985 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1986 case LyXRC::RC_DATE_INSERT_FORMAT:
1987 case LyXRC::RC_DEFAULT_LANGUAGE:
1988 case LyXRC::RC_DEFAULT_PAPERSIZE:
1989 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1990 case LyXRC::RC_DISPLAY_GRAPHICS:
1991 case LyXRC::RC_DOCUMENTPATH:
1992 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1993 if (fs::exists(lyxrc_new.document_path) &&
1994 fs::is_directory(lyxrc_new.document_path)) {
1995 using lyx::support::package;
1996 package().document_dir() = lyxrc.document_path;
1999 case LyXRC::RC_ESC_CHARS:
2000 case LyXRC::RC_FONT_ENCODING:
2001 case LyXRC::RC_FORMAT:
2002 case LyXRC::RC_INDEX_COMMAND:
2003 case LyXRC::RC_INPUT:
2004 case LyXRC::RC_KBMAP:
2005 case LyXRC::RC_KBMAP_PRIMARY:
2006 case LyXRC::RC_KBMAP_SECONDARY:
2007 case LyXRC::RC_LABEL_INIT_LENGTH:
2008 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2009 case LyXRC::RC_LANGUAGE_AUTO_END:
2010 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2011 case LyXRC::RC_LANGUAGE_COMMAND_END:
2012 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2013 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2014 case LyXRC::RC_LANGUAGE_PACKAGE:
2015 case LyXRC::RC_LANGUAGE_USE_BABEL:
2016 case LyXRC::RC_MAKE_BACKUP:
2017 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2018 case LyXRC::RC_NUMLASTFILES:
2019 case LyXRC::RC_PATH_PREFIX:
2020 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2021 using lyx::support::prependEnvPath;
2022 prependEnvPath("PATH", lyxrc.path_prefix);
2024 case LyXRC::RC_PERS_DICT:
2025 case LyXRC::RC_POPUP_BOLD_FONT:
2026 case LyXRC::RC_POPUP_FONT_ENCODING:
2027 case LyXRC::RC_POPUP_NORMAL_FONT:
2028 case LyXRC::RC_PREVIEW:
2029 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2030 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2031 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2032 case LyXRC::RC_PRINTCOPIESFLAG:
2033 case LyXRC::RC_PRINTER:
2034 case LyXRC::RC_PRINTEVENPAGEFLAG:
2035 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2036 case LyXRC::RC_PRINTFILEEXTENSION:
2037 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2038 case LyXRC::RC_PRINTODDPAGEFLAG:
2039 case LyXRC::RC_PRINTPAGERANGEFLAG:
2040 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2041 case LyXRC::RC_PRINTPAPERFLAG:
2042 case LyXRC::RC_PRINTREVERSEFLAG:
2043 case LyXRC::RC_PRINTSPOOL_COMMAND:
2044 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2045 case LyXRC::RC_PRINTTOFILE:
2046 case LyXRC::RC_PRINTTOPRINTER:
2047 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2048 case LyXRC::RC_PRINT_COMMAND:
2049 case LyXRC::RC_RTL_SUPPORT:
2050 case LyXRC::RC_SCREEN_DPI:
2051 case LyXRC::RC_SCREEN_FONT_ENCODING:
2052 case LyXRC::RC_SCREEN_FONT_ROMAN:
2053 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2054 case LyXRC::RC_SCREEN_FONT_SANS:
2055 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2056 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2057 case LyXRC::RC_SCREEN_FONT_SIZES:
2058 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2059 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2060 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2061 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2062 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2063 case LyXRC::RC_SCREEN_ZOOM:
2064 case LyXRC::RC_SERVERPIPE:
2065 case LyXRC::RC_SET_COLOR:
2066 case LyXRC::RC_SHOW_BANNER:
2067 case LyXRC::RC_SPELL_COMMAND:
2068 case LyXRC::RC_TEMPDIRPATH:
2069 case LyXRC::RC_TEMPLATEPATH:
2070 case LyXRC::RC_TEX_ALLOWS_SPACES:
2071 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2072 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2073 namespace os = lyx::support::os;
2074 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2076 case LyXRC::RC_UIFILE:
2077 case LyXRC::RC_USER_EMAIL:
2078 case LyXRC::RC_USER_NAME:
2079 case LyXRC::RC_USETEMPDIR:
2080 case LyXRC::RC_USE_ALT_LANG:
2081 case LyXRC::RC_USE_ESC_CHARS:
2082 case LyXRC::RC_USE_INP_ENC:
2083 case LyXRC::RC_USE_PERS_DICT:
2084 case LyXRC::RC_USE_SPELL_LIB:
2085 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2086 case LyXRC::RC_VIEWER:
2087 case LyXRC::RC_LAST: