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>
103 using bv_funcs::freefont2string;
105 using lyx::docstring;
107 using lyx::support::absolutePath;
108 using lyx::support::addName;
109 using lyx::support::addPath;
110 using lyx::support::bformat;
111 using lyx::support::changeExtension;
112 using lyx::support::contains;
113 using lyx::support::FileFilterList;
114 using lyx::support::fileSearch;
115 using lyx::support::ForkedcallsController;
116 using lyx::support::i18nLibFileSearch;
117 using lyx::support::isDirWriteable;
118 using lyx::support::isFileReadable;
119 using lyx::support::isStrInt;
120 using lyx::support::makeAbsPath;
121 using lyx::support::makeDisplayPath;
122 using lyx::support::package;
123 using lyx::support::quoteName;
124 using lyx::support::rtrim;
125 using lyx::support::split;
126 using lyx::support::subst;
127 using lyx::support::Systemcall;
128 using lyx::support::token;
129 using lyx::support::trim;
130 using lyx::support::prefixIs;
133 using std::make_pair;
136 using std::istringstream;
137 using std::ostringstream;
139 namespace Alert = lyx::frontend::Alert;
140 namespace biblio = lyx::biblio;
141 namespace fs = boost::filesystem;
145 extern tex_accent_struct get_accent(kb_action action);
150 bool getStatus(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 c = encoded_last_key;
228 if (keyseq->length()) {
232 lyx_view_->view()->getIntl().getTransManager()
233 .deadkey(c, get_accent(action).accent, view()->getLyXText());
234 // Need to clear, in case the minibuffer calls these
237 // copied verbatim from do_accent_char
238 view()->cursor().resetAnchor();
243 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
245 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
247 // Do nothing if we have nothing (JMarc)
248 if (!keysym->isOK()) {
249 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
254 if (keysym->isModifier()) {
255 lyxerr[Debug::KEY] << "isModifier true" << endl;
259 Encoding const * encoding = view()->cursor().getEncoding();
261 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
262 size_t encoded_last_key = keysym->getUCSEncoded();
264 // Do a one-deep top-level lookup for
265 // cancel and meta-fake keys. RVDK_PATCH_5
266 cancel_meta_seq->reset();
268 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
269 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
270 << " action first set to [" << func.action << ']'
273 // When not cancel or meta-fake, do the normal lookup.
274 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
275 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
276 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
277 // remove Caps Lock and Mod2 as a modifiers
278 func = keyseq->addkey(keysym, (state | meta_fake_bit));
279 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
280 << "action now set to ["
281 << func.action << ']' << endl;
284 // Dont remove this unless you know what you are doing.
285 meta_fake_bit = key_modifier::none;
287 // Can this happen now ?
288 if (func.action == LFUN_NOACTION) {
289 func = FuncRequest(LFUN_COMMAND_PREFIX);
292 if (lyxerr.debugging(Debug::KEY)) {
293 lyxerr << BOOST_CURRENT_FUNCTION
295 << func.action << "]["
296 << keyseq->print() << ']'
300 // already here we know if it any point in going further
301 // why not return already here if action == -1 and
302 // num_bytes == 0? (Lgb)
304 if (keyseq->length() > 1) {
305 lyx_view_->message(lyx::from_utf8(keyseq->print()));
309 // Maybe user can only reach the key via holding down shift.
310 // Let's see. But only if shift is the only modifier
311 if (func.action == LFUN_UNKNOWN_ACTION &&
312 state == key_modifier::shift) {
313 lyxerr[Debug::KEY] << "Trying without shift" << endl;
314 func = keyseq->addkey(keysym, key_modifier::none);
315 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
318 if (func.action == LFUN_UNKNOWN_ACTION) {
319 // Hmm, we didn't match any of the keysequences. See
320 // if it's normal insertable text not already covered
322 if (keysym->isText() && keyseq->length() == 1) {
323 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
324 func = FuncRequest(LFUN_SELF_INSERT,
325 FuncRequest::KEYBOARD);
327 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
328 lyx_view_->message(_("Unknown function."));
333 if (func.action == LFUN_SELF_INSERT) {
334 if (encoded_last_key != 0) {
335 docstring const arg(1, encoded_last_key);
336 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
337 FuncRequest::KEYBOARD));
339 << "SelfInsert arg[`" << lyx::to_utf8(arg) << "']" << endl;
347 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
349 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
351 LCursor & cur = view()->cursor();
353 /* In LyX/Mac, when a dialog is open, the menus of the
354 application can still be accessed without giving focus to
355 the main window. In this case, we want to disable the menu
356 entries that are buffer-related.
358 Note that this code is not perfect, as bug 1941 attests:
359 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
362 if (cmd.origin == FuncRequest::UI && !lyx_view_->hasFocus())
365 buf = lyx_view_->buffer();
367 if (cmd.action == LFUN_NOACTION) {
368 flag.message(lyx::from_utf8(N_("Nothing to do")));
373 switch (cmd.action) {
374 case LFUN_UNKNOWN_ACTION:
375 #ifndef HAVE_LIBAIKSAURUS
376 case LFUN_THESAURUS_ENTRY:
386 if (flag.unknown()) {
387 flag.message(lyx::from_utf8(N_("Unknown action")));
391 if (!flag.enabled()) {
392 if (flag.message().empty())
393 flag.message(lyx::from_utf8(N_("Command disabled")));
397 // Check whether we need a buffer
398 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
400 flag.message(lyx::from_utf8(N_("Command not allowed with"
401 "out any document open")));
406 // I would really like to avoid having this switch and rather try to
407 // encode this in the function itself.
408 // -- And I'd rather let an inset decide which LFUNs it is willing
409 // to handle (Andre')
411 switch (cmd.action) {
412 case LFUN_BUFFER_TOGGLE_READ_ONLY:
413 flag.setOnOff(buf->isReadonly());
416 case LFUN_BUFFER_SWITCH:
417 // toggle on the current buffer, but do not toggle off
418 // the other ones (is that a good idea?)
419 if (lyx::to_utf8(cmd.argument()) == buf->fileName())
423 case LFUN_BUFFER_EXPORT:
424 enable = cmd.argument() == "custom"
425 || Exporter::isExportable(*buf, lyx::to_utf8(cmd.argument()));
428 case LFUN_BUFFER_CHKTEX:
429 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
432 case LFUN_BUILD_PROGRAM:
433 enable = Exporter::isExportable(*buf, "program");
436 case LFUN_LAYOUT_TABULAR:
437 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
441 case LFUN_LAYOUT_PARAGRAPH:
442 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
445 case LFUN_VC_REGISTER:
446 enable = !buf->lyxvc().inUse();
448 case LFUN_VC_CHECK_IN:
449 enable = buf->lyxvc().inUse() && !buf->isReadonly();
451 case LFUN_VC_CHECK_OUT:
452 enable = buf->lyxvc().inUse() && buf->isReadonly();
455 case LFUN_VC_UNDO_LAST:
456 enable = buf->lyxvc().inUse();
458 case LFUN_BUFFER_RELOAD:
459 enable = !buf->isUnnamed() && !buf->isClean();
462 case LFUN_INSET_SETTINGS: {
466 InsetBase::Code code = cur.inset().lyxCode();
468 case InsetBase::TABULAR_CODE:
469 enable = cmd.argument() == "tabular";
471 case InsetBase::ERT_CODE:
472 enable = cmd.argument() == "ert";
474 case InsetBase::FLOAT_CODE:
475 enable = cmd.argument() == "float";
477 case InsetBase::WRAP_CODE:
478 enable = cmd.argument() == "wrap";
480 case InsetBase::NOTE_CODE:
481 enable = cmd.argument() == "note";
483 case InsetBase::BRANCH_CODE:
484 enable = cmd.argument() == "branch";
486 case InsetBase::BOX_CODE:
487 enable = cmd.argument() == "box";
495 case LFUN_INSET_APPLY: {
496 string const name = cmd.getArg(0);
497 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
499 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
501 bool const success = inset->getStatus(cur, fr, fs);
502 // Every inset is supposed to handle this
503 BOOST_ASSERT(success);
506 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
507 flag |= getStatus(fr);
509 enable = flag.enabled();
513 case LFUN_DIALOG_SHOW: {
514 string const name = cmd.getArg(0);
516 enable = name == "aboutlyx"
520 || name == "texinfo";
521 else if (name == "print")
522 enable = Exporter::isExportable(*buf, "dvi")
523 && lyxrc.print_command != "none";
524 else if (name == "character" || name == "mathpanel")
525 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
526 else if (name == "latexlog")
527 enable = isFileReadable(buf->getLogName().second);
528 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
529 else if (name == "spellchecker")
532 else if (name == "vclog")
533 enable = buf->lyxvc().inUse();
534 else if (name == "view-source")
539 case LFUN_DIALOG_SHOW_NEW_INSET:
540 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
543 case LFUN_DIALOG_UPDATE: {
544 string const name = cmd.getArg(0);
546 enable = name == "prefs";
550 case LFUN_CITATION_INSERT: {
551 FuncRequest fr(LFUN_INSET_INSERT, "citation");
552 enable = getStatus(fr).enabled();
556 case LFUN_BUFFER_WRITE: {
557 enable = view()->buffer()->isUnnamed()
558 || !view()->buffer()->isClean();
562 // this one is difficult to get right. As a half-baked
563 // solution, we consider only the first action of the sequence
564 case LFUN_COMMAND_SEQUENCE: {
565 // argument contains ';'-terminated commands
566 string const firstcmd = token(lyx::to_utf8(cmd.argument()), ';', 0);
567 FuncRequest func(lyxaction.lookupFunc(firstcmd));
568 func.origin = cmd.origin;
569 flag = getStatus(func);
572 case LFUN_BUFFER_NEW:
573 case LFUN_BUFFER_NEW_TEMPLATE:
574 case LFUN_WORD_FIND_FORWARD:
575 case LFUN_WORD_FIND_BACKWARD:
576 case LFUN_COMMAND_PREFIX:
577 case LFUN_COMMAND_EXECUTE:
579 case LFUN_META_PREFIX:
580 case LFUN_BUFFER_CLOSE:
581 case LFUN_BUFFER_WRITE_AS:
582 case LFUN_BUFFER_UPDATE:
583 case LFUN_BUFFER_VIEW:
584 case LFUN_BUFFER_IMPORT:
587 case LFUN_BUFFER_AUTO_SAVE:
588 case LFUN_RECONFIGURE:
592 case LFUN_DROP_LAYOUTS_CHOICE:
594 case LFUN_SERVER_GET_NAME:
595 case LFUN_SERVER_NOTIFY:
596 case LFUN_SERVER_GOTO_FILE_ROW:
597 case LFUN_DIALOG_HIDE:
598 case LFUN_DIALOG_DISCONNECT_INSET:
599 case LFUN_BUFFER_CHILD_OPEN:
600 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
601 case LFUN_KEYMAP_OFF:
602 case LFUN_KEYMAP_PRIMARY:
603 case LFUN_KEYMAP_SECONDARY:
604 case LFUN_KEYMAP_TOGGLE:
606 case LFUN_BUFFER_EXPORT_CUSTOM:
607 case LFUN_BUFFER_PRINT:
608 case LFUN_PREFERENCES_SAVE:
609 case LFUN_SCREEN_FONT_UPDATE:
612 case LFUN_EXTERNAL_EDIT:
613 case LFUN_GRAPHICS_EDIT:
614 case LFUN_ALL_INSETS_TOGGLE:
615 case LFUN_BUFFER_LANGUAGE:
616 case LFUN_TEXTCLASS_APPLY:
617 case LFUN_TEXTCLASS_LOAD:
618 case LFUN_BUFFER_SAVE_AS_DEFAULT:
619 case LFUN_BUFFER_PARAMS_APPLY:
620 case LFUN_LYXRC_APPLY:
621 case LFUN_BUFFER_NEXT:
622 case LFUN_BUFFER_PREVIOUS:
623 // these are handled in our dispatch()
628 if (!::getStatus(cur, cmd, flag))
629 flag = view()->getStatus(cmd);
635 // Can we use a readonly buffer?
636 if (buf && buf->isReadonly()
637 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
638 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
639 flag.message(lyx::from_utf8(N_("Document is read-only")));
643 // Are we in a DELETED change-tracking region?
644 if (buf && lookupChangeType(cur, true) == Change::DELETED
645 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
646 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
647 flag.message(lyx::from_utf8(N_("This portion of the document is deleted.")));
651 // the default error message if we disable the command
652 if (!flag.enabled() && flag.message().empty())
653 flag.message(lyx::from_utf8(N_("Command disabled")));
659 bool LyXFunc::ensureBufferClean(BufferView * bv)
661 Buffer & buf = *bv->buffer();
665 docstring const file = makeDisplayPath(buf.fileName(), 30);
666 docstring text = bformat(_("The document %1$s has unsaved "
667 "changes.\n\nDo you want to save "
668 "the document?"), file);
669 int const ret = Alert::prompt(_("Save changed document?"),
670 text, 0, 1, _("&Save"),
674 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
676 return buf.isClean();
682 void showPrintError(string const & name)
684 docstring str = bformat(_("Could not print the document %1$s.\n"
685 "Check that your printer is set up correctly."),
686 makeDisplayPath(name, 50));
687 Alert::error(_("Print document failed"), str);
691 void loadTextclass(string const & name)
693 std::pair<bool, lyx::textclass_type> const tc_pair =
694 textclasslist.numberOfClass(name);
696 if (!tc_pair.first) {
697 lyxerr << "Document class \"" << name
698 << "\" does not exist."
703 lyx::textclass_type const tc = tc_pair.second;
705 if (!textclasslist[tc].load()) {
706 docstring s = bformat(_("The document could not be converted\n"
707 "into the document class %1$s."),
708 lyx::from_utf8(textclasslist[tc].name()));
709 Alert::error(_("Could not change class"), s);
714 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
719 void LyXFunc::dispatch(FuncRequest const & cmd)
721 BOOST_ASSERT(view());
722 string const argument = lyx::to_utf8(cmd.argument());
723 kb_action const action = cmd.action;
725 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
726 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
728 // we have not done anything wrong yet.
730 dispatch_buffer.erase();
732 // redraw the screen at the end (first of the two drawing steps).
733 //This is done unless explicitely requested otherwise
735 // also do the second redrawing step. Only done if requested.
736 bool updateforce = false;
738 FuncStatus const flag = getStatus(cmd);
739 if (!flag.enabled()) {
740 // We cannot use this function here
741 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
742 << lyxaction.getActionName(action)
743 << " [" << action << "] is disabled at this location"
745 setErrorMessage(flag.message());
749 case LFUN_WORD_FIND_FORWARD:
750 case LFUN_WORD_FIND_BACKWARD: {
751 static string last_search;
752 string searched_string;
754 if (!argument.empty()) {
755 last_search = argument;
756 searched_string = argument;
758 searched_string = last_search;
761 if (searched_string.empty())
764 bool const fw = action == LFUN_WORD_FIND_FORWARD;
766 lyx::find::find2string(searched_string, true, false, fw);
767 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
771 case LFUN_COMMAND_PREFIX:
772 lyx_view_->message(lyx::from_utf8(keyseq->printOptions()));
775 case LFUN_COMMAND_EXECUTE:
776 lyx_view_->getToolbars().display("minibuffer", true);
777 lyx_view_->focus_command_buffer();
782 meta_fake_bit = key_modifier::none;
783 if (view()->buffer())
784 // cancel any selection
785 dispatch(FuncRequest(LFUN_MARK_OFF));
786 setMessage(_("Cancel"));
789 case LFUN_META_PREFIX:
790 meta_fake_bit = key_modifier::alt;
791 setMessage(lyx::from_utf8(keyseq->print()));
794 case LFUN_BUFFER_TOGGLE_READ_ONLY:
795 if (lyx_view_->buffer()->lyxvc().inUse())
796 lyx_view_->buffer()->lyxvc().toggleReadOnly();
798 lyx_view_->buffer()->setReadonly(
799 !lyx_view_->buffer()->isReadonly());
802 // --- Menus -----------------------------------------------
803 case LFUN_BUFFER_NEW:
804 menuNew(argument, false);
807 case LFUN_BUFFER_NEW_TEMPLATE:
808 menuNew(argument, true);
811 case LFUN_BUFFER_CLOSE:
815 case LFUN_BUFFER_WRITE:
816 if (!lyx_view_->buffer()->isUnnamed()) {
817 docstring const str = bformat(_("Saving document %1$s..."),
818 makeDisplayPath(lyx_view_->buffer()->fileName()));
819 lyx_view_->message(str);
820 menuWrite(lyx_view_->buffer());
821 lyx_view_->message(str + _(" done."));
823 writeAs(lyx_view_->buffer());
827 case LFUN_BUFFER_WRITE_AS:
828 writeAs(lyx_view_->buffer(), argument);
832 case LFUN_BUFFER_RELOAD: {
833 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
834 docstring text = bformat(_("Any changes will be lost. Are you sure "
835 "you want to revert to the saved version of the document %1$s?"), file);
836 int const ret = Alert::prompt(_("Revert to saved document?"),
837 text, 0, 1, _("&Revert"), _("&Cancel"));
844 case LFUN_BUFFER_UPDATE:
845 Exporter::Export(lyx_view_->buffer(), argument, true);
848 case LFUN_BUFFER_VIEW:
849 Exporter::preview(lyx_view_->buffer(), argument);
852 case LFUN_BUILD_PROGRAM:
853 Exporter::Export(lyx_view_->buffer(), "program", true);
856 case LFUN_BUFFER_CHKTEX:
857 lyx_view_->buffer()->runChktex();
860 case LFUN_BUFFER_EXPORT:
861 if (argument == "custom")
862 lyx_view_->getDialogs().show("sendto");
864 Exporter::Export(lyx_view_->buffer(), argument, false);
868 case LFUN_BUFFER_EXPORT_CUSTOM: {
870 string command = split(argument, format_name, ' ');
871 Format const * format = formats.getFormat(format_name);
873 lyxerr << "Format \"" << format_name
874 << "\" not recognized!"
879 Buffer * buffer = lyx_view_->buffer();
881 // The name of the file created by the conversion process
884 // Output to filename
885 if (format->name() == "lyx") {
886 string const latexname =
887 buffer->getLatexName(false);
888 filename = changeExtension(latexname,
889 format->extension());
890 filename = addName(buffer->temppath(), filename);
892 if (!buffer->writeFile(filename))
896 Exporter::Export(buffer, format_name, true, filename);
899 // Substitute $$FName for filename
900 if (!contains(command, "$$FName"))
901 command = "( " + command + " ) < $$FName";
902 command = subst(command, "$$FName", filename);
904 // Execute the command in the background
906 call.startscript(Systemcall::DontWait, command);
910 case LFUN_BUFFER_PRINT: {
913 string command = split(split(argument, target, ' '),
917 || target_name.empty()
918 || command.empty()) {
919 lyxerr << "Unable to parse \""
920 << argument << '"' << std::endl;
923 if (target != "printer" && target != "file") {
924 lyxerr << "Unrecognized target \""
925 << target << '"' << std::endl;
929 Buffer * buffer = lyx_view_->buffer();
931 if (!Exporter::Export(buffer, "dvi", true)) {
932 showPrintError(buffer->fileName());
936 // Push directory path.
937 string const path = buffer->temppath();
938 lyx::support::Path p(path);
940 // there are three cases here:
941 // 1. we print to a file
942 // 2. we print directly to a printer
943 // 3. we print using a spool command (print to file first)
946 string const dviname =
947 changeExtension(buffer->getLatexName(true),
950 if (target == "printer") {
951 if (!lyxrc.print_spool_command.empty()) {
952 // case 3: print using a spool
953 string const psname =
954 changeExtension(dviname,".ps");
955 command += lyxrc.print_to_file
958 + quoteName(dviname);
961 lyxrc.print_spool_command +' ';
962 if (target_name != "default") {
963 command2 += lyxrc.print_spool_printerprefix
967 command2 += quoteName(psname);
969 // If successful, then spool command
970 res = one.startscript(
975 res = one.startscript(
976 Systemcall::DontWait,
979 // case 2: print directly to a printer
980 res = one.startscript(
981 Systemcall::DontWait,
982 command + quoteName(dviname));
986 // case 1: print to a file
987 command += lyxrc.print_to_file
988 + quoteName(makeAbsPath(target_name,
991 + quoteName(dviname);
992 res = one.startscript(Systemcall::DontWait,
997 showPrintError(buffer->fileName());
1001 case LFUN_BUFFER_IMPORT:
1006 if (view()->buffer()) {
1007 // save cursor Position for opened files to .lyx/session
1008 LyX::ref().session().saveFilePosition(lyx_view_->buffer()->fileName(),
1009 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1010 // save bookmarks to .lyx/session
1011 view()->saveSavedPositions();
1013 LyX::ref().quit(argument == "force");
1016 case LFUN_TOC_VIEW: {
1017 InsetCommandParams p("tableofcontents");
1018 string const data = InsetCommandMailer::params2string("toc", p);
1019 lyx_view_->getDialogs().show("toc", data, 0);
1023 case LFUN_BUFFER_AUTO_SAVE:
1027 case LFUN_RECONFIGURE:
1028 reconfigure(view());
1031 case LFUN_HELP_OPEN: {
1032 string const arg = argument;
1034 setErrorMessage(_("Missing argument"));
1037 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1038 if (fname.empty()) {
1039 lyxerr << "LyX: unable to find documentation file `"
1040 << arg << "'. Bad installation?" << endl;
1043 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1044 makeDisplayPath(fname)));
1045 lyx_view_->loadLyXFile(fname, false);
1049 // --- version control -------------------------------
1050 case LFUN_VC_REGISTER:
1051 if (!ensureBufferClean(view()))
1053 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1054 lyx_view_->buffer()->lyxvc().registrer();
1059 case LFUN_VC_CHECK_IN:
1060 if (!ensureBufferClean(view()))
1062 if (lyx_view_->buffer()->lyxvc().inUse()
1063 && !lyx_view_->buffer()->isReadonly()) {
1064 lyx_view_->buffer()->lyxvc().checkIn();
1069 case LFUN_VC_CHECK_OUT:
1070 if (!ensureBufferClean(view()))
1072 if (lyx_view_->buffer()->lyxvc().inUse()
1073 && lyx_view_->buffer()->isReadonly()) {
1074 lyx_view_->buffer()->lyxvc().checkOut();
1079 case LFUN_VC_REVERT:
1080 lyx_view_->buffer()->lyxvc().revert();
1084 case LFUN_VC_UNDO_LAST:
1085 lyx_view_->buffer()->lyxvc().undoLast();
1089 // --- buffers ----------------------------------------
1090 case LFUN_BUFFER_SWITCH:
1091 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1094 case LFUN_BUFFER_NEXT:
1095 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1098 case LFUN_BUFFER_PREVIOUS:
1099 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1103 newFile(view(), argument);
1106 case LFUN_FILE_OPEN:
1110 case LFUN_DROP_LAYOUTS_CHOICE:
1111 lyx_view_->getToolbars().openLayoutList();
1114 case LFUN_MENU_OPEN:
1115 lyx_view_->getMenubar().openByName(lyx::from_utf8(argument));
1118 // --- lyxserver commands ----------------------------
1119 case LFUN_SERVER_GET_NAME:
1120 setMessage(lyx::from_utf8(lyx_view_->buffer()->fileName()));
1121 lyxerr[Debug::INFO] << "FNAME["
1122 << lyx_view_->buffer()->fileName()
1126 case LFUN_SERVER_NOTIFY:
1127 dispatch_buffer = lyx::from_utf8(keyseq->print());
1128 theLyXServer().notifyClient(lyx::to_utf8(dispatch_buffer));
1131 case LFUN_SERVER_GOTO_FILE_ROW: {
1134 istringstream is(argument);
1135 is >> file_name >> row;
1136 if (prefixIs(file_name, package().temp_dir())) {
1137 // Needed by inverse dvi search. If it is a file
1138 // in tmpdir, call the apropriated function
1139 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1141 // Must replace extension of the file to be .lyx
1142 // and get full path
1143 string const s = changeExtension(file_name, ".lyx");
1144 // Either change buffer or load the file
1145 if (theBufferList().exists(s)) {
1146 lyx_view_->setBuffer(theBufferList().getBuffer(s));
1148 lyx_view_->loadLyXFile(s);
1152 view()->setCursorFromRow(row);
1155 // see BufferView::center()
1159 case LFUN_DIALOG_SHOW: {
1160 string const name = cmd.getArg(0);
1161 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1163 if (name == "character") {
1164 data = freefont2string();
1166 lyx_view_->getDialogs().show("character", data);
1167 } else if (name == "latexlog") {
1168 pair<Buffer::LogType, string> const logfile =
1169 lyx_view_->buffer()->getLogName();
1170 switch (logfile.first) {
1171 case Buffer::latexlog:
1174 case Buffer::buildlog:
1178 data += LyXLex::quoteString(logfile.second);
1179 lyx_view_->getDialogs().show("log", data);
1180 } else if (name == "vclog") {
1181 string const data = "vc " +
1182 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1183 lyx_view_->getDialogs().show("log", data);
1185 lyx_view_->getDialogs().show(name, data);
1189 case LFUN_DIALOG_SHOW_NEW_INSET: {
1190 string const name = cmd.getArg(0);
1191 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1192 if (name == "bibitem" ||
1199 InsetCommandParams p(name);
1200 data = InsetCommandMailer::params2string(name, p);
1201 } else if (name == "include") {
1202 InsetCommandParams p(data);
1203 data = InsetIncludeMailer::params2string(p);
1204 } else if (name == "box") {
1205 // \c data == "Boxed" || "Frameless" etc
1206 InsetBoxParams p(data);
1207 data = InsetBoxMailer::params2string(p);
1208 } else if (name == "branch") {
1209 InsetBranchParams p;
1210 data = InsetBranchMailer::params2string(p);
1211 } else if (name == "citation") {
1212 InsetCommandParams p("cite");
1213 data = InsetCommandMailer::params2string(name, p);
1214 } else if (name == "ert") {
1215 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1216 } else if (name == "external") {
1217 InsetExternalParams p;
1218 Buffer const & buffer = *lyx_view_->buffer();
1219 data = InsetExternalMailer::params2string(p, buffer);
1220 } else if (name == "float") {
1222 data = InsetFloatMailer::params2string(p);
1223 } else if (name == "graphics") {
1224 InsetGraphicsParams p;
1225 Buffer const & buffer = *lyx_view_->buffer();
1226 data = InsetGraphicsMailer::params2string(p, buffer);
1227 } else if (name == "note") {
1229 data = InsetNoteMailer::params2string(p);
1230 } else if (name == "vspace") {
1232 data = InsetVSpaceMailer::params2string(space);
1233 } else if (name == "wrap") {
1235 data = InsetWrapMailer::params2string(p);
1237 lyx_view_->getDialogs().show(name, data, 0);
1241 case LFUN_DIALOG_UPDATE: {
1242 string const & name = argument;
1243 // Can only update a dialog connected to an existing inset
1244 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1246 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1247 inset->dispatch(view()->cursor(), fr);
1248 } else if (name == "paragraph") {
1249 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1250 } else if (name == "prefs") {
1251 lyx_view_->getDialogs().update(name, string());
1256 case LFUN_DIALOG_HIDE:
1257 Dialogs::hide(argument, 0);
1260 case LFUN_DIALOG_DISCONNECT_INSET:
1261 lyx_view_->getDialogs().disconnect(argument);
1265 case LFUN_CITATION_INSERT: {
1266 if (!argument.empty()) {
1267 // we can have one optional argument, delimited by '|'
1268 // citation-insert <key>|<text_before>
1269 // this should be enhanced to also support text_after
1270 // and citation style
1271 string arg = argument;
1273 if (contains(argument, "|")) {
1274 arg = token(argument, '|', 0);
1275 opt1 = '[' + token(argument, '|', 1) + ']';
1277 std::ostringstream os;
1278 os << "citation LatexCommand\n"
1279 << "\\cite" << opt1 << "{" << arg << "}\n"
1281 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1284 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1288 case LFUN_BUFFER_CHILD_OPEN: {
1289 string const filename =
1290 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1291 // FIXME Should use bformat
1292 setMessage(_("Opening child document ") +
1293 makeDisplayPath(filename) + "...");
1294 view()->savePosition(0);
1295 string const parentfilename = lyx_view_->buffer()->fileName();
1296 if (theBufferList().exists(filename))
1297 lyx_view_->setBuffer(theBufferList().getBuffer(filename));
1299 lyx_view_->loadLyXFile(filename);
1300 // Set the parent name of the child document.
1301 // This makes insertion of citations and references in the child work,
1302 // when the target is in the parent or another child document.
1303 lyx_view_->buffer()->setParentName(parentfilename);
1307 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1308 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1311 case LFUN_KEYMAP_OFF:
1312 lyx_view_->view()->getIntl().keyMapOn(false);
1315 case LFUN_KEYMAP_PRIMARY:
1316 lyx_view_->view()->getIntl().keyMapPrim();
1319 case LFUN_KEYMAP_SECONDARY:
1320 lyx_view_->view()->getIntl().keyMapSec();
1323 case LFUN_KEYMAP_TOGGLE:
1324 lyx_view_->view()->getIntl().toggleKeyMap();
1330 string rest = split(argument, countstr, ' ');
1331 istringstream is(countstr);
1334 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1335 for (int i = 0; i < count; ++i)
1336 dispatch(lyxaction.lookupFunc(rest));
1340 case LFUN_COMMAND_SEQUENCE: {
1341 // argument contains ';'-terminated commands
1342 string arg = argument;
1343 while (!arg.empty()) {
1345 arg = split(arg, first, ';');
1346 FuncRequest func(lyxaction.lookupFunc(first));
1347 func.origin = cmd.origin;
1353 case LFUN_PREFERENCES_SAVE: {
1354 lyx::support::Path p(package().user_support());
1355 lyxrc.write("preferences", false);
1359 case LFUN_SCREEN_FONT_UPDATE:
1360 // handle the screen font changes.
1361 lyxrc.set_font_norm_type();
1362 theFontLoader().update();
1363 // All visible buffers will need resize
1367 case LFUN_SET_COLOR: {
1369 string const x11_name = split(argument, lyx_name, ' ');
1370 if (lyx_name.empty() || x11_name.empty()) {
1371 setErrorMessage(_("Syntax: set-color <lyx_name>"
1376 bool const graphicsbg_changed =
1377 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1378 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1380 if (!lcolor.setColor(lyx_name, x11_name)) {
1382 bformat(_("Set-color \"%1$s\" failed "
1383 "- color is undefined or "
1384 "may not be redefined"),
1385 lyx::from_utf8(lyx_name)));
1389 theApp->updateColor(lcolor.getFromLyXName(lyx_name));
1391 if (graphicsbg_changed) {
1392 #ifdef WITH_WARNINGS
1393 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1396 lyx::graphics::GCache::get().changeDisplay(true);
1403 lyx_view_->message(lyx::from_utf8(argument));
1406 case LFUN_EXTERNAL_EDIT: {
1407 FuncRequest fr(action, argument);
1408 InsetExternal().dispatch(view()->cursor(), fr);
1412 case LFUN_GRAPHICS_EDIT: {
1413 FuncRequest fr(action, argument);
1414 InsetGraphics().dispatch(view()->cursor(), fr);
1418 case LFUN_INSET_APPLY: {
1419 string const name = cmd.getArg(0);
1420 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1422 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1423 inset->dispatch(view()->cursor(), fr);
1425 FuncRequest fr(LFUN_INSET_INSERT, argument);
1428 // ideally, the update flag should be set by the insets,
1429 // but this is not possible currently
1434 case LFUN_ALL_INSETS_TOGGLE: {
1436 string const name = split(argument, action, ' ');
1437 InsetBase::Code const inset_code =
1438 InsetBase::translate(name);
1440 LCursor & cur = view()->cursor();
1441 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1443 InsetBase & inset = lyx_view_->buffer()->inset();
1444 InsetIterator it = inset_iterator_begin(inset);
1445 InsetIterator const end = inset_iterator_end(inset);
1446 for (; it != end; ++it) {
1447 if (inset_code == InsetBase::NO_CODE
1448 || inset_code == it->lyxCode()) {
1449 LCursor tmpcur = cur;
1450 tmpcur.pushLeft(*it);
1451 it->dispatch(tmpcur, fr);
1458 case LFUN_BUFFER_LANGUAGE: {
1459 Buffer & buffer = *lyx_view_->buffer();
1460 Language const * oldL = buffer.params().language;
1461 Language const * newL = languages.getLanguage(argument);
1462 if (!newL || oldL == newL)
1465 if (oldL->rightToLeft() == newL->rightToLeft()
1466 && !buffer.isMultiLingual())
1467 buffer.changeLanguage(oldL, newL);
1469 buffer.updateDocLang(newL);
1473 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1474 string const fname =
1475 addName(addPath(package().user_support(), "templates/"),
1477 Buffer defaults(fname);
1479 istringstream ss(argument);
1482 int const unknown_tokens = defaults.readHeader(lex);
1484 if (unknown_tokens != 0) {
1485 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1486 << unknown_tokens << " unknown token"
1487 << (unknown_tokens == 1 ? "" : "s")
1491 if (defaults.writeFile(defaults.fileName()))
1492 // FIXME Should use bformat
1493 setMessage(_("Document defaults saved in ")
1494 + makeDisplayPath(fname));
1496 setErrorMessage(_("Unable to save document defaults"));
1500 case LFUN_BUFFER_PARAMS_APPLY: {
1501 biblio::CiteEngine const engine =
1502 lyx_view_->buffer()->params().cite_engine;
1504 istringstream ss(argument);
1507 int const unknown_tokens =
1508 lyx_view_->buffer()->readHeader(lex);
1510 if (unknown_tokens != 0) {
1511 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1512 << unknown_tokens << " unknown token"
1513 << (unknown_tokens == 1 ? "" : "s")
1516 if (engine == lyx_view_->buffer()->params().cite_engine)
1519 LCursor & cur = view()->cursor();
1520 FuncRequest fr(LFUN_INSET_REFRESH);
1522 InsetBase & inset = lyx_view_->buffer()->inset();
1523 InsetIterator it = inset_iterator_begin(inset);
1524 InsetIterator const end = inset_iterator_end(inset);
1525 for (; it != end; ++it)
1526 if (it->lyxCode() == InsetBase::CITE_CODE)
1527 it->dispatch(cur, fr);
1531 case LFUN_TEXTCLASS_APPLY: {
1532 Buffer * buffer = lyx_view_->buffer();
1534 lyx::textclass_type const old_class =
1535 buffer->params().textclass;
1537 loadTextclass(argument);
1539 std::pair<bool, lyx::textclass_type> const tc_pair =
1540 textclasslist.numberOfClass(argument);
1545 lyx::textclass_type const new_class = tc_pair.second;
1546 if (old_class == new_class)
1550 lyx_view_->message(_("Converting document to new document class..."));
1551 recordUndoFullDocument(view());
1552 buffer->params().textclass = new_class;
1553 StableDocIterator backcur(view()->cursor());
1554 ErrorList & el = buffer->errorList("Class Switch");
1555 lyx::cap::switchBetweenClasses(
1556 old_class, new_class,
1557 static_cast<InsetText &>(buffer->inset()), el);
1559 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1561 buffer->errors("Class Switch");
1562 updateLabels(*buffer);
1567 case LFUN_TEXTCLASS_LOAD:
1568 loadTextclass(argument);
1571 case LFUN_LYXRC_APPLY: {
1572 LyXRC const lyxrc_orig = lyxrc;
1574 istringstream ss(argument);
1575 bool const success = lyxrc.read(ss) == 0;
1578 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1579 << "Unable to read lyxrc data"
1584 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1589 view()->cursor().dispatch(cmd);
1590 updateforce |= view()->cursor().result().update();
1591 if (!view()->cursor().result().dispatched())
1592 updateforce |= view()->dispatch(cmd);
1597 if (view()->buffer()) {
1598 // Redraw screen unless explicitly told otherwise.
1599 // This also initializes the position cache for all insets
1600 // in (at least partially) visible top-level paragraphs.
1602 view()->update(Update::FitCursor | Update::Force);
1604 view()->update(Update::FitCursor);
1606 lyx_view_->redrawWorkArea();
1608 // if we executed a mutating lfun, mark the buffer as dirty
1610 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1611 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1612 view()->buffer()->markDirty();
1614 if (view()->cursor().inTexted()) {
1615 lyx_view_->updateLayoutChoice();
1620 // FIXME UNICODE: _() does not support anything but ascii.
1621 // Do we need a lyx::to_ascii() method?
1622 sendDispatchMessage(getMessage(), cmd);
1626 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1628 /* When an action did not originate from the UI/kbd, it makes
1629 * sense to avoid updating the GUI. It turns out that this
1630 * fixes bug 1941, for reasons that are described here:
1631 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1633 if (cmd.origin != FuncRequest::INTERNAL) {
1634 lyx_view_->updateMenubar();
1635 lyx_view_->updateToolbars();
1638 const bool verbose = (cmd.origin == FuncRequest::UI
1639 || cmd.origin == FuncRequest::COMMANDBUFFER);
1641 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1642 lyxerr[Debug::ACTION] << "dispatch msg is " << lyx::to_utf8(msg) << endl;
1644 lyx_view_->message(msg);
1648 docstring dispatch_msg = msg;
1649 if (!dispatch_msg.empty())
1650 dispatch_msg += ' ';
1652 string comname = lyxaction.getActionName(cmd.action);
1654 bool argsadded = false;
1656 if (!cmd.argument().empty()) {
1657 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1658 comname += ' ' + lyx::to_utf8(cmd.argument());
1663 string const shortcuts = theTopLevelKeymap().printbindings(cmd);
1665 if (!shortcuts.empty())
1666 comname += ": " + shortcuts;
1667 else if (!argsadded && !cmd.argument().empty())
1668 comname += ' ' + lyx::to_utf8(cmd.argument());
1670 if (!comname.empty()) {
1671 comname = rtrim(comname);
1672 dispatch_msg += lyx::from_utf8('(' + rtrim(comname) + ')');
1675 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1676 << lyx::to_utf8(dispatch_msg) << endl;
1677 if (!dispatch_msg.empty())
1678 lyx_view_->message(dispatch_msg);
1682 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1684 string initpath = lyxrc.document_path;
1685 string filename(name);
1687 if (view()->buffer()) {
1688 string const trypath = lyx_view_->buffer()->filePath();
1689 // If directory is writeable, use this as default.
1690 if (isDirWriteable(trypath))
1694 static int newfile_number;
1696 if (filename.empty()) {
1697 filename = addName(lyxrc.document_path,
1698 "newfile" + convert<string>(++newfile_number) + ".lyx");
1699 while (theBufferList().exists(filename) || fs::is_readable(filename)) {
1701 filename = addName(lyxrc.document_path,
1702 "newfile" + convert<string>(newfile_number) +
1707 // The template stuff
1710 FileDialog fileDlg(_("Select template file"),
1711 LFUN_SELECT_FILE_SYNC,
1712 make_pair(_("Documents|#o#O"), lyx::from_utf8(lyxrc.document_path)),
1713 make_pair(_("Templates|#T#t"), lyx::from_utf8(lyxrc.template_path)));
1715 FileDialog::Result result =
1716 fileDlg.open(lyx::from_utf8(lyxrc.template_path),
1717 FileFilterList(_("LyX Documents (*.lyx)")),
1720 if (result.first == FileDialog::Later)
1722 if (result.second.empty())
1724 templname = lyx::to_utf8(result.second);
1727 Buffer * const b = newFile(filename, templname, !name.empty());
1729 lyx_view_->setBuffer(b);
1733 void LyXFunc::open(string const & fname)
1735 string initpath = lyxrc.document_path;
1737 if (view()->buffer()) {
1738 string const trypath = lyx_view_->buffer()->filePath();
1739 // If directory is writeable, use this as default.
1740 if (isDirWriteable(trypath))
1746 if (fname.empty()) {
1747 FileDialog fileDlg(_("Select document to open"),
1749 make_pair(_("Documents|#o#O"), lyx::from_utf8(lyxrc.document_path)),
1750 make_pair(_("Examples|#E#e"), lyx::from_utf8(addPath(package().system_support(), "examples"))));
1752 FileDialog::Result result =
1753 fileDlg.open(lyx::from_utf8(initpath),
1754 FileFilterList(_("LyX Documents (*.lyx)")),
1757 if (result.first == FileDialog::Later)
1760 filename = lyx::to_utf8(result.second);
1762 // check selected filename
1763 if (filename.empty()) {
1764 lyx_view_->message(_("Canceled."));
1770 // get absolute path of file and add ".lyx" to the filename if
1772 string const fullpath = fileSearch(string(), filename, "lyx");
1773 if (!fullpath.empty()) {
1774 filename = fullpath;
1777 docstring const disp_fn = makeDisplayPath(filename);
1779 // if the file doesn't exist, let the user create one
1780 if (!fs::exists(filename)) {
1781 // the user specifically chose this name. Believe him.
1782 Buffer * const b = newFile(filename, string(), true);
1784 lyx_view_->setBuffer(b);
1788 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1791 if (lyx_view_->loadLyXFile(filename)) {
1792 str2 = bformat(_("Document %1$s opened."), disp_fn);
1794 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1796 lyx_view_->message(str2);
1800 void LyXFunc::doImport(string const & argument)
1803 string filename = split(argument, format, ' ');
1805 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1806 << " file: " << filename << endl;
1808 // need user interaction
1809 if (filename.empty()) {
1810 string initpath = lyxrc.document_path;
1812 if (view()->buffer()) {
1813 string const trypath = lyx_view_->buffer()->filePath();
1814 // If directory is writeable, use this as default.
1815 if (isDirWriteable(trypath))
1819 docstring const text = bformat(_("Select %1$s file to import"),
1820 formats.prettyName(format));
1822 FileDialog fileDlg(text,
1824 make_pair(_("Documents|#o#O"), lyx::from_utf8(lyxrc.document_path)),
1825 make_pair(_("Examples|#E#e"),
1826 lyx::from_utf8(addPath(package().system_support(), "examples"))));
1828 docstring filter = formats.prettyName(format);
1831 filter += lyx::from_utf8(formats.extension(format));
1834 FileDialog::Result result =
1835 fileDlg.open(lyx::from_utf8(initpath),
1836 FileFilterList(filter),
1839 if (result.first == FileDialog::Later)
1842 filename = lyx::to_utf8(result.second);
1844 // check selected filename
1845 if (filename.empty())
1846 lyx_view_->message(_("Canceled."));
1849 if (filename.empty())
1852 // get absolute path of file
1853 filename = makeAbsPath(filename);
1855 string const lyxfile = changeExtension(filename, ".lyx");
1857 // Check if the document already is open
1858 if (lyx::use_gui && theBufferList().exists(lyxfile)) {
1859 if (!theBufferList().close(theBufferList().getBuffer(lyxfile), true)) {
1860 lyx_view_->message(_("Canceled."));
1865 // if the file exists already, and we didn't do
1866 // -i lyx thefile.lyx, warn
1867 if (fs::exists(lyxfile) && filename != lyxfile) {
1868 docstring const file = makeDisplayPath(lyxfile, 30);
1870 docstring text = bformat(_("The document %1$s already exists.\n\n"
1871 "Do you want to over-write that document?"), file);
1872 int const ret = Alert::prompt(_("Over-write document?"),
1873 text, 0, 1, _("&Over-write"), _("&Cancel"));
1876 lyx_view_->message(_("Canceled."));
1881 ErrorList errorList;
1882 Importer::Import(lyx_view_, filename, format, errorList);
1883 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1887 void LyXFunc::closeBuffer()
1889 // save current cursor position
1890 LyX::ref().session().saveFilePosition(lyx_view_->buffer()->fileName(),
1891 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1892 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
1893 if (theBufferList().empty()) {
1894 // need this otherwise SEGV may occur while
1895 // trying to set variables that don't exist
1896 // since there's no current buffer
1897 lyx_view_->getDialogs().hideBufferDependent();
1899 lyx_view_->setBuffer(theBufferList().first());
1905 // Each "lyx_view_" should have it's own message method. lyxview and
1906 // the minibuffer would use the minibuffer, but lyxserver would
1907 // send an ERROR signal to its client. Alejandro 970603
1908 // This function is bit problematic when it comes to NLS, to make the
1909 // lyx servers client be language indepenent we must not translate
1910 // strings sent to this func.
1911 void LyXFunc::setErrorMessage(docstring const & m) const
1913 dispatch_buffer = m;
1918 void LyXFunc::setMessage(docstring const & m) const
1920 dispatch_buffer = m;
1924 string const LyXFunc::viewStatusMessage()
1926 // When meta-fake key is pressed, show the key sequence so far + "M-".
1928 return keyseq->print() + "M-";
1930 // Else, when a non-complete key sequence is pressed,
1931 // show the available options.
1932 if (keyseq->length() > 0 && !keyseq->deleted())
1933 return keyseq->printOptions();
1935 if (!view()->buffer())
1936 return lyx::to_utf8(_("Welcome to LyX!"));
1938 return view()->cursor().currentState();
1942 BufferView * LyXFunc::view() const
1944 BOOST_ASSERT(lyx_view_);
1945 return lyx_view_->view();
1949 bool LyXFunc::wasMetaKey() const
1951 return (meta_fake_bit != key_modifier::none);
1957 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1959 // Why the switch you might ask. It is a trick to ensure that all
1960 // the elements in the LyXRCTags enum is handled. As you can see
1961 // there are no breaks at all. So it is just a huge fall-through.
1962 // The nice thing is that we will get a warning from the compiler
1963 // if we forget an element.
1964 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1966 case LyXRC::RC_ACCEPT_COMPOUND:
1967 case LyXRC::RC_ALT_LANG:
1968 case LyXRC::RC_ASCIIROFF_COMMAND:
1969 case LyXRC::RC_ASCII_LINELEN:
1970 case LyXRC::RC_AUTOREGIONDELETE:
1971 case LyXRC::RC_AUTORESET_OPTIONS:
1972 case LyXRC::RC_AUTOSAVE:
1973 case LyXRC::RC_AUTO_NUMBER:
1974 case LyXRC::RC_BACKUPDIR_PATH:
1975 case LyXRC::RC_BIBTEX_COMMAND:
1976 case LyXRC::RC_BINDFILE:
1977 case LyXRC::RC_CHECKLASTFILES:
1978 case LyXRC::RC_USELASTFILEPOS:
1979 case LyXRC::RC_LOADSESSION:
1980 case LyXRC::RC_CHKTEX_COMMAND:
1981 case LyXRC::RC_CONVERTER:
1982 case LyXRC::RC_COPIER:
1983 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1984 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1985 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1986 case LyXRC::RC_DATE_INSERT_FORMAT:
1987 case LyXRC::RC_DEFAULT_LANGUAGE:
1988 case LyXRC::RC_DEFAULT_PAPERSIZE:
1989 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1990 case LyXRC::RC_DISPLAY_GRAPHICS:
1991 case LyXRC::RC_DOCUMENTPATH:
1992 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1993 if (fs::exists(lyxrc_new.document_path) &&
1994 fs::is_directory(lyxrc_new.document_path)) {
1995 using lyx::support::package;
1996 package().document_dir() = lyxrc.document_path;
1999 case LyXRC::RC_ESC_CHARS:
2000 case LyXRC::RC_FONT_ENCODING:
2001 case LyXRC::RC_FORMAT:
2002 case LyXRC::RC_INDEX_COMMAND:
2003 case LyXRC::RC_INPUT:
2004 case LyXRC::RC_KBMAP:
2005 case LyXRC::RC_KBMAP_PRIMARY:
2006 case LyXRC::RC_KBMAP_SECONDARY:
2007 case LyXRC::RC_LABEL_INIT_LENGTH:
2008 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2009 case LyXRC::RC_LANGUAGE_AUTO_END:
2010 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2011 case LyXRC::RC_LANGUAGE_COMMAND_END:
2012 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2013 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2014 case LyXRC::RC_LANGUAGE_PACKAGE:
2015 case LyXRC::RC_LANGUAGE_USE_BABEL:
2016 case LyXRC::RC_MAKE_BACKUP:
2017 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2018 case LyXRC::RC_NUMLASTFILES:
2019 case LyXRC::RC_PATH_PREFIX:
2020 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2021 using lyx::support::prependEnvPath;
2022 prependEnvPath("PATH", lyxrc.path_prefix);
2024 case LyXRC::RC_PERS_DICT:
2025 case LyXRC::RC_POPUP_BOLD_FONT:
2026 case LyXRC::RC_POPUP_FONT_ENCODING:
2027 case LyXRC::RC_POPUP_NORMAL_FONT:
2028 case LyXRC::RC_PREVIEW:
2029 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2030 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2031 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2032 case LyXRC::RC_PRINTCOPIESFLAG:
2033 case LyXRC::RC_PRINTER:
2034 case LyXRC::RC_PRINTEVENPAGEFLAG:
2035 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2036 case LyXRC::RC_PRINTFILEEXTENSION:
2037 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2038 case LyXRC::RC_PRINTODDPAGEFLAG:
2039 case LyXRC::RC_PRINTPAGERANGEFLAG:
2040 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2041 case LyXRC::RC_PRINTPAPERFLAG:
2042 case LyXRC::RC_PRINTREVERSEFLAG:
2043 case LyXRC::RC_PRINTSPOOL_COMMAND:
2044 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2045 case LyXRC::RC_PRINTTOFILE:
2046 case LyXRC::RC_PRINTTOPRINTER:
2047 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2048 case LyXRC::RC_PRINT_COMMAND:
2049 case LyXRC::RC_RTL_SUPPORT:
2050 case LyXRC::RC_SCREEN_DPI:
2051 case LyXRC::RC_SCREEN_FONT_ENCODING:
2052 case LyXRC::RC_SCREEN_FONT_ROMAN:
2053 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2054 case LyXRC::RC_SCREEN_FONT_SANS:
2055 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2056 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2057 case LyXRC::RC_SCREEN_FONT_SIZES:
2058 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2059 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2060 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2061 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2062 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2063 case LyXRC::RC_SCREEN_ZOOM:
2064 case LyXRC::RC_SERVERPIPE:
2065 case LyXRC::RC_SET_COLOR:
2066 case LyXRC::RC_SHOW_BANNER:
2067 case LyXRC::RC_SPELL_COMMAND:
2068 case LyXRC::RC_TEMPDIRPATH:
2069 case LyXRC::RC_TEMPLATEPATH:
2070 case LyXRC::RC_TEX_ALLOWS_SPACES:
2071 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2072 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2073 namespace os = lyx::support::os;
2074 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2076 case LyXRC::RC_UIFILE:
2077 case LyXRC::RC_USER_EMAIL:
2078 case LyXRC::RC_USER_NAME:
2079 case LyXRC::RC_USETEMPDIR:
2080 case LyXRC::RC_USE_ALT_LANG:
2081 case LyXRC::RC_USE_ESC_CHARS:
2082 case LyXRC::RC_USE_INP_ENC:
2083 case LyXRC::RC_USE_PERS_DICT:
2084 case LyXRC::RC_USE_SPELL_LIB:
2085 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2086 case LyXRC::RC_VIEWER:
2087 case LyXRC::RC_LAST: