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();
260 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
261 size_t encoded_last_key = keysym->getUCSEncoded();
263 // Do a one-deep top-level lookup for
264 // cancel and meta-fake keys. RVDK_PATCH_5
265 cancel_meta_seq->reset();
267 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
268 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
269 << " action first set to [" << func.action << ']'
272 // When not cancel or meta-fake, do the normal lookup.
273 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
274 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
275 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
276 // remove Caps Lock and Mod2 as a modifiers
277 func = keyseq->addkey(keysym, (state | meta_fake_bit));
278 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
279 << "action now set to ["
280 << func.action << ']' << endl;
283 // Dont remove this unless you know what you are doing.
284 meta_fake_bit = key_modifier::none;
286 // Can this happen now ?
287 if (func.action == LFUN_NOACTION) {
288 func = FuncRequest(LFUN_COMMAND_PREFIX);
291 if (lyxerr.debugging(Debug::KEY)) {
292 lyxerr << BOOST_CURRENT_FUNCTION
294 << func.action << "]["
295 << keyseq->print() << ']'
299 // already here we know if it any point in going further
300 // why not return already here if action == -1 and
301 // num_bytes == 0? (Lgb)
303 if (keyseq->length() > 1) {
304 lyx_view_->message(lyx::from_utf8(keyseq->print()));
308 // Maybe user can only reach the key via holding down shift.
309 // Let's see. But only if shift is the only modifier
310 if (func.action == LFUN_UNKNOWN_ACTION &&
311 state == key_modifier::shift) {
312 lyxerr[Debug::KEY] << "Trying without shift" << endl;
313 func = keyseq->addkey(keysym, key_modifier::none);
314 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
317 if (func.action == LFUN_UNKNOWN_ACTION) {
318 // Hmm, we didn't match any of the keysequences. See
319 // if it's normal insertable text not already covered
321 if (keysym->isText() && keyseq->length() == 1) {
322 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
323 func = FuncRequest(LFUN_SELF_INSERT,
324 FuncRequest::KEYBOARD);
326 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
327 lyx_view_->message(_("Unknown function."));
332 if (func.action == LFUN_SELF_INSERT) {
333 if (encoded_last_key != 0) {
334 docstring const arg(1, encoded_last_key);
335 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
336 FuncRequest::KEYBOARD));
338 << "SelfInsert arg[`" << lyx::to_utf8(arg) << "']" << endl;
346 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
348 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
350 LCursor & cur = view()->cursor();
352 /* In LyX/Mac, when a dialog is open, the menus of the
353 application can still be accessed without giving focus to
354 the main window. In this case, we want to disable the menu
355 entries that are buffer-related.
357 Note that this code is not perfect, as bug 1941 attests:
358 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
361 if (cmd.origin == FuncRequest::UI && !lyx_view_->hasFocus())
364 buf = lyx_view_->buffer();
366 if (cmd.action == LFUN_NOACTION) {
367 flag.message(lyx::from_utf8(N_("Nothing to do")));
372 switch (cmd.action) {
373 case LFUN_UNKNOWN_ACTION:
374 #ifndef HAVE_LIBAIKSAURUS
375 case LFUN_THESAURUS_ENTRY:
385 if (flag.unknown()) {
386 flag.message(lyx::from_utf8(N_("Unknown action")));
390 if (!flag.enabled()) {
391 if (flag.message().empty())
392 flag.message(lyx::from_utf8(N_("Command disabled")));
396 // Check whether we need a buffer
397 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
399 flag.message(lyx::from_utf8(N_("Command not allowed with"
400 "out any document open")));
405 // I would really like to avoid having this switch and rather try to
406 // encode this in the function itself.
407 // -- And I'd rather let an inset decide which LFUNs it is willing
408 // to handle (Andre')
410 switch (cmd.action) {
411 case LFUN_BUFFER_TOGGLE_READ_ONLY:
412 flag.setOnOff(buf->isReadonly());
415 case LFUN_BUFFER_SWITCH:
416 // toggle on the current buffer, but do not toggle off
417 // the other ones (is that a good idea?)
418 if (lyx::to_utf8(cmd.argument()) == buf->fileName())
422 case LFUN_BUFFER_EXPORT:
423 enable = cmd.argument() == "custom"
424 || Exporter::isExportable(*buf, lyx::to_utf8(cmd.argument()));
427 case LFUN_BUFFER_CHKTEX:
428 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
431 case LFUN_BUILD_PROGRAM:
432 enable = Exporter::isExportable(*buf, "program");
435 case LFUN_LAYOUT_TABULAR:
436 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
440 case LFUN_LAYOUT_PARAGRAPH:
441 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
444 case LFUN_VC_REGISTER:
445 enable = !buf->lyxvc().inUse();
447 case LFUN_VC_CHECK_IN:
448 enable = buf->lyxvc().inUse() && !buf->isReadonly();
450 case LFUN_VC_CHECK_OUT:
451 enable = buf->lyxvc().inUse() && buf->isReadonly();
454 case LFUN_VC_UNDO_LAST:
455 enable = buf->lyxvc().inUse();
457 case LFUN_BUFFER_RELOAD:
458 enable = !buf->isUnnamed() && !buf->isClean();
461 case LFUN_INSET_SETTINGS: {
465 InsetBase::Code code = cur.inset().lyxCode();
467 case InsetBase::TABULAR_CODE:
468 enable = cmd.argument() == "tabular";
470 case InsetBase::ERT_CODE:
471 enable = cmd.argument() == "ert";
473 case InsetBase::FLOAT_CODE:
474 enable = cmd.argument() == "float";
476 case InsetBase::WRAP_CODE:
477 enable = cmd.argument() == "wrap";
479 case InsetBase::NOTE_CODE:
480 enable = cmd.argument() == "note";
482 case InsetBase::BRANCH_CODE:
483 enable = cmd.argument() == "branch";
485 case InsetBase::BOX_CODE:
486 enable = cmd.argument() == "box";
494 case LFUN_INSET_APPLY: {
495 string const name = cmd.getArg(0);
496 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
498 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
500 bool const success = inset->getStatus(cur, fr, fs);
501 // Every inset is supposed to handle this
502 BOOST_ASSERT(success);
505 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
506 flag |= getStatus(fr);
508 enable = flag.enabled();
512 case LFUN_DIALOG_SHOW: {
513 string const name = cmd.getArg(0);
515 enable = name == "aboutlyx"
519 || name == "texinfo";
520 else if (name == "print")
521 enable = Exporter::isExportable(*buf, "dvi")
522 && lyxrc.print_command != "none";
523 else if (name == "character" || name == "mathpanel")
524 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
525 else if (name == "latexlog")
526 enable = isFileReadable(buf->getLogName().second);
527 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
528 else if (name == "spellchecker")
531 else if (name == "vclog")
532 enable = buf->lyxvc().inUse();
533 else if (name == "view-source")
538 case LFUN_DIALOG_SHOW_NEW_INSET:
539 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
542 case LFUN_DIALOG_UPDATE: {
543 string const name = cmd.getArg(0);
545 enable = name == "prefs";
549 case LFUN_CITATION_INSERT: {
550 FuncRequest fr(LFUN_INSET_INSERT, "citation");
551 enable = getStatus(fr).enabled();
555 case LFUN_BUFFER_WRITE: {
556 enable = view()->buffer()->isUnnamed()
557 || !view()->buffer()->isClean();
561 // this one is difficult to get right. As a half-baked
562 // solution, we consider only the first action of the sequence
563 case LFUN_COMMAND_SEQUENCE: {
564 // argument contains ';'-terminated commands
565 string const firstcmd = token(lyx::to_utf8(cmd.argument()), ';', 0);
566 FuncRequest func(lyxaction.lookupFunc(firstcmd));
567 func.origin = cmd.origin;
568 flag = getStatus(func);
571 case LFUN_BUFFER_NEW:
572 case LFUN_BUFFER_NEW_TEMPLATE:
573 case LFUN_WORD_FIND_FORWARD:
574 case LFUN_WORD_FIND_BACKWARD:
575 case LFUN_COMMAND_PREFIX:
576 case LFUN_COMMAND_EXECUTE:
578 case LFUN_META_PREFIX:
579 case LFUN_BUFFER_CLOSE:
580 case LFUN_BUFFER_WRITE_AS:
581 case LFUN_BUFFER_UPDATE:
582 case LFUN_BUFFER_VIEW:
583 case LFUN_BUFFER_IMPORT:
586 case LFUN_BUFFER_AUTO_SAVE:
587 case LFUN_RECONFIGURE:
591 case LFUN_DROP_LAYOUTS_CHOICE:
593 case LFUN_SERVER_GET_NAME:
594 case LFUN_SERVER_NOTIFY:
595 case LFUN_SERVER_GOTO_FILE_ROW:
596 case LFUN_DIALOG_HIDE:
597 case LFUN_DIALOG_DISCONNECT_INSET:
598 case LFUN_BUFFER_CHILD_OPEN:
599 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
600 case LFUN_KEYMAP_OFF:
601 case LFUN_KEYMAP_PRIMARY:
602 case LFUN_KEYMAP_SECONDARY:
603 case LFUN_KEYMAP_TOGGLE:
605 case LFUN_BUFFER_EXPORT_CUSTOM:
606 case LFUN_BUFFER_PRINT:
607 case LFUN_PREFERENCES_SAVE:
608 case LFUN_SCREEN_FONT_UPDATE:
611 case LFUN_EXTERNAL_EDIT:
612 case LFUN_GRAPHICS_EDIT:
613 case LFUN_ALL_INSETS_TOGGLE:
614 case LFUN_BUFFER_LANGUAGE:
615 case LFUN_TEXTCLASS_APPLY:
616 case LFUN_TEXTCLASS_LOAD:
617 case LFUN_BUFFER_SAVE_AS_DEFAULT:
618 case LFUN_BUFFER_PARAMS_APPLY:
619 case LFUN_LYXRC_APPLY:
620 case LFUN_BUFFER_NEXT:
621 case LFUN_BUFFER_PREVIOUS:
622 // these are handled in our dispatch()
627 if (!::getStatus(cur, cmd, flag))
628 flag = view()->getStatus(cmd);
634 // Can we use a readonly buffer?
635 if (buf && buf->isReadonly()
636 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
637 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
638 flag.message(lyx::from_utf8(N_("Document is read-only")));
642 // Are we in a DELETED change-tracking region?
643 if (buf && lookupChangeType(cur, true) == Change::DELETED
644 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
645 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
646 flag.message(lyx::from_utf8(N_("This portion of the document is deleted.")));
650 // the default error message if we disable the command
651 if (!flag.enabled() && flag.message().empty())
652 flag.message(lyx::from_utf8(N_("Command disabled")));
658 bool LyXFunc::ensureBufferClean(BufferView * bv)
660 Buffer & buf = *bv->buffer();
664 docstring const file = makeDisplayPath(buf.fileName(), 30);
665 docstring text = bformat(_("The document %1$s has unsaved "
666 "changes.\n\nDo you want to save "
667 "the document?"), file);
668 int const ret = Alert::prompt(_("Save changed document?"),
669 text, 0, 1, _("&Save"),
673 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
675 return buf.isClean();
681 void showPrintError(string const & name)
683 docstring str = bformat(_("Could not print the document %1$s.\n"
684 "Check that your printer is set up correctly."),
685 makeDisplayPath(name, 50));
686 Alert::error(_("Print document failed"), str);
690 void loadTextclass(string const & name)
692 std::pair<bool, lyx::textclass_type> const tc_pair =
693 textclasslist.numberOfClass(name);
695 if (!tc_pair.first) {
696 lyxerr << "Document class \"" << name
697 << "\" does not exist."
702 lyx::textclass_type const tc = tc_pair.second;
704 if (!textclasslist[tc].load()) {
705 docstring s = bformat(_("The document could not be converted\n"
706 "into the document class %1$s."),
707 lyx::from_utf8(textclasslist[tc].name()));
708 Alert::error(_("Could not change class"), s);
713 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
718 void LyXFunc::dispatch(FuncRequest const & cmd)
720 BOOST_ASSERT(view());
721 string const argument = lyx::to_utf8(cmd.argument());
722 kb_action const action = cmd.action;
724 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
725 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
727 // we have not done anything wrong yet.
729 dispatch_buffer.erase();
731 // redraw the screen at the end (first of the two drawing steps).
732 //This is done unless explicitely requested otherwise
734 // also do the second redrawing step. Only done if requested.
735 bool updateforce = false;
737 FuncStatus const flag = getStatus(cmd);
738 if (!flag.enabled()) {
739 // We cannot use this function here
740 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
741 << lyxaction.getActionName(action)
742 << " [" << action << "] is disabled at this location"
744 setErrorMessage(flag.message());
748 case LFUN_WORD_FIND_FORWARD:
749 case LFUN_WORD_FIND_BACKWARD: {
750 static string last_search;
751 string searched_string;
753 if (!argument.empty()) {
754 last_search = argument;
755 searched_string = argument;
757 searched_string = last_search;
760 if (searched_string.empty())
763 bool const fw = action == LFUN_WORD_FIND_FORWARD;
765 lyx::find::find2string(searched_string, true, false, fw);
766 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
770 case LFUN_COMMAND_PREFIX:
771 lyx_view_->message(lyx::from_utf8(keyseq->printOptions()));
774 case LFUN_COMMAND_EXECUTE:
775 lyx_view_->getToolbars().display("minibuffer", true);
776 lyx_view_->focus_command_buffer();
781 meta_fake_bit = key_modifier::none;
782 if (view()->buffer())
783 // cancel any selection
784 dispatch(FuncRequest(LFUN_MARK_OFF));
785 setMessage(_("Cancel"));
788 case LFUN_META_PREFIX:
789 meta_fake_bit = key_modifier::alt;
790 setMessage(lyx::from_utf8(keyseq->print()));
793 case LFUN_BUFFER_TOGGLE_READ_ONLY:
794 if (lyx_view_->buffer()->lyxvc().inUse())
795 lyx_view_->buffer()->lyxvc().toggleReadOnly();
797 lyx_view_->buffer()->setReadonly(
798 !lyx_view_->buffer()->isReadonly());
801 // --- Menus -----------------------------------------------
802 case LFUN_BUFFER_NEW:
803 menuNew(argument, false);
806 case LFUN_BUFFER_NEW_TEMPLATE:
807 menuNew(argument, true);
810 case LFUN_BUFFER_CLOSE:
814 case LFUN_BUFFER_WRITE:
815 if (!lyx_view_->buffer()->isUnnamed()) {
816 docstring const str = bformat(_("Saving document %1$s..."),
817 makeDisplayPath(lyx_view_->buffer()->fileName()));
818 lyx_view_->message(str);
819 menuWrite(lyx_view_->buffer());
820 lyx_view_->message(str + _(" done."));
822 writeAs(lyx_view_->buffer());
826 case LFUN_BUFFER_WRITE_AS:
827 writeAs(lyx_view_->buffer(), argument);
831 case LFUN_BUFFER_RELOAD: {
832 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
833 docstring text = bformat(_("Any changes will be lost. Are you sure "
834 "you want to revert to the saved version of the document %1$s?"), file);
835 int const ret = Alert::prompt(_("Revert to saved document?"),
836 text, 0, 1, _("&Revert"), _("&Cancel"));
843 case LFUN_BUFFER_UPDATE:
844 Exporter::Export(lyx_view_->buffer(), argument, true);
847 case LFUN_BUFFER_VIEW:
848 Exporter::preview(lyx_view_->buffer(), argument);
851 case LFUN_BUILD_PROGRAM:
852 Exporter::Export(lyx_view_->buffer(), "program", true);
855 case LFUN_BUFFER_CHKTEX:
856 lyx_view_->buffer()->runChktex();
859 case LFUN_BUFFER_EXPORT:
860 if (argument == "custom")
861 lyx_view_->getDialogs().show("sendto");
863 Exporter::Export(lyx_view_->buffer(), argument, false);
867 case LFUN_BUFFER_EXPORT_CUSTOM: {
869 string command = split(argument, format_name, ' ');
870 Format const * format = formats.getFormat(format_name);
872 lyxerr << "Format \"" << format_name
873 << "\" not recognized!"
878 Buffer * buffer = lyx_view_->buffer();
880 // The name of the file created by the conversion process
883 // Output to filename
884 if (format->name() == "lyx") {
885 string const latexname =
886 buffer->getLatexName(false);
887 filename = changeExtension(latexname,
888 format->extension());
889 filename = addName(buffer->temppath(), filename);
891 if (!buffer->writeFile(filename))
895 Exporter::Export(buffer, format_name, true, filename);
898 // Substitute $$FName for filename
899 if (!contains(command, "$$FName"))
900 command = "( " + command + " ) < $$FName";
901 command = subst(command, "$$FName", filename);
903 // Execute the command in the background
905 call.startscript(Systemcall::DontWait, command);
909 case LFUN_BUFFER_PRINT: {
912 string command = split(split(argument, target, ' '),
916 || target_name.empty()
917 || command.empty()) {
918 lyxerr << "Unable to parse \""
919 << argument << '"' << std::endl;
922 if (target != "printer" && target != "file") {
923 lyxerr << "Unrecognized target \""
924 << target << '"' << std::endl;
928 Buffer * buffer = lyx_view_->buffer();
930 if (!Exporter::Export(buffer, "dvi", true)) {
931 showPrintError(buffer->fileName());
935 // Push directory path.
936 string const path = buffer->temppath();
937 lyx::support::Path p(path);
939 // there are three cases here:
940 // 1. we print to a file
941 // 2. we print directly to a printer
942 // 3. we print using a spool command (print to file first)
945 string const dviname =
946 changeExtension(buffer->getLatexName(true),
949 if (target == "printer") {
950 if (!lyxrc.print_spool_command.empty()) {
951 // case 3: print using a spool
952 string const psname =
953 changeExtension(dviname,".ps");
954 command += lyxrc.print_to_file
957 + quoteName(dviname);
960 lyxrc.print_spool_command +' ';
961 if (target_name != "default") {
962 command2 += lyxrc.print_spool_printerprefix
966 command2 += quoteName(psname);
968 // If successful, then spool command
969 res = one.startscript(
974 res = one.startscript(
975 Systemcall::DontWait,
978 // case 2: print directly to a printer
979 res = one.startscript(
980 Systemcall::DontWait,
981 command + quoteName(dviname));
985 // case 1: print to a file
986 command += lyxrc.print_to_file
987 + quoteName(makeAbsPath(target_name,
990 + quoteName(dviname);
991 res = one.startscript(Systemcall::DontWait,
996 showPrintError(buffer->fileName());
1000 case LFUN_BUFFER_IMPORT:
1005 if (view()->buffer()) {
1006 // save cursor Position for opened files to .lyx/session
1007 LyX::ref().session().saveFilePosition(lyx_view_->buffer()->fileName(),
1008 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1009 // save bookmarks to .lyx/session
1010 view()->saveSavedPositions();
1012 LyX::ref().quit(argument == "force");
1015 case LFUN_TOC_VIEW: {
1016 InsetCommandParams p("tableofcontents");
1017 string const data = InsetCommandMailer::params2string("toc", p);
1018 lyx_view_->getDialogs().show("toc", data, 0);
1022 case LFUN_BUFFER_AUTO_SAVE:
1026 case LFUN_RECONFIGURE:
1027 reconfigure(view());
1030 case LFUN_HELP_OPEN: {
1031 string const arg = argument;
1033 setErrorMessage(_("Missing argument"));
1036 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1037 if (fname.empty()) {
1038 lyxerr << "LyX: unable to find documentation file `"
1039 << arg << "'. Bad installation?" << endl;
1042 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1043 makeDisplayPath(fname)));
1044 lyx_view_->loadLyXFile(fname, false);
1048 // --- version control -------------------------------
1049 case LFUN_VC_REGISTER:
1050 if (!ensureBufferClean(view()))
1052 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1053 lyx_view_->buffer()->lyxvc().registrer();
1058 case LFUN_VC_CHECK_IN:
1059 if (!ensureBufferClean(view()))
1061 if (lyx_view_->buffer()->lyxvc().inUse()
1062 && !lyx_view_->buffer()->isReadonly()) {
1063 lyx_view_->buffer()->lyxvc().checkIn();
1068 case LFUN_VC_CHECK_OUT:
1069 if (!ensureBufferClean(view()))
1071 if (lyx_view_->buffer()->lyxvc().inUse()
1072 && lyx_view_->buffer()->isReadonly()) {
1073 lyx_view_->buffer()->lyxvc().checkOut();
1078 case LFUN_VC_REVERT:
1079 lyx_view_->buffer()->lyxvc().revert();
1083 case LFUN_VC_UNDO_LAST:
1084 lyx_view_->buffer()->lyxvc().undoLast();
1088 // --- buffers ----------------------------------------
1089 case LFUN_BUFFER_SWITCH:
1090 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1093 case LFUN_BUFFER_NEXT:
1094 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1097 case LFUN_BUFFER_PREVIOUS:
1098 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1102 newFile(view(), argument);
1105 case LFUN_FILE_OPEN:
1109 case LFUN_DROP_LAYOUTS_CHOICE:
1110 lyx_view_->getToolbars().openLayoutList();
1113 case LFUN_MENU_OPEN:
1114 lyx_view_->getMenubar().openByName(lyx::from_utf8(argument));
1117 // --- lyxserver commands ----------------------------
1118 case LFUN_SERVER_GET_NAME:
1119 setMessage(lyx::from_utf8(lyx_view_->buffer()->fileName()));
1120 lyxerr[Debug::INFO] << "FNAME["
1121 << lyx_view_->buffer()->fileName()
1125 case LFUN_SERVER_NOTIFY:
1126 dispatch_buffer = lyx::from_utf8(keyseq->print());
1127 theLyXServer().notifyClient(lyx::to_utf8(dispatch_buffer));
1130 case LFUN_SERVER_GOTO_FILE_ROW: {
1133 istringstream is(argument);
1134 is >> file_name >> row;
1135 if (prefixIs(file_name, package().temp_dir())) {
1136 // Needed by inverse dvi search. If it is a file
1137 // in tmpdir, call the apropriated function
1138 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1140 // Must replace extension of the file to be .lyx
1141 // and get full path
1142 string const s = changeExtension(file_name, ".lyx");
1143 // Either change buffer or load the file
1144 if (theBufferList().exists(s)) {
1145 lyx_view_->setBuffer(theBufferList().getBuffer(s));
1147 lyx_view_->loadLyXFile(s);
1151 view()->setCursorFromRow(row);
1154 // see BufferView::center()
1158 case LFUN_DIALOG_SHOW: {
1159 string const name = cmd.getArg(0);
1160 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1162 if (name == "character") {
1163 data = freefont2string();
1165 lyx_view_->getDialogs().show("character", data);
1166 } else if (name == "latexlog") {
1167 pair<Buffer::LogType, string> const logfile =
1168 lyx_view_->buffer()->getLogName();
1169 switch (logfile.first) {
1170 case Buffer::latexlog:
1173 case Buffer::buildlog:
1177 data += LyXLex::quoteString(logfile.second);
1178 lyx_view_->getDialogs().show("log", data);
1179 } else if (name == "vclog") {
1180 string const data = "vc " +
1181 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1182 lyx_view_->getDialogs().show("log", data);
1184 lyx_view_->getDialogs().show(name, data);
1188 case LFUN_DIALOG_SHOW_NEW_INSET: {
1189 string const name = cmd.getArg(0);
1190 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1191 if (name == "bibitem" ||
1198 InsetCommandParams p(name);
1199 data = InsetCommandMailer::params2string(name, p);
1200 } else if (name == "include") {
1201 InsetCommandParams p(data);
1202 data = InsetIncludeMailer::params2string(p);
1203 } else if (name == "box") {
1204 // \c data == "Boxed" || "Frameless" etc
1205 InsetBoxParams p(data);
1206 data = InsetBoxMailer::params2string(p);
1207 } else if (name == "branch") {
1208 InsetBranchParams p;
1209 data = InsetBranchMailer::params2string(p);
1210 } else if (name == "citation") {
1211 InsetCommandParams p("cite");
1212 data = InsetCommandMailer::params2string(name, p);
1213 } else if (name == "ert") {
1214 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1215 } else if (name == "external") {
1216 InsetExternalParams p;
1217 Buffer const & buffer = *lyx_view_->buffer();
1218 data = InsetExternalMailer::params2string(p, buffer);
1219 } else if (name == "float") {
1221 data = InsetFloatMailer::params2string(p);
1222 } else if (name == "graphics") {
1223 InsetGraphicsParams p;
1224 Buffer const & buffer = *lyx_view_->buffer();
1225 data = InsetGraphicsMailer::params2string(p, buffer);
1226 } else if (name == "note") {
1228 data = InsetNoteMailer::params2string(p);
1229 } else if (name == "vspace") {
1231 data = InsetVSpaceMailer::params2string(space);
1232 } else if (name == "wrap") {
1234 data = InsetWrapMailer::params2string(p);
1236 lyx_view_->getDialogs().show(name, data, 0);
1240 case LFUN_DIALOG_UPDATE: {
1241 string const & name = argument;
1242 // Can only update a dialog connected to an existing inset
1243 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1245 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1246 inset->dispatch(view()->cursor(), fr);
1247 } else if (name == "paragraph") {
1248 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1249 } else if (name == "prefs") {
1250 lyx_view_->getDialogs().update(name, string());
1255 case LFUN_DIALOG_HIDE:
1256 Dialogs::hide(argument, 0);
1259 case LFUN_DIALOG_DISCONNECT_INSET:
1260 lyx_view_->getDialogs().disconnect(argument);
1264 case LFUN_CITATION_INSERT: {
1265 if (!argument.empty()) {
1266 // we can have one optional argument, delimited by '|'
1267 // citation-insert <key>|<text_before>
1268 // this should be enhanced to also support text_after
1269 // and citation style
1270 string arg = argument;
1272 if (contains(argument, "|")) {
1273 arg = token(argument, '|', 0);
1274 opt1 = '[' + token(argument, '|', 1) + ']';
1276 std::ostringstream os;
1277 os << "citation LatexCommand\n"
1278 << "\\cite" << opt1 << "{" << arg << "}\n"
1280 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1283 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1287 case LFUN_BUFFER_CHILD_OPEN: {
1288 string const filename =
1289 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1290 // FIXME Should use bformat
1291 setMessage(_("Opening child document ") +
1292 makeDisplayPath(filename) + "...");
1293 view()->savePosition(0);
1294 string const parentfilename = lyx_view_->buffer()->fileName();
1295 if (theBufferList().exists(filename))
1296 lyx_view_->setBuffer(theBufferList().getBuffer(filename));
1298 lyx_view_->loadLyXFile(filename);
1299 // Set the parent name of the child document.
1300 // This makes insertion of citations and references in the child work,
1301 // when the target is in the parent or another child document.
1302 lyx_view_->buffer()->setParentName(parentfilename);
1306 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1307 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1310 case LFUN_KEYMAP_OFF:
1311 lyx_view_->view()->getIntl().keyMapOn(false);
1314 case LFUN_KEYMAP_PRIMARY:
1315 lyx_view_->view()->getIntl().keyMapPrim();
1318 case LFUN_KEYMAP_SECONDARY:
1319 lyx_view_->view()->getIntl().keyMapSec();
1322 case LFUN_KEYMAP_TOGGLE:
1323 lyx_view_->view()->getIntl().toggleKeyMap();
1329 string rest = split(argument, countstr, ' ');
1330 istringstream is(countstr);
1333 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1334 for (int i = 0; i < count; ++i)
1335 dispatch(lyxaction.lookupFunc(rest));
1339 case LFUN_COMMAND_SEQUENCE: {
1340 // argument contains ';'-terminated commands
1341 string arg = argument;
1342 while (!arg.empty()) {
1344 arg = split(arg, first, ';');
1345 FuncRequest func(lyxaction.lookupFunc(first));
1346 func.origin = cmd.origin;
1352 case LFUN_PREFERENCES_SAVE: {
1353 lyx::support::Path p(package().user_support());
1354 lyxrc.write("preferences", false);
1358 case LFUN_SCREEN_FONT_UPDATE:
1359 // handle the screen font changes.
1360 lyxrc.set_font_norm_type();
1361 theFontLoader().update();
1362 // All visible buffers will need resize
1366 case LFUN_SET_COLOR: {
1368 string const x11_name = split(argument, lyx_name, ' ');
1369 if (lyx_name.empty() || x11_name.empty()) {
1370 setErrorMessage(_("Syntax: set-color <lyx_name>"
1375 bool const graphicsbg_changed =
1376 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1377 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1379 if (!lcolor.setColor(lyx_name, x11_name)) {
1381 bformat(_("Set-color \"%1$s\" failed "
1382 "- color is undefined or "
1383 "may not be redefined"),
1384 lyx::from_utf8(lyx_name)));
1388 theApp->updateColor(lcolor.getFromLyXName(lyx_name));
1390 if (graphicsbg_changed) {
1391 #ifdef WITH_WARNINGS
1392 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1395 lyx::graphics::GCache::get().changeDisplay(true);
1402 lyx_view_->message(lyx::from_utf8(argument));
1405 case LFUN_EXTERNAL_EDIT: {
1406 FuncRequest fr(action, argument);
1407 InsetExternal().dispatch(view()->cursor(), fr);
1411 case LFUN_GRAPHICS_EDIT: {
1412 FuncRequest fr(action, argument);
1413 InsetGraphics().dispatch(view()->cursor(), fr);
1417 case LFUN_INSET_APPLY: {
1418 string const name = cmd.getArg(0);
1419 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1421 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1422 inset->dispatch(view()->cursor(), fr);
1424 FuncRequest fr(LFUN_INSET_INSERT, argument);
1427 // ideally, the update flag should be set by the insets,
1428 // but this is not possible currently
1433 case LFUN_ALL_INSETS_TOGGLE: {
1435 string const name = split(argument, action, ' ');
1436 InsetBase::Code const inset_code =
1437 InsetBase::translate(name);
1439 LCursor & cur = view()->cursor();
1440 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1442 InsetBase & inset = lyx_view_->buffer()->inset();
1443 InsetIterator it = inset_iterator_begin(inset);
1444 InsetIterator const end = inset_iterator_end(inset);
1445 for (; it != end; ++it) {
1446 if (inset_code == InsetBase::NO_CODE
1447 || inset_code == it->lyxCode()) {
1448 LCursor tmpcur = cur;
1449 tmpcur.pushLeft(*it);
1450 it->dispatch(tmpcur, fr);
1457 case LFUN_BUFFER_LANGUAGE: {
1458 Buffer & buffer = *lyx_view_->buffer();
1459 Language const * oldL = buffer.params().language;
1460 Language const * newL = languages.getLanguage(argument);
1461 if (!newL || oldL == newL)
1464 if (oldL->rightToLeft() == newL->rightToLeft()
1465 && !buffer.isMultiLingual())
1466 buffer.changeLanguage(oldL, newL);
1468 buffer.updateDocLang(newL);
1472 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1473 string const fname =
1474 addName(addPath(package().user_support(), "templates/"),
1476 Buffer defaults(fname);
1478 istringstream ss(argument);
1481 int const unknown_tokens = defaults.readHeader(lex);
1483 if (unknown_tokens != 0) {
1484 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1485 << unknown_tokens << " unknown token"
1486 << (unknown_tokens == 1 ? "" : "s")
1490 if (defaults.writeFile(defaults.fileName()))
1491 // FIXME Should use bformat
1492 setMessage(_("Document defaults saved in ")
1493 + makeDisplayPath(fname));
1495 setErrorMessage(_("Unable to save document defaults"));
1499 case LFUN_BUFFER_PARAMS_APPLY: {
1500 biblio::CiteEngine const engine =
1501 lyx_view_->buffer()->params().cite_engine;
1503 istringstream ss(argument);
1506 int const unknown_tokens =
1507 lyx_view_->buffer()->readHeader(lex);
1509 if (unknown_tokens != 0) {
1510 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1511 << unknown_tokens << " unknown token"
1512 << (unknown_tokens == 1 ? "" : "s")
1515 if (engine == lyx_view_->buffer()->params().cite_engine)
1518 LCursor & cur = view()->cursor();
1519 FuncRequest fr(LFUN_INSET_REFRESH);
1521 InsetBase & inset = lyx_view_->buffer()->inset();
1522 InsetIterator it = inset_iterator_begin(inset);
1523 InsetIterator const end = inset_iterator_end(inset);
1524 for (; it != end; ++it)
1525 if (it->lyxCode() == InsetBase::CITE_CODE)
1526 it->dispatch(cur, fr);
1530 case LFUN_TEXTCLASS_APPLY: {
1531 Buffer * buffer = lyx_view_->buffer();
1533 lyx::textclass_type const old_class =
1534 buffer->params().textclass;
1536 loadTextclass(argument);
1538 std::pair<bool, lyx::textclass_type> const tc_pair =
1539 textclasslist.numberOfClass(argument);
1544 lyx::textclass_type const new_class = tc_pair.second;
1545 if (old_class == new_class)
1549 lyx_view_->message(_("Converting document to new document class..."));
1550 recordUndoFullDocument(view());
1551 buffer->params().textclass = new_class;
1552 StableDocIterator backcur(view()->cursor());
1553 ErrorList & el = buffer->errorList("Class Switch");
1554 lyx::cap::switchBetweenClasses(
1555 old_class, new_class,
1556 static_cast<InsetText &>(buffer->inset()), el);
1558 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1560 buffer->errors("Class Switch");
1561 updateLabels(*buffer);
1566 case LFUN_TEXTCLASS_LOAD:
1567 loadTextclass(argument);
1570 case LFUN_LYXRC_APPLY: {
1571 LyXRC const lyxrc_orig = lyxrc;
1573 istringstream ss(argument);
1574 bool const success = lyxrc.read(ss) == 0;
1577 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1578 << "Unable to read lyxrc data"
1583 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1588 view()->cursor().dispatch(cmd);
1589 updateforce |= view()->cursor().result().update();
1590 if (!view()->cursor().result().dispatched())
1591 updateforce |= view()->dispatch(cmd);
1596 if (view()->buffer()) {
1597 // Redraw screen unless explicitly told otherwise.
1598 // This also initializes the position cache for all insets
1599 // in (at least partially) visible top-level paragraphs.
1601 view()->update(Update::FitCursor | Update::Force);
1603 view()->update(Update::FitCursor);
1605 lyx_view_->redrawWorkArea();
1607 // if we executed a mutating lfun, mark the buffer as dirty
1609 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1610 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1611 view()->buffer()->markDirty();
1613 if (view()->cursor().inTexted()) {
1614 lyx_view_->updateLayoutChoice();
1619 // FIXME UNICODE: _() does not support anything but ascii.
1620 // Do we need a lyx::to_ascii() method?
1621 sendDispatchMessage(getMessage(), cmd);
1625 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1627 /* When an action did not originate from the UI/kbd, it makes
1628 * sense to avoid updating the GUI. It turns out that this
1629 * fixes bug 1941, for reasons that are described here:
1630 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1632 if (cmd.origin != FuncRequest::INTERNAL) {
1633 lyx_view_->updateMenubar();
1634 lyx_view_->updateToolbars();
1637 const bool verbose = (cmd.origin == FuncRequest::UI
1638 || cmd.origin == FuncRequest::COMMANDBUFFER);
1640 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1641 lyxerr[Debug::ACTION] << "dispatch msg is " << lyx::to_utf8(msg) << endl;
1643 lyx_view_->message(msg);
1647 docstring dispatch_msg = msg;
1648 if (!dispatch_msg.empty())
1649 dispatch_msg += ' ';
1651 string comname = lyxaction.getActionName(cmd.action);
1653 bool argsadded = false;
1655 if (!cmd.argument().empty()) {
1656 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1657 comname += ' ' + lyx::to_utf8(cmd.argument());
1662 string const shortcuts = theTopLevelKeymap().printbindings(cmd);
1664 if (!shortcuts.empty())
1665 comname += ": " + shortcuts;
1666 else if (!argsadded && !cmd.argument().empty())
1667 comname += ' ' + lyx::to_utf8(cmd.argument());
1669 if (!comname.empty()) {
1670 comname = rtrim(comname);
1671 dispatch_msg += lyx::from_utf8('(' + rtrim(comname) + ')');
1674 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1675 << lyx::to_utf8(dispatch_msg) << endl;
1676 if (!dispatch_msg.empty())
1677 lyx_view_->message(dispatch_msg);
1681 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1683 string initpath = lyxrc.document_path;
1684 string filename(name);
1686 if (view()->buffer()) {
1687 string const trypath = lyx_view_->buffer()->filePath();
1688 // If directory is writeable, use this as default.
1689 if (isDirWriteable(trypath))
1693 static int newfile_number;
1695 if (filename.empty()) {
1696 filename = addName(lyxrc.document_path,
1697 "newfile" + convert<string>(++newfile_number) + ".lyx");
1698 while (theBufferList().exists(filename) || fs::is_readable(filename)) {
1700 filename = addName(lyxrc.document_path,
1701 "newfile" + convert<string>(newfile_number) +
1706 // The template stuff
1709 FileDialog fileDlg(_("Select template file"),
1710 LFUN_SELECT_FILE_SYNC,
1711 make_pair(_("Documents|#o#O"), lyx::from_utf8(lyxrc.document_path)),
1712 make_pair(_("Templates|#T#t"), lyx::from_utf8(lyxrc.template_path)));
1714 FileDialog::Result result =
1715 fileDlg.open(lyx::from_utf8(lyxrc.template_path),
1716 FileFilterList(_("LyX Documents (*.lyx)")),
1719 if (result.first == FileDialog::Later)
1721 if (result.second.empty())
1723 templname = lyx::to_utf8(result.second);
1726 Buffer * const b = newFile(filename, templname, !name.empty());
1728 lyx_view_->setBuffer(b);
1732 void LyXFunc::open(string const & fname)
1734 string initpath = lyxrc.document_path;
1736 if (view()->buffer()) {
1737 string const trypath = lyx_view_->buffer()->filePath();
1738 // If directory is writeable, use this as default.
1739 if (isDirWriteable(trypath))
1745 if (fname.empty()) {
1746 FileDialog fileDlg(_("Select document to open"),
1748 make_pair(_("Documents|#o#O"), lyx::from_utf8(lyxrc.document_path)),
1749 make_pair(_("Examples|#E#e"), lyx::from_utf8(addPath(package().system_support(), "examples"))));
1751 FileDialog::Result result =
1752 fileDlg.open(lyx::from_utf8(initpath),
1753 FileFilterList(_("LyX Documents (*.lyx)")),
1756 if (result.first == FileDialog::Later)
1759 filename = lyx::to_utf8(result.second);
1761 // check selected filename
1762 if (filename.empty()) {
1763 lyx_view_->message(_("Canceled."));
1769 // get absolute path of file and add ".lyx" to the filename if
1771 string const fullpath = fileSearch(string(), filename, "lyx");
1772 if (!fullpath.empty()) {
1773 filename = fullpath;
1776 docstring const disp_fn = makeDisplayPath(filename);
1778 // if the file doesn't exist, let the user create one
1779 if (!fs::exists(filename)) {
1780 // the user specifically chose this name. Believe him.
1781 Buffer * const b = newFile(filename, string(), true);
1783 lyx_view_->setBuffer(b);
1787 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1790 if (lyx_view_->loadLyXFile(filename)) {
1791 str2 = bformat(_("Document %1$s opened."), disp_fn);
1793 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1795 lyx_view_->message(str2);
1799 void LyXFunc::doImport(string const & argument)
1802 string filename = split(argument, format, ' ');
1804 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1805 << " file: " << filename << endl;
1807 // need user interaction
1808 if (filename.empty()) {
1809 string initpath = lyxrc.document_path;
1811 if (view()->buffer()) {
1812 string const trypath = lyx_view_->buffer()->filePath();
1813 // If directory is writeable, use this as default.
1814 if (isDirWriteable(trypath))
1818 docstring const text = bformat(_("Select %1$s file to import"),
1819 formats.prettyName(format));
1821 FileDialog fileDlg(text,
1823 make_pair(_("Documents|#o#O"), lyx::from_utf8(lyxrc.document_path)),
1824 make_pair(_("Examples|#E#e"),
1825 lyx::from_utf8(addPath(package().system_support(), "examples"))));
1827 docstring filter = formats.prettyName(format);
1830 filter += lyx::from_utf8(formats.extension(format));
1833 FileDialog::Result result =
1834 fileDlg.open(lyx::from_utf8(initpath),
1835 FileFilterList(filter),
1838 if (result.first == FileDialog::Later)
1841 filename = lyx::to_utf8(result.second);
1843 // check selected filename
1844 if (filename.empty())
1845 lyx_view_->message(_("Canceled."));
1848 if (filename.empty())
1851 // get absolute path of file
1852 filename = makeAbsPath(filename);
1854 string const lyxfile = changeExtension(filename, ".lyx");
1856 // Check if the document already is open
1857 if (lyx::use_gui && theBufferList().exists(lyxfile)) {
1858 if (!theBufferList().close(theBufferList().getBuffer(lyxfile), true)) {
1859 lyx_view_->message(_("Canceled."));
1864 // if the file exists already, and we didn't do
1865 // -i lyx thefile.lyx, warn
1866 if (fs::exists(lyxfile) && filename != lyxfile) {
1867 docstring const file = makeDisplayPath(lyxfile, 30);
1869 docstring text = bformat(_("The document %1$s already exists.\n\n"
1870 "Do you want to over-write that document?"), file);
1871 int const ret = Alert::prompt(_("Over-write document?"),
1872 text, 0, 1, _("&Over-write"), _("&Cancel"));
1875 lyx_view_->message(_("Canceled."));
1880 ErrorList errorList;
1881 Importer::Import(lyx_view_, filename, format, errorList);
1882 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1886 void LyXFunc::closeBuffer()
1888 // save current cursor position
1889 LyX::ref().session().saveFilePosition(lyx_view_->buffer()->fileName(),
1890 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1891 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
1892 if (theBufferList().empty()) {
1893 // need this otherwise SEGV may occur while
1894 // trying to set variables that don't exist
1895 // since there's no current buffer
1896 lyx_view_->getDialogs().hideBufferDependent();
1898 lyx_view_->setBuffer(theBufferList().first());
1904 // Each "lyx_view_" should have it's own message method. lyxview and
1905 // the minibuffer would use the minibuffer, but lyxserver would
1906 // send an ERROR signal to its client. Alejandro 970603
1907 // This function is bit problematic when it comes to NLS, to make the
1908 // lyx servers client be language indepenent we must not translate
1909 // strings sent to this func.
1910 void LyXFunc::setErrorMessage(docstring const & m) const
1912 dispatch_buffer = m;
1917 void LyXFunc::setMessage(docstring const & m) const
1919 dispatch_buffer = m;
1923 string const LyXFunc::viewStatusMessage()
1925 // When meta-fake key is pressed, show the key sequence so far + "M-".
1927 return keyseq->print() + "M-";
1929 // Else, when a non-complete key sequence is pressed,
1930 // show the available options.
1931 if (keyseq->length() > 0 && !keyseq->deleted())
1932 return keyseq->printOptions();
1934 if (!view()->buffer())
1935 return lyx::to_utf8(_("Welcome to LyX!"));
1937 return view()->cursor().currentState();
1941 BufferView * LyXFunc::view() const
1943 BOOST_ASSERT(lyx_view_);
1944 return lyx_view_->view();
1948 bool LyXFunc::wasMetaKey() const
1950 return (meta_fake_bit != key_modifier::none);
1956 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1958 // Why the switch you might ask. It is a trick to ensure that all
1959 // the elements in the LyXRCTags enum is handled. As you can see
1960 // there are no breaks at all. So it is just a huge fall-through.
1961 // The nice thing is that we will get a warning from the compiler
1962 // if we forget an element.
1963 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1965 case LyXRC::RC_ACCEPT_COMPOUND:
1966 case LyXRC::RC_ALT_LANG:
1967 case LyXRC::RC_ASCIIROFF_COMMAND:
1968 case LyXRC::RC_ASCII_LINELEN:
1969 case LyXRC::RC_AUTOREGIONDELETE:
1970 case LyXRC::RC_AUTORESET_OPTIONS:
1971 case LyXRC::RC_AUTOSAVE:
1972 case LyXRC::RC_AUTO_NUMBER:
1973 case LyXRC::RC_BACKUPDIR_PATH:
1974 case LyXRC::RC_BIBTEX_COMMAND:
1975 case LyXRC::RC_BINDFILE:
1976 case LyXRC::RC_CHECKLASTFILES:
1977 case LyXRC::RC_USELASTFILEPOS:
1978 case LyXRC::RC_LOADSESSION:
1979 case LyXRC::RC_CHKTEX_COMMAND:
1980 case LyXRC::RC_CONVERTER:
1981 case LyXRC::RC_COPIER:
1982 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1983 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1984 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1985 case LyXRC::RC_DATE_INSERT_FORMAT:
1986 case LyXRC::RC_DEFAULT_LANGUAGE:
1987 case LyXRC::RC_DEFAULT_PAPERSIZE:
1988 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1989 case LyXRC::RC_DISPLAY_GRAPHICS:
1990 case LyXRC::RC_DOCUMENTPATH:
1991 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1992 if (fs::exists(lyxrc_new.document_path) &&
1993 fs::is_directory(lyxrc_new.document_path)) {
1994 using lyx::support::package;
1995 package().document_dir() = lyxrc.document_path;
1998 case LyXRC::RC_ESC_CHARS:
1999 case LyXRC::RC_FONT_ENCODING:
2000 case LyXRC::RC_FORMAT:
2001 case LyXRC::RC_INDEX_COMMAND:
2002 case LyXRC::RC_INPUT:
2003 case LyXRC::RC_KBMAP:
2004 case LyXRC::RC_KBMAP_PRIMARY:
2005 case LyXRC::RC_KBMAP_SECONDARY:
2006 case LyXRC::RC_LABEL_INIT_LENGTH:
2007 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2008 case LyXRC::RC_LANGUAGE_AUTO_END:
2009 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2010 case LyXRC::RC_LANGUAGE_COMMAND_END:
2011 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2012 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2013 case LyXRC::RC_LANGUAGE_PACKAGE:
2014 case LyXRC::RC_LANGUAGE_USE_BABEL:
2015 case LyXRC::RC_MAKE_BACKUP:
2016 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2017 case LyXRC::RC_NUMLASTFILES:
2018 case LyXRC::RC_PATH_PREFIX:
2019 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2020 using lyx::support::prependEnvPath;
2021 prependEnvPath("PATH", lyxrc.path_prefix);
2023 case LyXRC::RC_PERS_DICT:
2024 case LyXRC::RC_POPUP_BOLD_FONT:
2025 case LyXRC::RC_POPUP_FONT_ENCODING:
2026 case LyXRC::RC_POPUP_NORMAL_FONT:
2027 case LyXRC::RC_PREVIEW:
2028 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2029 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2030 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2031 case LyXRC::RC_PRINTCOPIESFLAG:
2032 case LyXRC::RC_PRINTER:
2033 case LyXRC::RC_PRINTEVENPAGEFLAG:
2034 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2035 case LyXRC::RC_PRINTFILEEXTENSION:
2036 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2037 case LyXRC::RC_PRINTODDPAGEFLAG:
2038 case LyXRC::RC_PRINTPAGERANGEFLAG:
2039 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2040 case LyXRC::RC_PRINTPAPERFLAG:
2041 case LyXRC::RC_PRINTREVERSEFLAG:
2042 case LyXRC::RC_PRINTSPOOL_COMMAND:
2043 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2044 case LyXRC::RC_PRINTTOFILE:
2045 case LyXRC::RC_PRINTTOPRINTER:
2046 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2047 case LyXRC::RC_PRINT_COMMAND:
2048 case LyXRC::RC_RTL_SUPPORT:
2049 case LyXRC::RC_SCREEN_DPI:
2050 case LyXRC::RC_SCREEN_FONT_ENCODING:
2051 case LyXRC::RC_SCREEN_FONT_ROMAN:
2052 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2053 case LyXRC::RC_SCREEN_FONT_SANS:
2054 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2055 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2056 case LyXRC::RC_SCREEN_FONT_SIZES:
2057 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2058 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2059 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2060 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2061 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2062 case LyXRC::RC_SCREEN_ZOOM:
2063 case LyXRC::RC_SERVERPIPE:
2064 case LyXRC::RC_SET_COLOR:
2065 case LyXRC::RC_SHOW_BANNER:
2066 case LyXRC::RC_SPELL_COMMAND:
2067 case LyXRC::RC_TEMPDIRPATH:
2068 case LyXRC::RC_TEMPLATEPATH:
2069 case LyXRC::RC_TEX_ALLOWS_SPACES:
2070 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2071 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2072 namespace os = lyx::support::os;
2073 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2075 case LyXRC::RC_UIFILE:
2076 case LyXRC::RC_USER_EMAIL:
2077 case LyXRC::RC_USER_NAME:
2078 case LyXRC::RC_USETEMPDIR:
2079 case LyXRC::RC_USE_ALT_LANG:
2080 case LyXRC::RC_USE_ESC_CHARS:
2081 case LyXRC::RC_USE_INP_ENC:
2082 case LyXRC::RC_USE_PERS_DICT:
2083 case LyXRC::RC_USE_SPELL_LIB:
2084 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2085 case LyXRC::RC_VIEWER:
2086 case LyXRC::RC_LAST: