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"
39 #include "FuncStatus.h"
42 #include "insetiterator.h"
50 #include "LyXAction.h"
55 #include "lyxserver.h"
56 #include "lyxtextclasslist.h"
58 #include "paragraph.h"
59 #include "pariterator.h"
60 #include "ParagraphParameters.h"
63 #include "insets/insetbox.h"
64 #include "insets/insetbranch.h"
65 #include "insets/insetcommand.h"
66 #include "insets/insetert.h"
67 #include "insets/insetexternal.h"
68 #include "insets/insetfloat.h"
69 #include "insets/insetgraphics.h"
70 #include "insets/insetinclude.h"
71 #include "insets/insetnote.h"
72 #include "insets/insettabular.h"
73 #include "insets/insetvspace.h"
74 #include "insets/insetwrap.h"
76 #include "frontends/Application.h"
77 #include "frontends/Alert.h"
78 #include "frontends/Dialogs.h"
79 #include "frontends/FileDialog.h"
80 #include "frontends/FontLoader.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>
106 using bv_funcs::freefont2string;
108 using support::absolutePath;
109 using support::addName;
110 using support::addPath;
111 using support::bformat;
112 using support::changeExtension;
113 using support::contains;
114 using support::FileFilterList;
115 using support::fileSearch;
116 using support::ForkedcallsController;
117 using support::i18nLibFileSearch;
118 using support::isDirWriteable;
119 using support::isFileReadable;
120 using support::isStrInt;
121 using support::makeAbsPath;
122 using support::makeDisplayPath;
123 using support::package;
124 using support::quoteName;
125 using support::rtrim;
126 using support::split;
127 using support::subst;
128 using support::Systemcall;
129 using support::token;
131 using support::prefixIs;
134 using std::make_pair;
137 using std::istringstream;
138 using std::ostringstream;
140 namespace Alert = frontend::Alert;
141 namespace fs = boost::filesystem;
145 extern tex_accent_struct get_accent(kb_action action);
150 bool getLocalStatus(LCursor cursor,
151 FuncRequest const & cmd, FuncStatus & status)
153 // Try to fix cursor in case it is broken.
154 cursor.fixIfBroken();
156 // This is, of course, a mess. Better create a new doc iterator and use
157 // this in Inset::getStatus. This might require an additional
158 // BufferView * arg, though (which should be avoided)
159 //LCursor safe = *this;
161 for ( ; cursor.depth(); cursor.pop()) {
162 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
163 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
164 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
165 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
167 // The inset's getStatus() will return 'true' if it made
168 // a definitive decision on whether it want to handle the
169 // request or not. The result of this decision is put into
170 // the 'status' parameter.
171 if (cursor.inset().getStatus(cursor, cmd, status)) {
180 /** Return the change status at cursor position, taking in account the
181 * status at each level of the document iterator (a table in a deleted
182 * footnote is deleted).
183 * When \param outer is true, the top slice is not looked at.
185 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
187 size_t const depth = dit.depth() - (outer ? 1 : 0);
189 for (size_t i = 0 ; i < depth ; ++i) {
190 CursorSlice const & slice = dit[i];
191 if (!slice.inset().inMathed()
192 && slice.pos() < slice.paragraph().size()) {
193 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
194 if (ch != Change::UNCHANGED)
198 return Change::UNCHANGED;
206 meta_fake_bit(key_modifier::none)
211 void LyXFunc::initKeySequences(kb_keymap * kb)
213 keyseq.reset(new kb_sequence(kb, kb));
214 cancel_meta_seq.reset(new kb_sequence(kb, kb));
218 void LyXFunc::setLyXView(LyXView * lv)
224 void LyXFunc::handleKeyFunc(kb_action action)
226 char_type c = encoded_last_key;
228 if (keyseq->length())
231 lyx_view_->view()->getIntl().getTransManager()
232 .deadkey(c, get_accent(action).accent, view()->getLyXText());
233 // Need to clear, in case the minibuffer calls these
236 // copied verbatim from do_accent_char
237 view()->cursor().resetAnchor();
242 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
244 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
246 // Do nothing if we have nothing (JMarc)
247 if (!keysym->isOK()) {
248 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
253 if (keysym->isModifier()) {
254 lyxerr[Debug::KEY] << "isModifier true" << endl;
258 //Encoding const * encoding = view()->cursor().getEncoding();
259 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
260 size_t encoded_last_key = keysym->getUCSEncoded();
262 // Do a one-deep top-level lookup for
263 // cancel and meta-fake keys. RVDK_PATCH_5
264 cancel_meta_seq->reset();
266 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
267 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
268 << " action first set to [" << func.action << ']'
271 // When not cancel or meta-fake, do the normal lookup.
272 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
273 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
274 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
275 // remove Caps Lock and Mod2 as a modifiers
276 func = keyseq->addkey(keysym, (state | meta_fake_bit));
277 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
278 << "action now set to ["
279 << func.action << ']' << endl;
282 // Dont remove this unless you know what you are doing.
283 meta_fake_bit = key_modifier::none;
285 // Can this happen now ?
286 if (func.action == LFUN_NOACTION) {
287 func = FuncRequest(LFUN_COMMAND_PREFIX);
290 if (lyxerr.debugging(Debug::KEY)) {
291 lyxerr << BOOST_CURRENT_FUNCTION
293 << func.action << "]["
294 << keyseq->print() << ']'
298 // already here we know if it any point in going further
299 // why not return already here if action == -1 and
300 // num_bytes == 0? (Lgb)
302 if (keyseq->length() > 1) {
303 lyx_view_->message(from_utf8(keyseq->print()));
307 // Maybe user can only reach the key via holding down shift.
308 // Let's see. But only if shift is the only modifier
309 if (func.action == LFUN_UNKNOWN_ACTION &&
310 state == key_modifier::shift) {
311 lyxerr[Debug::KEY] << "Trying without shift" << endl;
312 func = keyseq->addkey(keysym, key_modifier::none);
313 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
316 if (func.action == LFUN_UNKNOWN_ACTION) {
317 // Hmm, we didn't match any of the keysequences. See
318 // if it's normal insertable text not already covered
320 if (keysym->isText() && keyseq->length() == 1) {
321 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
322 func = FuncRequest(LFUN_SELF_INSERT,
323 FuncRequest::KEYBOARD);
325 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
326 lyx_view_->message(_("Unknown function."));
331 if (func.action == LFUN_SELF_INSERT) {
332 if (encoded_last_key != 0) {
333 docstring const arg(1, encoded_last_key);
334 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
335 FuncRequest::KEYBOARD));
337 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
345 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
347 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
349 LCursor & cur = view()->cursor();
351 /* In LyX/Mac, when a dialog is open, the menus of the
352 application can still be accessed without giving focus to
353 the main window. In this case, we want to disable the menu
354 entries that are buffer-related.
356 Note that this code is not perfect, as bug 1941 attests:
357 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
360 if (cmd.origin == FuncRequest::UI && !lyx_view_->hasFocus())
363 buf = lyx_view_->buffer();
365 if (cmd.action == LFUN_NOACTION) {
366 flag.message(from_utf8(N_("Nothing to do")));
371 switch (cmd.action) {
372 case LFUN_UNKNOWN_ACTION:
373 #ifndef HAVE_LIBAIKSAURUS
374 case LFUN_THESAURUS_ENTRY:
384 if (flag.unknown()) {
385 flag.message(from_utf8(N_("Unknown action")));
389 if (!flag.enabled()) {
390 if (flag.message().empty())
391 flag.message(from_utf8(N_("Command disabled")));
395 // Check whether we need a buffer
396 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
398 flag.message(from_utf8(N_("Command not allowed with"
399 "out any document open")));
404 // I would really like to avoid having this switch and rather try to
405 // encode this in the function itself.
406 // -- And I'd rather let an inset decide which LFUNs it is willing
407 // to handle (Andre')
409 switch (cmd.action) {
410 case LFUN_BUFFER_TOGGLE_READ_ONLY:
411 flag.setOnOff(buf->isReadonly());
414 case LFUN_BUFFER_SWITCH:
415 // toggle on the current buffer, but do not toggle off
416 // the other ones (is that a good idea?)
417 if (to_utf8(cmd.argument()) == buf->fileName())
421 case LFUN_BUFFER_EXPORT:
422 enable = cmd.argument() == "custom"
423 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
426 case LFUN_BUFFER_CHKTEX:
427 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
430 case LFUN_BUILD_PROGRAM:
431 enable = Exporter::isExportable(*buf, "program");
434 case LFUN_LAYOUT_TABULAR:
435 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
439 case LFUN_LAYOUT_PARAGRAPH:
440 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
443 case LFUN_VC_REGISTER:
444 enable = !buf->lyxvc().inUse();
446 case LFUN_VC_CHECK_IN:
447 enable = buf->lyxvc().inUse() && !buf->isReadonly();
449 case LFUN_VC_CHECK_OUT:
450 enable = buf->lyxvc().inUse() && buf->isReadonly();
453 case LFUN_VC_UNDO_LAST:
454 enable = buf->lyxvc().inUse();
456 case LFUN_BUFFER_RELOAD:
457 enable = !buf->isUnnamed() && !buf->isClean();
460 case LFUN_INSET_SETTINGS: {
464 InsetBase::Code code = cur.inset().lyxCode();
466 case InsetBase::TABULAR_CODE:
467 enable = cmd.argument() == "tabular";
469 case InsetBase::ERT_CODE:
470 enable = cmd.argument() == "ert";
472 case InsetBase::FLOAT_CODE:
473 enable = cmd.argument() == "float";
475 case InsetBase::WRAP_CODE:
476 enable = cmd.argument() == "wrap";
478 case InsetBase::NOTE_CODE:
479 enable = cmd.argument() == "note";
481 case InsetBase::BRANCH_CODE:
482 enable = cmd.argument() == "branch";
484 case InsetBase::BOX_CODE:
485 enable = cmd.argument() == "box";
493 case LFUN_INSET_APPLY: {
494 string const name = cmd.getArg(0);
495 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
497 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
499 bool const success = inset->getStatus(cur, fr, fs);
500 // Every inset is supposed to handle this
501 BOOST_ASSERT(success);
504 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
505 flag |= getStatus(fr);
507 enable = flag.enabled();
511 case LFUN_DIALOG_SHOW: {
512 string const name = cmd.getArg(0);
514 enable = name == "aboutlyx"
518 || name == "texinfo";
519 else if (name == "print")
520 enable = Exporter::isExportable(*buf, "dvi")
521 && lyxrc.print_command != "none";
522 else if (name == "character" || name == "mathpanel")
523 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
524 else if (name == "latexlog")
525 enable = isFileReadable(buf->getLogName().second);
526 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
527 else if (name == "spellchecker")
530 else if (name == "vclog")
531 enable = buf->lyxvc().inUse();
532 else if (name == "view-source")
537 case LFUN_DIALOG_SHOW_NEW_INSET:
538 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
541 case LFUN_DIALOG_UPDATE: {
542 string const name = cmd.getArg(0);
544 enable = name == "prefs";
548 case LFUN_CITATION_INSERT: {
549 FuncRequest fr(LFUN_INSET_INSERT, "citation");
550 enable = getStatus(fr).enabled();
554 case LFUN_BUFFER_WRITE: {
555 enable = view()->buffer()->isUnnamed()
556 || !view()->buffer()->isClean();
560 // this one is difficult to get right. As a half-baked
561 // solution, we consider only the first action of the sequence
562 case LFUN_COMMAND_SEQUENCE: {
563 // argument contains ';'-terminated commands
564 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
565 FuncRequest func(lyxaction.lookupFunc(firstcmd));
566 func.origin = cmd.origin;
567 flag = getStatus(func);
570 case LFUN_BUFFER_NEW:
571 case LFUN_BUFFER_NEW_TEMPLATE:
572 case LFUN_WORD_FIND_FORWARD:
573 case LFUN_WORD_FIND_BACKWARD:
574 case LFUN_COMMAND_PREFIX:
575 case LFUN_COMMAND_EXECUTE:
577 case LFUN_META_PREFIX:
578 case LFUN_BUFFER_CLOSE:
579 case LFUN_BUFFER_WRITE_AS:
580 case LFUN_BUFFER_UPDATE:
581 case LFUN_BUFFER_VIEW:
582 case LFUN_BUFFER_IMPORT:
585 case LFUN_BUFFER_AUTO_SAVE:
586 case LFUN_RECONFIGURE:
590 case LFUN_DROP_LAYOUTS_CHOICE:
592 case LFUN_SERVER_GET_NAME:
593 case LFUN_SERVER_NOTIFY:
594 case LFUN_SERVER_GOTO_FILE_ROW:
595 case LFUN_DIALOG_HIDE:
596 case LFUN_DIALOG_DISCONNECT_INSET:
597 case LFUN_BUFFER_CHILD_OPEN:
598 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
599 case LFUN_KEYMAP_OFF:
600 case LFUN_KEYMAP_PRIMARY:
601 case LFUN_KEYMAP_SECONDARY:
602 case LFUN_KEYMAP_TOGGLE:
604 case LFUN_BUFFER_EXPORT_CUSTOM:
605 case LFUN_BUFFER_PRINT:
606 case LFUN_PREFERENCES_SAVE:
607 case LFUN_SCREEN_FONT_UPDATE:
610 case LFUN_EXTERNAL_EDIT:
611 case LFUN_GRAPHICS_EDIT:
612 case LFUN_ALL_INSETS_TOGGLE:
613 case LFUN_BUFFER_LANGUAGE:
614 case LFUN_TEXTCLASS_APPLY:
615 case LFUN_TEXTCLASS_LOAD:
616 case LFUN_BUFFER_SAVE_AS_DEFAULT:
617 case LFUN_BUFFER_PARAMS_APPLY:
618 case LFUN_LYXRC_APPLY:
619 case LFUN_BUFFER_NEXT:
620 case LFUN_BUFFER_PREVIOUS:
621 case LFUN_WINDOW_NEW:
622 // these are handled in our dispatch()
626 if (!getLocalStatus(cur, cmd, flag))
627 flag = view()->getStatus(cmd);
633 // Can we use a readonly buffer?
634 if (buf && buf->isReadonly()
635 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
636 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
637 flag.message(from_utf8(N_("Document is read-only")));
641 // Are we in a DELETED change-tracking region?
642 if (buf && lookupChangeType(cur, true) == Change::DELETED
643 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
644 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
645 flag.message(from_utf8(N_("This portion of the document is deleted.")));
649 // the default error message if we disable the command
650 if (!flag.enabled() && flag.message().empty())
651 flag.message(from_utf8(N_("Command disabled")));
657 bool LyXFunc::ensureBufferClean(BufferView * bv)
659 Buffer & buf = *bv->buffer();
663 docstring const file = makeDisplayPath(buf.fileName(), 30);
664 docstring text = bformat(_("The document %1$s has unsaved "
665 "changes.\n\nDo you want to save "
666 "the document?"), file);
667 int const ret = Alert::prompt(_("Save changed document?"),
668 text, 0, 1, _("&Save"),
672 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
674 return buf.isClean();
680 void showPrintError(string const & name)
682 docstring str = bformat(_("Could not print the document %1$s.\n"
683 "Check that your printer is set up correctly."),
684 makeDisplayPath(name, 50));
685 Alert::error(_("Print document failed"), str);
689 void loadTextclass(string const & name)
691 std::pair<bool, textclass_type> const tc_pair =
692 textclasslist.numberOfClass(name);
694 if (!tc_pair.first) {
695 lyxerr << "Document class \"" << name
696 << "\" does not exist."
701 textclass_type const tc = tc_pair.second;
703 if (!textclasslist[tc].load()) {
704 docstring s = bformat(_("The document could not be converted\n"
705 "into the document class %1$s."),
706 from_utf8(textclasslist[tc].name()));
707 Alert::error(_("Could not change class"), s);
712 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
717 void LyXFunc::dispatch(FuncRequest const & cmd)
719 BOOST_ASSERT(view());
720 string const argument = to_utf8(cmd.argument());
721 kb_action const action = cmd.action;
723 lyxerr[Debug::ACTION] << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
724 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
726 // we have not done anything wrong yet.
728 dispatch_buffer.erase();
730 // redraw the screen at the end (first of the two drawing steps).
731 //This is done unless explicitely requested otherwise
732 Update::flags updateFlags = Update::FitCursor;
734 FuncStatus const flag = getStatus(cmd);
735 if (!flag.enabled()) {
736 // We cannot use this function here
737 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
738 << lyxaction.getActionName(action)
739 << " [" << action << "] is disabled at this location"
741 setErrorMessage(flag.message());
745 case LFUN_WORD_FIND_FORWARD:
746 case LFUN_WORD_FIND_BACKWARD: {
747 static string last_search;
748 string searched_string;
750 if (!argument.empty()) {
751 last_search = argument;
752 searched_string = argument;
754 searched_string = last_search;
757 if (searched_string.empty())
760 bool const fw = action == LFUN_WORD_FIND_FORWARD;
762 find2string(searched_string, true, false, fw);
763 find(view(), FuncRequest(LFUN_WORD_FIND, data));
767 case LFUN_COMMAND_PREFIX:
768 lyx_view_->message(from_utf8(keyseq->printOptions()));
771 case LFUN_COMMAND_EXECUTE:
772 lyx_view_->getToolbars().display("minibuffer", true);
773 lyx_view_->focus_command_buffer();
778 meta_fake_bit = key_modifier::none;
779 if (view()->buffer())
780 // cancel any selection
781 dispatch(FuncRequest(LFUN_MARK_OFF));
782 setMessage(_("Cancel"));
785 case LFUN_META_PREFIX:
786 meta_fake_bit = key_modifier::alt;
787 setMessage(from_utf8(keyseq->print()));
790 case LFUN_BUFFER_TOGGLE_READ_ONLY:
791 if (lyx_view_->buffer()->lyxvc().inUse())
792 lyx_view_->buffer()->lyxvc().toggleReadOnly();
794 lyx_view_->buffer()->setReadonly(
795 !lyx_view_->buffer()->isReadonly());
798 // --- Menus -----------------------------------------------
799 case LFUN_BUFFER_NEW:
800 menuNew(argument, false);
803 case LFUN_BUFFER_NEW_TEMPLATE:
804 menuNew(argument, true);
807 case LFUN_BUFFER_CLOSE:
812 case LFUN_BUFFER_WRITE:
813 if (!lyx_view_->buffer()->isUnnamed()) {
814 docstring const str = bformat(_("Saving document %1$s..."),
815 makeDisplayPath(lyx_view_->buffer()->fileName()));
816 lyx_view_->message(str);
817 menuWrite(lyx_view_->buffer());
818 lyx_view_->message(str + _(" done."));
820 writeAs(lyx_view_->buffer());
821 updateFlags = Update::None;
824 case LFUN_BUFFER_WRITE_AS:
825 writeAs(lyx_view_->buffer(), argument);
826 updateFlags = Update::None;
829 case LFUN_BUFFER_RELOAD: {
830 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
831 docstring text = bformat(_("Any changes will be lost. Are you sure "
832 "you want to revert to the saved version of the document %1$s?"), file);
833 int const ret = Alert::prompt(_("Revert to saved document?"),
834 text, 0, 1, _("&Revert"), _("&Cancel"));
841 case LFUN_BUFFER_UPDATE:
842 Exporter::Export(lyx_view_->buffer(), argument, true);
845 case LFUN_BUFFER_VIEW:
846 Exporter::preview(lyx_view_->buffer(), argument);
849 case LFUN_BUILD_PROGRAM:
850 Exporter::Export(lyx_view_->buffer(), "program", true);
853 case LFUN_BUFFER_CHKTEX:
854 lyx_view_->buffer()->runChktex();
857 case LFUN_BUFFER_EXPORT:
858 if (argument == "custom")
859 lyx_view_->getDialogs().show("sendto");
861 Exporter::Export(lyx_view_->buffer(), argument, false);
865 case LFUN_BUFFER_EXPORT_CUSTOM: {
867 string command = split(argument, format_name, ' ');
868 Format const * format = formats.getFormat(format_name);
870 lyxerr << "Format \"" << format_name
871 << "\" not recognized!"
876 Buffer * buffer = lyx_view_->buffer();
878 // The name of the file created by the conversion process
881 // Output to filename
882 if (format->name() == "lyx") {
883 string const latexname =
884 buffer->getLatexName(false);
885 filename = changeExtension(latexname,
886 format->extension());
887 filename = addName(buffer->temppath(), filename);
889 if (!buffer->writeFile(filename))
893 Exporter::Export(buffer, format_name, true, filename);
896 // Substitute $$FName for filename
897 if (!contains(command, "$$FName"))
898 command = "( " + command + " ) < $$FName";
899 command = subst(command, "$$FName", filename);
901 // Execute the command in the background
903 call.startscript(Systemcall::DontWait, command);
907 case LFUN_BUFFER_PRINT: {
910 string command = split(split(argument, target, ' '),
914 || target_name.empty()
915 || command.empty()) {
916 lyxerr << "Unable to parse \""
917 << argument << '"' << std::endl;
920 if (target != "printer" && target != "file") {
921 lyxerr << "Unrecognized target \""
922 << target << '"' << std::endl;
926 Buffer * buffer = lyx_view_->buffer();
928 if (!Exporter::Export(buffer, "dvi", true)) {
929 showPrintError(buffer->fileName());
933 // Push directory path.
934 string const path = buffer->temppath();
935 support::Path p(path);
937 // there are three cases here:
938 // 1. we print to a file
939 // 2. we print directly to a printer
940 // 3. we print using a spool command (print to file first)
943 string const dviname =
944 changeExtension(buffer->getLatexName(true),
947 if (target == "printer") {
948 if (!lyxrc.print_spool_command.empty()) {
949 // case 3: print using a spool
950 string const psname =
951 changeExtension(dviname,".ps");
952 command += lyxrc.print_to_file
955 + quoteName(dviname);
958 lyxrc.print_spool_command +' ';
959 if (target_name != "default") {
960 command2 += lyxrc.print_spool_printerprefix
964 command2 += quoteName(psname);
966 // If successful, then spool command
967 res = one.startscript(
972 res = one.startscript(
973 Systemcall::DontWait,
976 // case 2: print directly to a printer
977 res = one.startscript(
978 Systemcall::DontWait,
979 command + quoteName(dviname));
983 // case 1: print to a file
984 command += lyxrc.print_to_file
985 + quoteName(makeAbsPath(target_name,
988 + quoteName(dviname);
989 res = one.startscript(Systemcall::DontWait,
994 showPrintError(buffer->fileName());
998 case LFUN_BUFFER_IMPORT:
1003 if (view()->buffer()) {
1004 // save cursor Position for opened files to .lyx/session
1005 LyX::ref().session().saveFilePosition(lyx_view_->buffer()->fileName(),
1006 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1007 // save bookmarks to .lyx/session
1008 view()->saveSavedPositions();
1010 LyX::ref().quit(argument == "force");
1013 case LFUN_TOC_VIEW: {
1014 InsetCommandParams p("tableofcontents");
1015 string const data = InsetCommandMailer::params2string("toc", p);
1016 lyx_view_->getDialogs().show("toc", data, 0);
1020 case LFUN_BUFFER_AUTO_SAVE:
1024 case LFUN_RECONFIGURE:
1025 reconfigure(view());
1028 case LFUN_HELP_OPEN: {
1029 string const arg = argument;
1031 setErrorMessage(_("Missing argument"));
1034 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1035 if (fname.empty()) {
1036 lyxerr << "LyX: unable to find documentation file `"
1037 << arg << "'. Bad installation?" << endl;
1040 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1041 makeDisplayPath(fname)));
1042 lyx_view_->loadLyXFile(fname, false);
1046 // --- version control -------------------------------
1047 case LFUN_VC_REGISTER:
1048 if (!ensureBufferClean(view()))
1050 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1051 lyx_view_->buffer()->lyxvc().registrer();
1056 case LFUN_VC_CHECK_IN:
1057 if (!ensureBufferClean(view()))
1059 if (lyx_view_->buffer()->lyxvc().inUse()
1060 && !lyx_view_->buffer()->isReadonly()) {
1061 lyx_view_->buffer()->lyxvc().checkIn();
1066 case LFUN_VC_CHECK_OUT:
1067 if (!ensureBufferClean(view()))
1069 if (lyx_view_->buffer()->lyxvc().inUse()
1070 && lyx_view_->buffer()->isReadonly()) {
1071 lyx_view_->buffer()->lyxvc().checkOut();
1076 case LFUN_VC_REVERT:
1077 lyx_view_->buffer()->lyxvc().revert();
1081 case LFUN_VC_UNDO_LAST:
1082 lyx_view_->buffer()->lyxvc().undoLast();
1086 // --- buffers ----------------------------------------
1087 case LFUN_BUFFER_SWITCH:
1088 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1091 case LFUN_BUFFER_NEXT:
1092 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1095 case LFUN_BUFFER_PREVIOUS:
1096 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1100 newFile(view(), argument);
1103 case LFUN_FILE_OPEN:
1107 case LFUN_DROP_LAYOUTS_CHOICE:
1108 lyx_view_->getToolbars().openLayoutList();
1111 case LFUN_MENU_OPEN:
1112 lyx_view_->getMenubar().openByName(from_utf8(argument));
1115 // --- lyxserver commands ----------------------------
1116 case LFUN_SERVER_GET_NAME:
1117 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1118 lyxerr[Debug::INFO] << "FNAME["
1119 << lyx_view_->buffer()->fileName()
1123 case LFUN_SERVER_NOTIFY:
1124 dispatch_buffer = from_utf8(keyseq->print());
1125 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1128 case LFUN_SERVER_GOTO_FILE_ROW: {
1131 istringstream is(argument);
1132 is >> file_name >> row;
1133 if (prefixIs(file_name, package().temp_dir())) {
1134 // Needed by inverse dvi search. If it is a file
1135 // in tmpdir, call the apropriated function
1136 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1138 // Must replace extension of the file to be .lyx
1139 // and get full path
1140 string const s = changeExtension(file_name, ".lyx");
1141 // Either change buffer or load the file
1142 if (theBufferList().exists(s)) {
1143 lyx_view_->setBuffer(theBufferList().getBuffer(s));
1145 lyx_view_->loadLyXFile(s);
1149 view()->setCursorFromRow(row);
1152 // see BufferView::center()
1156 case LFUN_DIALOG_SHOW: {
1157 string const name = cmd.getArg(0);
1158 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1160 if (name == "character") {
1161 data = freefont2string();
1163 lyx_view_->getDialogs().show("character", data);
1164 } else if (name == "latexlog") {
1165 pair<Buffer::LogType, string> const logfile =
1166 lyx_view_->buffer()->getLogName();
1167 switch (logfile.first) {
1168 case Buffer::latexlog:
1171 case Buffer::buildlog:
1175 data += LyXLex::quoteString(logfile.second);
1176 lyx_view_->getDialogs().show("log", data);
1177 } else if (name == "vclog") {
1178 string const data = "vc " +
1179 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1180 lyx_view_->getDialogs().show("log", data);
1182 lyx_view_->getDialogs().show(name, data);
1186 case LFUN_DIALOG_SHOW_NEW_INSET: {
1187 string const name = cmd.getArg(0);
1188 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1189 if (name == "bibitem" ||
1196 InsetCommandParams p(name);
1197 data = InsetCommandMailer::params2string(name, p);
1198 } else if (name == "include") {
1199 InsetCommandParams p(data);
1200 data = InsetIncludeMailer::params2string(p);
1201 } else if (name == "box") {
1202 // \c data == "Boxed" || "Frameless" etc
1203 InsetBoxParams p(data);
1204 data = InsetBoxMailer::params2string(p);
1205 } else if (name == "branch") {
1206 InsetBranchParams p;
1207 data = InsetBranchMailer::params2string(p);
1208 } else if (name == "citation") {
1209 InsetCommandParams p("cite");
1210 data = InsetCommandMailer::params2string(name, p);
1211 } else if (name == "ert") {
1212 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1213 } else if (name == "external") {
1214 InsetExternalParams p;
1215 Buffer const & buffer = *lyx_view_->buffer();
1216 data = InsetExternalMailer::params2string(p, buffer);
1217 } else if (name == "float") {
1219 data = InsetFloatMailer::params2string(p);
1220 } else if (name == "graphics") {
1221 InsetGraphicsParams p;
1222 Buffer const & buffer = *lyx_view_->buffer();
1223 data = InsetGraphicsMailer::params2string(p, buffer);
1224 } else if (name == "note") {
1226 data = InsetNoteMailer::params2string(p);
1227 } else if (name == "vspace") {
1229 data = InsetVSpaceMailer::params2string(space);
1230 } else if (name == "wrap") {
1232 data = InsetWrapMailer::params2string(p);
1234 lyx_view_->getDialogs().show(name, data, 0);
1238 case LFUN_DIALOG_UPDATE: {
1239 string const & name = argument;
1240 // Can only update a dialog connected to an existing inset
1241 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1243 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1244 inset->dispatch(view()->cursor(), fr);
1245 } else if (name == "paragraph") {
1246 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1247 } else if (name == "prefs") {
1248 lyx_view_->getDialogs().update(name, string());
1253 case LFUN_DIALOG_HIDE:
1254 Dialogs::hide(argument, 0);
1257 case LFUN_DIALOG_DISCONNECT_INSET:
1258 lyx_view_->getDialogs().disconnect(argument);
1262 case LFUN_CITATION_INSERT: {
1263 if (!argument.empty()) {
1264 // we can have one optional argument, delimited by '|'
1265 // citation-insert <key>|<text_before>
1266 // this should be enhanced to also support text_after
1267 // and citation style
1268 string arg = argument;
1270 if (contains(argument, "|")) {
1271 arg = token(argument, '|', 0);
1272 opt1 = '[' + token(argument, '|', 1) + ']';
1274 std::ostringstream os;
1275 os << "citation LatexCommand\n"
1276 << "\\cite" << opt1 << "{" << arg << "}\n"
1278 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1281 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1285 case LFUN_BUFFER_CHILD_OPEN: {
1286 string const filename =
1287 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1288 // FIXME Should use bformat
1289 setMessage(_("Opening child document ") +
1290 makeDisplayPath(filename) + "...");
1291 view()->savePosition(0);
1292 string const parentfilename = lyx_view_->buffer()->fileName();
1293 if (theBufferList().exists(filename))
1294 lyx_view_->setBuffer(theBufferList().getBuffer(filename));
1296 lyx_view_->loadLyXFile(filename);
1297 // Set the parent name of the child document.
1298 // This makes insertion of citations and references in the child work,
1299 // when the target is in the parent or another child document.
1300 lyx_view_->buffer()->setParentName(parentfilename);
1304 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1305 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1308 case LFUN_KEYMAP_OFF:
1309 lyx_view_->view()->getIntl().keyMapOn(false);
1312 case LFUN_KEYMAP_PRIMARY:
1313 lyx_view_->view()->getIntl().keyMapPrim();
1316 case LFUN_KEYMAP_SECONDARY:
1317 lyx_view_->view()->getIntl().keyMapSec();
1320 case LFUN_KEYMAP_TOGGLE:
1321 lyx_view_->view()->getIntl().toggleKeyMap();
1327 string rest = split(argument, countstr, ' ');
1328 istringstream is(countstr);
1331 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1332 for (int i = 0; i < count; ++i)
1333 dispatch(lyxaction.lookupFunc(rest));
1337 case LFUN_COMMAND_SEQUENCE: {
1338 // argument contains ';'-terminated commands
1339 string arg = argument;
1340 while (!arg.empty()) {
1342 arg = split(arg, first, ';');
1343 FuncRequest func(lyxaction.lookupFunc(first));
1344 func.origin = cmd.origin;
1350 case LFUN_PREFERENCES_SAVE: {
1351 support::Path p(package().user_support());
1352 lyxrc.write("preferences", false);
1356 case LFUN_SCREEN_FONT_UPDATE:
1357 // handle the screen font changes.
1358 lyxrc.set_font_norm_type();
1359 theFontLoader().update();
1360 // All visible buffers will need resize
1364 case LFUN_SET_COLOR: {
1366 string const x11_name = split(argument, lyx_name, ' ');
1367 if (lyx_name.empty() || x11_name.empty()) {
1368 setErrorMessage(_("Syntax: set-color <lyx_name>"
1373 bool const graphicsbg_changed =
1374 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1375 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1377 if (!lcolor.setColor(lyx_name, x11_name)) {
1379 bformat(_("Set-color \"%1$s\" failed "
1380 "- color is undefined or "
1381 "may not be redefined"),
1382 from_utf8(lyx_name)));
1386 theApp->updateColor(lcolor.getFromLyXName(lyx_name));
1388 if (graphicsbg_changed) {
1389 #ifdef WITH_WARNINGS
1390 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1393 graphics::GCache::get().changeDisplay(true);
1400 lyx_view_->message(from_utf8(argument));
1403 case LFUN_EXTERNAL_EDIT: {
1404 FuncRequest fr(action, argument);
1405 InsetExternal().dispatch(view()->cursor(), fr);
1409 case LFUN_GRAPHICS_EDIT: {
1410 FuncRequest fr(action, argument);
1411 InsetGraphics().dispatch(view()->cursor(), fr);
1415 case LFUN_INSET_APPLY: {
1416 string const name = cmd.getArg(0);
1417 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1419 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1420 inset->dispatch(view()->cursor(), fr);
1422 FuncRequest fr(LFUN_INSET_INSERT, argument);
1425 // ideally, the update flag should be set by the insets,
1426 // but this is not possible currently
1427 updateFlags = Update::Force | Update::FitCursor;
1431 case LFUN_ALL_INSETS_TOGGLE: {
1433 string const name = split(argument, action, ' ');
1434 InsetBase::Code const inset_code =
1435 InsetBase::translate(name);
1437 LCursor & cur = view()->cursor();
1438 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1440 InsetBase & inset = lyx_view_->buffer()->inset();
1441 InsetIterator it = inset_iterator_begin(inset);
1442 InsetIterator const end = inset_iterator_end(inset);
1443 for (; it != end; ++it) {
1444 if (inset_code == InsetBase::NO_CODE
1445 || inset_code == it->lyxCode()) {
1446 LCursor tmpcur = cur;
1447 tmpcur.pushLeft(*it);
1448 it->dispatch(tmpcur, fr);
1451 updateFlags = Update::Force | Update::FitCursor;
1455 case LFUN_BUFFER_LANGUAGE: {
1456 Buffer & buffer = *lyx_view_->buffer();
1457 Language const * oldL = buffer.params().language;
1458 Language const * newL = languages.getLanguage(argument);
1459 if (!newL || oldL == newL)
1462 if (oldL->rightToLeft() == newL->rightToLeft()
1463 && !buffer.isMultiLingual())
1464 buffer.changeLanguage(oldL, newL);
1466 buffer.updateDocLang(newL);
1470 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1471 string const fname =
1472 addName(addPath(package().user_support(), "templates/"),
1474 Buffer defaults(fname);
1476 istringstream ss(argument);
1479 int const unknown_tokens = defaults.readHeader(lex);
1481 if (unknown_tokens != 0) {
1482 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1483 << unknown_tokens << " unknown token"
1484 << (unknown_tokens == 1 ? "" : "s")
1488 if (defaults.writeFile(defaults.fileName()))
1489 // FIXME Should use bformat
1490 setMessage(_("Document defaults saved in ")
1491 + makeDisplayPath(fname));
1493 setErrorMessage(_("Unable to save document defaults"));
1497 case LFUN_BUFFER_PARAMS_APPLY: {
1498 biblio::CiteEngine const engine =
1499 lyx_view_->buffer()->params().cite_engine;
1501 istringstream ss(argument);
1504 int const unknown_tokens =
1505 lyx_view_->buffer()->readHeader(lex);
1507 if (unknown_tokens != 0) {
1508 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1509 << unknown_tokens << " unknown token"
1510 << (unknown_tokens == 1 ? "" : "s")
1513 if (engine == lyx_view_->buffer()->params().cite_engine)
1516 LCursor & cur = view()->cursor();
1517 FuncRequest fr(LFUN_INSET_REFRESH);
1519 InsetBase & inset = lyx_view_->buffer()->inset();
1520 InsetIterator it = inset_iterator_begin(inset);
1521 InsetIterator const end = inset_iterator_end(inset);
1522 for (; it != end; ++it)
1523 if (it->lyxCode() == InsetBase::CITE_CODE)
1524 it->dispatch(cur, fr);
1528 case LFUN_TEXTCLASS_APPLY: {
1529 Buffer * buffer = lyx_view_->buffer();
1531 textclass_type const old_class =
1532 buffer->params().textclass;
1534 loadTextclass(argument);
1536 std::pair<bool, textclass_type> const tc_pair =
1537 textclasslist.numberOfClass(argument);
1542 textclass_type const new_class = tc_pair.second;
1543 if (old_class == new_class)
1547 lyx_view_->message(_("Converting document to new document class..."));
1548 recordUndoFullDocument(view());
1549 buffer->params().textclass = new_class;
1550 StableDocIterator backcur(view()->cursor());
1551 ErrorList & el = buffer->errorList("Class Switch");
1552 cap::switchBetweenClasses(
1553 old_class, new_class,
1554 static_cast<InsetText &>(buffer->inset()), el);
1556 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1558 buffer->errors("Class Switch");
1559 updateLabels(*buffer);
1560 updateFlags = Update::Force | Update::FitCursor;
1564 case LFUN_TEXTCLASS_LOAD:
1565 loadTextclass(argument);
1568 case LFUN_LYXRC_APPLY: {
1569 LyXRC const lyxrc_orig = lyxrc;
1571 istringstream ss(argument);
1572 bool const success = lyxrc.read(ss) == 0;
1575 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1576 << "Unable to read lyxrc data"
1581 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1585 case LFUN_WINDOW_NEW:
1586 BOOST_ASSERT(theApp);
1587 LyX::ref().newLyXView();
1590 view()->cursor().dispatch(cmd);
1591 updateFlags = view()->cursor().result().update();
1592 if (!view()->cursor().result().dispatched())
1593 if (view()->dispatch(cmd))
1594 updateFlags = Update::Force | Update::FitCursor;
1599 if (view()->buffer()) {
1600 // Redraw screen unless explicitly told otherwise.
1601 // This also initializes the position cache for all insets
1602 // in (at least partially) visible top-level paragraphs.
1603 bool needSecondUpdate = false;
1604 if (updateFlags != Update::None)
1605 view()->update(updateFlags);
1607 needSecondUpdate = view()->fitCursor();
1609 if (needSecondUpdate || updateFlags != Update::None) {
1610 view()->buffer()->changed();
1612 lyx_view_->updateStatusBar();
1614 // if we executed a mutating lfun, mark the buffer as dirty
1616 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1617 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1618 view()->buffer()->markDirty();
1620 if (view()->cursor().inTexted()) {
1621 lyx_view_->updateLayoutChoice();
1626 // FIXME UNICODE: _() does not support anything but ascii.
1627 // Do we need a to_ascii() method?
1628 sendDispatchMessage(getMessage(), cmd);
1632 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1634 /* When an action did not originate from the UI/kbd, it makes
1635 * sense to avoid updating the GUI. It turns out that this
1636 * fixes bug 1941, for reasons that are described here:
1637 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1639 if (cmd.origin != FuncRequest::INTERNAL) {
1640 lyx_view_->updateMenubar();
1641 lyx_view_->updateToolbars();
1644 const bool verbose = (cmd.origin == FuncRequest::UI
1645 || cmd.origin == FuncRequest::COMMANDBUFFER);
1647 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1648 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1650 lyx_view_->message(msg);
1654 docstring dispatch_msg = msg;
1655 if (!dispatch_msg.empty())
1656 dispatch_msg += ' ';
1658 string comname = lyxaction.getActionName(cmd.action);
1660 bool argsadded = false;
1662 if (!cmd.argument().empty()) {
1663 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1664 comname += ' ' + to_utf8(cmd.argument());
1669 string const shortcuts = theTopLevelKeymap().printbindings(cmd);
1671 if (!shortcuts.empty())
1672 comname += ": " + shortcuts;
1673 else if (!argsadded && !cmd.argument().empty())
1674 comname += ' ' + to_utf8(cmd.argument());
1676 if (!comname.empty()) {
1677 comname = rtrim(comname);
1678 dispatch_msg += from_utf8('(' + rtrim(comname) + ')');
1681 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1682 << to_utf8(dispatch_msg) << endl;
1683 if (!dispatch_msg.empty())
1684 lyx_view_->message(dispatch_msg);
1688 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1690 string initpath = lyxrc.document_path;
1691 string filename(name);
1693 if (view()->buffer()) {
1694 string const trypath = lyx_view_->buffer()->filePath();
1695 // If directory is writeable, use this as default.
1696 if (isDirWriteable(trypath))
1700 static int newfile_number;
1702 if (filename.empty()) {
1703 filename = addName(lyxrc.document_path,
1704 "newfile" + convert<string>(++newfile_number) + ".lyx");
1705 while (theBufferList().exists(filename) || fs::is_readable(filename)) {
1707 filename = addName(lyxrc.document_path,
1708 "newfile" + convert<string>(newfile_number) +
1713 // The template stuff
1716 FileDialog fileDlg(_("Select template file"),
1717 LFUN_SELECT_FILE_SYNC,
1718 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1719 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1721 FileDialog::Result result =
1722 fileDlg.open(from_utf8(lyxrc.template_path),
1723 FileFilterList(_("LyX Documents (*.lyx)")),
1726 if (result.first == FileDialog::Later)
1728 if (result.second.empty())
1730 templname = to_utf8(result.second);
1733 Buffer * const b = newFile(filename, templname, !name.empty());
1735 lyx_view_->setBuffer(b);
1739 void LyXFunc::open(string const & fname)
1741 string initpath = lyxrc.document_path;
1743 if (view()->buffer()) {
1744 string const trypath = lyx_view_->buffer()->filePath();
1745 // If directory is writeable, use this as default.
1746 if (isDirWriteable(trypath))
1752 if (fname.empty()) {
1753 FileDialog fileDlg(_("Select document to open"),
1755 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1756 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1758 FileDialog::Result result =
1759 fileDlg.open(from_utf8(initpath),
1760 FileFilterList(_("LyX Documents (*.lyx)")),
1763 if (result.first == FileDialog::Later)
1766 filename = to_utf8(result.second);
1768 // check selected filename
1769 if (filename.empty()) {
1770 lyx_view_->message(_("Canceled."));
1776 // get absolute path of file and add ".lyx" to the filename if
1778 string const fullpath = fileSearch(string(), filename, "lyx");
1779 if (!fullpath.empty()) {
1780 filename = fullpath;
1783 docstring const disp_fn = makeDisplayPath(filename);
1785 // if the file doesn't exist, let the user create one
1786 if (!fs::exists(filename)) {
1787 // the user specifically chose this name. Believe him.
1788 Buffer * const b = newFile(filename, string(), true);
1790 lyx_view_->setBuffer(b);
1794 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1797 if (lyx_view_->loadLyXFile(filename)) {
1798 str2 = bformat(_("Document %1$s opened."), disp_fn);
1800 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1802 lyx_view_->message(str2);
1806 void LyXFunc::doImport(string const & argument)
1809 string filename = split(argument, format, ' ');
1811 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1812 << " file: " << filename << endl;
1814 // need user interaction
1815 if (filename.empty()) {
1816 string initpath = lyxrc.document_path;
1818 if (view()->buffer()) {
1819 string const trypath = lyx_view_->buffer()->filePath();
1820 // If directory is writeable, use this as default.
1821 if (isDirWriteable(trypath))
1825 docstring const text = bformat(_("Select %1$s file to import"),
1826 formats.prettyName(format));
1828 FileDialog fileDlg(text,
1830 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1831 make_pair(_("Examples|#E#e"),
1832 from_utf8(addPath(package().system_support(), "examples"))));
1834 docstring filter = formats.prettyName(format);
1837 filter += from_utf8(formats.extension(format));
1840 FileDialog::Result result =
1841 fileDlg.open(from_utf8(initpath),
1842 FileFilterList(filter),
1845 if (result.first == FileDialog::Later)
1848 filename = to_utf8(result.second);
1850 // check selected filename
1851 if (filename.empty())
1852 lyx_view_->message(_("Canceled."));
1855 if (filename.empty())
1858 // get absolute path of file
1859 filename = makeAbsPath(filename);
1861 string const lyxfile = changeExtension(filename, ".lyx");
1863 // Check if the document already is open
1864 if (use_gui && theBufferList().exists(lyxfile)) {
1865 if (!theBufferList().close(theBufferList().getBuffer(lyxfile), true)) {
1866 lyx_view_->message(_("Canceled."));
1871 // if the file exists already, and we didn't do
1872 // -i lyx thefile.lyx, warn
1873 if (fs::exists(lyxfile) && filename != lyxfile) {
1874 docstring const file = makeDisplayPath(lyxfile, 30);
1876 docstring text = bformat(_("The document %1$s already exists.\n\n"
1877 "Do you want to over-write that document?"), file);
1878 int const ret = Alert::prompt(_("Over-write document?"),
1879 text, 0, 1, _("&Over-write"), _("&Cancel"));
1882 lyx_view_->message(_("Canceled."));
1887 ErrorList errorList;
1888 Importer::Import(lyx_view_, filename, format, errorList);
1889 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1893 void LyXFunc::closeBuffer()
1895 // save current cursor position
1896 LyX::ref().session().saveFilePosition(lyx_view_->buffer()->fileName(),
1897 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1898 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
1899 if (theBufferList().empty()) {
1900 // need this otherwise SEGV may occur while
1901 // trying to set variables that don't exist
1902 // since there's no current buffer
1903 lyx_view_->getDialogs().hideBufferDependent();
1905 lyx_view_->setBuffer(theBufferList().first());
1911 // Each "lyx_view_" should have it's own message method. lyxview and
1912 // the minibuffer would use the minibuffer, but lyxserver would
1913 // send an ERROR signal to its client. Alejandro 970603
1914 // This function is bit problematic when it comes to NLS, to make the
1915 // lyx servers client be language indepenent we must not translate
1916 // strings sent to this func.
1917 void LyXFunc::setErrorMessage(docstring const & m) const
1919 dispatch_buffer = m;
1924 void LyXFunc::setMessage(docstring const & m) const
1926 dispatch_buffer = m;
1930 string const LyXFunc::viewStatusMessage()
1932 // When meta-fake key is pressed, show the key sequence so far + "M-".
1934 return keyseq->print() + "M-";
1936 // Else, when a non-complete key sequence is pressed,
1937 // show the available options.
1938 if (keyseq->length() > 0 && !keyseq->deleted())
1939 return keyseq->printOptions();
1941 if (!view()->buffer())
1942 return to_utf8(_("Welcome to LyX!"));
1944 return view()->cursor().currentState();
1948 BufferView * LyXFunc::view() const
1950 BOOST_ASSERT(lyx_view_);
1951 return lyx_view_->view();
1955 bool LyXFunc::wasMetaKey() const
1957 return (meta_fake_bit != key_modifier::none);
1963 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1965 // Why the switch you might ask. It is a trick to ensure that all
1966 // the elements in the LyXRCTags enum is handled. As you can see
1967 // there are no breaks at all. So it is just a huge fall-through.
1968 // The nice thing is that we will get a warning from the compiler
1969 // if we forget an element.
1970 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1972 case LyXRC::RC_ACCEPT_COMPOUND:
1973 case LyXRC::RC_ALT_LANG:
1974 case LyXRC::RC_ASCIIROFF_COMMAND:
1975 case LyXRC::RC_ASCII_LINELEN:
1976 case LyXRC::RC_AUTOREGIONDELETE:
1977 case LyXRC::RC_AUTORESET_OPTIONS:
1978 case LyXRC::RC_AUTOSAVE:
1979 case LyXRC::RC_AUTO_NUMBER:
1980 case LyXRC::RC_BACKUPDIR_PATH:
1981 case LyXRC::RC_BIBTEX_COMMAND:
1982 case LyXRC::RC_BINDFILE:
1983 case LyXRC::RC_CHECKLASTFILES:
1984 case LyXRC::RC_USELASTFILEPOS:
1985 case LyXRC::RC_LOADSESSION:
1986 case LyXRC::RC_CHKTEX_COMMAND:
1987 case LyXRC::RC_CONVERTER:
1988 case LyXRC::RC_COPIER:
1989 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1990 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1991 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1992 case LyXRC::RC_DATE_INSERT_FORMAT:
1993 case LyXRC::RC_DEFAULT_LANGUAGE:
1994 case LyXRC::RC_DEFAULT_PAPERSIZE:
1995 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1996 case LyXRC::RC_DISPLAY_GRAPHICS:
1997 case LyXRC::RC_DOCUMENTPATH:
1998 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1999 if (fs::exists(lyxrc_new.document_path) &&
2000 fs::is_directory(lyxrc_new.document_path)) {
2001 support::package().document_dir() = lyxrc.document_path;
2004 case LyXRC::RC_ESC_CHARS:
2005 case LyXRC::RC_FONT_ENCODING:
2006 case LyXRC::RC_FORMAT:
2007 case LyXRC::RC_INDEX_COMMAND:
2008 case LyXRC::RC_INPUT:
2009 case LyXRC::RC_KBMAP:
2010 case LyXRC::RC_KBMAP_PRIMARY:
2011 case LyXRC::RC_KBMAP_SECONDARY:
2012 case LyXRC::RC_LABEL_INIT_LENGTH:
2013 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2014 case LyXRC::RC_LANGUAGE_AUTO_END:
2015 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2016 case LyXRC::RC_LANGUAGE_COMMAND_END:
2017 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2018 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2019 case LyXRC::RC_LANGUAGE_PACKAGE:
2020 case LyXRC::RC_LANGUAGE_USE_BABEL:
2021 case LyXRC::RC_MAKE_BACKUP:
2022 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2023 case LyXRC::RC_NUMLASTFILES:
2024 case LyXRC::RC_PATH_PREFIX:
2025 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2026 support::prependEnvPath("PATH", lyxrc.path_prefix);
2028 case LyXRC::RC_PERS_DICT:
2029 case LyXRC::RC_POPUP_BOLD_FONT:
2030 case LyXRC::RC_POPUP_FONT_ENCODING:
2031 case LyXRC::RC_POPUP_NORMAL_FONT:
2032 case LyXRC::RC_PREVIEW:
2033 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2034 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2035 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2036 case LyXRC::RC_PRINTCOPIESFLAG:
2037 case LyXRC::RC_PRINTER:
2038 case LyXRC::RC_PRINTEVENPAGEFLAG:
2039 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2040 case LyXRC::RC_PRINTFILEEXTENSION:
2041 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2042 case LyXRC::RC_PRINTODDPAGEFLAG:
2043 case LyXRC::RC_PRINTPAGERANGEFLAG:
2044 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2045 case LyXRC::RC_PRINTPAPERFLAG:
2046 case LyXRC::RC_PRINTREVERSEFLAG:
2047 case LyXRC::RC_PRINTSPOOL_COMMAND:
2048 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2049 case LyXRC::RC_PRINTTOFILE:
2050 case LyXRC::RC_PRINTTOPRINTER:
2051 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2052 case LyXRC::RC_PRINT_COMMAND:
2053 case LyXRC::RC_RTL_SUPPORT:
2054 case LyXRC::RC_SCREEN_DPI:
2055 case LyXRC::RC_SCREEN_FONT_ENCODING:
2056 case LyXRC::RC_SCREEN_FONT_ROMAN:
2057 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2058 case LyXRC::RC_SCREEN_FONT_SANS:
2059 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2060 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2061 case LyXRC::RC_SCREEN_FONT_SIZES:
2062 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2063 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2064 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2065 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2066 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2067 case LyXRC::RC_SCREEN_ZOOM:
2068 case LyXRC::RC_SERVERPIPE:
2069 case LyXRC::RC_SET_COLOR:
2070 case LyXRC::RC_SHOW_BANNER:
2071 case LyXRC::RC_SPELL_COMMAND:
2072 case LyXRC::RC_TEMPDIRPATH:
2073 case LyXRC::RC_TEMPLATEPATH:
2074 case LyXRC::RC_TEX_ALLOWS_SPACES:
2075 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2076 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2077 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2079 case LyXRC::RC_UIFILE:
2080 case LyXRC::RC_USER_EMAIL:
2081 case LyXRC::RC_USER_NAME:
2082 case LyXRC::RC_USETEMPDIR:
2083 case LyXRC::RC_USE_ALT_LANG:
2084 case LyXRC::RC_USE_ESC_CHARS:
2085 case LyXRC::RC_USE_INP_ENC:
2086 case LyXRC::RC_USE_PERS_DICT:
2087 case LyXRC::RC_USE_SPELL_LIB:
2088 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2089 case LyXRC::RC_VIEWER:
2090 case LyXRC::RC_LAST: