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;
203 /// quotes a string for use as an argument of the "log" dialog
204 string const quoteArg(string const & arg)
207 os << '"' << subst(subst(arg, "\\", "\\\\"), "\"", "\\\"") << '"';
213 LyXFunc::LyXFunc(LyXView * lv)
216 keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
217 cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
218 meta_fake_bit(key_modifier::none)
223 void LyXFunc::handleKeyFunc(kb_action action)
225 char c = encoded_last_key;
227 if (keyseq.length()) {
231 owner->getIntl().getTransManager()
232 .deadkey(c, get_accent(action).accent, view()->getLyXText());
233 // Need to clear, in case the minibuffer calls these
236 // copied verbatim from do_accent_char
237 view()->cursor().resetAnchor();
242 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
244 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
246 // Do nothing if we have nothing (JMarc)
247 if (!keysym->isOK()) {
248 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
253 if (keysym->isModifier()) {
254 lyxerr[Debug::KEY] << "isModifier true" << endl;
258 Encoding const * encoding = view()->cursor().getEncoding();
260 encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
262 // Do a one-deep top-level lookup for
263 // cancel and meta-fake keys. RVDK_PATCH_5
264 cancel_meta_seq.reset();
266 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
267 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
268 << " action first set to [" << func.action << ']'
271 // When not cancel or meta-fake, do the normal lookup.
272 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
273 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
274 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
275 // remove Caps Lock and Mod2 as a modifiers
276 func = keyseq.addkey(keysym, (state | meta_fake_bit));
277 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
278 << "action now set to ["
279 << func.action << ']' << endl;
282 // Dont remove this unless you know what you are doing.
283 meta_fake_bit = key_modifier::none;
285 // Can this happen now ?
286 if (func.action == LFUN_NOACTION) {
287 func = FuncRequest(LFUN_COMMAND_PREFIX);
290 if (lyxerr.debugging(Debug::KEY)) {
291 lyxerr << BOOST_CURRENT_FUNCTION
293 << func.action << "]["
294 << keyseq.print() << ']'
298 // already here we know if it any point in going further
299 // why not return already here if action == -1 and
300 // num_bytes == 0? (Lgb)
302 if (keyseq.length() > 1) {
303 owner->message(keyseq.print());
307 // Maybe user can only reach the key via holding down shift.
308 // Let's see. But only if shift is the only modifier
309 if (func.action == LFUN_UNKNOWN_ACTION &&
310 state == key_modifier::shift) {
311 lyxerr[Debug::KEY] << "Trying without shift" << endl;
312 func = keyseq.addkey(keysym, key_modifier::none);
313 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
316 if (func.action == LFUN_UNKNOWN_ACTION) {
317 // Hmm, we didn't match any of the keysequences. See
318 // if it's normal insertable text not already covered
320 if (keysym->isText() && keyseq.length() == 1) {
321 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
322 func = FuncRequest(LFUN_SELF_INSERT,
323 FuncRequest::KEYBOARD);
325 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
326 owner->message(_("Unknown function."));
331 if (func.action == LFUN_SELF_INSERT) {
332 if (encoded_last_key != 0) {
333 string const arg(1, encoded_last_key);
334 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
335 FuncRequest::KEYBOARD));
337 << "SelfInsert arg[`" << arg << "']" << endl;
345 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
347 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
349 LCursor & cur = view()->cursor();
351 /* In LyX/Mac, when a dialog is open, the menus of the
352 application can still be accessed without giving focus to
353 the main window. In this case, we want to disable the menu
354 entries that are buffer-related.
356 Note that this code is not perfect, as bug 1941 attests:
357 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
360 if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
363 buf = owner->buffer();
365 if (cmd.action == LFUN_NOACTION) {
366 flag.message(N_("Nothing to do"));
371 switch (cmd.action) {
372 case LFUN_UNKNOWN_ACTION:
373 #ifndef HAVE_LIBAIKSAURUS
374 case LFUN_THESAURUS_ENTRY:
380 flag |= lyx_gui::getStatus(cmd);
383 if (flag.unknown()) {
384 flag.message(N_("Unknown action"));
388 if (!flag.enabled()) {
389 if (flag.message().empty())
390 flag.message(N_("Command disabled"));
394 // Check whether we need a buffer
395 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
397 flag.message(N_("Command not allowed with"
398 "out any document open"));
403 // I would really like to avoid having this switch and rather try to
404 // encode this in the function itself.
405 // -- And I'd rather let an inset decide which LFUNs it is willing
406 // to handle (Andre')
408 switch (cmd.action) {
409 case LFUN_TOOLTIPS_TOGGLE:
410 flag.setOnOff(owner->getDialogs().tooltipsEnabled());
413 case LFUN_BUFFER_TOGGLE_READ_ONLY:
414 flag.setOnOff(buf->isReadonly());
417 case LFUN_BUFFER_SWITCH:
418 // toggle on the current buffer, but do not toggle off
419 // the other ones (is that a good idea?)
420 if (cmd.argument == buf->fileName())
424 case LFUN_BUFFER_EXPORT:
425 enable = cmd.argument == "custom"
426 || Exporter::isExportable(*buf, cmd.argument);
429 case LFUN_BUFFER_CHKTEX:
430 enable = buf->isLatex() && lyxrc.chktex_command != "none";
433 case LFUN_BUILD_PROGRAM:
434 enable = Exporter::isExportable(*buf, "program");
437 case LFUN_LAYOUT_TABULAR:
438 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
442 case LFUN_LAYOUT_PARAGRAPH:
443 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
446 case LFUN_VC_REGISTER:
447 enable = !buf->lyxvc().inUse();
449 case LFUN_VC_CHECK_IN:
450 enable = buf->lyxvc().inUse() && !buf->isReadonly();
452 case LFUN_VC_CHECK_OUT:
453 enable = buf->lyxvc().inUse() && buf->isReadonly();
456 case LFUN_VC_UNDO_LAST:
457 enable = buf->lyxvc().inUse();
459 case LFUN_BUFFER_RELOAD:
460 enable = !buf->isUnnamed() && !buf->isClean();
463 case LFUN_INSET_SETTINGS: {
467 InsetBase::Code code = cur.inset().lyxCode();
469 case InsetBase::TABULAR_CODE:
470 enable = cmd.argument == "tabular";
472 case InsetBase::ERT_CODE:
473 enable = cmd.argument == "ert";
475 case InsetBase::FLOAT_CODE:
476 enable = cmd.argument == "float";
478 case InsetBase::WRAP_CODE:
479 enable = cmd.argument == "wrap";
481 case InsetBase::NOTE_CODE:
482 enable = cmd.argument == "note";
484 case InsetBase::BRANCH_CODE:
485 enable = cmd.argument == "branch";
487 case InsetBase::BOX_CODE:
488 enable = cmd.argument == "box";
496 case LFUN_INSET_APPLY: {
497 string const name = cmd.getArg(0);
498 InsetBase * inset = owner->getDialogs().getOpenInset(name);
500 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument);
502 bool const success = inset->getStatus(cur, fr, fs);
503 // Every inset is supposed to handle this
504 BOOST_ASSERT(success);
507 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument);
508 flag |= getStatus(fr);
510 enable = flag.enabled();
514 case LFUN_DIALOG_SHOW: {
515 string const name = cmd.getArg(0);
517 enable = name == "aboutlyx"
521 || name == "texinfo";
522 else if (name == "print")
523 enable = Exporter::isExportable(*buf, "dvi")
524 && lyxrc.print_command != "none";
525 else if (name == "character" || name == "mathpanel")
526 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
527 else if (name == "latexlog")
528 enable = isFileReadable(buf->getLogName().second);
529 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
530 else if (name == "spellchecker")
533 else if (name == "vclog")
534 enable = buf->lyxvc().inUse();
535 else if (name == "view-source")
540 case LFUN_DIALOG_SHOW_NEW_INSET:
541 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
544 case LFUN_DIALOG_UPDATE: {
545 string const name = cmd.getArg(0);
547 enable = name == "prefs";
551 case LFUN_CITATION_INSERT: {
552 FuncRequest fr(LFUN_INSET_INSERT, "citation");
553 enable = getStatus(fr).enabled();
557 case LFUN_BUFFER_WRITE: {
558 enable = view()->buffer()->isUnnamed()
559 || !view()->buffer()->isClean();
563 // this one is difficult to get right. As a half-baked
564 // solution, we consider only the first action of the sequence
565 case LFUN_COMMAND_SEQUENCE: {
566 // argument contains ';'-terminated commands
567 string const firstcmd = token(cmd.argument, ';', 0);
568 FuncRequest func(lyxaction.lookupFunc(firstcmd));
569 func.origin = cmd.origin;
570 flag = getStatus(func);
573 case LFUN_BUFFER_NEW:
574 case LFUN_BUFFER_NEW_TEMPLATE:
575 case LFUN_WORD_FIND_FORWARD:
576 case LFUN_WORD_FIND_BACKWARD:
577 case LFUN_COMMAND_PREFIX:
578 case LFUN_COMMAND_EXECUTE:
580 case LFUN_META_PREFIX:
581 case LFUN_BUFFER_CLOSE:
582 case LFUN_BUFFER_WRITE_AS:
583 case LFUN_BUFFER_UPDATE:
584 case LFUN_BUFFER_VIEW:
585 case LFUN_BUFFER_IMPORT:
588 case LFUN_BUFFER_AUTO_SAVE:
589 case LFUN_RECONFIGURE:
593 case LFUN_DROP_LAYOUTS_CHOICE:
595 case LFUN_SERVER_GET_NAME:
596 case LFUN_SERVER_NOTIFY:
597 case LFUN_SERVER_GOTO_FILE_ROW:
598 case LFUN_DIALOG_SHOW_NEXT_INSET:
599 case LFUN_DIALOG_HIDE:
600 case LFUN_DIALOG_DISCONNECT_INSET:
601 case LFUN_BUFFER_CHILD_OPEN:
602 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
603 case LFUN_KEYMAP_OFF:
604 case LFUN_KEYMAP_PRIMARY:
605 case LFUN_KEYMAP_SECONDARY:
606 case LFUN_KEYMAP_TOGGLE:
608 case LFUN_BUFFER_EXPORT_CUSTOM:
609 case LFUN_BUFFER_PRINT:
610 case LFUN_PREFERENCES_SAVE:
611 case LFUN_SCREEN_FONT_UPDATE:
614 case LFUN_EXTERNAL_EDIT:
615 case LFUN_GRAPHICS_EDIT:
616 case LFUN_ALL_INSETS_TOGGLE:
617 case LFUN_BUFFER_LANGUAGE:
618 case LFUN_TEXTCLASS_APPLY:
619 case LFUN_TEXTCLASS_LOAD:
620 case LFUN_BUFFER_SAVE_AS_DEFAULT:
621 case LFUN_BUFFER_PARAMS_APPLY:
622 case LFUN_LYXRC_APPLY:
623 case LFUN_BUFFER_NEXT:
624 case LFUN_BUFFER_PREVIOUS:
625 // these are handled in our dispatch()
630 if (!::getStatus(cur, cmd, flag))
631 flag = view()->getStatus(cmd);
637 // Can we use a readonly buffer?
638 if (buf && buf->isReadonly()
639 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
640 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
641 flag.message(N_("Document is read-only"));
645 // Are we in a DELETED change-tracking region?
646 if (buf && buf->params().tracking_changes
647 && lookupChangeType(cur, true) == Change::DELETED
648 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
649 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
650 flag.message(N_("This portion of the document is deleted."));
654 // the default error message if we disable the command
655 if (!flag.enabled() && flag.message().empty())
656 flag.message(N_("Command disabled"));
664 bool ensureBufferClean(BufferView * bv)
666 Buffer & buf = *bv->buffer();
670 string const file = makeDisplayPath(buf.fileName(), 30);
671 string text = bformat(_("The document %1$s has unsaved "
672 "changes.\n\nDo you want to save "
673 "the document?"), file);
674 int const ret = Alert::prompt(_("Save changed document?"),
675 text, 0, 1, _("&Save"),
679 bv->owner()->dispatch(FuncRequest(LFUN_BUFFER_WRITE));
681 return buf.isClean();
685 void showPrintError(string const & name)
687 string str = bformat(_("Could not print the document %1$s.\n"
688 "Check that your printer is set up correctly."),
689 makeDisplayPath(name, 50));
690 Alert::error(_("Print document failed"), str);
694 void loadTextclass(string const & name)
696 std::pair<bool, lyx::textclass_type> const tc_pair =
697 textclasslist.numberOfClass(name);
699 if (!tc_pair.first) {
700 lyxerr << "Document class \"" << name
701 << "\" does not exist."
706 lyx::textclass_type const tc = tc_pair.second;
708 if (!textclasslist[tc].load()) {
709 string s = bformat(_("The document could not be converted\n"
710 "into the document class %1$s."),
711 textclasslist[tc].name());
712 Alert::error(_("Could not change class"), s);
717 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
722 void LyXFunc::dispatch(FuncRequest const & cmd)
724 BOOST_ASSERT(view());
725 string const argument = cmd.argument;
726 kb_action const action = cmd.action;
728 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
729 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
731 // we have not done anything wrong yet.
733 dispatch_buffer.erase();
735 // redraw the screen at the end (first of the two drawing steps).
736 //This is done unless explicitely requested otherwise
738 // also do the second redrawing step. Only done if requested.
739 bool updateforce = false;
741 FuncStatus const flag = getStatus(cmd);
742 if (!flag.enabled()) {
743 // We cannot use this function here
744 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
745 << lyxaction.getActionName(action)
746 << " [" << action << "] is disabled at this location"
748 setErrorMessage(flag.message());
752 case LFUN_WORD_FIND_FORWARD:
753 case LFUN_WORD_FIND_BACKWARD: {
754 static string last_search;
755 string searched_string;
757 if (!argument.empty()) {
758 last_search = argument;
759 searched_string = argument;
761 searched_string = last_search;
764 if (searched_string.empty())
767 bool const fw = action == LFUN_WORD_FIND_FORWARD;
769 lyx::find::find2string(searched_string, true, false, fw);
770 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
774 case LFUN_COMMAND_PREFIX:
775 owner->message(keyseq.printOptions());
778 case LFUN_COMMAND_EXECUTE:
779 owner->getToolbars().display("minibuffer", true);
780 owner->focus_command_buffer();
785 meta_fake_bit = key_modifier::none;
786 if (view()->available())
787 // cancel any selection
788 dispatch(FuncRequest(LFUN_MARK_OFF));
789 setMessage(N_("Cancel"));
792 case LFUN_META_PREFIX:
793 meta_fake_bit = key_modifier::alt;
794 setMessage(keyseq.print());
797 case LFUN_BUFFER_TOGGLE_READ_ONLY:
798 if (owner->buffer()->lyxvc().inUse())
799 owner->buffer()->lyxvc().toggleReadOnly();
801 owner->buffer()->setReadonly(
802 !owner->buffer()->isReadonly());
805 // --- Menus -----------------------------------------------
806 case LFUN_BUFFER_NEW:
807 menuNew(argument, false);
810 case LFUN_BUFFER_NEW_TEMPLATE:
811 menuNew(argument, true);
814 case LFUN_BUFFER_CLOSE:
818 case LFUN_BUFFER_WRITE:
819 if (!owner->buffer()->isUnnamed()) {
820 string const str = bformat(_("Saving document %1$s..."),
821 makeDisplayPath(owner->buffer()->fileName()));
823 menuWrite(owner->buffer());
824 owner->message(str + _(" done."));
826 writeAs(owner->buffer());
830 case LFUN_BUFFER_WRITE_AS:
831 writeAs(owner->buffer(), argument);
835 case LFUN_BUFFER_RELOAD: {
836 string const file = makeDisplayPath(view()->buffer()->fileName(), 20);
837 string text = bformat(_("Any changes will be lost. Are you sure "
838 "you want to revert to the saved version of the document %1$s?"), file);
839 int const ret = Alert::prompt(_("Revert to saved document?"),
840 text, 0, 1, _("&Revert"), _("&Cancel"));
847 case LFUN_BUFFER_UPDATE:
848 Exporter::Export(owner->buffer(), argument, true);
849 owner->showErrorList(bufferFormat(*owner->buffer()));
852 case LFUN_BUFFER_VIEW:
853 Exporter::preview(owner->buffer(), argument);
854 owner->showErrorList(bufferFormat(*owner->buffer()));
857 case LFUN_BUILD_PROGRAM:
858 Exporter::Export(owner->buffer(), "program", true);
859 owner->showErrorList(_("Build"));
862 case LFUN_BUFFER_CHKTEX:
863 owner->buffer()->runChktex();
864 owner->showErrorList(_("ChkTeX"));
867 case LFUN_BUFFER_EXPORT:
868 if (argument == "custom")
869 owner->getDialogs().show("sendto");
871 Exporter::Export(owner->buffer(), argument, false);
872 owner->showErrorList(bufferFormat(*owner->buffer()));
876 case LFUN_BUFFER_EXPORT_CUSTOM: {
878 string command = split(argument, format_name, ' ');
879 Format const * format = formats.getFormat(format_name);
881 lyxerr << "Format \"" << format_name
882 << "\" not recognized!"
887 Buffer * buffer = owner->buffer();
889 // The name of the file created by the conversion process
892 // Output to filename
893 if (format->name() == "lyx") {
894 string const latexname =
895 buffer->getLatexName(false);
896 filename = changeExtension(latexname,
897 format->extension());
898 filename = addName(buffer->temppath(), filename);
900 if (!buffer->writeFile(filename))
904 Exporter::Export(buffer, format_name, true,
908 // Substitute $$FName for filename
909 if (!contains(command, "$$FName"))
910 command = "( " + command + " ) < $$FName";
911 command = subst(command, "$$FName", filename);
913 // Execute the command in the background
915 call.startscript(Systemcall::DontWait, command);
919 case LFUN_BUFFER_PRINT: {
922 string command = split(split(argument, target, ' '),
926 || target_name.empty()
927 || command.empty()) {
928 lyxerr << "Unable to parse \""
929 << argument << '"' << std::endl;
932 if (target != "printer" && target != "file") {
933 lyxerr << "Unrecognized target \""
934 << target << '"' << std::endl;
938 Buffer * buffer = owner->buffer();
940 if (!Exporter::Export(buffer, "dvi", true)) {
941 showPrintError(buffer->fileName());
945 // Push directory path.
946 string const path = buffer->temppath();
949 // there are three cases here:
950 // 1. we print to a file
951 // 2. we print directly to a printer
952 // 3. we print using a spool command (print to file first)
955 string const dviname =
956 changeExtension(buffer->getLatexName(true),
959 if (target == "printer") {
960 if (!lyxrc.print_spool_command.empty()) {
961 // case 3: print using a spool
962 string const psname =
963 changeExtension(dviname,".ps");
964 command += lyxrc.print_to_file
967 + quoteName(dviname);
970 lyxrc.print_spool_command +' ';
971 if (target_name != "default") {
972 command2 += lyxrc.print_spool_printerprefix
976 command2 += quoteName(psname);
978 // If successful, then spool command
979 res = one.startscript(
984 res = one.startscript(
985 Systemcall::DontWait,
988 // case 2: print directly to a printer
989 res = one.startscript(
990 Systemcall::DontWait,
991 command + quoteName(dviname));
995 // case 1: print to a file
996 command += lyxrc.print_to_file
997 + quoteName(makeAbsPath(target_name,
1000 + quoteName(dviname);
1001 res = one.startscript(Systemcall::DontWait,
1006 showPrintError(buffer->fileName());
1010 case LFUN_BUFFER_IMPORT:
1015 if (view()->available()) {
1016 // save cursor Position for opened files to .lyx/session
1017 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1018 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1019 // save bookmarks to .lyx/session
1020 view()->saveSavedPositions();
1022 quitLyX(argument == "force");
1025 case LFUN_TOC_VIEW: {
1026 InsetCommandParams p("tableofcontents");
1027 string const data = InsetCommandMailer::params2string("toc", p);
1028 owner->getDialogs().show("toc", data, 0);
1032 case LFUN_BUFFER_AUTO_SAVE:
1036 case LFUN_RECONFIGURE:
1037 reconfigure(view());
1040 case LFUN_HELP_OPEN: {
1041 string const arg = argument;
1043 setErrorMessage(N_("Missing argument"));
1046 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1047 if (fname.empty()) {
1048 lyxerr << "LyX: unable to find documentation file `"
1049 << arg << "'. Bad installation?" << endl;
1052 owner->message(bformat(_("Opening help file %1$s..."),
1053 makeDisplayPath(fname)));
1054 owner->loadLyXFile(fname, false);
1058 // --- version control -------------------------------
1059 case LFUN_VC_REGISTER:
1060 if (!ensureBufferClean(view()))
1062 if (!owner->buffer()->lyxvc().inUse()) {
1063 owner->buffer()->lyxvc().registrer();
1068 case LFUN_VC_CHECK_IN:
1069 if (!ensureBufferClean(view()))
1071 if (owner->buffer()->lyxvc().inUse()
1072 && !owner->buffer()->isReadonly()) {
1073 owner->buffer()->lyxvc().checkIn();
1078 case LFUN_VC_CHECK_OUT:
1079 if (!ensureBufferClean(view()))
1081 if (owner->buffer()->lyxvc().inUse()
1082 && owner->buffer()->isReadonly()) {
1083 owner->buffer()->lyxvc().checkOut();
1088 case LFUN_VC_REVERT:
1089 owner->buffer()->lyxvc().revert();
1093 case LFUN_VC_UNDO_LAST:
1094 owner->buffer()->lyxvc().undoLast();
1098 // --- buffers ----------------------------------------
1099 case LFUN_BUFFER_SWITCH:
1100 owner->setBuffer(bufferlist.getBuffer(argument));
1103 case LFUN_BUFFER_NEXT:
1104 owner->setBuffer(bufferlist.next(view()->buffer()));
1107 case LFUN_BUFFER_PREVIOUS:
1108 owner->setBuffer(bufferlist.previous(view()->buffer()));
1112 newFile(view(), argument);
1115 case LFUN_FILE_OPEN:
1119 case LFUN_DROP_LAYOUTS_CHOICE:
1120 owner->getToolbars().openLayoutList();
1123 case LFUN_MENU_OPEN:
1124 owner->getMenubar().openByName(argument);
1127 // --- lyxserver commands ----------------------------
1128 case LFUN_SERVER_GET_NAME:
1129 setMessage(owner->buffer()->fileName());
1130 lyxerr[Debug::INFO] << "FNAME["
1131 << owner->buffer()->fileName()
1135 case LFUN_SERVER_NOTIFY:
1136 dispatch_buffer = keyseq.print();
1137 lyxserver->notifyClient(dispatch_buffer);
1140 case LFUN_SERVER_GOTO_FILE_ROW: {
1143 istringstream is(argument);
1144 is >> file_name >> row;
1145 if (prefixIs(file_name, package().temp_dir())) {
1146 // Needed by inverse dvi search. If it is a file
1147 // in tmpdir, call the apropriated function
1148 owner->setBuffer(bufferlist.getBufferFromTmp(file_name));
1150 // Must replace extension of the file to be .lyx
1151 // and get full path
1152 string const s = changeExtension(file_name, ".lyx");
1153 // Either change buffer or load the file
1154 if (bufferlist.exists(s)) {
1155 owner->setBuffer(bufferlist.getBuffer(s));
1157 owner->loadLyXFile(s);
1161 view()->setCursorFromRow(row);
1164 // see BufferView_pimpl::center()
1168 case LFUN_DIALOG_SHOW: {
1169 string const name = cmd.getArg(0);
1170 string data = trim(cmd.argument.substr(name.size()));
1172 if (name == "character") {
1173 data = freefont2string();
1175 owner->getDialogs().show("character", data);
1176 } else if (name == "latexlog") {
1177 pair<Buffer::LogType, string> const logfile =
1178 owner->buffer()->getLogName();
1179 switch (logfile.first) {
1180 case Buffer::latexlog:
1183 case Buffer::buildlog:
1187 data += quoteArg(logfile.second);
1188 owner->getDialogs().show("log", data);
1189 } else if (name == "vclog") {
1190 string const data = "vc " +
1191 quoteArg(owner->buffer()->lyxvc().getLogFile());
1192 owner->getDialogs().show("log", data);
1194 owner->getDialogs().show(name, data);
1198 case LFUN_DIALOG_SHOW_NEW_INSET: {
1199 string const name = cmd.getArg(0);
1200 string data = trim(cmd.argument.substr(name.size()));
1201 if (name == "bibitem" ||
1208 InsetCommandParams p(name);
1209 data = InsetCommandMailer::params2string(name, p);
1210 } else if (name == "include") {
1211 InsetCommandParams p(data);
1212 data = InsetIncludeMailer::params2string(p);
1213 } else if (name == "box") {
1214 // \c data == "Boxed" || "Frameless" etc
1215 InsetBoxParams p(data);
1216 data = InsetBoxMailer::params2string(p);
1217 } else if (name == "branch") {
1218 InsetBranchParams p;
1219 data = InsetBranchMailer::params2string(p);
1220 } else if (name == "citation") {
1221 InsetCommandParams p("cite");
1222 data = InsetCommandMailer::params2string(name, p);
1223 } else if (name == "ert") {
1224 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1225 } else if (name == "external") {
1226 InsetExternalParams p;
1227 Buffer const & buffer = *owner->buffer();
1228 data = InsetExternalMailer::params2string(p, buffer);
1229 } else if (name == "float") {
1231 data = InsetFloatMailer::params2string(p);
1232 } else if (name == "graphics") {
1233 InsetGraphicsParams p;
1234 Buffer const & buffer = *owner->buffer();
1235 data = InsetGraphicsMailer::params2string(p, buffer);
1236 } else if (name == "note") {
1238 data = InsetNoteMailer::params2string(p);
1239 } else if (name == "vspace") {
1241 data = InsetVSpaceMailer::params2string(space);
1242 } else if (name == "wrap") {
1244 data = InsetWrapMailer::params2string(p);
1246 owner->getDialogs().show(name, data, 0);
1250 case LFUN_DIALOG_SHOW_NEXT_INSET:
1253 case LFUN_DIALOG_UPDATE: {
1254 string const & name = argument;
1255 // Can only update a dialog connected to an existing inset
1256 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1258 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument);
1259 inset->dispatch(view()->cursor(), fr);
1260 } else if (name == "paragraph") {
1261 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1262 } else if (name == "prefs") {
1263 owner->getDialogs().update(name, string());
1268 case LFUN_DIALOG_HIDE:
1269 Dialogs::hide(argument, 0);
1272 case LFUN_DIALOG_DISCONNECT_INSET:
1273 owner->getDialogs().disconnect(argument);
1277 case LFUN_CITATION_INSERT: {
1278 if (!argument.empty()) {
1279 // we can have one optional argument, delimited by '|'
1280 // citation-insert <key>|<text_before>
1281 // this should be enhanced to also support text_after
1282 // and citation style
1283 string arg = argument;
1285 if (contains(argument, "|")) {
1286 arg = token(argument, '|', 0);
1287 opt1 = '[' + token(argument, '|', 1) + ']';
1289 std::ostringstream os;
1290 os << "citation LatexCommand\n"
1291 << "\\cite" << opt1 << "{" << arg << "}\n"
1293 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1296 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1300 case LFUN_BUFFER_CHILD_OPEN: {
1301 string const filename =
1302 makeAbsPath(argument, owner->buffer()->filePath());
1303 setMessage(N_("Opening child document ") +
1304 makeDisplayPath(filename) + "...");
1305 view()->savePosition(0);
1306 string const parentfilename = owner->buffer()->fileName();
1307 if (bufferlist.exists(filename))
1308 owner->setBuffer(bufferlist.getBuffer(filename));
1310 owner->loadLyXFile(filename);
1311 // Set the parent name of the child document.
1312 // This makes insertion of citations and references in the child work,
1313 // when the target is in the parent or another child document.
1314 owner->buffer()->setParentName(parentfilename);
1318 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1319 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1322 case LFUN_KEYMAP_OFF:
1323 owner->getIntl().keyMapOn(false);
1326 case LFUN_KEYMAP_PRIMARY:
1327 owner->getIntl().keyMapPrim();
1330 case LFUN_KEYMAP_SECONDARY:
1331 owner->getIntl().keyMapSec();
1334 case LFUN_KEYMAP_TOGGLE:
1335 owner->getIntl().toggleKeyMap();
1341 string rest = split(argument, countstr, ' ');
1342 istringstream is(countstr);
1345 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1346 for (int i = 0; i < count; ++i)
1347 dispatch(lyxaction.lookupFunc(rest));
1351 case LFUN_COMMAND_SEQUENCE: {
1352 // argument contains ';'-terminated commands
1353 string arg = argument;
1354 while (!arg.empty()) {
1356 arg = split(arg, first, ';');
1357 FuncRequest func(lyxaction.lookupFunc(first));
1358 func.origin = cmd.origin;
1364 case LFUN_PREFERENCES_SAVE: {
1365 Path p(package().user_support());
1366 lyxrc.write("preferences", false);
1370 case LFUN_SCREEN_FONT_UPDATE:
1371 // handle the screen font changes.
1372 lyxrc.set_font_norm_type();
1373 lyx_gui::update_fonts();
1374 // All visible buffers will need resize
1378 case LFUN_SET_COLOR: {
1380 string const x11_name = split(argument, lyx_name, ' ');
1381 if (lyx_name.empty() || x11_name.empty()) {
1382 setErrorMessage(N_("Syntax: set-color <lyx_name>"
1387 bool const graphicsbg_changed =
1388 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1389 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1391 if (!lcolor.setColor(lyx_name, x11_name)) {
1393 bformat(_("Set-color \"%1$s\" failed "
1394 "- color is undefined or "
1395 "may not be redefined"), lyx_name));
1399 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1401 if (graphicsbg_changed) {
1402 #ifdef WITH_WARNINGS
1403 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1406 lyx::graphics::GCache::get().changeDisplay(true);
1413 owner->message(argument);
1416 case LFUN_TOOLTIPS_TOGGLE:
1417 owner->getDialogs().toggleTooltips();
1420 case LFUN_EXTERNAL_EDIT: {
1421 FuncRequest fr(action, argument);
1422 InsetExternal().dispatch(view()->cursor(), fr);
1426 case LFUN_GRAPHICS_EDIT: {
1427 FuncRequest fr(action, argument);
1428 InsetGraphics().dispatch(view()->cursor(), fr);
1432 case LFUN_INSET_APPLY: {
1433 string const name = cmd.getArg(0);
1434 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1436 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1437 inset->dispatch(view()->cursor(), fr);
1439 FuncRequest fr(LFUN_INSET_INSERT, argument);
1442 // ideally, the update flag should be set by the insets,
1443 // but this is not possible currently
1448 case LFUN_ALL_INSETS_TOGGLE: {
1450 string const name = split(argument, action, ' ');
1451 InsetBase::Code const inset_code =
1452 InsetBase::translate(name);
1454 LCursor & cur = view()->cursor();
1455 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1457 InsetBase & inset = owner->buffer()->inset();
1458 InsetIterator it = inset_iterator_begin(inset);
1459 InsetIterator const end = inset_iterator_end(inset);
1460 for (; it != end; ++it) {
1461 if (inset_code == InsetBase::NO_CODE
1462 || inset_code == it->lyxCode()) {
1463 LCursor tmpcur = cur;
1464 tmpcur.pushLeft(*it);
1465 it->dispatch(tmpcur, fr);
1472 case LFUN_BUFFER_LANGUAGE: {
1473 Buffer & buffer = *owner->buffer();
1474 Language const * oldL = buffer.params().language;
1475 Language const * newL = languages.getLanguage(argument);
1476 if (!newL || oldL == newL)
1479 if (oldL->rightToLeft() == newL->rightToLeft()
1480 && !buffer.isMultiLingual())
1481 buffer.changeLanguage(oldL, newL);
1483 buffer.updateDocLang(newL);
1487 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1488 string const fname =
1489 addName(addPath(package().user_support(), "templates/"),
1491 Buffer defaults(fname);
1493 istringstream ss(argument);
1496 int const unknown_tokens = defaults.readHeader(lex);
1498 if (unknown_tokens != 0) {
1499 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1500 << unknown_tokens << " unknown token"
1501 << (unknown_tokens == 1 ? "" : "s")
1505 if (defaults.writeFile(defaults.fileName()))
1506 setMessage(_("Document defaults saved in ")
1507 + makeDisplayPath(fname));
1509 setErrorMessage(_("Unable to save document defaults"));
1513 case LFUN_BUFFER_PARAMS_APPLY: {
1514 biblio::CiteEngine const engine =
1515 owner->buffer()->params().cite_engine;
1517 istringstream ss(argument);
1520 int const unknown_tokens =
1521 owner->buffer()->readHeader(lex);
1523 if (unknown_tokens != 0) {
1524 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1525 << unknown_tokens << " unknown token"
1526 << (unknown_tokens == 1 ? "" : "s")
1529 if (engine == owner->buffer()->params().cite_engine)
1532 LCursor & cur = view()->cursor();
1533 FuncRequest fr(LFUN_INSET_REFRESH);
1535 InsetBase & inset = owner->buffer()->inset();
1536 InsetIterator it = inset_iterator_begin(inset);
1537 InsetIterator const end = inset_iterator_end(inset);
1538 for (; it != end; ++it)
1539 if (it->lyxCode() == InsetBase::CITE_CODE)
1540 it->dispatch(cur, fr);
1544 case LFUN_TEXTCLASS_APPLY: {
1545 Buffer * buffer = owner->buffer();
1547 lyx::textclass_type const old_class =
1548 buffer->params().textclass;
1550 loadTextclass(argument);
1552 std::pair<bool, lyx::textclass_type> const tc_pair =
1553 textclasslist.numberOfClass(argument);
1558 lyx::textclass_type const new_class = tc_pair.second;
1559 if (old_class == new_class)
1563 owner->message(_("Converting document to new document class..."));
1564 recordUndoFullDocument(view());
1565 buffer->params().textclass = new_class;
1566 StableDocIterator backcur(view()->cursor());
1568 lyx::cap::switchBetweenClasses(
1569 old_class, new_class,
1570 static_cast<InsetText &>(buffer->inset()), el);
1572 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1573 bufferErrors(*buffer, el);
1574 owner->showErrorList(_("Class switch"));
1575 updateLabels(*buffer);
1580 case LFUN_TEXTCLASS_LOAD:
1581 loadTextclass(argument);
1584 case LFUN_LYXRC_APPLY: {
1585 LyXRC const lyxrc_orig = lyxrc;
1587 istringstream ss(argument);
1588 bool const success = lyxrc.read(ss) == 0;
1591 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1592 << "Unable to read lyxrc data"
1597 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1602 view()->cursor().dispatch(cmd);
1603 updateforce |= view()->cursor().result().update();
1604 if (!view()->cursor().result().dispatched())
1605 updateforce |= view()->dispatch(cmd);
1610 if (view()->available()) {
1611 // Redraw screen unless explicitly told otherwise.
1612 // This also initializes the position cache for all insets
1613 // in (at least partially) visible top-level paragraphs.
1615 view()->update(Update::FitCursor | Update::Force);
1617 view()->update(Update::FitCursor);
1619 owner->redrawWorkArea();
1621 // if we executed a mutating lfun, mark the buffer as dirty
1623 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1624 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1625 view()->buffer()->markDirty();
1628 if (view()->cursor().inTexted()) {
1629 view()->owner()->updateLayoutChoice();
1632 sendDispatchMessage(_(getMessage()), cmd);
1636 void LyXFunc::sendDispatchMessage(string const & msg, FuncRequest const & cmd)
1638 /* When an action did not originate from the UI/kbd, it makes
1639 * sense to avoid updating the GUI. It turns out that this
1640 * fixes bug 1941, for reasons that are described here:
1641 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1643 if (cmd.origin != FuncRequest::INTERNAL) {
1644 owner->updateMenubar();
1645 owner->updateToolbars();
1648 const bool verbose = (cmd.origin == FuncRequest::UI
1649 || cmd.origin == FuncRequest::COMMANDBUFFER);
1651 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1652 lyxerr[Debug::ACTION] << "dispatch msg is " << msg << endl;
1654 owner->message(msg);
1658 string dispatch_msg = msg;
1659 if (!dispatch_msg.empty())
1660 dispatch_msg += ' ';
1662 string comname = lyxaction.getActionName(cmd.action);
1664 bool argsadded = false;
1666 if (!cmd.argument.empty()) {
1667 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1668 comname += ' ' + cmd.argument;
1673 string const shortcuts = toplevel_keymap->printbindings(cmd);
1675 if (!shortcuts.empty())
1676 comname += ": " + shortcuts;
1677 else if (!argsadded && !cmd.argument.empty())
1678 comname += ' ' + cmd.argument;
1680 if (!comname.empty()) {
1681 comname = rtrim(comname);
1682 dispatch_msg += '(' + rtrim(comname) + ')';
1685 lyxerr[Debug::ACTION] << "verbose dispatch msg " << dispatch_msg << endl;
1686 if (!dispatch_msg.empty())
1687 owner->message(dispatch_msg);
1691 void LyXFunc::setupLocalKeymap()
1693 keyseq.stdmap = toplevel_keymap.get();
1694 keyseq.curmap = toplevel_keymap.get();
1695 cancel_meta_seq.stdmap = toplevel_keymap.get();
1696 cancel_meta_seq.curmap = toplevel_keymap.get();
1700 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1702 string initpath = lyxrc.document_path;
1703 string filename(name);
1705 if (view()->available()) {
1706 string const trypath = owner->buffer()->filePath();
1707 // If directory is writeable, use this as default.
1708 if (isDirWriteable(trypath))
1712 static int newfile_number;
1714 if (filename.empty()) {
1715 filename = addName(lyxrc.document_path,
1716 "newfile" + convert<string>(++newfile_number) + ".lyx");
1717 while (bufferlist.exists(filename) || fs::is_readable(filename)) {
1719 filename = addName(lyxrc.document_path,
1720 "newfile" + convert<string>(newfile_number) +
1725 // The template stuff
1728 FileDialog fileDlg(_("Select template file"),
1729 LFUN_SELECT_FILE_SYNC,
1730 make_pair(string(_("Documents|#o#O")),
1731 string(lyxrc.document_path)),
1732 make_pair(string(_("Templates|#T#t")),
1733 string(lyxrc.template_path)));
1735 FileDialog::Result result =
1736 fileDlg.open(lyxrc.template_path,
1737 FileFilterList(_("LyX Documents (*.lyx)")),
1740 if (result.first == FileDialog::Later)
1742 if (result.second.empty())
1744 templname = result.second;
1747 Buffer * const b = newFile(filename, templname, !name.empty());
1749 owner->setBuffer(b);
1753 void LyXFunc::open(string const & fname)
1755 string initpath = lyxrc.document_path;
1757 if (view()->available()) {
1758 string const trypath = owner->buffer()->filePath();
1759 // If directory is writeable, use this as default.
1760 if (isDirWriteable(trypath))
1766 if (fname.empty()) {
1767 FileDialog fileDlg(_("Select document to open"),
1769 make_pair(string(_("Documents|#o#O")),
1770 string(lyxrc.document_path)),
1771 make_pair(string(_("Examples|#E#e")),
1772 string(addPath(package().system_support(), "examples"))));
1774 FileDialog::Result result =
1775 fileDlg.open(initpath,
1776 FileFilterList(_("LyX Documents (*.lyx)")),
1779 if (result.first == FileDialog::Later)
1782 filename = result.second;
1784 // check selected filename
1785 if (filename.empty()) {
1786 owner->message(_("Canceled."));
1792 // get absolute path of file and add ".lyx" to the filename if
1794 string const fullpath = fileSearch(string(), filename, "lyx");
1795 if (!fullpath.empty()) {
1796 filename = fullpath;
1799 string const disp_fn(makeDisplayPath(filename));
1801 // if the file doesn't exist, let the user create one
1802 if (!fs::exists(filename)) {
1803 // the user specifically chose this name. Believe him.
1804 Buffer * const b = newFile(filename, string(), true);
1806 owner->setBuffer(b);
1810 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1813 if (owner->loadLyXFile(filename)) {
1814 str2 = bformat(_("Document %1$s opened."), disp_fn);
1816 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1818 owner->message(str2);
1822 void LyXFunc::doImport(string const & argument)
1825 string filename = split(argument, format, ' ');
1827 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1828 << " file: " << filename << endl;
1830 // need user interaction
1831 if (filename.empty()) {
1832 string initpath = lyxrc.document_path;
1834 if (view()->available()) {
1835 string const trypath = owner->buffer()->filePath();
1836 // If directory is writeable, use this as default.
1837 if (isDirWriteable(trypath))
1841 string const text = bformat(_("Select %1$s file to import"),
1842 formats.prettyName(format));
1844 FileDialog fileDlg(text,
1846 make_pair(string(_("Documents|#o#O")),
1847 string(lyxrc.document_path)),
1848 make_pair(string(_("Examples|#E#e")),
1849 string(addPath(package().system_support(), "examples"))));
1851 string const filter = formats.prettyName(format)
1852 + " (*." + formats.extension(format) + ')';
1854 FileDialog::Result result =
1855 fileDlg.open(initpath,
1856 FileFilterList(filter),
1859 if (result.first == FileDialog::Later)
1862 filename = result.second;
1864 // check selected filename
1865 if (filename.empty())
1866 owner->message(_("Canceled."));
1869 if (filename.empty())
1872 // get absolute path of file
1873 filename = makeAbsPath(filename);
1875 string const lyxfile = changeExtension(filename, ".lyx");
1877 // Check if the document already is open
1878 if (lyx_gui::use_gui && bufferlist.exists(lyxfile)) {
1879 if (!bufferlist.close(bufferlist.getBuffer(lyxfile), true)) {
1880 owner->message(_("Canceled."));
1885 // if the file exists already, and we didn't do
1886 // -i lyx thefile.lyx, warn
1887 if (fs::exists(lyxfile) && filename != lyxfile) {
1888 string const file = makeDisplayPath(lyxfile, 30);
1890 string text = bformat(_("The document %1$s already exists.\n\n"
1891 "Do you want to over-write that document?"), file);
1892 int const ret = Alert::prompt(_("Over-write document?"),
1893 text, 0, 1, _("&Over-write"), _("&Cancel"));
1896 owner->message(_("Canceled."));
1901 Importer::Import(owner, filename, format);
1905 void LyXFunc::closeBuffer()
1907 // save current cursor position
1908 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1909 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1910 if (bufferlist.close(owner->buffer(), true) && !quitting) {
1911 if (bufferlist.empty()) {
1912 // need this otherwise SEGV may occur while
1913 // trying to set variables that don't exist
1914 // since there's no current buffer
1915 owner->getDialogs().hideBufferDependent();
1917 owner->setBuffer(bufferlist.first());
1923 // Each "owner" should have it's own message method. lyxview and
1924 // the minibuffer would use the minibuffer, but lyxserver would
1925 // send an ERROR signal to its client. Alejandro 970603
1926 // This function is bit problematic when it comes to NLS, to make the
1927 // lyx servers client be language indepenent we must not translate
1928 // strings sent to this func.
1929 void LyXFunc::setErrorMessage(string const & m) const
1931 dispatch_buffer = m;
1936 void LyXFunc::setMessage(string const & m) const
1938 dispatch_buffer = m;
1942 string const LyXFunc::viewStatusMessage()
1944 // When meta-fake key is pressed, show the key sequence so far + "M-".
1946 return keyseq.print() + "M-";
1948 // Else, when a non-complete key sequence is pressed,
1949 // show the available options.
1950 if (keyseq.length() > 0 && !keyseq.deleted())
1951 return keyseq.printOptions();
1953 if (!view()->available())
1954 return _("Welcome to LyX!");
1956 return view()->cursor().currentState();
1960 BufferView * LyXFunc::view() const
1962 BOOST_ASSERT(owner);
1963 return owner->view();
1967 bool LyXFunc::wasMetaKey() const
1969 return (meta_fake_bit != key_modifier::none);
1975 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1977 // Why the switch you might ask. It is a trick to ensure that all
1978 // the elements in the LyXRCTags enum is handled. As you can see
1979 // there are no breaks at all. So it is just a huge fall-through.
1980 // The nice thing is that we will get a warning from the compiler
1981 // if we forget an element.
1982 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1984 case LyXRC::RC_ACCEPT_COMPOUND:
1985 case LyXRC::RC_ALT_LANG:
1986 case LyXRC::RC_ASCIIROFF_COMMAND:
1987 case LyXRC::RC_ASCII_LINELEN:
1988 case LyXRC::RC_AUTOREGIONDELETE:
1989 case LyXRC::RC_AUTORESET_OPTIONS:
1990 case LyXRC::RC_AUTOSAVE:
1991 case LyXRC::RC_AUTO_NUMBER:
1992 case LyXRC::RC_BACKUPDIR_PATH:
1993 case LyXRC::RC_BIBTEX_COMMAND:
1994 case LyXRC::RC_BINDFILE:
1995 case LyXRC::RC_CHECKLASTFILES:
1996 case LyXRC::RC_USELASTFILEPOS:
1997 case LyXRC::RC_LOADSESSION:
1998 case LyXRC::RC_CHKTEX_COMMAND:
1999 case LyXRC::RC_CONVERTER:
2000 case LyXRC::RC_COPIER:
2001 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2002 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2003 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2004 case LyXRC::RC_DATE_INSERT_FORMAT:
2005 case LyXRC::RC_DEFAULT_LANGUAGE:
2006 case LyXRC::RC_DEFAULT_PAPERSIZE:
2007 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2008 case LyXRC::RC_DISPLAY_GRAPHICS:
2009 case LyXRC::RC_DOCUMENTPATH:
2010 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2011 if (fs::exists(lyxrc_new.document_path) &&
2012 fs::is_directory(lyxrc_new.document_path)) {
2013 using lyx::support::package;
2014 package().document_dir() = lyxrc.document_path;
2017 case LyXRC::RC_ESC_CHARS:
2018 case LyXRC::RC_FONT_ENCODING:
2019 case LyXRC::RC_FORMAT:
2020 case LyXRC::RC_INDEX_COMMAND:
2021 case LyXRC::RC_INPUT:
2022 case LyXRC::RC_KBMAP:
2023 case LyXRC::RC_KBMAP_PRIMARY:
2024 case LyXRC::RC_KBMAP_SECONDARY:
2025 case LyXRC::RC_LABEL_INIT_LENGTH:
2026 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2027 case LyXRC::RC_LANGUAGE_AUTO_END:
2028 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2029 case LyXRC::RC_LANGUAGE_COMMAND_END:
2030 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2031 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2032 case LyXRC::RC_LANGUAGE_PACKAGE:
2033 case LyXRC::RC_LANGUAGE_USE_BABEL:
2034 case LyXRC::RC_MAKE_BACKUP:
2035 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2036 case LyXRC::RC_NUMLASTFILES:
2037 case LyXRC::RC_PATH_PREFIX:
2038 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2039 using lyx::support::prependEnvPath;
2040 prependEnvPath("PATH", lyxrc.path_prefix);
2042 case LyXRC::RC_PERS_DICT:
2043 case LyXRC::RC_POPUP_BOLD_FONT:
2044 case LyXRC::RC_POPUP_FONT_ENCODING:
2045 case LyXRC::RC_POPUP_NORMAL_FONT:
2046 case LyXRC::RC_PREVIEW:
2047 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2048 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2049 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2050 case LyXRC::RC_PRINTCOPIESFLAG:
2051 case LyXRC::RC_PRINTER:
2052 case LyXRC::RC_PRINTEVENPAGEFLAG:
2053 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2054 case LyXRC::RC_PRINTFILEEXTENSION:
2055 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2056 case LyXRC::RC_PRINTODDPAGEFLAG:
2057 case LyXRC::RC_PRINTPAGERANGEFLAG:
2058 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2059 case LyXRC::RC_PRINTPAPERFLAG:
2060 case LyXRC::RC_PRINTREVERSEFLAG:
2061 case LyXRC::RC_PRINTSPOOL_COMMAND:
2062 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2063 case LyXRC::RC_PRINTTOFILE:
2064 case LyXRC::RC_PRINTTOPRINTER:
2065 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2066 case LyXRC::RC_PRINT_COMMAND:
2067 case LyXRC::RC_RTL_SUPPORT:
2068 case LyXRC::RC_SCREEN_DPI:
2069 case LyXRC::RC_SCREEN_FONT_ENCODING:
2070 case LyXRC::RC_SCREEN_FONT_ROMAN:
2071 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2072 case LyXRC::RC_SCREEN_FONT_SANS:
2073 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2074 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2075 case LyXRC::RC_SCREEN_FONT_SIZES:
2076 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2077 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2078 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2079 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2080 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2081 case LyXRC::RC_SCREEN_ZOOM:
2082 case LyXRC::RC_SERVERPIPE:
2083 case LyXRC::RC_SET_COLOR:
2084 case LyXRC::RC_SHOW_BANNER:
2085 case LyXRC::RC_SPELL_COMMAND:
2086 case LyXRC::RC_TEMPDIRPATH:
2087 case LyXRC::RC_TEMPLATEPATH:
2088 case LyXRC::RC_TEX_ALLOWS_SPACES:
2089 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2090 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2091 namespace os = lyx::support::os;
2092 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2094 case LyXRC::RC_UIFILE:
2095 case LyXRC::RC_USER_EMAIL:
2096 case LyXRC::RC_USER_NAME:
2097 case LyXRC::RC_USETEMPDIR:
2098 case LyXRC::RC_USE_ALT_LANG:
2099 case LyXRC::RC_USE_ESC_CHARS:
2100 case LyXRC::RC_USE_INP_ENC:
2101 case LyXRC::RC_USE_PERS_DICT:
2102 case LyXRC::RC_USE_SPELL_LIB:
2103 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2104 case LyXRC::RC_VIEWER:
2105 case LyXRC::RC_LAST: