3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
9 * \author Angus Leeming
11 * \author André Pönitz
14 * \author Martin Vermeer
15 * \author Jürgen Vigna
17 * Full author contact details are available in file CREDITS.
24 #include "BranchList.h"
26 #include "buffer_funcs.h"
27 #include "bufferlist.h"
28 #include "bufferparams.h"
29 #include "BufferView.h"
31 #include "CutAndPaste.h"
33 #include "dispatchresult.h"
35 #include "errorlist.h"
38 #include "funcrequest.h"
41 #include "insetiterator.h"
49 #include "LyXAction.h"
54 #include "lyxserver.h"
55 #include "lyxtextclasslist.h"
57 #include "paragraph.h"
58 #include "pariterator.h"
59 #include "ParagraphParameters.h"
62 #include "insets/insetbox.h"
63 #include "insets/insetbranch.h"
64 #include "insets/insetcommand.h"
65 #include "insets/insetert.h"
66 #include "insets/insetexternal.h"
67 #include "insets/insetfloat.h"
68 #include "insets/insetgraphics.h"
69 #include "insets/insetinclude.h"
70 #include "insets/insetnote.h"
71 #include "insets/insettabular.h"
72 #include "insets/insetvspace.h"
73 #include "insets/insetwrap.h"
75 #include "frontends/Alert.h"
76 #include "frontends/Dialogs.h"
77 #include "frontends/FileDialog.h"
78 #include "frontends/lyx_gui.h"
79 #include "frontends/LyXKeySym.h"
80 #include "frontends/LyXView.h"
81 #include "frontends/Menubar.h"
82 #include "frontends/Toolbars.h"
84 #include "support/environment.h"
85 #include "support/filefilterlist.h"
86 #include "support/filetools.h"
87 #include "support/forkedcontr.h"
88 #include "support/fs_extras.h"
89 #include "support/lstrings.h"
90 #include "support/path.h"
91 #include "support/package.h"
92 #include "support/systemcall.h"
93 #include "support/convert.h"
94 #include "support/os.h"
96 #include <boost/current_function.hpp>
97 #include <boost/filesystem/operations.hpp>
101 using bv_funcs::freefont2string;
103 using lyx::support::absolutePath;
104 using lyx::support::addName;
105 using lyx::support::addPath;
106 using lyx::support::bformat;
107 using lyx::support::changeExtension;
108 using lyx::support::contains;
109 using lyx::support::FileFilterList;
110 using lyx::support::fileSearch;
111 using lyx::support::ForkedcallsController;
112 using lyx::support::i18nLibFileSearch;
113 using lyx::support::isDirWriteable;
114 using lyx::support::isFileReadable;
115 using lyx::support::isStrInt;
116 using lyx::support::makeAbsPath;
117 using lyx::support::makeDisplayPath;
118 using lyx::support::package;
119 using lyx::support::Path;
120 using lyx::support::quoteName;
121 using lyx::support::rtrim;
122 using lyx::support::split;
123 using lyx::support::subst;
124 using lyx::support::Systemcall;
125 using lyx::support::token;
126 using lyx::support::trim;
127 using lyx::support::prefixIs;
130 using std::make_pair;
133 using std::istringstream;
134 using std::ostringstream;
136 namespace biblio = lyx::biblio;
137 namespace fs = boost::filesystem;
140 extern BufferList bufferlist;
141 extern LyXServer * lyxserver;
143 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
146 extern tex_accent_struct get_accent(kb_action action);
151 bool getStatus(LCursor cursor,
152 FuncRequest const & cmd, FuncStatus & status)
154 // Try to fix cursor in case it is broken.
155 cursor.fixIfBroken();
157 // This is, of course, a mess. Better create a new doc iterator and use
158 // this in Inset::getStatus. This might require an additional
159 // BufferView * arg, though (which should be avoided)
160 //LCursor safe = *this;
162 for ( ; cursor.depth(); cursor.pop()) {
163 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
164 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
165 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
166 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
168 // The inset's getStatus() will return 'true' if it made
169 // a definitive decision on whether it want to handle the
170 // request or not. The result of this decision is put into
171 // the 'status' parameter.
172 if (cursor.inset().getStatus(cursor, cmd, status)) {
181 /** Return the change status at cursor position, taking in account the
182 * status at each level of the document iterator (a table in a deleted
183 * footnote is deleted).
184 * When \param outer is true, the top slice is not looked at.
186 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
188 size_t const depth = dit.depth() - (outer ? 1 : 0);
190 for (size_t i = 0 ; i < depth ; ++i) {
191 CursorSlice const & slice = dit[i];
192 if (!slice.inset().inMathed()
193 && slice.pos() < slice.paragraph().size()) {
194 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
195 if (ch != Change::UNCHANGED)
199 return Change::UNCHANGED;
204 LyXFunc::LyXFunc(LyXView * lv)
207 keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
208 cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
209 meta_fake_bit(key_modifier::none)
214 void LyXFunc::handleKeyFunc(kb_action action)
216 char c = encoded_last_key;
218 if (keyseq.length()) {
222 owner->getIntl().getTransManager()
223 .deadkey(c, get_accent(action).accent, view()->getLyXText());
224 // Need to clear, in case the minibuffer calls these
227 // copied verbatim from do_accent_char
228 view()->cursor().resetAnchor();
233 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
235 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
237 // Do nothing if we have nothing (JMarc)
238 if (!keysym->isOK()) {
239 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
244 if (keysym->isModifier()) {
245 lyxerr[Debug::KEY] << "isModifier true" << endl;
249 Encoding const * encoding = view()->cursor().getEncoding();
251 encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
253 // Do a one-deep top-level lookup for
254 // cancel and meta-fake keys. RVDK_PATCH_5
255 cancel_meta_seq.reset();
257 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
258 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
259 << " action first set to [" << func.action << ']'
262 // When not cancel or meta-fake, do the normal lookup.
263 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
264 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
265 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
266 // remove Caps Lock and Mod2 as a modifiers
267 func = keyseq.addkey(keysym, (state | meta_fake_bit));
268 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
269 << "action now set to ["
270 << func.action << ']' << endl;
273 // Dont remove this unless you know what you are doing.
274 meta_fake_bit = key_modifier::none;
276 // Can this happen now ?
277 if (func.action == LFUN_NOACTION) {
278 func = FuncRequest(LFUN_COMMAND_PREFIX);
281 if (lyxerr.debugging(Debug::KEY)) {
282 lyxerr << BOOST_CURRENT_FUNCTION
284 << func.action << "]["
285 << keyseq.print() << ']'
289 // already here we know if it any point in going further
290 // why not return already here if action == -1 and
291 // num_bytes == 0? (Lgb)
293 if (keyseq.length() > 1) {
294 owner->message(keyseq.print());
298 // Maybe user can only reach the key via holding down shift.
299 // Let's see. But only if shift is the only modifier
300 if (func.action == LFUN_UNKNOWN_ACTION &&
301 state == key_modifier::shift) {
302 lyxerr[Debug::KEY] << "Trying without shift" << endl;
303 func = keyseq.addkey(keysym, key_modifier::none);
304 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
307 if (func.action == LFUN_UNKNOWN_ACTION) {
308 // Hmm, we didn't match any of the keysequences. See
309 // if it's normal insertable text not already covered
311 if (keysym->isText() && keyseq.length() == 1) {
312 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
313 func = FuncRequest(LFUN_SELF_INSERT,
314 FuncRequest::KEYBOARD);
316 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
317 owner->message(_("Unknown function."));
322 if (func.action == LFUN_SELF_INSERT) {
323 if (encoded_last_key != 0) {
324 string const arg(1, encoded_last_key);
325 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
326 FuncRequest::KEYBOARD));
328 << "SelfInsert arg[`" << arg << "']" << endl;
336 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
338 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
340 LCursor & cur = view()->cursor();
342 /* In LyX/Mac, when a dialog is open, the menus of the
343 application can still be accessed without giving focus to
344 the main window. In this case, we want to disable the menu
345 entries that are buffer-related.
347 Note that this code is not perfect, as bug 1941 attests:
348 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
351 if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
354 buf = owner->buffer();
356 if (cmd.action == LFUN_NOACTION) {
357 flag.message(N_("Nothing to do"));
362 switch (cmd.action) {
363 case LFUN_UNKNOWN_ACTION:
364 #ifndef HAVE_LIBAIKSAURUS
365 case LFUN_THESAURUS_ENTRY:
371 flag |= lyx_gui::getStatus(cmd);
374 if (flag.unknown()) {
375 flag.message(N_("Unknown action"));
379 if (!flag.enabled()) {
380 if (flag.message().empty())
381 flag.message(N_("Command disabled"));
385 // Check whether we need a buffer
386 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
388 flag.message(N_("Command not allowed with"
389 "out any document open"));
394 // I would really like to avoid having this switch and rather try to
395 // encode this in the function itself.
396 // -- And I'd rather let an inset decide which LFUNs it is willing
397 // to handle (Andre')
399 switch (cmd.action) {
400 case LFUN_TOOLTIPS_TOGGLE:
401 flag.setOnOff(owner->getDialogs().tooltipsEnabled());
404 case LFUN_BUFFER_TOGGLE_READ_ONLY:
405 flag.setOnOff(buf->isReadonly());
408 case LFUN_BUFFER_SWITCH:
409 // toggle on the current buffer, but do not toggle off
410 // the other ones (is that a good idea?)
411 if (cmd.argument == buf->fileName())
415 case LFUN_BUFFER_EXPORT:
416 enable = cmd.argument == "custom"
417 || Exporter::isExportable(*buf, cmd.argument);
420 case LFUN_BUFFER_CHKTEX:
421 enable = buf->isLatex() && lyxrc.chktex_command != "none";
424 case LFUN_BUILD_PROGRAM:
425 enable = Exporter::isExportable(*buf, "program");
428 case LFUN_LAYOUT_TABULAR:
429 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
433 case LFUN_LAYOUT_PARAGRAPH:
434 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
437 case LFUN_VC_REGISTER:
438 enable = !buf->lyxvc().inUse();
440 case LFUN_VC_CHECK_IN:
441 enable = buf->lyxvc().inUse() && !buf->isReadonly();
443 case LFUN_VC_CHECK_OUT:
444 enable = buf->lyxvc().inUse() && buf->isReadonly();
447 case LFUN_VC_UNDO_LAST:
448 enable = buf->lyxvc().inUse();
450 case LFUN_BUFFER_RELOAD:
451 enable = !buf->isUnnamed() && !buf->isClean();
454 case LFUN_INSET_SETTINGS: {
458 InsetBase::Code code = cur.inset().lyxCode();
460 case InsetBase::TABULAR_CODE:
461 enable = cmd.argument == "tabular";
463 case InsetBase::ERT_CODE:
464 enable = cmd.argument == "ert";
466 case InsetBase::FLOAT_CODE:
467 enable = cmd.argument == "float";
469 case InsetBase::WRAP_CODE:
470 enable = cmd.argument == "wrap";
472 case InsetBase::NOTE_CODE:
473 enable = cmd.argument == "note";
475 case InsetBase::BRANCH_CODE:
476 enable = cmd.argument == "branch";
478 case InsetBase::BOX_CODE:
479 enable = cmd.argument == "box";
487 case LFUN_INSET_APPLY: {
488 string const name = cmd.getArg(0);
489 InsetBase * inset = owner->getDialogs().getOpenInset(name);
491 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument);
493 bool const success = inset->getStatus(cur, fr, fs);
494 // Every inset is supposed to handle this
495 BOOST_ASSERT(success);
498 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument);
499 flag |= getStatus(fr);
501 enable = flag.enabled();
505 case LFUN_DIALOG_SHOW: {
506 string const name = cmd.getArg(0);
508 enable = name == "aboutlyx"
512 || name == "texinfo";
513 else if (name == "print")
514 enable = Exporter::isExportable(*buf, "dvi")
515 && lyxrc.print_command != "none";
516 else if (name == "character" || name == "mathpanel")
517 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
518 else if (name == "latexlog")
519 enable = isFileReadable(buf->getLogName().second);
520 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
521 else if (name == "spellchecker")
524 else if (name == "vclog")
525 enable = buf->lyxvc().inUse();
526 else if (name == "view-source")
531 case LFUN_DIALOG_SHOW_NEW_INSET:
532 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
535 case LFUN_DIALOG_UPDATE: {
536 string const name = cmd.getArg(0);
538 enable = name == "prefs";
542 case LFUN_CITATION_INSERT: {
543 FuncRequest fr(LFUN_INSET_INSERT, "citation");
544 enable = getStatus(fr).enabled();
548 case LFUN_BUFFER_WRITE: {
549 enable = view()->buffer()->isUnnamed()
550 || !view()->buffer()->isClean();
554 // this one is difficult to get right. As a half-baked
555 // solution, we consider only the first action of the sequence
556 case LFUN_COMMAND_SEQUENCE: {
557 // argument contains ';'-terminated commands
558 string const firstcmd = token(cmd.argument, ';', 0);
559 FuncRequest func(lyxaction.lookupFunc(firstcmd));
560 func.origin = cmd.origin;
561 flag = getStatus(func);
564 case LFUN_BUFFER_NEW:
565 case LFUN_BUFFER_NEW_TEMPLATE:
566 case LFUN_WORD_FIND_FORWARD:
567 case LFUN_WORD_FIND_BACKWARD:
568 case LFUN_COMMAND_PREFIX:
569 case LFUN_COMMAND_EXECUTE:
571 case LFUN_META_PREFIX:
572 case LFUN_BUFFER_CLOSE:
573 case LFUN_BUFFER_WRITE_AS:
574 case LFUN_BUFFER_UPDATE:
575 case LFUN_BUFFER_VIEW:
576 case LFUN_BUFFER_IMPORT:
579 case LFUN_BUFFER_AUTO_SAVE:
580 case LFUN_RECONFIGURE:
584 case LFUN_DROP_LAYOUTS_CHOICE:
586 case LFUN_SERVER_GET_NAME:
587 case LFUN_SERVER_NOTIFY:
588 case LFUN_SERVER_GOTO_FILE_ROW:
589 case LFUN_DIALOG_SHOW_NEXT_INSET:
590 case LFUN_DIALOG_HIDE:
591 case LFUN_DIALOG_DISCONNECT_INSET:
592 case LFUN_BUFFER_CHILD_OPEN:
593 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
594 case LFUN_KEYMAP_OFF:
595 case LFUN_KEYMAP_PRIMARY:
596 case LFUN_KEYMAP_SECONDARY:
597 case LFUN_KEYMAP_TOGGLE:
599 case LFUN_BUFFER_EXPORT_CUSTOM:
600 case LFUN_BUFFER_PRINT:
601 case LFUN_PREFERENCES_SAVE:
602 case LFUN_SCREEN_FONT_UPDATE:
605 case LFUN_EXTERNAL_EDIT:
606 case LFUN_GRAPHICS_EDIT:
607 case LFUN_ALL_INSETS_TOGGLE:
608 case LFUN_BUFFER_LANGUAGE:
609 case LFUN_TEXTCLASS_APPLY:
610 case LFUN_TEXTCLASS_LOAD:
611 case LFUN_BUFFER_SAVE_AS_DEFAULT:
612 case LFUN_BUFFER_PARAMS_APPLY:
613 case LFUN_LYXRC_APPLY:
614 case LFUN_BUFFER_NEXT:
615 case LFUN_BUFFER_PREVIOUS:
616 // these are handled in our dispatch()
621 if (!::getStatus(cur, cmd, flag))
622 flag = view()->getStatus(cmd);
628 // Can we use a readonly buffer?
629 if (buf && buf->isReadonly()
630 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
631 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
632 flag.message(N_("Document is read-only"));
636 // Are we in a DELETED change-tracking region?
637 if (buf && buf->params().tracking_changes
638 && lookupChangeType(cur, true) == Change::DELETED
639 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
640 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
641 flag.message(N_("This portion of the document is deleted."));
645 // the default error message if we disable the command
646 if (!flag.enabled() && flag.message().empty())
647 flag.message(N_("Command disabled"));
655 bool ensureBufferClean(BufferView * bv)
657 Buffer & buf = *bv->buffer();
661 string const file = makeDisplayPath(buf.fileName(), 30);
662 string text = bformat(_("The document %1$s has unsaved "
663 "changes.\n\nDo you want to save "
664 "the document?"), file);
665 int const ret = Alert::prompt(_("Save changed document?"),
666 text, 0, 1, _("&Save"),
670 bv->owner()->dispatch(FuncRequest(LFUN_BUFFER_WRITE));
672 return buf.isClean();
676 void showPrintError(string const & name)
678 string str = bformat(_("Could not print the document %1$s.\n"
679 "Check that your printer is set up correctly."),
680 makeDisplayPath(name, 50));
681 Alert::error(_("Print document failed"), str);
685 void loadTextclass(string const & name)
687 std::pair<bool, lyx::textclass_type> const tc_pair =
688 textclasslist.numberOfClass(name);
690 if (!tc_pair.first) {
691 lyxerr << "Document class \"" << name
692 << "\" does not exist."
697 lyx::textclass_type const tc = tc_pair.second;
699 if (!textclasslist[tc].load()) {
700 string s = bformat(_("The document could not be converted\n"
701 "into the document class %1$s."),
702 textclasslist[tc].name());
703 Alert::error(_("Could not change class"), s);
708 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
713 void LyXFunc::dispatch(FuncRequest const & cmd)
715 BOOST_ASSERT(view());
716 string const argument = cmd.argument;
717 kb_action const action = cmd.action;
719 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
720 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
722 // we have not done anything wrong yet.
724 dispatch_buffer.erase();
726 // redraw the screen at the end (first of the two drawing steps).
727 //This is done unless explicitely requested otherwise
729 // also do the second redrawing step. Only done if requested.
730 bool updateforce = false;
732 FuncStatus const flag = getStatus(cmd);
733 if (!flag.enabled()) {
734 // We cannot use this function here
735 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
736 << lyxaction.getActionName(action)
737 << " [" << action << "] is disabled at this location"
739 setErrorMessage(flag.message());
743 case LFUN_WORD_FIND_FORWARD:
744 case LFUN_WORD_FIND_BACKWARD: {
745 static string last_search;
746 string searched_string;
748 if (!argument.empty()) {
749 last_search = argument;
750 searched_string = argument;
752 searched_string = last_search;
755 if (searched_string.empty())
758 bool const fw = action == LFUN_WORD_FIND_FORWARD;
760 lyx::find::find2string(searched_string, true, false, fw);
761 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
765 case LFUN_COMMAND_PREFIX:
766 owner->message(keyseq.printOptions());
769 case LFUN_COMMAND_EXECUTE:
770 owner->getToolbars().display("minibuffer", true);
771 owner->focus_command_buffer();
776 meta_fake_bit = key_modifier::none;
777 if (view()->available())
778 // cancel any selection
779 dispatch(FuncRequest(LFUN_MARK_OFF));
780 setMessage(N_("Cancel"));
783 case LFUN_META_PREFIX:
784 meta_fake_bit = key_modifier::alt;
785 setMessage(keyseq.print());
788 case LFUN_BUFFER_TOGGLE_READ_ONLY:
789 if (owner->buffer()->lyxvc().inUse())
790 owner->buffer()->lyxvc().toggleReadOnly();
792 owner->buffer()->setReadonly(
793 !owner->buffer()->isReadonly());
796 // --- Menus -----------------------------------------------
797 case LFUN_BUFFER_NEW:
798 menuNew(argument, false);
801 case LFUN_BUFFER_NEW_TEMPLATE:
802 menuNew(argument, true);
805 case LFUN_BUFFER_CLOSE:
809 case LFUN_BUFFER_WRITE:
810 if (!owner->buffer()->isUnnamed()) {
811 string const str = bformat(_("Saving document %1$s..."),
812 makeDisplayPath(owner->buffer()->fileName()));
814 menuWrite(owner->buffer());
815 owner->message(str + _(" done."));
817 writeAs(owner->buffer());
821 case LFUN_BUFFER_WRITE_AS:
822 writeAs(owner->buffer(), argument);
826 case LFUN_BUFFER_RELOAD: {
827 string const file = makeDisplayPath(view()->buffer()->fileName(), 20);
828 string text = bformat(_("Any changes will be lost. Are you sure "
829 "you want to revert to the saved version of the document %1$s?"), file);
830 int const ret = Alert::prompt(_("Revert to saved document?"),
831 text, 0, 1, _("&Revert"), _("&Cancel"));
838 case LFUN_BUFFER_UPDATE:
839 Exporter::Export(owner->buffer(), argument, true);
840 owner->showErrorList(bufferFormat(*owner->buffer()));
843 case LFUN_BUFFER_VIEW:
844 Exporter::preview(owner->buffer(), argument);
845 owner->showErrorList(bufferFormat(*owner->buffer()));
848 case LFUN_BUILD_PROGRAM:
849 Exporter::Export(owner->buffer(), "program", true);
850 owner->showErrorList(_("Build"));
853 case LFUN_BUFFER_CHKTEX:
854 owner->buffer()->runChktex();
855 owner->showErrorList(_("ChkTeX"));
858 case LFUN_BUFFER_EXPORT:
859 if (argument == "custom")
860 owner->getDialogs().show("sendto");
862 Exporter::Export(owner->buffer(), argument, false);
863 owner->showErrorList(bufferFormat(*owner->buffer()));
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 = owner->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,
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 = owner->buffer();
931 if (!Exporter::Export(buffer, "dvi", true)) {
932 showPrintError(buffer->fileName());
936 // Push directory path.
937 string const path = buffer->temppath();
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()->available()) {
1007 // save cursor Position for opened files to .lyx/session
1008 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1009 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1010 // save bookmarks to .lyx/session
1011 view()->saveSavedPositions();
1013 quitLyX(argument == "force");
1016 case LFUN_TOC_VIEW: {
1017 InsetCommandParams p("tableofcontents");
1018 string const data = InsetCommandMailer::params2string("toc", p);
1019 owner->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(N_("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 owner->message(bformat(_("Opening help file %1$s..."),
1044 makeDisplayPath(fname)));
1045 owner->loadLyXFile(fname, false);
1049 // --- version control -------------------------------
1050 case LFUN_VC_REGISTER:
1051 if (!ensureBufferClean(view()))
1053 if (!owner->buffer()->lyxvc().inUse()) {
1054 owner->buffer()->lyxvc().registrer();
1059 case LFUN_VC_CHECK_IN:
1060 if (!ensureBufferClean(view()))
1062 if (owner->buffer()->lyxvc().inUse()
1063 && !owner->buffer()->isReadonly()) {
1064 owner->buffer()->lyxvc().checkIn();
1069 case LFUN_VC_CHECK_OUT:
1070 if (!ensureBufferClean(view()))
1072 if (owner->buffer()->lyxvc().inUse()
1073 && owner->buffer()->isReadonly()) {
1074 owner->buffer()->lyxvc().checkOut();
1079 case LFUN_VC_REVERT:
1080 owner->buffer()->lyxvc().revert();
1084 case LFUN_VC_UNDO_LAST:
1085 owner->buffer()->lyxvc().undoLast();
1089 // --- buffers ----------------------------------------
1090 case LFUN_BUFFER_SWITCH:
1091 owner->setBuffer(bufferlist.getBuffer(argument));
1094 case LFUN_BUFFER_NEXT:
1095 owner->setBuffer(bufferlist.next(view()->buffer()));
1098 case LFUN_BUFFER_PREVIOUS:
1099 owner->setBuffer(bufferlist.previous(view()->buffer()));
1103 newFile(view(), argument);
1106 case LFUN_FILE_OPEN:
1110 case LFUN_DROP_LAYOUTS_CHOICE:
1111 owner->getToolbars().openLayoutList();
1114 case LFUN_MENU_OPEN:
1115 owner->getMenubar().openByName(argument);
1118 // --- lyxserver commands ----------------------------
1119 case LFUN_SERVER_GET_NAME:
1120 setMessage(owner->buffer()->fileName());
1121 lyxerr[Debug::INFO] << "FNAME["
1122 << owner->buffer()->fileName()
1126 case LFUN_SERVER_NOTIFY:
1127 dispatch_buffer = keyseq.print();
1128 lyxserver->notifyClient(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 owner->setBuffer(bufferlist.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 (bufferlist.exists(s)) {
1146 owner->setBuffer(bufferlist.getBuffer(s));
1148 owner->loadLyXFile(s);
1152 view()->setCursorFromRow(row);
1155 // see BufferView_pimpl::center()
1159 case LFUN_DIALOG_SHOW: {
1160 string const name = cmd.getArg(0);
1161 string data = trim(cmd.argument.substr(name.size()));
1163 if (name == "character") {
1164 data = freefont2string();
1166 owner->getDialogs().show("character", data);
1167 } else if (name == "latexlog") {
1168 pair<Buffer::LogType, string> const logfile =
1169 owner->buffer()->getLogName();
1170 switch (logfile.first) {
1171 case Buffer::latexlog:
1174 case Buffer::buildlog:
1178 data += LyXLex::quoteString(logfile.second);
1179 owner->getDialogs().show("log", data);
1180 } else if (name == "vclog") {
1181 string const data = "vc " +
1182 LyXLex::quoteString(owner->buffer()->lyxvc().getLogFile());
1183 owner->getDialogs().show("log", data);
1185 owner->getDialogs().show(name, data);
1189 case LFUN_DIALOG_SHOW_NEW_INSET: {
1190 string const name = cmd.getArg(0);
1191 string data = trim(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 = *owner->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 = *owner->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 owner->getDialogs().show(name, data, 0);
1241 case LFUN_DIALOG_SHOW_NEXT_INSET:
1244 case LFUN_DIALOG_UPDATE: {
1245 string const & name = argument;
1246 // Can only update a dialog connected to an existing inset
1247 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1249 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument);
1250 inset->dispatch(view()->cursor(), fr);
1251 } else if (name == "paragraph") {
1252 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1253 } else if (name == "prefs") {
1254 owner->getDialogs().update(name, string());
1259 case LFUN_DIALOG_HIDE:
1260 Dialogs::hide(argument, 0);
1263 case LFUN_DIALOG_DISCONNECT_INSET:
1264 owner->getDialogs().disconnect(argument);
1268 case LFUN_CITATION_INSERT: {
1269 if (!argument.empty()) {
1270 // we can have one optional argument, delimited by '|'
1271 // citation-insert <key>|<text_before>
1272 // this should be enhanced to also support text_after
1273 // and citation style
1274 string arg = argument;
1276 if (contains(argument, "|")) {
1277 arg = token(argument, '|', 0);
1278 opt1 = '[' + token(argument, '|', 1) + ']';
1280 std::ostringstream os;
1281 os << "citation LatexCommand\n"
1282 << "\\cite" << opt1 << "{" << arg << "}\n"
1284 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1287 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1291 case LFUN_BUFFER_CHILD_OPEN: {
1292 string const filename =
1293 makeAbsPath(argument, owner->buffer()->filePath());
1294 setMessage(N_("Opening child document ") +
1295 makeDisplayPath(filename) + "...");
1296 view()->savePosition(0);
1297 string const parentfilename = owner->buffer()->fileName();
1298 if (bufferlist.exists(filename))
1299 owner->setBuffer(bufferlist.getBuffer(filename));
1301 owner->loadLyXFile(filename);
1302 // Set the parent name of the child document.
1303 // This makes insertion of citations and references in the child work,
1304 // when the target is in the parent or another child document.
1305 owner->buffer()->setParentName(parentfilename);
1309 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1310 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1313 case LFUN_KEYMAP_OFF:
1314 owner->getIntl().keyMapOn(false);
1317 case LFUN_KEYMAP_PRIMARY:
1318 owner->getIntl().keyMapPrim();
1321 case LFUN_KEYMAP_SECONDARY:
1322 owner->getIntl().keyMapSec();
1325 case LFUN_KEYMAP_TOGGLE:
1326 owner->getIntl().toggleKeyMap();
1332 string rest = split(argument, countstr, ' ');
1333 istringstream is(countstr);
1336 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1337 for (int i = 0; i < count; ++i)
1338 dispatch(lyxaction.lookupFunc(rest));
1342 case LFUN_COMMAND_SEQUENCE: {
1343 // argument contains ';'-terminated commands
1344 string arg = argument;
1345 while (!arg.empty()) {
1347 arg = split(arg, first, ';');
1348 FuncRequest func(lyxaction.lookupFunc(first));
1349 func.origin = cmd.origin;
1355 case LFUN_PREFERENCES_SAVE: {
1356 Path p(package().user_support());
1357 lyxrc.write("preferences", false);
1361 case LFUN_SCREEN_FONT_UPDATE:
1362 // handle the screen font changes.
1363 lyxrc.set_font_norm_type();
1364 lyx_gui::update_fonts();
1365 // All visible buffers will need resize
1369 case LFUN_SET_COLOR: {
1371 string const x11_name = split(argument, lyx_name, ' ');
1372 if (lyx_name.empty() || x11_name.empty()) {
1373 setErrorMessage(N_("Syntax: set-color <lyx_name>"
1378 bool const graphicsbg_changed =
1379 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1380 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1382 if (!lcolor.setColor(lyx_name, x11_name)) {
1384 bformat(_("Set-color \"%1$s\" failed "
1385 "- color is undefined or "
1386 "may not be redefined"), lyx_name));
1390 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1392 if (graphicsbg_changed) {
1393 #ifdef WITH_WARNINGS
1394 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1397 lyx::graphics::GCache::get().changeDisplay(true);
1404 owner->message(argument);
1407 case LFUN_TOOLTIPS_TOGGLE:
1408 owner->getDialogs().toggleTooltips();
1411 case LFUN_EXTERNAL_EDIT: {
1412 FuncRequest fr(action, argument);
1413 InsetExternal().dispatch(view()->cursor(), fr);
1417 case LFUN_GRAPHICS_EDIT: {
1418 FuncRequest fr(action, argument);
1419 InsetGraphics().dispatch(view()->cursor(), fr);
1423 case LFUN_INSET_APPLY: {
1424 string const name = cmd.getArg(0);
1425 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1427 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1428 inset->dispatch(view()->cursor(), fr);
1430 FuncRequest fr(LFUN_INSET_INSERT, argument);
1433 // ideally, the update flag should be set by the insets,
1434 // but this is not possible currently
1439 case LFUN_ALL_INSETS_TOGGLE: {
1441 string const name = split(argument, action, ' ');
1442 InsetBase::Code const inset_code =
1443 InsetBase::translate(name);
1445 LCursor & cur = view()->cursor();
1446 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1448 InsetBase & inset = owner->buffer()->inset();
1449 InsetIterator it = inset_iterator_begin(inset);
1450 InsetIterator const end = inset_iterator_end(inset);
1451 for (; it != end; ++it) {
1452 if (inset_code == InsetBase::NO_CODE
1453 || inset_code == it->lyxCode()) {
1454 LCursor tmpcur = cur;
1455 tmpcur.pushLeft(*it);
1456 it->dispatch(tmpcur, fr);
1463 case LFUN_BUFFER_LANGUAGE: {
1464 Buffer & buffer = *owner->buffer();
1465 Language const * oldL = buffer.params().language;
1466 Language const * newL = languages.getLanguage(argument);
1467 if (!newL || oldL == newL)
1470 if (oldL->rightToLeft() == newL->rightToLeft()
1471 && !buffer.isMultiLingual())
1472 buffer.changeLanguage(oldL, newL);
1474 buffer.updateDocLang(newL);
1478 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1479 string const fname =
1480 addName(addPath(package().user_support(), "templates/"),
1482 Buffer defaults(fname);
1484 istringstream ss(argument);
1487 int const unknown_tokens = defaults.readHeader(lex);
1489 if (unknown_tokens != 0) {
1490 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1491 << unknown_tokens << " unknown token"
1492 << (unknown_tokens == 1 ? "" : "s")
1496 if (defaults.writeFile(defaults.fileName()))
1497 setMessage(_("Document defaults saved in ")
1498 + makeDisplayPath(fname));
1500 setErrorMessage(_("Unable to save document defaults"));
1504 case LFUN_BUFFER_PARAMS_APPLY: {
1505 biblio::CiteEngine const engine =
1506 owner->buffer()->params().cite_engine;
1508 istringstream ss(argument);
1511 int const unknown_tokens =
1512 owner->buffer()->readHeader(lex);
1514 if (unknown_tokens != 0) {
1515 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1516 << unknown_tokens << " unknown token"
1517 << (unknown_tokens == 1 ? "" : "s")
1520 if (engine == owner->buffer()->params().cite_engine)
1523 LCursor & cur = view()->cursor();
1524 FuncRequest fr(LFUN_INSET_REFRESH);
1526 InsetBase & inset = owner->buffer()->inset();
1527 InsetIterator it = inset_iterator_begin(inset);
1528 InsetIterator const end = inset_iterator_end(inset);
1529 for (; it != end; ++it)
1530 if (it->lyxCode() == InsetBase::CITE_CODE)
1531 it->dispatch(cur, fr);
1535 case LFUN_TEXTCLASS_APPLY: {
1536 Buffer * buffer = owner->buffer();
1538 lyx::textclass_type const old_class =
1539 buffer->params().textclass;
1541 loadTextclass(argument);
1543 std::pair<bool, lyx::textclass_type> const tc_pair =
1544 textclasslist.numberOfClass(argument);
1549 lyx::textclass_type const new_class = tc_pair.second;
1550 if (old_class == new_class)
1554 owner->message(_("Converting document to new document class..."));
1555 recordUndoFullDocument(view());
1556 buffer->params().textclass = new_class;
1557 StableDocIterator backcur(view()->cursor());
1559 lyx::cap::switchBetweenClasses(
1560 old_class, new_class,
1561 static_cast<InsetText &>(buffer->inset()), el);
1563 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1564 bufferErrors(*buffer, el);
1565 owner->showErrorList(_("Class switch"));
1566 updateLabels(*buffer);
1571 case LFUN_TEXTCLASS_LOAD:
1572 loadTextclass(argument);
1575 case LFUN_LYXRC_APPLY: {
1576 LyXRC const lyxrc_orig = lyxrc;
1578 istringstream ss(argument);
1579 bool const success = lyxrc.read(ss) == 0;
1582 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1583 << "Unable to read lyxrc data"
1588 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1593 view()->cursor().dispatch(cmd);
1594 updateforce |= view()->cursor().result().update();
1595 if (!view()->cursor().result().dispatched())
1596 updateforce |= view()->dispatch(cmd);
1601 if (view()->available()) {
1602 // Redraw screen unless explicitly told otherwise.
1603 // This also initializes the position cache for all insets
1604 // in (at least partially) visible top-level paragraphs.
1606 view()->update(Update::FitCursor | Update::Force);
1608 view()->update(Update::FitCursor);
1610 owner->redrawWorkArea();
1612 // if we executed a mutating lfun, mark the buffer as dirty
1614 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1615 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1616 view()->buffer()->markDirty();
1619 if (view()->cursor().inTexted()) {
1620 view()->owner()->updateLayoutChoice();
1623 sendDispatchMessage(_(getMessage()), cmd);
1627 void LyXFunc::sendDispatchMessage(string const & msg, FuncRequest const & cmd)
1629 /* When an action did not originate from the UI/kbd, it makes
1630 * sense to avoid updating the GUI. It turns out that this
1631 * fixes bug 1941, for reasons that are described here:
1632 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1634 if (cmd.origin != FuncRequest::INTERNAL) {
1635 owner->updateMenubar();
1636 owner->updateToolbars();
1639 const bool verbose = (cmd.origin == FuncRequest::UI
1640 || cmd.origin == FuncRequest::COMMANDBUFFER);
1642 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1643 lyxerr[Debug::ACTION] << "dispatch msg is " << msg << endl;
1645 owner->message(msg);
1649 string dispatch_msg = msg;
1650 if (!dispatch_msg.empty())
1651 dispatch_msg += ' ';
1653 string comname = lyxaction.getActionName(cmd.action);
1655 bool argsadded = false;
1657 if (!cmd.argument.empty()) {
1658 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1659 comname += ' ' + cmd.argument;
1664 string const shortcuts = toplevel_keymap->printbindings(cmd);
1666 if (!shortcuts.empty())
1667 comname += ": " + shortcuts;
1668 else if (!argsadded && !cmd.argument.empty())
1669 comname += ' ' + cmd.argument;
1671 if (!comname.empty()) {
1672 comname = rtrim(comname);
1673 dispatch_msg += '(' + rtrim(comname) + ')';
1676 lyxerr[Debug::ACTION] << "verbose dispatch msg " << dispatch_msg << endl;
1677 if (!dispatch_msg.empty())
1678 owner->message(dispatch_msg);
1682 void LyXFunc::setupLocalKeymap()
1684 keyseq.stdmap = toplevel_keymap.get();
1685 keyseq.curmap = toplevel_keymap.get();
1686 cancel_meta_seq.stdmap = toplevel_keymap.get();
1687 cancel_meta_seq.curmap = toplevel_keymap.get();
1691 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1693 string initpath = lyxrc.document_path;
1694 string filename(name);
1696 if (view()->available()) {
1697 string const trypath = owner->buffer()->filePath();
1698 // If directory is writeable, use this as default.
1699 if (isDirWriteable(trypath))
1703 static int newfile_number;
1705 if (filename.empty()) {
1706 filename = addName(lyxrc.document_path,
1707 "newfile" + convert<string>(++newfile_number) + ".lyx");
1708 while (bufferlist.exists(filename) || fs::is_readable(filename)) {
1710 filename = addName(lyxrc.document_path,
1711 "newfile" + convert<string>(newfile_number) +
1716 // The template stuff
1719 FileDialog fileDlg(_("Select template file"),
1720 LFUN_SELECT_FILE_SYNC,
1721 make_pair(string(_("Documents|#o#O")),
1722 string(lyxrc.document_path)),
1723 make_pair(string(_("Templates|#T#t")),
1724 string(lyxrc.template_path)));
1726 FileDialog::Result result =
1727 fileDlg.open(lyxrc.template_path,
1728 FileFilterList(_("LyX Documents (*.lyx)")),
1731 if (result.first == FileDialog::Later)
1733 if (result.second.empty())
1735 templname = result.second;
1738 Buffer * const b = newFile(filename, templname, !name.empty());
1740 owner->setBuffer(b);
1744 void LyXFunc::open(string const & fname)
1746 string initpath = lyxrc.document_path;
1748 if (view()->available()) {
1749 string const trypath = owner->buffer()->filePath();
1750 // If directory is writeable, use this as default.
1751 if (isDirWriteable(trypath))
1757 if (fname.empty()) {
1758 FileDialog fileDlg(_("Select document to open"),
1760 make_pair(string(_("Documents|#o#O")),
1761 string(lyxrc.document_path)),
1762 make_pair(string(_("Examples|#E#e")),
1763 string(addPath(package().system_support(), "examples"))));
1765 FileDialog::Result result =
1766 fileDlg.open(initpath,
1767 FileFilterList(_("LyX Documents (*.lyx)")),
1770 if (result.first == FileDialog::Later)
1773 filename = result.second;
1775 // check selected filename
1776 if (filename.empty()) {
1777 owner->message(_("Canceled."));
1783 // get absolute path of file and add ".lyx" to the filename if
1785 string const fullpath = fileSearch(string(), filename, "lyx");
1786 if (!fullpath.empty()) {
1787 filename = fullpath;
1790 string const disp_fn(makeDisplayPath(filename));
1792 // if the file doesn't exist, let the user create one
1793 if (!fs::exists(filename)) {
1794 // the user specifically chose this name. Believe him.
1795 Buffer * const b = newFile(filename, string(), true);
1797 owner->setBuffer(b);
1801 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1804 if (owner->loadLyXFile(filename)) {
1805 str2 = bformat(_("Document %1$s opened."), disp_fn);
1807 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1809 owner->message(str2);
1813 void LyXFunc::doImport(string const & argument)
1816 string filename = split(argument, format, ' ');
1818 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1819 << " file: " << filename << endl;
1821 // need user interaction
1822 if (filename.empty()) {
1823 string initpath = lyxrc.document_path;
1825 if (view()->available()) {
1826 string const trypath = owner->buffer()->filePath();
1827 // If directory is writeable, use this as default.
1828 if (isDirWriteable(trypath))
1832 string const text = bformat(_("Select %1$s file to import"),
1833 formats.prettyName(format));
1835 FileDialog fileDlg(text,
1837 make_pair(string(_("Documents|#o#O")),
1838 string(lyxrc.document_path)),
1839 make_pair(string(_("Examples|#E#e")),
1840 string(addPath(package().system_support(), "examples"))));
1842 string const filter = formats.prettyName(format)
1843 + " (*." + formats.extension(format) + ')';
1845 FileDialog::Result result =
1846 fileDlg.open(initpath,
1847 FileFilterList(filter),
1850 if (result.first == FileDialog::Later)
1853 filename = result.second;
1855 // check selected filename
1856 if (filename.empty())
1857 owner->message(_("Canceled."));
1860 if (filename.empty())
1863 // get absolute path of file
1864 filename = makeAbsPath(filename);
1866 string const lyxfile = changeExtension(filename, ".lyx");
1868 // Check if the document already is open
1869 if (lyx_gui::use_gui && bufferlist.exists(lyxfile)) {
1870 if (!bufferlist.close(bufferlist.getBuffer(lyxfile), true)) {
1871 owner->message(_("Canceled."));
1876 // if the file exists already, and we didn't do
1877 // -i lyx thefile.lyx, warn
1878 if (fs::exists(lyxfile) && filename != lyxfile) {
1879 string const file = makeDisplayPath(lyxfile, 30);
1881 string text = bformat(_("The document %1$s already exists.\n\n"
1882 "Do you want to over-write that document?"), file);
1883 int const ret = Alert::prompt(_("Over-write document?"),
1884 text, 0, 1, _("&Over-write"), _("&Cancel"));
1887 owner->message(_("Canceled."));
1892 Importer::Import(owner, filename, format);
1896 void LyXFunc::closeBuffer()
1898 // save current cursor position
1899 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1900 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1901 if (bufferlist.close(owner->buffer(), true) && !quitting) {
1902 if (bufferlist.empty()) {
1903 // need this otherwise SEGV may occur while
1904 // trying to set variables that don't exist
1905 // since there's no current buffer
1906 owner->getDialogs().hideBufferDependent();
1908 owner->setBuffer(bufferlist.first());
1914 // Each "owner" should have it's own message method. lyxview and
1915 // the minibuffer would use the minibuffer, but lyxserver would
1916 // send an ERROR signal to its client. Alejandro 970603
1917 // This function is bit problematic when it comes to NLS, to make the
1918 // lyx servers client be language indepenent we must not translate
1919 // strings sent to this func.
1920 void LyXFunc::setErrorMessage(string const & m) const
1922 dispatch_buffer = m;
1927 void LyXFunc::setMessage(string const & m) const
1929 dispatch_buffer = m;
1933 string const LyXFunc::viewStatusMessage()
1935 // When meta-fake key is pressed, show the key sequence so far + "M-".
1937 return keyseq.print() + "M-";
1939 // Else, when a non-complete key sequence is pressed,
1940 // show the available options.
1941 if (keyseq.length() > 0 && !keyseq.deleted())
1942 return keyseq.printOptions();
1944 if (!view()->available())
1945 return _("Welcome to LyX!");
1947 return view()->cursor().currentState();
1951 BufferView * LyXFunc::view() const
1953 BOOST_ASSERT(owner);
1954 return owner->view();
1958 bool LyXFunc::wasMetaKey() const
1960 return (meta_fake_bit != key_modifier::none);
1966 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1968 // Why the switch you might ask. It is a trick to ensure that all
1969 // the elements in the LyXRCTags enum is handled. As you can see
1970 // there are no breaks at all. So it is just a huge fall-through.
1971 // The nice thing is that we will get a warning from the compiler
1972 // if we forget an element.
1973 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1975 case LyXRC::RC_ACCEPT_COMPOUND:
1976 case LyXRC::RC_ALT_LANG:
1977 case LyXRC::RC_ASCIIROFF_COMMAND:
1978 case LyXRC::RC_ASCII_LINELEN:
1979 case LyXRC::RC_AUTOREGIONDELETE:
1980 case LyXRC::RC_AUTORESET_OPTIONS:
1981 case LyXRC::RC_AUTOSAVE:
1982 case LyXRC::RC_AUTO_NUMBER:
1983 case LyXRC::RC_BACKUPDIR_PATH:
1984 case LyXRC::RC_BIBTEX_COMMAND:
1985 case LyXRC::RC_BINDFILE:
1986 case LyXRC::RC_CHECKLASTFILES:
1987 case LyXRC::RC_USELASTFILEPOS:
1988 case LyXRC::RC_LOADSESSION:
1989 case LyXRC::RC_CHKTEX_COMMAND:
1990 case LyXRC::RC_CONVERTER:
1991 case LyXRC::RC_COPIER:
1992 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1993 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1994 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1995 case LyXRC::RC_DATE_INSERT_FORMAT:
1996 case LyXRC::RC_DEFAULT_LANGUAGE:
1997 case LyXRC::RC_DEFAULT_PAPERSIZE:
1998 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1999 case LyXRC::RC_DISPLAY_GRAPHICS:
2000 case LyXRC::RC_DOCUMENTPATH:
2001 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2002 if (fs::exists(lyxrc_new.document_path) &&
2003 fs::is_directory(lyxrc_new.document_path)) {
2004 using lyx::support::package;
2005 package().document_dir() = lyxrc.document_path;
2008 case LyXRC::RC_ESC_CHARS:
2009 case LyXRC::RC_FONT_ENCODING:
2010 case LyXRC::RC_FORMAT:
2011 case LyXRC::RC_INDEX_COMMAND:
2012 case LyXRC::RC_INPUT:
2013 case LyXRC::RC_KBMAP:
2014 case LyXRC::RC_KBMAP_PRIMARY:
2015 case LyXRC::RC_KBMAP_SECONDARY:
2016 case LyXRC::RC_LABEL_INIT_LENGTH:
2017 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2018 case LyXRC::RC_LANGUAGE_AUTO_END:
2019 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2020 case LyXRC::RC_LANGUAGE_COMMAND_END:
2021 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2022 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2023 case LyXRC::RC_LANGUAGE_PACKAGE:
2024 case LyXRC::RC_LANGUAGE_USE_BABEL:
2025 case LyXRC::RC_MAKE_BACKUP:
2026 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2027 case LyXRC::RC_NUMLASTFILES:
2028 case LyXRC::RC_PATH_PREFIX:
2029 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2030 using lyx::support::prependEnvPath;
2031 prependEnvPath("PATH", lyxrc.path_prefix);
2033 case LyXRC::RC_PERS_DICT:
2034 case LyXRC::RC_POPUP_BOLD_FONT:
2035 case LyXRC::RC_POPUP_FONT_ENCODING:
2036 case LyXRC::RC_POPUP_NORMAL_FONT:
2037 case LyXRC::RC_PREVIEW:
2038 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2039 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2040 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2041 case LyXRC::RC_PRINTCOPIESFLAG:
2042 case LyXRC::RC_PRINTER:
2043 case LyXRC::RC_PRINTEVENPAGEFLAG:
2044 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2045 case LyXRC::RC_PRINTFILEEXTENSION:
2046 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2047 case LyXRC::RC_PRINTODDPAGEFLAG:
2048 case LyXRC::RC_PRINTPAGERANGEFLAG:
2049 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2050 case LyXRC::RC_PRINTPAPERFLAG:
2051 case LyXRC::RC_PRINTREVERSEFLAG:
2052 case LyXRC::RC_PRINTSPOOL_COMMAND:
2053 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2054 case LyXRC::RC_PRINTTOFILE:
2055 case LyXRC::RC_PRINTTOPRINTER:
2056 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2057 case LyXRC::RC_PRINT_COMMAND:
2058 case LyXRC::RC_RTL_SUPPORT:
2059 case LyXRC::RC_SCREEN_DPI:
2060 case LyXRC::RC_SCREEN_FONT_ENCODING:
2061 case LyXRC::RC_SCREEN_FONT_ROMAN:
2062 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2063 case LyXRC::RC_SCREEN_FONT_SANS:
2064 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2065 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2066 case LyXRC::RC_SCREEN_FONT_SIZES:
2067 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2068 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2069 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2070 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2071 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2072 case LyXRC::RC_SCREEN_ZOOM:
2073 case LyXRC::RC_SERVERPIPE:
2074 case LyXRC::RC_SET_COLOR:
2075 case LyXRC::RC_SHOW_BANNER:
2076 case LyXRC::RC_SPELL_COMMAND:
2077 case LyXRC::RC_TEMPDIRPATH:
2078 case LyXRC::RC_TEMPLATEPATH:
2079 case LyXRC::RC_TEX_ALLOWS_SPACES:
2080 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2081 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2082 namespace os = lyx::support::os;
2083 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2085 case LyXRC::RC_UIFILE:
2086 case LyXRC::RC_USER_EMAIL:
2087 case LyXRC::RC_USER_NAME:
2088 case LyXRC::RC_USETEMPDIR:
2089 case LyXRC::RC_USE_ALT_LANG:
2090 case LyXRC::RC_USE_ESC_CHARS:
2091 case LyXRC::RC_USE_INP_ENC:
2092 case LyXRC::RC_USE_PERS_DICT:
2093 case LyXRC::RC_USE_SPELL_LIB:
2094 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2095 case LyXRC::RC_VIEWER:
2096 case LyXRC::RC_LAST: