3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
9 * \author Angus Leeming
11 * \author André Pönitz
14 * \author Martin Vermeer
15 * \author Jürgen Vigna
17 * Full author contact details are available in file CREDITS.
24 #include "BranchList.h"
26 #include "buffer_funcs.h"
27 #include "bufferlist.h"
28 #include "bufferparams.h"
29 #include "BufferView.h"
31 #include "CutAndPaste.h"
33 #include "dispatchresult.h"
35 #include "errorlist.h"
38 #include "funcrequest.h"
39 #include "FuncStatus.h"
42 #include "insetiterator.h"
50 #include "LyXAction.h"
55 #include "lyxserver.h"
56 #include "lyxtextclasslist.h"
58 #include "paragraph.h"
59 #include "pariterator.h"
60 #include "ParagraphParameters.h"
63 #include "insets/insetbox.h"
64 #include "insets/insetbranch.h"
65 #include "insets/insetcommand.h"
66 #include "insets/insetert.h"
67 #include "insets/insetexternal.h"
68 #include "insets/insetfloat.h"
69 #include "insets/insetgraphics.h"
70 #include "insets/insetinclude.h"
71 #include "insets/insetnote.h"
72 #include "insets/insettabular.h"
73 #include "insets/insetvspace.h"
74 #include "insets/insetwrap.h"
76 #include "frontends/Application.h"
77 #include "frontends/Alert.h"
78 #include "frontends/Dialogs.h"
79 #include "frontends/FileDialog.h"
80 #include "frontends/FontLoader.h"
81 #include "frontends/LyXKeySym.h"
82 #include "frontends/LyXView.h"
83 #include "frontends/Menubar.h"
84 #include "frontends/Toolbars.h"
86 #include "support/environment.h"
87 #include "support/filefilterlist.h"
88 #include "support/filetools.h"
89 #include "support/forkedcontr.h"
90 #include "support/fs_extras.h"
91 #include "support/lstrings.h"
92 #include "support/path.h"
93 #include "support/package.h"
94 #include "support/systemcall.h"
95 #include "support/convert.h"
96 #include "support/os.h"
98 #include <boost/current_function.hpp>
99 #include <boost/filesystem/operations.hpp>
103 using bv_funcs::freefont2string;
105 using lyx::docstring;
107 using lyx::support::absolutePath;
108 using lyx::support::addName;
109 using lyx::support::addPath;
110 using lyx::support::bformat;
111 using lyx::support::changeExtension;
112 using lyx::support::contains;
113 using lyx::support::FileFilterList;
114 using lyx::support::fileSearch;
115 using lyx::support::ForkedcallsController;
116 using lyx::support::i18nLibFileSearch;
117 using lyx::support::isDirWriteable;
118 using lyx::support::isFileReadable;
119 using lyx::support::isStrInt;
120 using lyx::support::makeAbsPath;
121 using lyx::support::makeDisplayPath;
122 using lyx::support::package;
123 using lyx::support::quoteName;
124 using lyx::support::rtrim;
125 using lyx::support::split;
126 using lyx::support::subst;
127 using lyx::support::Systemcall;
128 using lyx::support::token;
129 using lyx::support::trim;
130 using lyx::support::prefixIs;
133 using std::make_pair;
136 using std::istringstream;
137 using std::ostringstream;
139 namespace Alert = lyx::frontend::Alert;
140 namespace biblio = lyx::biblio;
141 namespace fs = boost::filesystem;
144 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
147 extern tex_accent_struct get_accent(kb_action action);
152 bool getStatus(LCursor cursor,
153 FuncRequest const & cmd, FuncStatus & status)
155 // Try to fix cursor in case it is broken.
156 cursor.fixIfBroken();
158 // This is, of course, a mess. Better create a new doc iterator and use
159 // this in Inset::getStatus. This might require an additional
160 // BufferView * arg, though (which should be avoided)
161 //LCursor safe = *this;
163 for ( ; cursor.depth(); cursor.pop()) {
164 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
165 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
166 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
167 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
169 // The inset's getStatus() will return 'true' if it made
170 // a definitive decision on whether it want to handle the
171 // request or not. The result of this decision is put into
172 // the 'status' parameter.
173 if (cursor.inset().getStatus(cursor, cmd, status)) {
182 /** Return the change status at cursor position, taking in account the
183 * status at each level of the document iterator (a table in a deleted
184 * footnote is deleted).
185 * When \param outer is true, the top slice is not looked at.
187 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
189 size_t const depth = dit.depth() - (outer ? 1 : 0);
191 for (size_t i = 0 ; i < depth ; ++i) {
192 CursorSlice const & slice = dit[i];
193 if (!slice.inset().inMathed()
194 && slice.pos() < slice.paragraph().size()) {
195 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
196 if (ch != Change::UNCHANGED)
200 return Change::UNCHANGED;
205 LyXFunc::LyXFunc(LyXView * lv)
208 keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
209 cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
210 meta_fake_bit(key_modifier::none)
215 void LyXFunc::setLyXView(LyXView * lv)
220 void LyXFunc::handleKeyFunc(kb_action action)
222 char c = encoded_last_key;
224 if (keyseq.length()) {
228 owner->view()->getIntl().getTransManager()
229 .deadkey(c, get_accent(action).accent, view()->getLyXText());
230 // Need to clear, in case the minibuffer calls these
233 // copied verbatim from do_accent_char
234 view()->cursor().resetAnchor();
239 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
241 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
243 // Do nothing if we have nothing (JMarc)
244 if (!keysym->isOK()) {
245 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
250 if (keysym->isModifier()) {
251 lyxerr[Debug::KEY] << "isModifier true" << endl;
255 Encoding const * encoding = view()->cursor().getEncoding();
257 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
258 size_t encoded_last_key = keysym->getUCSEncoded();
260 // Do a one-deep top-level lookup for
261 // cancel and meta-fake keys. RVDK_PATCH_5
262 cancel_meta_seq.reset();
264 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
265 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
266 << " action first set to [" << func.action << ']'
269 // When not cancel or meta-fake, do the normal lookup.
270 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
271 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
272 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
273 // remove Caps Lock and Mod2 as a modifiers
274 func = keyseq.addkey(keysym, (state | meta_fake_bit));
275 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
276 << "action now set to ["
277 << func.action << ']' << endl;
280 // Dont remove this unless you know what you are doing.
281 meta_fake_bit = key_modifier::none;
283 // Can this happen now ?
284 if (func.action == LFUN_NOACTION) {
285 func = FuncRequest(LFUN_COMMAND_PREFIX);
288 if (lyxerr.debugging(Debug::KEY)) {
289 lyxerr << BOOST_CURRENT_FUNCTION
291 << func.action << "]["
292 << keyseq.print() << ']'
296 // already here we know if it any point in going further
297 // why not return already here if action == -1 and
298 // num_bytes == 0? (Lgb)
300 if (keyseq.length() > 1) {
301 owner->message(lyx::from_utf8(keyseq.print()));
305 // Maybe user can only reach the key via holding down shift.
306 // Let's see. But only if shift is the only modifier
307 if (func.action == LFUN_UNKNOWN_ACTION &&
308 state == key_modifier::shift) {
309 lyxerr[Debug::KEY] << "Trying without shift" << endl;
310 func = keyseq.addkey(keysym, key_modifier::none);
311 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
314 if (func.action == LFUN_UNKNOWN_ACTION) {
315 // Hmm, we didn't match any of the keysequences. See
316 // if it's normal insertable text not already covered
318 if (keysym->isText() && keyseq.length() == 1) {
319 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
320 func = FuncRequest(LFUN_SELF_INSERT,
321 FuncRequest::KEYBOARD);
323 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
324 owner->message(_("Unknown function."));
329 if (func.action == LFUN_SELF_INSERT) {
330 if (encoded_last_key != 0) {
331 docstring const arg(1, encoded_last_key);
332 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
333 FuncRequest::KEYBOARD));
335 << "SelfInsert arg[`" << lyx::to_utf8(arg) << "']" << endl;
343 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
345 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
347 LCursor & cur = view()->cursor();
349 /* In LyX/Mac, when a dialog is open, the menus of the
350 application can still be accessed without giving focus to
351 the main window. In this case, we want to disable the menu
352 entries that are buffer-related.
354 Note that this code is not perfect, as bug 1941 attests:
355 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
358 if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
361 buf = owner->buffer();
363 if (cmd.action == LFUN_NOACTION) {
364 flag.message(lyx::from_utf8(N_("Nothing to do")));
369 switch (cmd.action) {
370 case LFUN_UNKNOWN_ACTION:
371 #ifndef HAVE_LIBAIKSAURUS
372 case LFUN_THESAURUS_ENTRY:
382 if (flag.unknown()) {
383 flag.message(lyx::from_utf8(N_("Unknown action")));
387 if (!flag.enabled()) {
388 if (flag.message().empty())
389 flag.message(lyx::from_utf8(N_("Command disabled")));
393 // Check whether we need a buffer
394 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
396 flag.message(lyx::from_utf8(N_("Command not allowed with"
397 "out any document open")));
402 // I would really like to avoid having this switch and rather try to
403 // encode this in the function itself.
404 // -- And I'd rather let an inset decide which LFUNs it is willing
405 // to handle (Andre')
407 switch (cmd.action) {
408 case LFUN_BUFFER_TOGGLE_READ_ONLY:
409 flag.setOnOff(buf->isReadonly());
412 case LFUN_BUFFER_SWITCH:
413 // toggle on the current buffer, but do not toggle off
414 // the other ones (is that a good idea?)
415 if (lyx::to_utf8(cmd.argument()) == buf->fileName())
419 case LFUN_BUFFER_EXPORT:
420 enable = cmd.argument() == "custom"
421 || Exporter::isExportable(*buf, lyx::to_utf8(cmd.argument()));
424 case LFUN_BUFFER_CHKTEX:
425 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
428 case LFUN_BUILD_PROGRAM:
429 enable = Exporter::isExportable(*buf, "program");
432 case LFUN_LAYOUT_TABULAR:
433 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
437 case LFUN_LAYOUT_PARAGRAPH:
438 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
441 case LFUN_VC_REGISTER:
442 enable = !buf->lyxvc().inUse();
444 case LFUN_VC_CHECK_IN:
445 enable = buf->lyxvc().inUse() && !buf->isReadonly();
447 case LFUN_VC_CHECK_OUT:
448 enable = buf->lyxvc().inUse() && buf->isReadonly();
451 case LFUN_VC_UNDO_LAST:
452 enable = buf->lyxvc().inUse();
454 case LFUN_BUFFER_RELOAD:
455 enable = !buf->isUnnamed() && !buf->isClean();
458 case LFUN_INSET_SETTINGS: {
462 InsetBase::Code code = cur.inset().lyxCode();
464 case InsetBase::TABULAR_CODE:
465 enable = cmd.argument() == "tabular";
467 case InsetBase::ERT_CODE:
468 enable = cmd.argument() == "ert";
470 case InsetBase::FLOAT_CODE:
471 enable = cmd.argument() == "float";
473 case InsetBase::WRAP_CODE:
474 enable = cmd.argument() == "wrap";
476 case InsetBase::NOTE_CODE:
477 enable = cmd.argument() == "note";
479 case InsetBase::BRANCH_CODE:
480 enable = cmd.argument() == "branch";
482 case InsetBase::BOX_CODE:
483 enable = cmd.argument() == "box";
491 case LFUN_INSET_APPLY: {
492 string const name = cmd.getArg(0);
493 InsetBase * inset = owner->getDialogs().getOpenInset(name);
495 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
497 bool const success = inset->getStatus(cur, fr, fs);
498 // Every inset is supposed to handle this
499 BOOST_ASSERT(success);
502 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
503 flag |= getStatus(fr);
505 enable = flag.enabled();
509 case LFUN_DIALOG_SHOW: {
510 string const name = cmd.getArg(0);
512 enable = name == "aboutlyx"
516 || name == "texinfo";
517 else if (name == "print")
518 enable = Exporter::isExportable(*buf, "dvi")
519 && lyxrc.print_command != "none";
520 else if (name == "character" || name == "mathpanel")
521 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
522 else if (name == "latexlog")
523 enable = isFileReadable(buf->getLogName().second);
524 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
525 else if (name == "spellchecker")
528 else if (name == "vclog")
529 enable = buf->lyxvc().inUse();
530 else if (name == "view-source")
535 case LFUN_DIALOG_SHOW_NEW_INSET:
536 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
539 case LFUN_DIALOG_UPDATE: {
540 string const name = cmd.getArg(0);
542 enable = name == "prefs";
546 case LFUN_CITATION_INSERT: {
547 FuncRequest fr(LFUN_INSET_INSERT, "citation");
548 enable = getStatus(fr).enabled();
552 case LFUN_BUFFER_WRITE: {
553 enable = view()->buffer()->isUnnamed()
554 || !view()->buffer()->isClean();
558 // this one is difficult to get right. As a half-baked
559 // solution, we consider only the first action of the sequence
560 case LFUN_COMMAND_SEQUENCE: {
561 // argument contains ';'-terminated commands
562 string const firstcmd = token(lyx::to_utf8(cmd.argument()), ';', 0);
563 FuncRequest func(lyxaction.lookupFunc(firstcmd));
564 func.origin = cmd.origin;
565 flag = getStatus(func);
568 case LFUN_BUFFER_NEW:
569 case LFUN_BUFFER_NEW_TEMPLATE:
570 case LFUN_WORD_FIND_FORWARD:
571 case LFUN_WORD_FIND_BACKWARD:
572 case LFUN_COMMAND_PREFIX:
573 case LFUN_COMMAND_EXECUTE:
575 case LFUN_META_PREFIX:
576 case LFUN_BUFFER_CLOSE:
577 case LFUN_BUFFER_WRITE_AS:
578 case LFUN_BUFFER_UPDATE:
579 case LFUN_BUFFER_VIEW:
580 case LFUN_BUFFER_IMPORT:
583 case LFUN_BUFFER_AUTO_SAVE:
584 case LFUN_RECONFIGURE:
588 case LFUN_DROP_LAYOUTS_CHOICE:
590 case LFUN_SERVER_GET_NAME:
591 case LFUN_SERVER_NOTIFY:
592 case LFUN_SERVER_GOTO_FILE_ROW:
593 case LFUN_DIALOG_HIDE:
594 case LFUN_DIALOG_DISCONNECT_INSET:
595 case LFUN_BUFFER_CHILD_OPEN:
596 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
597 case LFUN_KEYMAP_OFF:
598 case LFUN_KEYMAP_PRIMARY:
599 case LFUN_KEYMAP_SECONDARY:
600 case LFUN_KEYMAP_TOGGLE:
602 case LFUN_BUFFER_EXPORT_CUSTOM:
603 case LFUN_BUFFER_PRINT:
604 case LFUN_PREFERENCES_SAVE:
605 case LFUN_SCREEN_FONT_UPDATE:
608 case LFUN_EXTERNAL_EDIT:
609 case LFUN_GRAPHICS_EDIT:
610 case LFUN_ALL_INSETS_TOGGLE:
611 case LFUN_BUFFER_LANGUAGE:
612 case LFUN_TEXTCLASS_APPLY:
613 case LFUN_TEXTCLASS_LOAD:
614 case LFUN_BUFFER_SAVE_AS_DEFAULT:
615 case LFUN_BUFFER_PARAMS_APPLY:
616 case LFUN_LYXRC_APPLY:
617 case LFUN_BUFFER_NEXT:
618 case LFUN_BUFFER_PREVIOUS:
619 // these are handled in our dispatch()
624 if (!::getStatus(cur, cmd, flag))
625 flag = view()->getStatus(cmd);
631 // Can we use a readonly buffer?
632 if (buf && buf->isReadonly()
633 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
634 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
635 flag.message(lyx::from_utf8(N_("Document is read-only")));
639 // Are we in a DELETED change-tracking region?
640 if (buf && lookupChangeType(cur, true) == Change::DELETED
641 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
642 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
643 flag.message(lyx::from_utf8(N_("This portion of the document is deleted.")));
647 // the default error message if we disable the command
648 if (!flag.enabled() && flag.message().empty())
649 flag.message(lyx::from_utf8(N_("Command disabled")));
655 bool LyXFunc::ensureBufferClean(BufferView * bv)
657 Buffer & buf = *bv->buffer();
661 docstring const file = makeDisplayPath(buf.fileName(), 30);
662 docstring 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 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
672 return buf.isClean();
678 void showPrintError(string const & name)
680 docstring str = bformat(_("Could not print the document %1$s.\n"
681 "Check that your printer is set up correctly."),
682 makeDisplayPath(name, 50));
683 Alert::error(_("Print document failed"), str);
687 void loadTextclass(string const & name)
689 std::pair<bool, lyx::textclass_type> const tc_pair =
690 textclasslist.numberOfClass(name);
692 if (!tc_pair.first) {
693 lyxerr << "Document class \"" << name
694 << "\" does not exist."
699 lyx::textclass_type const tc = tc_pair.second;
701 if (!textclasslist[tc].load()) {
702 docstring s = bformat(_("The document could not be converted\n"
703 "into the document class %1$s."),
704 lyx::from_utf8(textclasslist[tc].name()));
705 Alert::error(_("Could not change class"), s);
710 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
715 void LyXFunc::dispatch(FuncRequest const & cmd)
717 BOOST_ASSERT(view());
718 string const argument = lyx::to_utf8(cmd.argument());
719 kb_action const action = cmd.action;
721 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
722 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
724 // we have not done anything wrong yet.
726 dispatch_buffer.erase();
728 // redraw the screen at the end (first of the two drawing steps).
729 //This is done unless explicitely requested otherwise
731 // also do the second redrawing step. Only done if requested.
732 bool updateforce = false;
734 FuncStatus const flag = getStatus(cmd);
735 if (!flag.enabled()) {
736 // We cannot use this function here
737 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
738 << lyxaction.getActionName(action)
739 << " [" << action << "] is disabled at this location"
741 setErrorMessage(flag.message());
745 case LFUN_WORD_FIND_FORWARD:
746 case LFUN_WORD_FIND_BACKWARD: {
747 static string last_search;
748 string searched_string;
750 if (!argument.empty()) {
751 last_search = argument;
752 searched_string = argument;
754 searched_string = last_search;
757 if (searched_string.empty())
760 bool const fw = action == LFUN_WORD_FIND_FORWARD;
762 lyx::find::find2string(searched_string, true, false, fw);
763 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
767 case LFUN_COMMAND_PREFIX:
768 owner->message(lyx::from_utf8(keyseq.printOptions()));
771 case LFUN_COMMAND_EXECUTE:
772 owner->getToolbars().display("minibuffer", true);
773 owner->focus_command_buffer();
778 meta_fake_bit = key_modifier::none;
779 if (view()->buffer())
780 // cancel any selection
781 dispatch(FuncRequest(LFUN_MARK_OFF));
782 setMessage(_("Cancel"));
785 case LFUN_META_PREFIX:
786 meta_fake_bit = key_modifier::alt;
787 setMessage(lyx::from_utf8(keyseq.print()));
790 case LFUN_BUFFER_TOGGLE_READ_ONLY:
791 if (owner->buffer()->lyxvc().inUse())
792 owner->buffer()->lyxvc().toggleReadOnly();
794 owner->buffer()->setReadonly(
795 !owner->buffer()->isReadonly());
798 // --- Menus -----------------------------------------------
799 case LFUN_BUFFER_NEW:
800 menuNew(argument, false);
803 case LFUN_BUFFER_NEW_TEMPLATE:
804 menuNew(argument, true);
807 case LFUN_BUFFER_CLOSE:
811 case LFUN_BUFFER_WRITE:
812 if (!owner->buffer()->isUnnamed()) {
813 docstring const str = bformat(_("Saving document %1$s..."),
814 makeDisplayPath(owner->buffer()->fileName()));
816 menuWrite(owner->buffer());
817 owner->message(str + _(" done."));
819 writeAs(owner->buffer());
823 case LFUN_BUFFER_WRITE_AS:
824 writeAs(owner->buffer(), argument);
828 case LFUN_BUFFER_RELOAD: {
829 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
830 docstring text = bformat(_("Any changes will be lost. Are you sure "
831 "you want to revert to the saved version of the document %1$s?"), file);
832 int const ret = Alert::prompt(_("Revert to saved document?"),
833 text, 0, 1, _("&Revert"), _("&Cancel"));
840 case LFUN_BUFFER_UPDATE:
841 Exporter::Export(owner->buffer(), argument, true);
844 case LFUN_BUFFER_VIEW:
845 Exporter::preview(owner->buffer(), argument);
848 case LFUN_BUILD_PROGRAM:
849 Exporter::Export(owner->buffer(), "program", true);
852 case LFUN_BUFFER_CHKTEX:
853 owner->buffer()->runChktex();
856 case LFUN_BUFFER_EXPORT:
857 if (argument == "custom")
858 owner->getDialogs().show("sendto");
860 Exporter::Export(owner->buffer(), argument, false);
864 case LFUN_BUFFER_EXPORT_CUSTOM: {
866 string command = split(argument, format_name, ' ');
867 Format const * format = formats.getFormat(format_name);
869 lyxerr << "Format \"" << format_name
870 << "\" not recognized!"
875 Buffer * buffer = owner->buffer();
877 // The name of the file created by the conversion process
880 // Output to filename
881 if (format->name() == "lyx") {
882 string const latexname =
883 buffer->getLatexName(false);
884 filename = changeExtension(latexname,
885 format->extension());
886 filename = addName(buffer->temppath(), filename);
888 if (!buffer->writeFile(filename))
892 Exporter::Export(buffer, format_name, true, filename);
895 // Substitute $$FName for filename
896 if (!contains(command, "$$FName"))
897 command = "( " + command + " ) < $$FName";
898 command = subst(command, "$$FName", filename);
900 // Execute the command in the background
902 call.startscript(Systemcall::DontWait, command);
906 case LFUN_BUFFER_PRINT: {
909 string command = split(split(argument, target, ' '),
913 || target_name.empty()
914 || command.empty()) {
915 lyxerr << "Unable to parse \""
916 << argument << '"' << std::endl;
919 if (target != "printer" && target != "file") {
920 lyxerr << "Unrecognized target \""
921 << target << '"' << std::endl;
925 Buffer * buffer = owner->buffer();
927 if (!Exporter::Export(buffer, "dvi", true)) {
928 showPrintError(buffer->fileName());
932 // Push directory path.
933 string const path = buffer->temppath();
934 lyx::support::Path p(path);
936 // there are three cases here:
937 // 1. we print to a file
938 // 2. we print directly to a printer
939 // 3. we print using a spool command (print to file first)
942 string const dviname =
943 changeExtension(buffer->getLatexName(true),
946 if (target == "printer") {
947 if (!lyxrc.print_spool_command.empty()) {
948 // case 3: print using a spool
949 string const psname =
950 changeExtension(dviname,".ps");
951 command += lyxrc.print_to_file
954 + quoteName(dviname);
957 lyxrc.print_spool_command +' ';
958 if (target_name != "default") {
959 command2 += lyxrc.print_spool_printerprefix
963 command2 += quoteName(psname);
965 // If successful, then spool command
966 res = one.startscript(
971 res = one.startscript(
972 Systemcall::DontWait,
975 // case 2: print directly to a printer
976 res = one.startscript(
977 Systemcall::DontWait,
978 command + quoteName(dviname));
982 // case 1: print to a file
983 command += lyxrc.print_to_file
984 + quoteName(makeAbsPath(target_name,
987 + quoteName(dviname);
988 res = one.startscript(Systemcall::DontWait,
993 showPrintError(buffer->fileName());
997 case LFUN_BUFFER_IMPORT:
1002 if (view()->buffer()) {
1003 // save cursor Position for opened files to .lyx/session
1004 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1005 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1006 // save bookmarks to .lyx/session
1007 view()->saveSavedPositions();
1009 quitLyX(argument == "force");
1012 case LFUN_TOC_VIEW: {
1013 InsetCommandParams p("tableofcontents");
1014 string const data = InsetCommandMailer::params2string("toc", p);
1015 owner->getDialogs().show("toc", data, 0);
1019 case LFUN_BUFFER_AUTO_SAVE:
1023 case LFUN_RECONFIGURE:
1024 reconfigure(view());
1027 case LFUN_HELP_OPEN: {
1028 string const arg = argument;
1030 setErrorMessage(_("Missing argument"));
1033 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1034 if (fname.empty()) {
1035 lyxerr << "LyX: unable to find documentation file `"
1036 << arg << "'. Bad installation?" << endl;
1039 owner->message(bformat(_("Opening help file %1$s..."),
1040 makeDisplayPath(fname)));
1041 owner->loadLyXFile(fname, false);
1045 // --- version control -------------------------------
1046 case LFUN_VC_REGISTER:
1047 if (!ensureBufferClean(view()))
1049 if (!owner->buffer()->lyxvc().inUse()) {
1050 owner->buffer()->lyxvc().registrer();
1055 case LFUN_VC_CHECK_IN:
1056 if (!ensureBufferClean(view()))
1058 if (owner->buffer()->lyxvc().inUse()
1059 && !owner->buffer()->isReadonly()) {
1060 owner->buffer()->lyxvc().checkIn();
1065 case LFUN_VC_CHECK_OUT:
1066 if (!ensureBufferClean(view()))
1068 if (owner->buffer()->lyxvc().inUse()
1069 && owner->buffer()->isReadonly()) {
1070 owner->buffer()->lyxvc().checkOut();
1075 case LFUN_VC_REVERT:
1076 owner->buffer()->lyxvc().revert();
1080 case LFUN_VC_UNDO_LAST:
1081 owner->buffer()->lyxvc().undoLast();
1085 // --- buffers ----------------------------------------
1086 case LFUN_BUFFER_SWITCH:
1087 owner->setBuffer(theBufferList().getBuffer(argument));
1090 case LFUN_BUFFER_NEXT:
1091 owner->setBuffer(theBufferList().next(view()->buffer()));
1094 case LFUN_BUFFER_PREVIOUS:
1095 owner->setBuffer(theBufferList().previous(view()->buffer()));
1099 newFile(view(), argument);
1102 case LFUN_FILE_OPEN:
1106 case LFUN_DROP_LAYOUTS_CHOICE:
1107 owner->getToolbars().openLayoutList();
1110 case LFUN_MENU_OPEN:
1111 owner->getMenubar().openByName(lyx::from_utf8(argument));
1114 // --- lyxserver commands ----------------------------
1115 case LFUN_SERVER_GET_NAME:
1116 setMessage(lyx::from_utf8(owner->buffer()->fileName()));
1117 lyxerr[Debug::INFO] << "FNAME["
1118 << owner->buffer()->fileName()
1122 case LFUN_SERVER_NOTIFY:
1123 dispatch_buffer = lyx::from_utf8(keyseq.print());
1124 theApp->server().notifyClient(lyx::to_utf8(dispatch_buffer));
1127 case LFUN_SERVER_GOTO_FILE_ROW: {
1130 istringstream is(argument);
1131 is >> file_name >> row;
1132 if (prefixIs(file_name, package().temp_dir())) {
1133 // Needed by inverse dvi search. If it is a file
1134 // in tmpdir, call the apropriated function
1135 owner->setBuffer(theBufferList().getBufferFromTmp(file_name));
1137 // Must replace extension of the file to be .lyx
1138 // and get full path
1139 string const s = changeExtension(file_name, ".lyx");
1140 // Either change buffer or load the file
1141 if (theBufferList().exists(s)) {
1142 owner->setBuffer(theBufferList().getBuffer(s));
1144 owner->loadLyXFile(s);
1148 view()->setCursorFromRow(row);
1151 // see BufferView::center()
1155 case LFUN_DIALOG_SHOW: {
1156 string const name = cmd.getArg(0);
1157 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1159 if (name == "character") {
1160 data = freefont2string();
1162 owner->getDialogs().show("character", data);
1163 } else if (name == "latexlog") {
1164 pair<Buffer::LogType, string> const logfile =
1165 owner->buffer()->getLogName();
1166 switch (logfile.first) {
1167 case Buffer::latexlog:
1170 case Buffer::buildlog:
1174 data += LyXLex::quoteString(logfile.second);
1175 owner->getDialogs().show("log", data);
1176 } else if (name == "vclog") {
1177 string const data = "vc " +
1178 LyXLex::quoteString(owner->buffer()->lyxvc().getLogFile());
1179 owner->getDialogs().show("log", data);
1181 owner->getDialogs().show(name, data);
1185 case LFUN_DIALOG_SHOW_NEW_INSET: {
1186 string const name = cmd.getArg(0);
1187 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1188 if (name == "bibitem" ||
1195 InsetCommandParams p(name);
1196 data = InsetCommandMailer::params2string(name, p);
1197 } else if (name == "include") {
1198 InsetCommandParams p(data);
1199 data = InsetIncludeMailer::params2string(p);
1200 } else if (name == "box") {
1201 // \c data == "Boxed" || "Frameless" etc
1202 InsetBoxParams p(data);
1203 data = InsetBoxMailer::params2string(p);
1204 } else if (name == "branch") {
1205 InsetBranchParams p;
1206 data = InsetBranchMailer::params2string(p);
1207 } else if (name == "citation") {
1208 InsetCommandParams p("cite");
1209 data = InsetCommandMailer::params2string(name, p);
1210 } else if (name == "ert") {
1211 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1212 } else if (name == "external") {
1213 InsetExternalParams p;
1214 Buffer const & buffer = *owner->buffer();
1215 data = InsetExternalMailer::params2string(p, buffer);
1216 } else if (name == "float") {
1218 data = InsetFloatMailer::params2string(p);
1219 } else if (name == "graphics") {
1220 InsetGraphicsParams p;
1221 Buffer const & buffer = *owner->buffer();
1222 data = InsetGraphicsMailer::params2string(p, buffer);
1223 } else if (name == "note") {
1225 data = InsetNoteMailer::params2string(p);
1226 } else if (name == "vspace") {
1228 data = InsetVSpaceMailer::params2string(space);
1229 } else if (name == "wrap") {
1231 data = InsetWrapMailer::params2string(p);
1233 owner->getDialogs().show(name, data, 0);
1237 case LFUN_DIALOG_UPDATE: {
1238 string const & name = argument;
1239 // Can only update a dialog connected to an existing inset
1240 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1242 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1243 inset->dispatch(view()->cursor(), fr);
1244 } else if (name == "paragraph") {
1245 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1246 } else if (name == "prefs") {
1247 owner->getDialogs().update(name, string());
1252 case LFUN_DIALOG_HIDE:
1253 Dialogs::hide(argument, 0);
1256 case LFUN_DIALOG_DISCONNECT_INSET:
1257 owner->getDialogs().disconnect(argument);
1261 case LFUN_CITATION_INSERT: {
1262 if (!argument.empty()) {
1263 // we can have one optional argument, delimited by '|'
1264 // citation-insert <key>|<text_before>
1265 // this should be enhanced to also support text_after
1266 // and citation style
1267 string arg = argument;
1269 if (contains(argument, "|")) {
1270 arg = token(argument, '|', 0);
1271 opt1 = '[' + token(argument, '|', 1) + ']';
1273 std::ostringstream os;
1274 os << "citation LatexCommand\n"
1275 << "\\cite" << opt1 << "{" << arg << "}\n"
1277 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1280 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1284 case LFUN_BUFFER_CHILD_OPEN: {
1285 string const filename =
1286 makeAbsPath(argument, owner->buffer()->filePath());
1287 // FIXME Should use bformat
1288 setMessage(_("Opening child document ") +
1289 makeDisplayPath(filename) + "...");
1290 view()->savePosition(0);
1291 string const parentfilename = owner->buffer()->fileName();
1292 if (theBufferList().exists(filename))
1293 owner->setBuffer(theBufferList().getBuffer(filename));
1295 owner->loadLyXFile(filename);
1296 // Set the parent name of the child document.
1297 // This makes insertion of citations and references in the child work,
1298 // when the target is in the parent or another child document.
1299 owner->buffer()->setParentName(parentfilename);
1303 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1304 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1307 case LFUN_KEYMAP_OFF:
1308 owner->view()->getIntl().keyMapOn(false);
1311 case LFUN_KEYMAP_PRIMARY:
1312 owner->view()->getIntl().keyMapPrim();
1315 case LFUN_KEYMAP_SECONDARY:
1316 owner->view()->getIntl().keyMapSec();
1319 case LFUN_KEYMAP_TOGGLE:
1320 owner->view()->getIntl().toggleKeyMap();
1326 string rest = split(argument, countstr, ' ');
1327 istringstream is(countstr);
1330 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1331 for (int i = 0; i < count; ++i)
1332 dispatch(lyxaction.lookupFunc(rest));
1336 case LFUN_COMMAND_SEQUENCE: {
1337 // argument contains ';'-terminated commands
1338 string arg = argument;
1339 while (!arg.empty()) {
1341 arg = split(arg, first, ';');
1342 FuncRequest func(lyxaction.lookupFunc(first));
1343 func.origin = cmd.origin;
1349 case LFUN_PREFERENCES_SAVE: {
1350 lyx::support::Path p(package().user_support());
1351 lyxrc.write("preferences", false);
1355 case LFUN_SCREEN_FONT_UPDATE:
1356 // handle the screen font changes.
1357 lyxrc.set_font_norm_type();
1358 theFontLoader().update();
1359 // All visible buffers will need resize
1363 case LFUN_SET_COLOR: {
1365 string const x11_name = split(argument, lyx_name, ' ');
1366 if (lyx_name.empty() || x11_name.empty()) {
1367 setErrorMessage(_("Syntax: set-color <lyx_name>"
1372 bool const graphicsbg_changed =
1373 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1374 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1376 if (!lcolor.setColor(lyx_name, x11_name)) {
1378 bformat(_("Set-color \"%1$s\" failed "
1379 "- color is undefined or "
1380 "may not be redefined"),
1381 lyx::from_utf8(lyx_name)));
1385 theApp->updateColor(lcolor.getFromLyXName(lyx_name));
1387 if (graphicsbg_changed) {
1388 #ifdef WITH_WARNINGS
1389 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1392 lyx::graphics::GCache::get().changeDisplay(true);
1399 owner->message(lyx::from_utf8(argument));
1402 case LFUN_EXTERNAL_EDIT: {
1403 FuncRequest fr(action, argument);
1404 InsetExternal().dispatch(view()->cursor(), fr);
1408 case LFUN_GRAPHICS_EDIT: {
1409 FuncRequest fr(action, argument);
1410 InsetGraphics().dispatch(view()->cursor(), fr);
1414 case LFUN_INSET_APPLY: {
1415 string const name = cmd.getArg(0);
1416 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1418 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1419 inset->dispatch(view()->cursor(), fr);
1421 FuncRequest fr(LFUN_INSET_INSERT, argument);
1424 // ideally, the update flag should be set by the insets,
1425 // but this is not possible currently
1430 case LFUN_ALL_INSETS_TOGGLE: {
1432 string const name = split(argument, action, ' ');
1433 InsetBase::Code const inset_code =
1434 InsetBase::translate(name);
1436 LCursor & cur = view()->cursor();
1437 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1439 InsetBase & inset = owner->buffer()->inset();
1440 InsetIterator it = inset_iterator_begin(inset);
1441 InsetIterator const end = inset_iterator_end(inset);
1442 for (; it != end; ++it) {
1443 if (inset_code == InsetBase::NO_CODE
1444 || inset_code == it->lyxCode()) {
1445 LCursor tmpcur = cur;
1446 tmpcur.pushLeft(*it);
1447 it->dispatch(tmpcur, fr);
1454 case LFUN_BUFFER_LANGUAGE: {
1455 Buffer & buffer = *owner->buffer();
1456 Language const * oldL = buffer.params().language;
1457 Language const * newL = languages.getLanguage(argument);
1458 if (!newL || oldL == newL)
1461 if (oldL->rightToLeft() == newL->rightToLeft()
1462 && !buffer.isMultiLingual())
1463 buffer.changeLanguage(oldL, newL);
1465 buffer.updateDocLang(newL);
1469 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1470 string const fname =
1471 addName(addPath(package().user_support(), "templates/"),
1473 Buffer defaults(fname);
1475 istringstream ss(argument);
1478 int const unknown_tokens = defaults.readHeader(lex);
1480 if (unknown_tokens != 0) {
1481 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1482 << unknown_tokens << " unknown token"
1483 << (unknown_tokens == 1 ? "" : "s")
1487 if (defaults.writeFile(defaults.fileName()))
1488 // FIXME Should use bformat
1489 setMessage(_("Document defaults saved in ")
1490 + makeDisplayPath(fname));
1492 setErrorMessage(_("Unable to save document defaults"));
1496 case LFUN_BUFFER_PARAMS_APPLY: {
1497 biblio::CiteEngine const engine =
1498 owner->buffer()->params().cite_engine;
1500 istringstream ss(argument);
1503 int const unknown_tokens =
1504 owner->buffer()->readHeader(lex);
1506 if (unknown_tokens != 0) {
1507 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1508 << unknown_tokens << " unknown token"
1509 << (unknown_tokens == 1 ? "" : "s")
1512 if (engine == owner->buffer()->params().cite_engine)
1515 LCursor & cur = view()->cursor();
1516 FuncRequest fr(LFUN_INSET_REFRESH);
1518 InsetBase & inset = owner->buffer()->inset();
1519 InsetIterator it = inset_iterator_begin(inset);
1520 InsetIterator const end = inset_iterator_end(inset);
1521 for (; it != end; ++it)
1522 if (it->lyxCode() == InsetBase::CITE_CODE)
1523 it->dispatch(cur, fr);
1527 case LFUN_TEXTCLASS_APPLY: {
1528 Buffer * buffer = owner->buffer();
1530 lyx::textclass_type const old_class =
1531 buffer->params().textclass;
1533 loadTextclass(argument);
1535 std::pair<bool, lyx::textclass_type> const tc_pair =
1536 textclasslist.numberOfClass(argument);
1541 lyx::textclass_type const new_class = tc_pair.second;
1542 if (old_class == new_class)
1546 owner->message(_("Converting document to new document class..."));
1547 recordUndoFullDocument(view());
1548 buffer->params().textclass = new_class;
1549 StableDocIterator backcur(view()->cursor());
1550 ErrorList & el = buffer->errorList("Class Switch");
1551 lyx::cap::switchBetweenClasses(
1552 old_class, new_class,
1553 static_cast<InsetText &>(buffer->inset()), el);
1555 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1557 buffer->errors("Class Switch");
1558 updateLabels(*buffer);
1563 case LFUN_TEXTCLASS_LOAD:
1564 loadTextclass(argument);
1567 case LFUN_LYXRC_APPLY: {
1568 LyXRC const lyxrc_orig = lyxrc;
1570 istringstream ss(argument);
1571 bool const success = lyxrc.read(ss) == 0;
1574 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1575 << "Unable to read lyxrc data"
1580 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1585 view()->cursor().dispatch(cmd);
1586 updateforce |= view()->cursor().result().update();
1587 if (!view()->cursor().result().dispatched())
1588 updateforce |= view()->dispatch(cmd);
1593 if (view()->buffer()) {
1594 // Redraw screen unless explicitly told otherwise.
1595 // This also initializes the position cache for all insets
1596 // in (at least partially) visible top-level paragraphs.
1598 view()->update(Update::FitCursor | Update::Force);
1600 view()->update(Update::FitCursor);
1602 owner->redrawWorkArea();
1604 // if we executed a mutating lfun, mark the buffer as dirty
1606 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1607 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1608 view()->buffer()->markDirty();
1610 if (view()->cursor().inTexted()) {
1611 owner->updateLayoutChoice();
1616 // FIXME UNICODE: _() does not support anything but ascii.
1617 // Do we need a lyx::to_ascii() method?
1618 sendDispatchMessage(getMessage(), cmd);
1622 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1624 /* When an action did not originate from the UI/kbd, it makes
1625 * sense to avoid updating the GUI. It turns out that this
1626 * fixes bug 1941, for reasons that are described here:
1627 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1629 if (cmd.origin != FuncRequest::INTERNAL) {
1630 owner->updateMenubar();
1631 owner->updateToolbars();
1634 const bool verbose = (cmd.origin == FuncRequest::UI
1635 || cmd.origin == FuncRequest::COMMANDBUFFER);
1637 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1638 lyxerr[Debug::ACTION] << "dispatch msg is " << lyx::to_utf8(msg) << endl;
1640 owner->message(msg);
1644 docstring dispatch_msg = msg;
1645 if (!dispatch_msg.empty())
1646 dispatch_msg += ' ';
1648 string comname = lyxaction.getActionName(cmd.action);
1650 bool argsadded = false;
1652 if (!cmd.argument().empty()) {
1653 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1654 comname += ' ' + lyx::to_utf8(cmd.argument());
1659 string const shortcuts = toplevel_keymap->printbindings(cmd);
1661 if (!shortcuts.empty())
1662 comname += ": " + shortcuts;
1663 else if (!argsadded && !cmd.argument().empty())
1664 comname += ' ' + lyx::to_utf8(cmd.argument());
1666 if (!comname.empty()) {
1667 comname = rtrim(comname);
1668 dispatch_msg += lyx::from_utf8('(' + rtrim(comname) + ')');
1671 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1672 << lyx::to_utf8(dispatch_msg) << endl;
1673 if (!dispatch_msg.empty())
1674 owner->message(dispatch_msg);
1678 void LyXFunc::setupLocalKeymap()
1680 keyseq.stdmap = toplevel_keymap.get();
1681 keyseq.curmap = toplevel_keymap.get();
1682 cancel_meta_seq.stdmap = toplevel_keymap.get();
1683 cancel_meta_seq.curmap = toplevel_keymap.get();
1687 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1689 string initpath = lyxrc.document_path;
1690 string filename(name);
1692 if (view()->buffer()) {
1693 string const trypath = owner->buffer()->filePath();
1694 // If directory is writeable, use this as default.
1695 if (isDirWriteable(trypath))
1699 static int newfile_number;
1701 if (filename.empty()) {
1702 filename = addName(lyxrc.document_path,
1703 "newfile" + convert<string>(++newfile_number) + ".lyx");
1704 while (theBufferList().exists(filename) || fs::is_readable(filename)) {
1706 filename = addName(lyxrc.document_path,
1707 "newfile" + convert<string>(newfile_number) +
1712 // The template stuff
1715 FileDialog fileDlg(_("Select template file"),
1716 LFUN_SELECT_FILE_SYNC,
1717 make_pair(_("Documents|#o#O"), lyx::from_utf8(lyxrc.document_path)),
1718 make_pair(_("Templates|#T#t"), lyx::from_utf8(lyxrc.template_path)));
1720 FileDialog::Result result =
1721 fileDlg.open(lyx::from_utf8(lyxrc.template_path),
1722 FileFilterList(_("LyX Documents (*.lyx)")),
1725 if (result.first == FileDialog::Later)
1727 if (result.second.empty())
1729 templname = lyx::to_utf8(result.second);
1732 Buffer * const b = newFile(filename, templname, !name.empty());
1734 owner->setBuffer(b);
1738 void LyXFunc::open(string const & fname)
1740 string initpath = lyxrc.document_path;
1742 if (view()->buffer()) {
1743 string const trypath = owner->buffer()->filePath();
1744 // If directory is writeable, use this as default.
1745 if (isDirWriteable(trypath))
1751 if (fname.empty()) {
1752 FileDialog fileDlg(_("Select document to open"),
1754 make_pair(_("Documents|#o#O"), lyx::from_utf8(lyxrc.document_path)),
1755 make_pair(_("Examples|#E#e"), lyx::from_utf8(addPath(package().system_support(), "examples"))));
1757 FileDialog::Result result =
1758 fileDlg.open(lyx::from_utf8(initpath),
1759 FileFilterList(_("LyX Documents (*.lyx)")),
1762 if (result.first == FileDialog::Later)
1765 filename = lyx::to_utf8(result.second);
1767 // check selected filename
1768 if (filename.empty()) {
1769 owner->message(_("Canceled."));
1775 // get absolute path of file and add ".lyx" to the filename if
1777 string const fullpath = fileSearch(string(), filename, "lyx");
1778 if (!fullpath.empty()) {
1779 filename = fullpath;
1782 docstring const disp_fn = makeDisplayPath(filename);
1784 // if the file doesn't exist, let the user create one
1785 if (!fs::exists(filename)) {
1786 // the user specifically chose this name. Believe him.
1787 Buffer * const b = newFile(filename, string(), true);
1789 owner->setBuffer(b);
1793 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1796 if (owner->loadLyXFile(filename)) {
1797 str2 = bformat(_("Document %1$s opened."), disp_fn);
1799 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1801 owner->message(str2);
1805 void LyXFunc::doImport(string const & argument)
1808 string filename = split(argument, format, ' ');
1810 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1811 << " file: " << filename << endl;
1813 // need user interaction
1814 if (filename.empty()) {
1815 string initpath = lyxrc.document_path;
1817 if (view()->buffer()) {
1818 string const trypath = owner->buffer()->filePath();
1819 // If directory is writeable, use this as default.
1820 if (isDirWriteable(trypath))
1824 docstring const text = bformat(_("Select %1$s file to import"),
1825 formats.prettyName(format));
1827 FileDialog fileDlg(text,
1829 make_pair(_("Documents|#o#O"), lyx::from_utf8(lyxrc.document_path)),
1830 make_pair(_("Examples|#E#e"),
1831 lyx::from_utf8(addPath(package().system_support(), "examples"))));
1833 docstring filter = formats.prettyName(format);
1836 filter += lyx::from_utf8(formats.extension(format));
1839 FileDialog::Result result =
1840 fileDlg.open(lyx::from_utf8(initpath),
1841 FileFilterList(filter),
1844 if (result.first == FileDialog::Later)
1847 filename = lyx::to_utf8(result.second);
1849 // check selected filename
1850 if (filename.empty())
1851 owner->message(_("Canceled."));
1854 if (filename.empty())
1857 // get absolute path of file
1858 filename = makeAbsPath(filename);
1860 string const lyxfile = changeExtension(filename, ".lyx");
1862 // Check if the document already is open
1863 if (lyx::use_gui && theBufferList().exists(lyxfile)) {
1864 if (!theBufferList().close(theBufferList().getBuffer(lyxfile), true)) {
1865 owner->message(_("Canceled."));
1870 // if the file exists already, and we didn't do
1871 // -i lyx thefile.lyx, warn
1872 if (fs::exists(lyxfile) && filename != lyxfile) {
1873 docstring const file = makeDisplayPath(lyxfile, 30);
1875 docstring text = bformat(_("The document %1$s already exists.\n\n"
1876 "Do you want to over-write that document?"), file);
1877 int const ret = Alert::prompt(_("Over-write document?"),
1878 text, 0, 1, _("&Over-write"), _("&Cancel"));
1881 owner->message(_("Canceled."));
1886 ErrorList errorList;
1887 Importer::Import(owner, filename, format, errorList);
1888 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1892 void LyXFunc::closeBuffer()
1894 // save current cursor position
1895 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1896 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1897 if (theBufferList().close(owner->buffer(), true) && !quitting) {
1898 if (theBufferList().empty()) {
1899 // need this otherwise SEGV may occur while
1900 // trying to set variables that don't exist
1901 // since there's no current buffer
1902 owner->getDialogs().hideBufferDependent();
1904 owner->setBuffer(theBufferList().first());
1910 // Each "owner" should have it's own message method. lyxview and
1911 // the minibuffer would use the minibuffer, but lyxserver would
1912 // send an ERROR signal to its client. Alejandro 970603
1913 // This function is bit problematic when it comes to NLS, to make the
1914 // lyx servers client be language indepenent we must not translate
1915 // strings sent to this func.
1916 void LyXFunc::setErrorMessage(docstring const & m) const
1918 dispatch_buffer = m;
1923 void LyXFunc::setMessage(docstring const & m) const
1925 dispatch_buffer = m;
1929 string const LyXFunc::viewStatusMessage()
1931 // When meta-fake key is pressed, show the key sequence so far + "M-".
1933 return keyseq.print() + "M-";
1935 // Else, when a non-complete key sequence is pressed,
1936 // show the available options.
1937 if (keyseq.length() > 0 && !keyseq.deleted())
1938 return keyseq.printOptions();
1940 if (!view()->buffer())
1941 return lyx::to_utf8(_("Welcome to LyX!"));
1943 return view()->cursor().currentState();
1947 BufferView * LyXFunc::view() const
1949 BOOST_ASSERT(owner);
1950 return owner->view();
1954 bool LyXFunc::wasMetaKey() const
1956 return (meta_fake_bit != key_modifier::none);
1962 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1964 // Why the switch you might ask. It is a trick to ensure that all
1965 // the elements in the LyXRCTags enum is handled. As you can see
1966 // there are no breaks at all. So it is just a huge fall-through.
1967 // The nice thing is that we will get a warning from the compiler
1968 // if we forget an element.
1969 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1971 case LyXRC::RC_ACCEPT_COMPOUND:
1972 case LyXRC::RC_ALT_LANG:
1973 case LyXRC::RC_ASCIIROFF_COMMAND:
1974 case LyXRC::RC_ASCII_LINELEN:
1975 case LyXRC::RC_AUTOREGIONDELETE:
1976 case LyXRC::RC_AUTORESET_OPTIONS:
1977 case LyXRC::RC_AUTOSAVE:
1978 case LyXRC::RC_AUTO_NUMBER:
1979 case LyXRC::RC_BACKUPDIR_PATH:
1980 case LyXRC::RC_BIBTEX_COMMAND:
1981 case LyXRC::RC_BINDFILE:
1982 case LyXRC::RC_CHECKLASTFILES:
1983 case LyXRC::RC_USELASTFILEPOS:
1984 case LyXRC::RC_LOADSESSION:
1985 case LyXRC::RC_CHKTEX_COMMAND:
1986 case LyXRC::RC_CONVERTER:
1987 case LyXRC::RC_COPIER:
1988 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1989 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1990 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1991 case LyXRC::RC_DATE_INSERT_FORMAT:
1992 case LyXRC::RC_DEFAULT_LANGUAGE:
1993 case LyXRC::RC_DEFAULT_PAPERSIZE:
1994 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1995 case LyXRC::RC_DISPLAY_GRAPHICS:
1996 case LyXRC::RC_DOCUMENTPATH:
1997 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1998 if (fs::exists(lyxrc_new.document_path) &&
1999 fs::is_directory(lyxrc_new.document_path)) {
2000 using lyx::support::package;
2001 package().document_dir() = lyxrc.document_path;
2004 case LyXRC::RC_ESC_CHARS:
2005 case LyXRC::RC_FONT_ENCODING:
2006 case LyXRC::RC_FORMAT:
2007 case LyXRC::RC_INDEX_COMMAND:
2008 case LyXRC::RC_INPUT:
2009 case LyXRC::RC_KBMAP:
2010 case LyXRC::RC_KBMAP_PRIMARY:
2011 case LyXRC::RC_KBMAP_SECONDARY:
2012 case LyXRC::RC_LABEL_INIT_LENGTH:
2013 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2014 case LyXRC::RC_LANGUAGE_AUTO_END:
2015 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2016 case LyXRC::RC_LANGUAGE_COMMAND_END:
2017 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2018 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2019 case LyXRC::RC_LANGUAGE_PACKAGE:
2020 case LyXRC::RC_LANGUAGE_USE_BABEL:
2021 case LyXRC::RC_MAKE_BACKUP:
2022 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2023 case LyXRC::RC_NUMLASTFILES:
2024 case LyXRC::RC_PATH_PREFIX:
2025 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2026 using lyx::support::prependEnvPath;
2027 prependEnvPath("PATH", lyxrc.path_prefix);
2029 case LyXRC::RC_PERS_DICT:
2030 case LyXRC::RC_POPUP_BOLD_FONT:
2031 case LyXRC::RC_POPUP_FONT_ENCODING:
2032 case LyXRC::RC_POPUP_NORMAL_FONT:
2033 case LyXRC::RC_PREVIEW:
2034 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2035 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2036 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2037 case LyXRC::RC_PRINTCOPIESFLAG:
2038 case LyXRC::RC_PRINTER:
2039 case LyXRC::RC_PRINTEVENPAGEFLAG:
2040 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2041 case LyXRC::RC_PRINTFILEEXTENSION:
2042 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2043 case LyXRC::RC_PRINTODDPAGEFLAG:
2044 case LyXRC::RC_PRINTPAGERANGEFLAG:
2045 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2046 case LyXRC::RC_PRINTPAPERFLAG:
2047 case LyXRC::RC_PRINTREVERSEFLAG:
2048 case LyXRC::RC_PRINTSPOOL_COMMAND:
2049 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2050 case LyXRC::RC_PRINTTOFILE:
2051 case LyXRC::RC_PRINTTOPRINTER:
2052 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2053 case LyXRC::RC_PRINT_COMMAND:
2054 case LyXRC::RC_RTL_SUPPORT:
2055 case LyXRC::RC_SCREEN_DPI:
2056 case LyXRC::RC_SCREEN_FONT_ENCODING:
2057 case LyXRC::RC_SCREEN_FONT_ROMAN:
2058 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2059 case LyXRC::RC_SCREEN_FONT_SANS:
2060 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2061 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2062 case LyXRC::RC_SCREEN_FONT_SIZES:
2063 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2064 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2065 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2066 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2067 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2068 case LyXRC::RC_SCREEN_ZOOM:
2069 case LyXRC::RC_SERVERPIPE:
2070 case LyXRC::RC_SET_COLOR:
2071 case LyXRC::RC_SHOW_BANNER:
2072 case LyXRC::RC_SPELL_COMMAND:
2073 case LyXRC::RC_TEMPDIRPATH:
2074 case LyXRC::RC_TEMPLATEPATH:
2075 case LyXRC::RC_TEX_ALLOWS_SPACES:
2076 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2077 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2078 namespace os = lyx::support::os;
2079 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2081 case LyXRC::RC_UIFILE:
2082 case LyXRC::RC_USER_EMAIL:
2083 case LyXRC::RC_USER_NAME:
2084 case LyXRC::RC_USETEMPDIR:
2085 case LyXRC::RC_USE_ALT_LANG:
2086 case LyXRC::RC_USE_ESC_CHARS:
2087 case LyXRC::RC_USE_INP_ENC:
2088 case LyXRC::RC_USE_PERS_DICT:
2089 case LyXRC::RC_USE_SPELL_LIB:
2090 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2091 case LyXRC::RC_VIEWER:
2092 case LyXRC::RC_LAST: