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:
811 case LFUN_BUFFER_WRITE:
812 if (!lyx_view_->buffer()->isUnnamed()) {
813 docstring const str = bformat(_("Saving document %1$s..."),
814 makeDisplayPath(lyx_view_->buffer()->fileName()));
815 lyx_view_->message(str);
816 menuWrite(lyx_view_->buffer());
817 lyx_view_->message(str + _(" done."));
819 writeAs(lyx_view_->buffer());
820 updateFlags = Update::None;
823 case LFUN_BUFFER_WRITE_AS:
824 writeAs(lyx_view_->buffer(), argument);
825 updateFlags = Update::None;
828 case LFUN_BUFFER_RELOAD: {
829 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
830 docstring text = bformat(_("Any changes will be lost. Are you sure "
831 "you want to revert to the saved version of the document %1$s?"), file);
832 int const ret = Alert::prompt(_("Revert to saved document?"),
833 text, 0, 1, _("&Revert"), _("&Cancel"));
840 case LFUN_BUFFER_UPDATE:
841 Exporter::Export(lyx_view_->buffer(), argument, true);
844 case LFUN_BUFFER_VIEW:
845 Exporter::preview(lyx_view_->buffer(), argument);
848 case LFUN_BUILD_PROGRAM:
849 Exporter::Export(lyx_view_->buffer(), "program", true);
852 case LFUN_BUFFER_CHKTEX:
853 lyx_view_->buffer()->runChktex();
856 case LFUN_BUFFER_EXPORT:
857 if (argument == "custom")
858 lyx_view_->getDialogs().show("sendto");
860 Exporter::Export(lyx_view_->buffer(), argument, false);
864 case LFUN_BUFFER_EXPORT_CUSTOM: {
866 string command = split(argument, format_name, ' ');
867 Format const * format = formats.getFormat(format_name);
869 lyxerr << "Format \"" << format_name
870 << "\" not recognized!"
875 Buffer * buffer = lyx_view_->buffer();
877 // The name of the file created by the conversion process
880 // Output to filename
881 if (format->name() == "lyx") {
882 string const latexname =
883 buffer->getLatexName(false);
884 filename = changeExtension(latexname,
885 format->extension());
886 filename = addName(buffer->temppath(), filename);
888 if (!buffer->writeFile(filename))
892 Exporter::Export(buffer, format_name, true, filename);
895 // Substitute $$FName for filename
896 if (!contains(command, "$$FName"))
897 command = "( " + command + " ) < $$FName";
898 command = subst(command, "$$FName", filename);
900 // Execute the command in the background
902 call.startscript(Systemcall::DontWait, command);
906 case LFUN_BUFFER_PRINT: {
909 string command = split(split(argument, target, ' '),
913 || target_name.empty()
914 || command.empty()) {
915 lyxerr << "Unable to parse \""
916 << argument << '"' << std::endl;
919 if (target != "printer" && target != "file") {
920 lyxerr << "Unrecognized target \""
921 << target << '"' << std::endl;
925 Buffer * buffer = lyx_view_->buffer();
927 if (!Exporter::Export(buffer, "dvi", true)) {
928 showPrintError(buffer->fileName());
932 // Push directory path.
933 string const path = buffer->temppath();
934 support::Path p(path);
936 // there are three cases here:
937 // 1. we print to a file
938 // 2. we print directly to a printer
939 // 3. we print using a spool command (print to file first)
942 string const dviname =
943 changeExtension(buffer->getLatexName(true),
946 if (target == "printer") {
947 if (!lyxrc.print_spool_command.empty()) {
948 // case 3: print using a spool
949 string const psname =
950 changeExtension(dviname,".ps");
951 command += lyxrc.print_to_file
954 + quoteName(dviname);
957 lyxrc.print_spool_command +' ';
958 if (target_name != "default") {
959 command2 += lyxrc.print_spool_printerprefix
963 command2 += quoteName(psname);
965 // If successful, then spool command
966 res = one.startscript(
971 res = one.startscript(
972 Systemcall::DontWait,
975 // case 2: print directly to a printer
976 res = one.startscript(
977 Systemcall::DontWait,
978 command + quoteName(dviname));
982 // case 1: print to a file
983 command += lyxrc.print_to_file
984 + quoteName(makeAbsPath(target_name,
987 + quoteName(dviname);
988 res = one.startscript(Systemcall::DontWait,
993 showPrintError(buffer->fileName());
997 case LFUN_BUFFER_IMPORT:
1002 if (view()->buffer()) {
1003 // save cursor Position for opened files to .lyx/session
1004 LyX::ref().session().saveFilePosition(lyx_view_->buffer()->fileName(),
1005 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1006 // save bookmarks to .lyx/session
1007 view()->saveSavedPositions();
1009 LyX::ref().quit(argument == "force");
1012 case LFUN_TOC_VIEW: {
1013 InsetCommandParams p("tableofcontents");
1014 string const data = InsetCommandMailer::params2string("toc", p);
1015 lyx_view_->getDialogs().show("toc", data, 0);
1019 case LFUN_BUFFER_AUTO_SAVE:
1023 case LFUN_RECONFIGURE:
1024 reconfigure(view());
1027 case LFUN_HELP_OPEN: {
1028 string const arg = argument;
1030 setErrorMessage(_("Missing argument"));
1033 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1034 if (fname.empty()) {
1035 lyxerr << "LyX: unable to find documentation file `"
1036 << arg << "'. Bad installation?" << endl;
1039 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1040 makeDisplayPath(fname)));
1041 lyx_view_->loadLyXFile(fname, false);
1045 // --- version control -------------------------------
1046 case LFUN_VC_REGISTER:
1047 if (!ensureBufferClean(view()))
1049 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1050 lyx_view_->buffer()->lyxvc().registrer();
1055 case LFUN_VC_CHECK_IN:
1056 if (!ensureBufferClean(view()))
1058 if (lyx_view_->buffer()->lyxvc().inUse()
1059 && !lyx_view_->buffer()->isReadonly()) {
1060 lyx_view_->buffer()->lyxvc().checkIn();
1065 case LFUN_VC_CHECK_OUT:
1066 if (!ensureBufferClean(view()))
1068 if (lyx_view_->buffer()->lyxvc().inUse()
1069 && lyx_view_->buffer()->isReadonly()) {
1070 lyx_view_->buffer()->lyxvc().checkOut();
1075 case LFUN_VC_REVERT:
1076 lyx_view_->buffer()->lyxvc().revert();
1080 case LFUN_VC_UNDO_LAST:
1081 lyx_view_->buffer()->lyxvc().undoLast();
1085 // --- buffers ----------------------------------------
1086 case LFUN_BUFFER_SWITCH:
1087 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1090 case LFUN_BUFFER_NEXT:
1091 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1094 case LFUN_BUFFER_PREVIOUS:
1095 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1099 newFile(view(), argument);
1102 case LFUN_FILE_OPEN:
1106 case LFUN_DROP_LAYOUTS_CHOICE:
1107 lyx_view_->getToolbars().openLayoutList();
1110 case LFUN_MENU_OPEN:
1111 lyx_view_->getMenubar().openByName(from_utf8(argument));
1114 // --- lyxserver commands ----------------------------
1115 case LFUN_SERVER_GET_NAME:
1116 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1117 lyxerr[Debug::INFO] << "FNAME["
1118 << lyx_view_->buffer()->fileName()
1122 case LFUN_SERVER_NOTIFY:
1123 dispatch_buffer = from_utf8(keyseq->print());
1124 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1127 case LFUN_SERVER_GOTO_FILE_ROW: {
1130 istringstream is(argument);
1131 is >> file_name >> row;
1132 if (prefixIs(file_name, package().temp_dir())) {
1133 // Needed by inverse dvi search. If it is a file
1134 // in tmpdir, call the apropriated function
1135 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1137 // Must replace extension of the file to be .lyx
1138 // and get full path
1139 string const s = changeExtension(file_name, ".lyx");
1140 // Either change buffer or load the file
1141 if (theBufferList().exists(s)) {
1142 lyx_view_->setBuffer(theBufferList().getBuffer(s));
1144 lyx_view_->loadLyXFile(s);
1148 view()->setCursorFromRow(row);
1151 // see BufferView::center()
1155 case LFUN_DIALOG_SHOW: {
1156 string const name = cmd.getArg(0);
1157 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1159 if (name == "character") {
1160 data = freefont2string();
1162 lyx_view_->getDialogs().show("character", data);
1163 } else if (name == "latexlog") {
1164 pair<Buffer::LogType, string> const logfile =
1165 lyx_view_->buffer()->getLogName();
1166 switch (logfile.first) {
1167 case Buffer::latexlog:
1170 case Buffer::buildlog:
1174 data += LyXLex::quoteString(logfile.second);
1175 lyx_view_->getDialogs().show("log", data);
1176 } else if (name == "vclog") {
1177 string const data = "vc " +
1178 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1179 lyx_view_->getDialogs().show("log", data);
1181 lyx_view_->getDialogs().show(name, data);
1185 case LFUN_DIALOG_SHOW_NEW_INSET: {
1186 string const name = cmd.getArg(0);
1187 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1188 if (name == "bibitem" ||
1195 InsetCommandParams p(name);
1196 data = InsetCommandMailer::params2string(name, p);
1197 } else if (name == "include") {
1198 InsetCommandParams p(data);
1199 data = InsetIncludeMailer::params2string(p);
1200 } else if (name == "box") {
1201 // \c data == "Boxed" || "Frameless" etc
1202 InsetBoxParams p(data);
1203 data = InsetBoxMailer::params2string(p);
1204 } else if (name == "branch") {
1205 InsetBranchParams p;
1206 data = InsetBranchMailer::params2string(p);
1207 } else if (name == "citation") {
1208 InsetCommandParams p("cite");
1209 data = InsetCommandMailer::params2string(name, p);
1210 } else if (name == "ert") {
1211 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1212 } else if (name == "external") {
1213 InsetExternalParams p;
1214 Buffer const & buffer = *lyx_view_->buffer();
1215 data = InsetExternalMailer::params2string(p, buffer);
1216 } else if (name == "float") {
1218 data = InsetFloatMailer::params2string(p);
1219 } else if (name == "graphics") {
1220 InsetGraphicsParams p;
1221 Buffer const & buffer = *lyx_view_->buffer();
1222 data = InsetGraphicsMailer::params2string(p, buffer);
1223 } else if (name == "note") {
1225 data = InsetNoteMailer::params2string(p);
1226 } else if (name == "vspace") {
1228 data = InsetVSpaceMailer::params2string(space);
1229 } else if (name == "wrap") {
1231 data = InsetWrapMailer::params2string(p);
1233 lyx_view_->getDialogs().show(name, data, 0);
1237 case LFUN_DIALOG_UPDATE: {
1238 string const & name = argument;
1239 // Can only update a dialog connected to an existing inset
1240 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1242 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1243 inset->dispatch(view()->cursor(), fr);
1244 } else if (name == "paragraph") {
1245 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1246 } else if (name == "prefs") {
1247 lyx_view_->getDialogs().update(name, string());
1252 case LFUN_DIALOG_HIDE:
1253 Dialogs::hide(argument, 0);
1256 case LFUN_DIALOG_DISCONNECT_INSET:
1257 lyx_view_->getDialogs().disconnect(argument);
1261 case LFUN_CITATION_INSERT: {
1262 if (!argument.empty()) {
1263 // we can have one optional argument, delimited by '|'
1264 // citation-insert <key>|<text_before>
1265 // this should be enhanced to also support text_after
1266 // and citation style
1267 string arg = argument;
1269 if (contains(argument, "|")) {
1270 arg = token(argument, '|', 0);
1271 opt1 = '[' + token(argument, '|', 1) + ']';
1273 std::ostringstream os;
1274 os << "citation LatexCommand\n"
1275 << "\\cite" << opt1 << "{" << arg << "}\n"
1277 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1280 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1284 case LFUN_BUFFER_CHILD_OPEN: {
1285 string const filename =
1286 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1287 // FIXME Should use bformat
1288 setMessage(_("Opening child document ") +
1289 makeDisplayPath(filename) + "...");
1290 view()->savePosition(0);
1291 string const parentfilename = lyx_view_->buffer()->fileName();
1292 if (theBufferList().exists(filename))
1293 lyx_view_->setBuffer(theBufferList().getBuffer(filename));
1295 lyx_view_->loadLyXFile(filename);
1296 // Set the parent name of the child document.
1297 // This makes insertion of citations and references in the child work,
1298 // when the target is in the parent or another child document.
1299 lyx_view_->buffer()->setParentName(parentfilename);
1303 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1304 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1307 case LFUN_KEYMAP_OFF:
1308 lyx_view_->view()->getIntl().keyMapOn(false);
1311 case LFUN_KEYMAP_PRIMARY:
1312 lyx_view_->view()->getIntl().keyMapPrim();
1315 case LFUN_KEYMAP_SECONDARY:
1316 lyx_view_->view()->getIntl().keyMapSec();
1319 case LFUN_KEYMAP_TOGGLE:
1320 lyx_view_->view()->getIntl().toggleKeyMap();
1326 string rest = split(argument, countstr, ' ');
1327 istringstream is(countstr);
1330 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1331 for (int i = 0; i < count; ++i)
1332 dispatch(lyxaction.lookupFunc(rest));
1336 case LFUN_COMMAND_SEQUENCE: {
1337 // argument contains ';'-terminated commands
1338 string arg = argument;
1339 while (!arg.empty()) {
1341 arg = split(arg, first, ';');
1342 FuncRequest func(lyxaction.lookupFunc(first));
1343 func.origin = cmd.origin;
1349 case LFUN_PREFERENCES_SAVE: {
1350 support::Path p(package().user_support());
1351 lyxrc.write("preferences", false);
1355 case LFUN_SCREEN_FONT_UPDATE:
1356 // handle the screen font changes.
1357 lyxrc.set_font_norm_type();
1358 theFontLoader().update();
1359 // All visible buffers will need resize
1363 case LFUN_SET_COLOR: {
1365 string const x11_name = split(argument, lyx_name, ' ');
1366 if (lyx_name.empty() || x11_name.empty()) {
1367 setErrorMessage(_("Syntax: set-color <lyx_name>"
1372 bool const graphicsbg_changed =
1373 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1374 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1376 if (!lcolor.setColor(lyx_name, x11_name)) {
1378 bformat(_("Set-color \"%1$s\" failed "
1379 "- color is undefined or "
1380 "may not be redefined"),
1381 from_utf8(lyx_name)));
1385 theApp->updateColor(lcolor.getFromLyXName(lyx_name));
1387 if (graphicsbg_changed) {
1388 #ifdef WITH_WARNINGS
1389 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1392 graphics::GCache::get().changeDisplay(true);
1399 lyx_view_->message(from_utf8(argument));
1402 case LFUN_EXTERNAL_EDIT: {
1403 FuncRequest fr(action, argument);
1404 InsetExternal().dispatch(view()->cursor(), fr);
1408 case LFUN_GRAPHICS_EDIT: {
1409 FuncRequest fr(action, argument);
1410 InsetGraphics().dispatch(view()->cursor(), fr);
1414 case LFUN_INSET_APPLY: {
1415 string const name = cmd.getArg(0);
1416 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1418 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1419 inset->dispatch(view()->cursor(), fr);
1421 FuncRequest fr(LFUN_INSET_INSERT, argument);
1424 // ideally, the update flag should be set by the insets,
1425 // but this is not possible currently
1426 updateFlags = Update::Force | Update::FitCursor;
1430 case LFUN_ALL_INSETS_TOGGLE: {
1432 string const name = split(argument, action, ' ');
1433 InsetBase::Code const inset_code =
1434 InsetBase::translate(name);
1436 LCursor & cur = view()->cursor();
1437 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1439 InsetBase & inset = lyx_view_->buffer()->inset();
1440 InsetIterator it = inset_iterator_begin(inset);
1441 InsetIterator const end = inset_iterator_end(inset);
1442 for (; it != end; ++it) {
1443 if (inset_code == InsetBase::NO_CODE
1444 || inset_code == it->lyxCode()) {
1445 LCursor tmpcur = cur;
1446 tmpcur.pushLeft(*it);
1447 it->dispatch(tmpcur, fr);
1450 updateFlags = Update::Force | Update::FitCursor;
1454 case LFUN_BUFFER_LANGUAGE: {
1455 Buffer & buffer = *lyx_view_->buffer();
1456 Language const * oldL = buffer.params().language;
1457 Language const * newL = languages.getLanguage(argument);
1458 if (!newL || oldL == newL)
1461 if (oldL->rightToLeft() == newL->rightToLeft()
1462 && !buffer.isMultiLingual())
1463 buffer.changeLanguage(oldL, newL);
1465 buffer.updateDocLang(newL);
1469 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1470 string const fname =
1471 addName(addPath(package().user_support(), "templates/"),
1473 Buffer defaults(fname);
1475 istringstream ss(argument);
1478 int const unknown_tokens = defaults.readHeader(lex);
1480 if (unknown_tokens != 0) {
1481 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1482 << unknown_tokens << " unknown token"
1483 << (unknown_tokens == 1 ? "" : "s")
1487 if (defaults.writeFile(defaults.fileName()))
1488 // FIXME Should use bformat
1489 setMessage(_("Document defaults saved in ")
1490 + makeDisplayPath(fname));
1492 setErrorMessage(_("Unable to save document defaults"));
1496 case LFUN_BUFFER_PARAMS_APPLY: {
1497 biblio::CiteEngine const engine =
1498 lyx_view_->buffer()->params().cite_engine;
1500 istringstream ss(argument);
1503 int const unknown_tokens =
1504 lyx_view_->buffer()->readHeader(lex);
1506 if (unknown_tokens != 0) {
1507 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1508 << unknown_tokens << " unknown token"
1509 << (unknown_tokens == 1 ? "" : "s")
1512 if (engine == lyx_view_->buffer()->params().cite_engine)
1515 LCursor & cur = view()->cursor();
1516 FuncRequest fr(LFUN_INSET_REFRESH);
1518 InsetBase & inset = lyx_view_->buffer()->inset();
1519 InsetIterator it = inset_iterator_begin(inset);
1520 InsetIterator const end = inset_iterator_end(inset);
1521 for (; it != end; ++it)
1522 if (it->lyxCode() == InsetBase::CITE_CODE)
1523 it->dispatch(cur, fr);
1527 case LFUN_TEXTCLASS_APPLY: {
1528 Buffer * buffer = lyx_view_->buffer();
1530 textclass_type const old_class =
1531 buffer->params().textclass;
1533 loadTextclass(argument);
1535 std::pair<bool, textclass_type> const tc_pair =
1536 textclasslist.numberOfClass(argument);
1541 textclass_type const new_class = tc_pair.second;
1542 if (old_class == new_class)
1546 lyx_view_->message(_("Converting document to new document class..."));
1547 recordUndoFullDocument(view());
1548 buffer->params().textclass = new_class;
1549 StableDocIterator backcur(view()->cursor());
1550 ErrorList & el = buffer->errorList("Class Switch");
1551 cap::switchBetweenClasses(
1552 old_class, new_class,
1553 static_cast<InsetText &>(buffer->inset()), el);
1555 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1557 buffer->errors("Class Switch");
1558 updateLabels(*buffer);
1559 updateFlags = Update::Force | Update::FitCursor;
1563 case LFUN_TEXTCLASS_LOAD:
1564 loadTextclass(argument);
1567 case LFUN_LYXRC_APPLY: {
1568 LyXRC const lyxrc_orig = lyxrc;
1570 istringstream ss(argument);
1571 bool const success = lyxrc.read(ss) == 0;
1574 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1575 << "Unable to read lyxrc data"
1580 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1584 case LFUN_WINDOW_NEW:
1585 BOOST_ASSERT(theApp);
1586 LyX::ref().newLyXView();
1589 view()->cursor().dispatch(cmd);
1590 updateFlags = view()->cursor().result().update();
1591 if (!view()->cursor().result().dispatched())
1592 if (view()->dispatch(cmd))
1593 updateFlags = Update::Force | Update::FitCursor;
1598 if (view()->buffer()) {
1599 // Redraw screen unless explicitly told otherwise.
1600 // This also initializes the position cache for all insets
1601 // in (at least partially) visible top-level paragraphs.
1602 bool needSecondUpdate = false;
1603 if (updateFlags != Update::None)
1604 view()->update(updateFlags);
1606 needSecondUpdate = view()->fitCursor();
1608 if (needSecondUpdate || updateFlags != Update::None) {
1609 view()->buffer()->changed();
1611 lyx_view_->updateStatusBar();
1613 // if we executed a mutating lfun, mark the buffer as dirty
1615 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1616 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1617 view()->buffer()->markDirty();
1619 if (view()->cursor().inTexted()) {
1620 lyx_view_->updateLayoutChoice();
1625 // FIXME UNICODE: _() does not support anything but ascii.
1626 // Do we need a to_ascii() method?
1627 sendDispatchMessage(getMessage(), cmd);
1631 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1633 /* When an action did not originate from the UI/kbd, it makes
1634 * sense to avoid updating the GUI. It turns out that this
1635 * fixes bug 1941, for reasons that are described here:
1636 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1638 if (cmd.origin != FuncRequest::INTERNAL) {
1639 lyx_view_->updateMenubar();
1640 lyx_view_->updateToolbars();
1643 const bool verbose = (cmd.origin == FuncRequest::UI
1644 || cmd.origin == FuncRequest::COMMANDBUFFER);
1646 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1647 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1649 lyx_view_->message(msg);
1653 docstring dispatch_msg = msg;
1654 if (!dispatch_msg.empty())
1655 dispatch_msg += ' ';
1657 string comname = lyxaction.getActionName(cmd.action);
1659 bool argsadded = false;
1661 if (!cmd.argument().empty()) {
1662 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1663 comname += ' ' + to_utf8(cmd.argument());
1668 string const shortcuts = theTopLevelKeymap().printbindings(cmd);
1670 if (!shortcuts.empty())
1671 comname += ": " + shortcuts;
1672 else if (!argsadded && !cmd.argument().empty())
1673 comname += ' ' + to_utf8(cmd.argument());
1675 if (!comname.empty()) {
1676 comname = rtrim(comname);
1677 dispatch_msg += from_utf8('(' + rtrim(comname) + ')');
1680 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1681 << to_utf8(dispatch_msg) << endl;
1682 if (!dispatch_msg.empty())
1683 lyx_view_->message(dispatch_msg);
1687 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1689 string initpath = lyxrc.document_path;
1690 string filename(name);
1692 if (view()->buffer()) {
1693 string const trypath = lyx_view_->buffer()->filePath();
1694 // If directory is writeable, use this as default.
1695 if (isDirWriteable(trypath))
1699 static int newfile_number;
1701 if (filename.empty()) {
1702 filename = addName(lyxrc.document_path,
1703 "newfile" + convert<string>(++newfile_number) + ".lyx");
1704 while (theBufferList().exists(filename) || fs::is_readable(filename)) {
1706 filename = addName(lyxrc.document_path,
1707 "newfile" + convert<string>(newfile_number) +
1712 // The template stuff
1715 FileDialog fileDlg(_("Select template file"),
1716 LFUN_SELECT_FILE_SYNC,
1717 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1718 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1720 FileDialog::Result result =
1721 fileDlg.open(from_utf8(lyxrc.template_path),
1722 FileFilterList(_("LyX Documents (*.lyx)")),
1725 if (result.first == FileDialog::Later)
1727 if (result.second.empty())
1729 templname = to_utf8(result.second);
1732 Buffer * const b = newFile(filename, templname, !name.empty());
1734 lyx_view_->setBuffer(b);
1738 void LyXFunc::open(string const & fname)
1740 string initpath = lyxrc.document_path;
1742 if (view()->buffer()) {
1743 string const trypath = lyx_view_->buffer()->filePath();
1744 // If directory is writeable, use this as default.
1745 if (isDirWriteable(trypath))
1751 if (fname.empty()) {
1752 FileDialog fileDlg(_("Select document to open"),
1754 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1755 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1757 FileDialog::Result result =
1758 fileDlg.open(from_utf8(initpath),
1759 FileFilterList(_("LyX Documents (*.lyx)")),
1762 if (result.first == FileDialog::Later)
1765 filename = to_utf8(result.second);
1767 // check selected filename
1768 if (filename.empty()) {
1769 lyx_view_->message(_("Canceled."));
1775 // get absolute path of file and add ".lyx" to the filename if
1777 string const fullpath = fileSearch(string(), filename, "lyx");
1778 if (!fullpath.empty()) {
1779 filename = fullpath;
1782 docstring const disp_fn = makeDisplayPath(filename);
1784 // if the file doesn't exist, let the user create one
1785 if (!fs::exists(filename)) {
1786 // the user specifically chose this name. Believe him.
1787 Buffer * const b = newFile(filename, string(), true);
1789 lyx_view_->setBuffer(b);
1793 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1796 if (lyx_view_->loadLyXFile(filename)) {
1797 str2 = bformat(_("Document %1$s opened."), disp_fn);
1799 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1801 lyx_view_->message(str2);
1805 void LyXFunc::doImport(string const & argument)
1808 string filename = split(argument, format, ' ');
1810 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1811 << " file: " << filename << endl;
1813 // need user interaction
1814 if (filename.empty()) {
1815 string initpath = lyxrc.document_path;
1817 if (view()->buffer()) {
1818 string const trypath = lyx_view_->buffer()->filePath();
1819 // If directory is writeable, use this as default.
1820 if (isDirWriteable(trypath))
1824 docstring const text = bformat(_("Select %1$s file to import"),
1825 formats.prettyName(format));
1827 FileDialog fileDlg(text,
1829 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1830 make_pair(_("Examples|#E#e"),
1831 from_utf8(addPath(package().system_support(), "examples"))));
1833 docstring filter = formats.prettyName(format);
1836 filter += from_utf8(formats.extension(format));
1839 FileDialog::Result result =
1840 fileDlg.open(from_utf8(initpath),
1841 FileFilterList(filter),
1844 if (result.first == FileDialog::Later)
1847 filename = to_utf8(result.second);
1849 // check selected filename
1850 if (filename.empty())
1851 lyx_view_->message(_("Canceled."));
1854 if (filename.empty())
1857 // get absolute path of file
1858 filename = makeAbsPath(filename);
1860 string const lyxfile = changeExtension(filename, ".lyx");
1862 // Check if the document already is open
1863 if (use_gui && theBufferList().exists(lyxfile)) {
1864 if (!theBufferList().close(theBufferList().getBuffer(lyxfile), true)) {
1865 lyx_view_->message(_("Canceled."));
1870 // if the file exists already, and we didn't do
1871 // -i lyx thefile.lyx, warn
1872 if (fs::exists(lyxfile) && filename != lyxfile) {
1873 docstring const file = makeDisplayPath(lyxfile, 30);
1875 docstring text = bformat(_("The document %1$s already exists.\n\n"
1876 "Do you want to over-write that document?"), file);
1877 int const ret = Alert::prompt(_("Over-write document?"),
1878 text, 0, 1, _("&Over-write"), _("&Cancel"));
1881 lyx_view_->message(_("Canceled."));
1886 ErrorList errorList;
1887 Importer::Import(lyx_view_, filename, format, errorList);
1888 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1892 void LyXFunc::closeBuffer()
1894 // save current cursor position
1895 LyX::ref().session().saveFilePosition(lyx_view_->buffer()->fileName(),
1896 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1897 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
1898 if (theBufferList().empty()) {
1899 // need this otherwise SEGV may occur while
1900 // trying to set variables that don't exist
1901 // since there's no current buffer
1902 lyx_view_->getDialogs().hideBufferDependent();
1904 lyx_view_->setBuffer(theBufferList().first());
1910 // Each "lyx_view_" should have it's own message method. lyxview and
1911 // the minibuffer would use the minibuffer, but lyxserver would
1912 // send an ERROR signal to its client. Alejandro 970603
1913 // This function is bit problematic when it comes to NLS, to make the
1914 // lyx servers client be language indepenent we must not translate
1915 // strings sent to this func.
1916 void LyXFunc::setErrorMessage(docstring const & m) const
1918 dispatch_buffer = m;
1923 void LyXFunc::setMessage(docstring const & m) const
1925 dispatch_buffer = m;
1929 string const LyXFunc::viewStatusMessage()
1931 // When meta-fake key is pressed, show the key sequence so far + "M-".
1933 return keyseq->print() + "M-";
1935 // Else, when a non-complete key sequence is pressed,
1936 // show the available options.
1937 if (keyseq->length() > 0 && !keyseq->deleted())
1938 return keyseq->printOptions();
1940 if (!view()->buffer())
1941 return to_utf8(_("Welcome to LyX!"));
1943 return view()->cursor().currentState();
1947 BufferView * LyXFunc::view() const
1949 BOOST_ASSERT(lyx_view_);
1950 return lyx_view_->view();
1954 bool LyXFunc::wasMetaKey() const
1956 return (meta_fake_bit != key_modifier::none);
1962 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1964 // Why the switch you might ask. It is a trick to ensure that all
1965 // the elements in the LyXRCTags enum is handled. As you can see
1966 // there are no breaks at all. So it is just a huge fall-through.
1967 // The nice thing is that we will get a warning from the compiler
1968 // if we forget an element.
1969 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1971 case LyXRC::RC_ACCEPT_COMPOUND:
1972 case LyXRC::RC_ALT_LANG:
1973 case LyXRC::RC_ASCIIROFF_COMMAND:
1974 case LyXRC::RC_ASCII_LINELEN:
1975 case LyXRC::RC_AUTOREGIONDELETE:
1976 case LyXRC::RC_AUTORESET_OPTIONS:
1977 case LyXRC::RC_AUTOSAVE:
1978 case LyXRC::RC_AUTO_NUMBER:
1979 case LyXRC::RC_BACKUPDIR_PATH:
1980 case LyXRC::RC_BIBTEX_COMMAND:
1981 case LyXRC::RC_BINDFILE:
1982 case LyXRC::RC_CHECKLASTFILES:
1983 case LyXRC::RC_USELASTFILEPOS:
1984 case LyXRC::RC_LOADSESSION:
1985 case LyXRC::RC_CHKTEX_COMMAND:
1986 case LyXRC::RC_CONVERTER:
1987 case LyXRC::RC_COPIER:
1988 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1989 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1990 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1991 case LyXRC::RC_DATE_INSERT_FORMAT:
1992 case LyXRC::RC_DEFAULT_LANGUAGE:
1993 case LyXRC::RC_DEFAULT_PAPERSIZE:
1994 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1995 case LyXRC::RC_DISPLAY_GRAPHICS:
1996 case LyXRC::RC_DOCUMENTPATH:
1997 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1998 if (fs::exists(lyxrc_new.document_path) &&
1999 fs::is_directory(lyxrc_new.document_path)) {
2000 support::package().document_dir() = lyxrc.document_path;
2003 case LyXRC::RC_ESC_CHARS:
2004 case LyXRC::RC_FONT_ENCODING:
2005 case LyXRC::RC_FORMAT:
2006 case LyXRC::RC_INDEX_COMMAND:
2007 case LyXRC::RC_INPUT:
2008 case LyXRC::RC_KBMAP:
2009 case LyXRC::RC_KBMAP_PRIMARY:
2010 case LyXRC::RC_KBMAP_SECONDARY:
2011 case LyXRC::RC_LABEL_INIT_LENGTH:
2012 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2013 case LyXRC::RC_LANGUAGE_AUTO_END:
2014 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2015 case LyXRC::RC_LANGUAGE_COMMAND_END:
2016 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2017 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2018 case LyXRC::RC_LANGUAGE_PACKAGE:
2019 case LyXRC::RC_LANGUAGE_USE_BABEL:
2020 case LyXRC::RC_MAKE_BACKUP:
2021 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2022 case LyXRC::RC_NUMLASTFILES:
2023 case LyXRC::RC_PATH_PREFIX:
2024 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2025 support::prependEnvPath("PATH", lyxrc.path_prefix);
2027 case LyXRC::RC_PERS_DICT:
2028 case LyXRC::RC_POPUP_BOLD_FONT:
2029 case LyXRC::RC_POPUP_FONT_ENCODING:
2030 case LyXRC::RC_POPUP_NORMAL_FONT:
2031 case LyXRC::RC_PREVIEW:
2032 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2033 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2034 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2035 case LyXRC::RC_PRINTCOPIESFLAG:
2036 case LyXRC::RC_PRINTER:
2037 case LyXRC::RC_PRINTEVENPAGEFLAG:
2038 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2039 case LyXRC::RC_PRINTFILEEXTENSION:
2040 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2041 case LyXRC::RC_PRINTODDPAGEFLAG:
2042 case LyXRC::RC_PRINTPAGERANGEFLAG:
2043 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2044 case LyXRC::RC_PRINTPAPERFLAG:
2045 case LyXRC::RC_PRINTREVERSEFLAG:
2046 case LyXRC::RC_PRINTSPOOL_COMMAND:
2047 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2048 case LyXRC::RC_PRINTTOFILE:
2049 case LyXRC::RC_PRINTTOPRINTER:
2050 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2051 case LyXRC::RC_PRINT_COMMAND:
2052 case LyXRC::RC_RTL_SUPPORT:
2053 case LyXRC::RC_SCREEN_DPI:
2054 case LyXRC::RC_SCREEN_FONT_ENCODING:
2055 case LyXRC::RC_SCREEN_FONT_ROMAN:
2056 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2057 case LyXRC::RC_SCREEN_FONT_SANS:
2058 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2059 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2060 case LyXRC::RC_SCREEN_FONT_SIZES:
2061 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2062 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2063 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2064 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2065 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2066 case LyXRC::RC_SCREEN_ZOOM:
2067 case LyXRC::RC_SERVERPIPE:
2068 case LyXRC::RC_SET_COLOR:
2069 case LyXRC::RC_SHOW_BANNER:
2070 case LyXRC::RC_SPELL_COMMAND:
2071 case LyXRC::RC_TEMPDIRPATH:
2072 case LyXRC::RC_TEMPLATEPATH:
2073 case LyXRC::RC_TEX_ALLOWS_SPACES:
2074 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2075 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2076 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2078 case LyXRC::RC_UIFILE:
2079 case LyXRC::RC_USER_EMAIL:
2080 case LyXRC::RC_USER_NAME:
2081 case LyXRC::RC_USETEMPDIR:
2082 case LyXRC::RC_USE_ALT_LANG:
2083 case LyXRC::RC_USE_ESC_CHARS:
2084 case LyXRC::RC_USE_INP_ENC:
2085 case LyXRC::RC_USE_PERS_DICT:
2086 case LyXRC::RC_USE_SPELL_LIB:
2087 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2088 case LyXRC::RC_VIEWER:
2089 case LyXRC::RC_LAST: