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/Application.h"
76 #include "frontends/Alert.h"
77 #include "frontends/Dialogs.h"
78 #include "frontends/FileDialog.h"
79 #include "frontends/lyx_gui.h"
80 #include "frontends/LyXKeySym.h"
81 #include "frontends/LyXView.h"
82 #include "frontends/Menubar.h"
83 #include "frontends/Toolbars.h"
85 #include "support/environment.h"
86 #include "support/filefilterlist.h"
87 #include "support/filetools.h"
88 #include "support/forkedcontr.h"
89 #include "support/fs_extras.h"
90 #include "support/lstrings.h"
91 #include "support/path.h"
92 #include "support/package.h"
93 #include "support/systemcall.h"
94 #include "support/convert.h"
95 #include "support/os.h"
97 #include <boost/current_function.hpp>
98 #include <boost/filesystem/operations.hpp>
102 using bv_funcs::freefont2string;
104 using lyx::docstring;
106 using lyx::support::absolutePath;
107 using lyx::support::addName;
108 using lyx::support::addPath;
109 using lyx::support::bformat;
110 using lyx::support::changeExtension;
111 using lyx::support::contains;
112 using lyx::support::FileFilterList;
113 using lyx::support::fileSearch;
114 using lyx::support::ForkedcallsController;
115 using lyx::support::i18nLibFileSearch;
116 using lyx::support::isDirWriteable;
117 using lyx::support::isFileReadable;
118 using lyx::support::isStrInt;
119 using lyx::support::makeAbsPath;
120 using lyx::support::makeDisplayPath;
121 using lyx::support::package;
122 using lyx::support::quoteName;
123 using lyx::support::rtrim;
124 using lyx::support::split;
125 using lyx::support::subst;
126 using lyx::support::Systemcall;
127 using lyx::support::token;
128 using lyx::support::trim;
129 using lyx::support::prefixIs;
132 using std::make_pair;
135 using std::istringstream;
136 using std::ostringstream;
138 namespace biblio = lyx::biblio;
139 namespace fs = boost::filesystem;
142 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
145 extern tex_accent_struct get_accent(kb_action action);
150 bool getStatus(LCursor cursor,
151 FuncRequest const & cmd, FuncStatus & status)
153 // Try to fix cursor in case it is broken.
154 cursor.fixIfBroken();
156 // This is, of course, a mess. Better create a new doc iterator and use
157 // this in Inset::getStatus. This might require an additional
158 // BufferView * arg, though (which should be avoided)
159 //LCursor safe = *this;
161 for ( ; cursor.depth(); cursor.pop()) {
162 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
163 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
164 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
165 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
167 // The inset's getStatus() will return 'true' if it made
168 // a definitive decision on whether it want to handle the
169 // request or not. The result of this decision is put into
170 // the 'status' parameter.
171 if (cursor.inset().getStatus(cursor, cmd, status)) {
180 /** Return the change status at cursor position, taking in account the
181 * status at each level of the document iterator (a table in a deleted
182 * footnote is deleted).
183 * When \param outer is true, the top slice is not looked at.
185 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
187 size_t const depth = dit.depth() - (outer ? 1 : 0);
189 for (size_t i = 0 ; i < depth ; ++i) {
190 CursorSlice const & slice = dit[i];
191 if (!slice.inset().inMathed()
192 && slice.pos() < slice.paragraph().size()) {
193 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
194 if (ch != Change::UNCHANGED)
198 return Change::UNCHANGED;
203 LyXFunc::LyXFunc(LyXView * lv)
206 keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
207 cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
208 meta_fake_bit(key_modifier::none)
213 void LyXFunc::handleKeyFunc(kb_action action)
215 char c = encoded_last_key;
217 if (keyseq.length()) {
221 owner->view()->getIntl().getTransManager()
222 .deadkey(c, get_accent(action).accent, view()->getLyXText());
223 // Need to clear, in case the minibuffer calls these
226 // copied verbatim from do_accent_char
227 view()->cursor().resetAnchor();
232 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
234 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
236 // Do nothing if we have nothing (JMarc)
237 if (!keysym->isOK()) {
238 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
243 if (keysym->isModifier()) {
244 lyxerr[Debug::KEY] << "isModifier true" << endl;
248 Encoding const * encoding = view()->cursor().getEncoding();
250 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
251 size_t encoded_last_key = keysym->getUCSEncoded();
253 // Do a one-deep top-level lookup for
254 // cancel and meta-fake keys. RVDK_PATCH_5
255 cancel_meta_seq.reset();
257 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
258 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
259 << " action first set to [" << func.action << ']'
262 // When not cancel or meta-fake, do the normal lookup.
263 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
264 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
265 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
266 // remove Caps Lock and Mod2 as a modifiers
267 func = keyseq.addkey(keysym, (state | meta_fake_bit));
268 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
269 << "action now set to ["
270 << func.action << ']' << endl;
273 // Dont remove this unless you know what you are doing.
274 meta_fake_bit = key_modifier::none;
276 // Can this happen now ?
277 if (func.action == LFUN_NOACTION) {
278 func = FuncRequest(LFUN_COMMAND_PREFIX);
281 if (lyxerr.debugging(Debug::KEY)) {
282 lyxerr << BOOST_CURRENT_FUNCTION
284 << func.action << "]["
285 << keyseq.print() << ']'
289 // already here we know if it any point in going further
290 // why not return already here if action == -1 and
291 // num_bytes == 0? (Lgb)
293 if (keyseq.length() > 1) {
294 owner->message(lyx::from_utf8(keyseq.print()));
298 // Maybe user can only reach the key via holding down shift.
299 // Let's see. But only if shift is the only modifier
300 if (func.action == LFUN_UNKNOWN_ACTION &&
301 state == key_modifier::shift) {
302 lyxerr[Debug::KEY] << "Trying without shift" << endl;
303 func = keyseq.addkey(keysym, key_modifier::none);
304 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
307 if (func.action == LFUN_UNKNOWN_ACTION) {
308 // Hmm, we didn't match any of the keysequences. See
309 // if it's normal insertable text not already covered
311 if (keysym->isText() && keyseq.length() == 1) {
312 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
313 func = FuncRequest(LFUN_SELF_INSERT,
314 FuncRequest::KEYBOARD);
316 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
317 owner->message(_("Unknown function."));
322 if (func.action == LFUN_SELF_INSERT) {
323 if (encoded_last_key != 0) {
324 docstring const arg(1, encoded_last_key);
325 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
326 FuncRequest::KEYBOARD));
328 << "SelfInsert arg[`" << lyx::to_utf8(arg) << "']" << endl;
336 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
338 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
340 LCursor & cur = view()->cursor();
342 /* In LyX/Mac, when a dialog is open, the menus of the
343 application can still be accessed without giving focus to
344 the main window. In this case, we want to disable the menu
345 entries that are buffer-related.
347 Note that this code is not perfect, as bug 1941 attests:
348 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
351 if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
354 buf = owner->buffer();
356 if (cmd.action == LFUN_NOACTION) {
357 flag.message(lyx::from_utf8(N_("Nothing to do")));
362 switch (cmd.action) {
363 case LFUN_UNKNOWN_ACTION:
364 #ifndef HAVE_LIBAIKSAURUS
365 case LFUN_THESAURUS_ENTRY:
371 flag |= lyx_gui::getStatus(cmd);
374 if (flag.unknown()) {
375 flag.message(lyx::from_utf8(N_("Unknown action")));
379 if (!flag.enabled()) {
380 if (flag.message().empty())
381 flag.message(lyx::from_utf8(N_("Command disabled")));
385 // Check whether we need a buffer
386 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
388 flag.message(lyx::from_utf8(N_("Command not allowed with"
389 "out any document open")));
394 // I would really like to avoid having this switch and rather try to
395 // encode this in the function itself.
396 // -- And I'd rather let an inset decide which LFUNs it is willing
397 // to handle (Andre')
399 switch (cmd.action) {
400 case LFUN_TOOLTIPS_TOGGLE:
401 flag.setOnOff(owner->getDialogs().tooltipsEnabled());
404 case LFUN_BUFFER_TOGGLE_READ_ONLY:
405 flag.setOnOff(buf->isReadonly());
408 case LFUN_BUFFER_SWITCH:
409 // toggle on the current buffer, but do not toggle off
410 // the other ones (is that a good idea?)
411 if (lyx::to_utf8(cmd.argument()) == buf->fileName())
415 case LFUN_BUFFER_EXPORT:
416 enable = cmd.argument() == "custom"
417 || Exporter::isExportable(*buf, lyx::to_utf8(cmd.argument()));
420 case LFUN_BUFFER_CHKTEX:
421 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
424 case LFUN_BUILD_PROGRAM:
425 enable = Exporter::isExportable(*buf, "program");
428 case LFUN_LAYOUT_TABULAR:
429 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
433 case LFUN_LAYOUT_PARAGRAPH:
434 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
437 case LFUN_VC_REGISTER:
438 enable = !buf->lyxvc().inUse();
440 case LFUN_VC_CHECK_IN:
441 enable = buf->lyxvc().inUse() && !buf->isReadonly();
443 case LFUN_VC_CHECK_OUT:
444 enable = buf->lyxvc().inUse() && buf->isReadonly();
447 case LFUN_VC_UNDO_LAST:
448 enable = buf->lyxvc().inUse();
450 case LFUN_BUFFER_RELOAD:
451 enable = !buf->isUnnamed() && !buf->isClean();
454 case LFUN_INSET_SETTINGS: {
458 InsetBase::Code code = cur.inset().lyxCode();
460 case InsetBase::TABULAR_CODE:
461 enable = cmd.argument() == "tabular";
463 case InsetBase::ERT_CODE:
464 enable = cmd.argument() == "ert";
466 case InsetBase::FLOAT_CODE:
467 enable = cmd.argument() == "float";
469 case InsetBase::WRAP_CODE:
470 enable = cmd.argument() == "wrap";
472 case InsetBase::NOTE_CODE:
473 enable = cmd.argument() == "note";
475 case InsetBase::BRANCH_CODE:
476 enable = cmd.argument() == "branch";
478 case InsetBase::BOX_CODE:
479 enable = cmd.argument() == "box";
487 case LFUN_INSET_APPLY: {
488 string const name = cmd.getArg(0);
489 InsetBase * inset = owner->getDialogs().getOpenInset(name);
491 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
493 bool const success = inset->getStatus(cur, fr, fs);
494 // Every inset is supposed to handle this
495 BOOST_ASSERT(success);
498 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
499 flag |= getStatus(fr);
501 enable = flag.enabled();
505 case LFUN_DIALOG_SHOW: {
506 string const name = cmd.getArg(0);
508 enable = name == "aboutlyx"
512 || name == "texinfo";
513 else if (name == "print")
514 enable = Exporter::isExportable(*buf, "dvi")
515 && lyxrc.print_command != "none";
516 else if (name == "character" || name == "mathpanel")
517 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
518 else if (name == "latexlog")
519 enable = isFileReadable(buf->getLogName().second);
520 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
521 else if (name == "spellchecker")
524 else if (name == "vclog")
525 enable = buf->lyxvc().inUse();
526 else if (name == "view-source")
531 case LFUN_DIALOG_SHOW_NEW_INSET:
532 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
535 case LFUN_DIALOG_UPDATE: {
536 string const name = cmd.getArg(0);
538 enable = name == "prefs";
542 case LFUN_CITATION_INSERT: {
543 FuncRequest fr(LFUN_INSET_INSERT, "citation");
544 enable = getStatus(fr).enabled();
548 case LFUN_BUFFER_WRITE: {
549 enable = view()->buffer()->isUnnamed()
550 || !view()->buffer()->isClean();
554 // this one is difficult to get right. As a half-baked
555 // solution, we consider only the first action of the sequence
556 case LFUN_COMMAND_SEQUENCE: {
557 // argument contains ';'-terminated commands
558 string const firstcmd = token(lyx::to_utf8(cmd.argument()), ';', 0);
559 FuncRequest func(lyxaction.lookupFunc(firstcmd));
560 func.origin = cmd.origin;
561 flag = getStatus(func);
564 case LFUN_BUFFER_NEW:
565 case LFUN_BUFFER_NEW_TEMPLATE:
566 case LFUN_WORD_FIND_FORWARD:
567 case LFUN_WORD_FIND_BACKWARD:
568 case LFUN_COMMAND_PREFIX:
569 case LFUN_COMMAND_EXECUTE:
571 case LFUN_META_PREFIX:
572 case LFUN_BUFFER_CLOSE:
573 case LFUN_BUFFER_WRITE_AS:
574 case LFUN_BUFFER_UPDATE:
575 case LFUN_BUFFER_VIEW:
576 case LFUN_BUFFER_IMPORT:
579 case LFUN_BUFFER_AUTO_SAVE:
580 case LFUN_RECONFIGURE:
584 case LFUN_DROP_LAYOUTS_CHOICE:
586 case LFUN_SERVER_GET_NAME:
587 case LFUN_SERVER_NOTIFY:
588 case LFUN_SERVER_GOTO_FILE_ROW:
589 case LFUN_DIALOG_HIDE:
590 case LFUN_DIALOG_DISCONNECT_INSET:
591 case LFUN_BUFFER_CHILD_OPEN:
592 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
593 case LFUN_KEYMAP_OFF:
594 case LFUN_KEYMAP_PRIMARY:
595 case LFUN_KEYMAP_SECONDARY:
596 case LFUN_KEYMAP_TOGGLE:
598 case LFUN_BUFFER_EXPORT_CUSTOM:
599 case LFUN_BUFFER_PRINT:
600 case LFUN_PREFERENCES_SAVE:
601 case LFUN_SCREEN_FONT_UPDATE:
604 case LFUN_EXTERNAL_EDIT:
605 case LFUN_GRAPHICS_EDIT:
606 case LFUN_ALL_INSETS_TOGGLE:
607 case LFUN_BUFFER_LANGUAGE:
608 case LFUN_TEXTCLASS_APPLY:
609 case LFUN_TEXTCLASS_LOAD:
610 case LFUN_BUFFER_SAVE_AS_DEFAULT:
611 case LFUN_BUFFER_PARAMS_APPLY:
612 case LFUN_LYXRC_APPLY:
613 case LFUN_BUFFER_NEXT:
614 case LFUN_BUFFER_PREVIOUS:
615 // these are handled in our dispatch()
620 if (!::getStatus(cur, cmd, flag))
621 flag = view()->getStatus(cmd);
627 // Can we use a readonly buffer?
628 if (buf && buf->isReadonly()
629 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
630 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
631 flag.message(lyx::from_utf8(N_("Document is read-only")));
635 // Are we in a DELETED change-tracking region?
636 if (buf && buf->params().tracking_changes
637 && lookupChangeType(cur, true) == Change::DELETED
638 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
639 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
640 flag.message(lyx::from_utf8(N_("This portion of the document is deleted.")));
644 // the default error message if we disable the command
645 if (!flag.enabled() && flag.message().empty())
646 flag.message(lyx::from_utf8(N_("Command disabled")));
652 bool LyXFunc::ensureBufferClean(BufferView * bv)
654 Buffer & buf = *bv->buffer();
658 docstring const file = makeDisplayPath(buf.fileName(), 30);
659 docstring text = bformat(_("The document %1$s has unsaved "
660 "changes.\n\nDo you want to save "
661 "the document?"), file);
662 int const ret = Alert::prompt(_("Save changed document?"),
663 text, 0, 1, _("&Save"),
667 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
669 return buf.isClean();
675 void showPrintError(string const & name)
677 docstring str = bformat(_("Could not print the document %1$s.\n"
678 "Check that your printer is set up correctly."),
679 makeDisplayPath(name, 50));
680 Alert::error(_("Print document failed"), str);
684 void loadTextclass(string const & name)
686 std::pair<bool, lyx::textclass_type> const tc_pair =
687 textclasslist.numberOfClass(name);
689 if (!tc_pair.first) {
690 lyxerr << "Document class \"" << name
691 << "\" does not exist."
696 lyx::textclass_type const tc = tc_pair.second;
698 if (!textclasslist[tc].load()) {
699 docstring s = bformat(_("The document could not be converted\n"
700 "into the document class %1$s."),
701 lyx::from_utf8(textclasslist[tc].name()));
702 Alert::error(_("Could not change class"), s);
707 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
712 void LyXFunc::dispatch(FuncRequest const & cmd)
714 BOOST_ASSERT(view());
715 string const argument = lyx::to_utf8(cmd.argument());
716 kb_action const action = cmd.action;
718 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
719 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
721 // we have not done anything wrong yet.
723 dispatch_buffer.erase();
725 // redraw the screen at the end (first of the two drawing steps).
726 //This is done unless explicitely requested otherwise
728 // also do the second redrawing step. Only done if requested.
729 bool updateforce = false;
731 FuncStatus const flag = getStatus(cmd);
732 if (!flag.enabled()) {
733 // We cannot use this function here
734 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
735 << lyxaction.getActionName(action)
736 << " [" << action << "] is disabled at this location"
738 setErrorMessage(flag.message());
742 case LFUN_WORD_FIND_FORWARD:
743 case LFUN_WORD_FIND_BACKWARD: {
744 static string last_search;
745 string searched_string;
747 if (!argument.empty()) {
748 last_search = argument;
749 searched_string = argument;
751 searched_string = last_search;
754 if (searched_string.empty())
757 bool const fw = action == LFUN_WORD_FIND_FORWARD;
759 lyx::find::find2string(searched_string, true, false, fw);
760 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
764 case LFUN_COMMAND_PREFIX:
765 owner->message(lyx::from_utf8(keyseq.printOptions()));
768 case LFUN_COMMAND_EXECUTE:
769 owner->getToolbars().display("minibuffer", true);
770 owner->focus_command_buffer();
775 meta_fake_bit = key_modifier::none;
776 if (view()->buffer())
777 // cancel any selection
778 dispatch(FuncRequest(LFUN_MARK_OFF));
779 setMessage(_("Cancel"));
782 case LFUN_META_PREFIX:
783 meta_fake_bit = key_modifier::alt;
784 setMessage(lyx::from_utf8(keyseq.print()));
787 case LFUN_BUFFER_TOGGLE_READ_ONLY:
788 if (owner->buffer()->lyxvc().inUse())
789 owner->buffer()->lyxvc().toggleReadOnly();
791 owner->buffer()->setReadonly(
792 !owner->buffer()->isReadonly());
795 // --- Menus -----------------------------------------------
796 case LFUN_BUFFER_NEW:
797 menuNew(argument, false);
800 case LFUN_BUFFER_NEW_TEMPLATE:
801 menuNew(argument, true);
804 case LFUN_BUFFER_CLOSE:
808 case LFUN_BUFFER_WRITE:
809 if (!owner->buffer()->isUnnamed()) {
810 docstring const str = bformat(_("Saving document %1$s..."),
811 makeDisplayPath(owner->buffer()->fileName()));
813 menuWrite(owner->buffer());
814 owner->message(str + _(" done."));
816 writeAs(owner->buffer());
820 case LFUN_BUFFER_WRITE_AS:
821 writeAs(owner->buffer(), argument);
825 case LFUN_BUFFER_RELOAD: {
826 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
827 docstring text = bformat(_("Any changes will be lost. Are you sure "
828 "you want to revert to the saved version of the document %1$s?"), file);
829 int const ret = Alert::prompt(_("Revert to saved document?"),
830 text, 0, 1, _("&Revert"), _("&Cancel"));
837 case LFUN_BUFFER_UPDATE:
838 Exporter::Export(owner->buffer(), argument, true);
841 case LFUN_BUFFER_VIEW:
842 Exporter::preview(owner->buffer(), argument);
845 case LFUN_BUILD_PROGRAM:
846 Exporter::Export(owner->buffer(), "program", true);
849 case LFUN_BUFFER_CHKTEX:
850 owner->buffer()->runChktex();
853 case LFUN_BUFFER_EXPORT:
854 if (argument == "custom")
855 owner->getDialogs().show("sendto");
857 Exporter::Export(owner->buffer(), argument, false);
861 case LFUN_BUFFER_EXPORT_CUSTOM: {
863 string command = split(argument, format_name, ' ');
864 Format const * format = formats.getFormat(format_name);
866 lyxerr << "Format \"" << format_name
867 << "\" not recognized!"
872 Buffer * buffer = owner->buffer();
874 // The name of the file created by the conversion process
877 // Output to filename
878 if (format->name() == "lyx") {
879 string const latexname =
880 buffer->getLatexName(false);
881 filename = changeExtension(latexname,
882 format->extension());
883 filename = addName(buffer->temppath(), filename);
885 if (!buffer->writeFile(filename))
889 Exporter::Export(buffer, format_name, true, filename);
892 // Substitute $$FName for filename
893 if (!contains(command, "$$FName"))
894 command = "( " + command + " ) < $$FName";
895 command = subst(command, "$$FName", filename);
897 // Execute the command in the background
899 call.startscript(Systemcall::DontWait, command);
903 case LFUN_BUFFER_PRINT: {
906 string command = split(split(argument, target, ' '),
910 || target_name.empty()
911 || command.empty()) {
912 lyxerr << "Unable to parse \""
913 << argument << '"' << std::endl;
916 if (target != "printer" && target != "file") {
917 lyxerr << "Unrecognized target \""
918 << target << '"' << std::endl;
922 Buffer * buffer = owner->buffer();
924 if (!Exporter::Export(buffer, "dvi", true)) {
925 showPrintError(buffer->fileName());
929 // Push directory path.
930 string const path = buffer->temppath();
931 lyx::support::Path p(path);
933 // there are three cases here:
934 // 1. we print to a file
935 // 2. we print directly to a printer
936 // 3. we print using a spool command (print to file first)
939 string const dviname =
940 changeExtension(buffer->getLatexName(true),
943 if (target == "printer") {
944 if (!lyxrc.print_spool_command.empty()) {
945 // case 3: print using a spool
946 string const psname =
947 changeExtension(dviname,".ps");
948 command += lyxrc.print_to_file
951 + quoteName(dviname);
954 lyxrc.print_spool_command +' ';
955 if (target_name != "default") {
956 command2 += lyxrc.print_spool_printerprefix
960 command2 += quoteName(psname);
962 // If successful, then spool command
963 res = one.startscript(
968 res = one.startscript(
969 Systemcall::DontWait,
972 // case 2: print directly to a printer
973 res = one.startscript(
974 Systemcall::DontWait,
975 command + quoteName(dviname));
979 // case 1: print to a file
980 command += lyxrc.print_to_file
981 + quoteName(makeAbsPath(target_name,
984 + quoteName(dviname);
985 res = one.startscript(Systemcall::DontWait,
990 showPrintError(buffer->fileName());
994 case LFUN_BUFFER_IMPORT:
999 if (view()->buffer()) {
1000 // save cursor Position for opened files to .lyx/session
1001 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1002 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1003 // save bookmarks to .lyx/session
1004 view()->saveSavedPositions();
1006 quitLyX(argument == "force");
1009 case LFUN_TOC_VIEW: {
1010 InsetCommandParams p("tableofcontents");
1011 string const data = InsetCommandMailer::params2string("toc", p);
1012 owner->getDialogs().show("toc", data, 0);
1016 case LFUN_BUFFER_AUTO_SAVE:
1020 case LFUN_RECONFIGURE:
1021 reconfigure(view());
1024 case LFUN_HELP_OPEN: {
1025 string const arg = argument;
1027 setErrorMessage(_("Missing argument"));
1030 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1031 if (fname.empty()) {
1032 lyxerr << "LyX: unable to find documentation file `"
1033 << arg << "'. Bad installation?" << endl;
1036 owner->message(bformat(_("Opening help file %1$s..."),
1037 makeDisplayPath(fname)));
1038 owner->loadLyXFile(fname, false);
1042 // --- version control -------------------------------
1043 case LFUN_VC_REGISTER:
1044 if (!ensureBufferClean(view()))
1046 if (!owner->buffer()->lyxvc().inUse()) {
1047 owner->buffer()->lyxvc().registrer();
1052 case LFUN_VC_CHECK_IN:
1053 if (!ensureBufferClean(view()))
1055 if (owner->buffer()->lyxvc().inUse()
1056 && !owner->buffer()->isReadonly()) {
1057 owner->buffer()->lyxvc().checkIn();
1062 case LFUN_VC_CHECK_OUT:
1063 if (!ensureBufferClean(view()))
1065 if (owner->buffer()->lyxvc().inUse()
1066 && owner->buffer()->isReadonly()) {
1067 owner->buffer()->lyxvc().checkOut();
1072 case LFUN_VC_REVERT:
1073 owner->buffer()->lyxvc().revert();
1077 case LFUN_VC_UNDO_LAST:
1078 owner->buffer()->lyxvc().undoLast();
1082 // --- buffers ----------------------------------------
1083 case LFUN_BUFFER_SWITCH:
1084 owner->setBuffer(theApp->bufferList().getBuffer(argument));
1087 case LFUN_BUFFER_NEXT:
1088 owner->setBuffer(theApp->bufferList().next(view()->buffer()));
1091 case LFUN_BUFFER_PREVIOUS:
1092 owner->setBuffer(theApp->bufferList().previous(view()->buffer()));
1096 newFile(view(), argument);
1099 case LFUN_FILE_OPEN:
1103 case LFUN_DROP_LAYOUTS_CHOICE:
1104 owner->getToolbars().openLayoutList();
1107 case LFUN_MENU_OPEN:
1108 owner->getMenubar().openByName(lyx::from_utf8(argument));
1111 // --- lyxserver commands ----------------------------
1112 case LFUN_SERVER_GET_NAME:
1113 setMessage(lyx::from_utf8(owner->buffer()->fileName()));
1114 lyxerr[Debug::INFO] << "FNAME["
1115 << owner->buffer()->fileName()
1119 case LFUN_SERVER_NOTIFY:
1120 dispatch_buffer = lyx::from_utf8(keyseq.print());
1121 theApp->server().notifyClient(lyx::to_utf8(dispatch_buffer));
1124 case LFUN_SERVER_GOTO_FILE_ROW: {
1127 istringstream is(argument);
1128 is >> file_name >> row;
1129 if (prefixIs(file_name, package().temp_dir())) {
1130 // Needed by inverse dvi search. If it is a file
1131 // in tmpdir, call the apropriated function
1132 owner->setBuffer(theApp->bufferList().getBufferFromTmp(file_name));
1134 // Must replace extension of the file to be .lyx
1135 // and get full path
1136 string const s = changeExtension(file_name, ".lyx");
1137 // Either change buffer or load the file
1138 if (theApp->bufferList().exists(s)) {
1139 owner->setBuffer(theApp->bufferList().getBuffer(s));
1141 owner->loadLyXFile(s);
1145 view()->setCursorFromRow(row);
1148 // see BufferView::center()
1152 case LFUN_DIALOG_SHOW: {
1153 string const name = cmd.getArg(0);
1154 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1156 if (name == "character") {
1157 data = freefont2string();
1159 owner->getDialogs().show("character", data);
1160 } else if (name == "latexlog") {
1161 pair<Buffer::LogType, string> const logfile =
1162 owner->buffer()->getLogName();
1163 switch (logfile.first) {
1164 case Buffer::latexlog:
1167 case Buffer::buildlog:
1171 data += LyXLex::quoteString(logfile.second);
1172 owner->getDialogs().show("log", data);
1173 } else if (name == "vclog") {
1174 string const data = "vc " +
1175 LyXLex::quoteString(owner->buffer()->lyxvc().getLogFile());
1176 owner->getDialogs().show("log", data);
1178 owner->getDialogs().show(name, data);
1182 case LFUN_DIALOG_SHOW_NEW_INSET: {
1183 string const name = cmd.getArg(0);
1184 string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1185 if (name == "bibitem" ||
1192 InsetCommandParams p(name);
1193 data = InsetCommandMailer::params2string(name, p);
1194 } else if (name == "include") {
1195 InsetCommandParams p(data);
1196 data = InsetIncludeMailer::params2string(p);
1197 } else if (name == "box") {
1198 // \c data == "Boxed" || "Frameless" etc
1199 InsetBoxParams p(data);
1200 data = InsetBoxMailer::params2string(p);
1201 } else if (name == "branch") {
1202 InsetBranchParams p;
1203 data = InsetBranchMailer::params2string(p);
1204 } else if (name == "citation") {
1205 InsetCommandParams p("cite");
1206 data = InsetCommandMailer::params2string(name, p);
1207 } else if (name == "ert") {
1208 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1209 } else if (name == "external") {
1210 InsetExternalParams p;
1211 Buffer const & buffer = *owner->buffer();
1212 data = InsetExternalMailer::params2string(p, buffer);
1213 } else if (name == "float") {
1215 data = InsetFloatMailer::params2string(p);
1216 } else if (name == "graphics") {
1217 InsetGraphicsParams p;
1218 Buffer const & buffer = *owner->buffer();
1219 data = InsetGraphicsMailer::params2string(p, buffer);
1220 } else if (name == "note") {
1222 data = InsetNoteMailer::params2string(p);
1223 } else if (name == "vspace") {
1225 data = InsetVSpaceMailer::params2string(space);
1226 } else if (name == "wrap") {
1228 data = InsetWrapMailer::params2string(p);
1230 owner->getDialogs().show(name, data, 0);
1234 case LFUN_DIALOG_UPDATE: {
1235 string const & name = argument;
1236 // Can only update a dialog connected to an existing inset
1237 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1239 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1240 inset->dispatch(view()->cursor(), fr);
1241 } else if (name == "paragraph") {
1242 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1243 } else if (name == "prefs") {
1244 owner->getDialogs().update(name, string());
1249 case LFUN_DIALOG_HIDE:
1250 Dialogs::hide(argument, 0);
1253 case LFUN_DIALOG_DISCONNECT_INSET:
1254 owner->getDialogs().disconnect(argument);
1258 case LFUN_CITATION_INSERT: {
1259 if (!argument.empty()) {
1260 // we can have one optional argument, delimited by '|'
1261 // citation-insert <key>|<text_before>
1262 // this should be enhanced to also support text_after
1263 // and citation style
1264 string arg = argument;
1266 if (contains(argument, "|")) {
1267 arg = token(argument, '|', 0);
1268 opt1 = '[' + token(argument, '|', 1) + ']';
1270 std::ostringstream os;
1271 os << "citation LatexCommand\n"
1272 << "\\cite" << opt1 << "{" << arg << "}\n"
1274 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1277 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1281 case LFUN_BUFFER_CHILD_OPEN: {
1282 string const filename =
1283 makeAbsPath(argument, owner->buffer()->filePath());
1284 // FIXME Should use bformat
1285 setMessage(_("Opening child document ") +
1286 makeDisplayPath(filename) + lyx::from_ascii("..."));
1287 view()->savePosition(0);
1288 string const parentfilename = owner->buffer()->fileName();
1289 if (theApp->bufferList().exists(filename))
1290 owner->setBuffer(theApp->bufferList().getBuffer(filename));
1292 owner->loadLyXFile(filename);
1293 // Set the parent name of the child document.
1294 // This makes insertion of citations and references in the child work,
1295 // when the target is in the parent or another child document.
1296 owner->buffer()->setParentName(parentfilename);
1300 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1301 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1304 case LFUN_KEYMAP_OFF:
1305 owner->view()->getIntl().keyMapOn(false);
1308 case LFUN_KEYMAP_PRIMARY:
1309 owner->view()->getIntl().keyMapPrim();
1312 case LFUN_KEYMAP_SECONDARY:
1313 owner->view()->getIntl().keyMapSec();
1316 case LFUN_KEYMAP_TOGGLE:
1317 owner->view()->getIntl().toggleKeyMap();
1323 string rest = split(argument, countstr, ' ');
1324 istringstream is(countstr);
1327 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1328 for (int i = 0; i < count; ++i)
1329 dispatch(lyxaction.lookupFunc(rest));
1333 case LFUN_COMMAND_SEQUENCE: {
1334 // argument contains ';'-terminated commands
1335 string arg = argument;
1336 while (!arg.empty()) {
1338 arg = split(arg, first, ';');
1339 FuncRequest func(lyxaction.lookupFunc(first));
1340 func.origin = cmd.origin;
1346 case LFUN_PREFERENCES_SAVE: {
1347 lyx::support::Path p(package().user_support());
1348 lyxrc.write("preferences", false);
1352 case LFUN_SCREEN_FONT_UPDATE:
1353 // handle the screen font changes.
1354 lyxrc.set_font_norm_type();
1355 lyx_gui::update_fonts();
1356 // All visible buffers will need resize
1360 case LFUN_SET_COLOR: {
1362 string const x11_name = split(argument, lyx_name, ' ');
1363 if (lyx_name.empty() || x11_name.empty()) {
1364 setErrorMessage(_("Syntax: set-color <lyx_name>"
1369 bool const graphicsbg_changed =
1370 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1371 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1373 if (!lcolor.setColor(lyx_name, x11_name)) {
1375 bformat(_("Set-color \"%1$s\" failed "
1376 "- color is undefined or "
1377 "may not be redefined"),
1378 lyx::from_utf8(lyx_name)));
1382 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1384 if (graphicsbg_changed) {
1385 #ifdef WITH_WARNINGS
1386 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1389 lyx::graphics::GCache::get().changeDisplay(true);
1396 owner->message(lyx::from_utf8(argument));
1399 case LFUN_TOOLTIPS_TOGGLE:
1400 owner->getDialogs().toggleTooltips();
1403 case LFUN_EXTERNAL_EDIT: {
1404 FuncRequest fr(action, argument);
1405 InsetExternal().dispatch(view()->cursor(), fr);
1409 case LFUN_GRAPHICS_EDIT: {
1410 FuncRequest fr(action, argument);
1411 InsetGraphics().dispatch(view()->cursor(), fr);
1415 case LFUN_INSET_APPLY: {
1416 string const name = cmd.getArg(0);
1417 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1419 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1420 inset->dispatch(view()->cursor(), fr);
1422 FuncRequest fr(LFUN_INSET_INSERT, argument);
1425 // ideally, the update flag should be set by the insets,
1426 // but this is not possible currently
1431 case LFUN_ALL_INSETS_TOGGLE: {
1433 string const name = split(argument, action, ' ');
1434 InsetBase::Code const inset_code =
1435 InsetBase::translate(name);
1437 LCursor & cur = view()->cursor();
1438 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1440 InsetBase & inset = owner->buffer()->inset();
1441 InsetIterator it = inset_iterator_begin(inset);
1442 InsetIterator const end = inset_iterator_end(inset);
1443 for (; it != end; ++it) {
1444 if (inset_code == InsetBase::NO_CODE
1445 || inset_code == it->lyxCode()) {
1446 LCursor tmpcur = cur;
1447 tmpcur.pushLeft(*it);
1448 it->dispatch(tmpcur, fr);
1455 case LFUN_BUFFER_LANGUAGE: {
1456 Buffer & buffer = *owner->buffer();
1457 Language const * oldL = buffer.params().language;
1458 Language const * newL = languages.getLanguage(argument);
1459 if (!newL || oldL == newL)
1462 if (oldL->rightToLeft() == newL->rightToLeft()
1463 && !buffer.isMultiLingual())
1464 buffer.changeLanguage(oldL, newL);
1466 buffer.updateDocLang(newL);
1470 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1471 string const fname =
1472 addName(addPath(package().user_support(), "templates/"),
1474 Buffer defaults(fname);
1476 istringstream ss(argument);
1479 int const unknown_tokens = defaults.readHeader(lex);
1481 if (unknown_tokens != 0) {
1482 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1483 << unknown_tokens << " unknown token"
1484 << (unknown_tokens == 1 ? "" : "s")
1488 if (defaults.writeFile(defaults.fileName()))
1489 // FIXME Should use bformat
1490 setMessage(_("Document defaults saved in ")
1491 + makeDisplayPath(fname));
1493 setErrorMessage(_("Unable to save document defaults"));
1497 case LFUN_BUFFER_PARAMS_APPLY: {
1498 biblio::CiteEngine const engine =
1499 owner->buffer()->params().cite_engine;
1501 istringstream ss(argument);
1504 int const unknown_tokens =
1505 owner->buffer()->readHeader(lex);
1507 if (unknown_tokens != 0) {
1508 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1509 << unknown_tokens << " unknown token"
1510 << (unknown_tokens == 1 ? "" : "s")
1513 if (engine == owner->buffer()->params().cite_engine)
1516 LCursor & cur = view()->cursor();
1517 FuncRequest fr(LFUN_INSET_REFRESH);
1519 InsetBase & inset = owner->buffer()->inset();
1520 InsetIterator it = inset_iterator_begin(inset);
1521 InsetIterator const end = inset_iterator_end(inset);
1522 for (; it != end; ++it)
1523 if (it->lyxCode() == InsetBase::CITE_CODE)
1524 it->dispatch(cur, fr);
1528 case LFUN_TEXTCLASS_APPLY: {
1529 Buffer * buffer = owner->buffer();
1531 lyx::textclass_type const old_class =
1532 buffer->params().textclass;
1534 loadTextclass(argument);
1536 std::pair<bool, lyx::textclass_type> const tc_pair =
1537 textclasslist.numberOfClass(argument);
1542 lyx::textclass_type const new_class = tc_pair.second;
1543 if (old_class == new_class)
1547 owner->message(_("Converting document to new document class..."));
1548 recordUndoFullDocument(view());
1549 buffer->params().textclass = new_class;
1550 StableDocIterator backcur(view()->cursor());
1551 ErrorList & el = buffer->errorList("Class Switch");
1552 lyx::cap::switchBetweenClasses(
1553 old_class, new_class,
1554 static_cast<InsetText &>(buffer->inset()), el);
1556 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1558 buffer->errors("Class Switch");
1559 updateLabels(*buffer);
1564 case LFUN_TEXTCLASS_LOAD:
1565 loadTextclass(argument);
1568 case LFUN_LYXRC_APPLY: {
1569 LyXRC const lyxrc_orig = lyxrc;
1571 istringstream ss(argument);
1572 bool const success = lyxrc.read(ss) == 0;
1575 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1576 << "Unable to read lyxrc data"
1581 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1586 view()->cursor().dispatch(cmd);
1587 updateforce |= view()->cursor().result().update();
1588 if (!view()->cursor().result().dispatched())
1589 updateforce |= view()->dispatch(cmd);
1594 if (view()->buffer()) {
1595 // Redraw screen unless explicitly told otherwise.
1596 // This also initializes the position cache for all insets
1597 // in (at least partially) visible top-level paragraphs.
1599 view()->update(Update::FitCursor | Update::Force);
1601 view()->update(Update::FitCursor);
1603 owner->redrawWorkArea();
1605 // if we executed a mutating lfun, mark the buffer as dirty
1607 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1608 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1609 view()->buffer()->markDirty();
1611 if (view()->cursor().inTexted()) {
1612 owner->updateLayoutChoice();
1617 // FIXME UNICODE: _() does not support anything but ascii.
1618 // Do we need a lyx::to_ascii() method?
1619 sendDispatchMessage(getMessage(), cmd);
1623 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1625 /* When an action did not originate from the UI/kbd, it makes
1626 * sense to avoid updating the GUI. It turns out that this
1627 * fixes bug 1941, for reasons that are described here:
1628 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1630 if (cmd.origin != FuncRequest::INTERNAL) {
1631 owner->updateMenubar();
1632 owner->updateToolbars();
1635 const bool verbose = (cmd.origin == FuncRequest::UI
1636 || cmd.origin == FuncRequest::COMMANDBUFFER);
1638 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1639 lyxerr[Debug::ACTION] << "dispatch msg is " << lyx::to_utf8(msg) << endl;
1641 owner->message(msg);
1645 docstring dispatch_msg = msg;
1646 if (!dispatch_msg.empty())
1647 dispatch_msg += ' ';
1649 string comname = lyxaction.getActionName(cmd.action);
1651 bool argsadded = false;
1653 if (!cmd.argument().empty()) {
1654 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1655 comname += ' ' + lyx::to_utf8(cmd.argument());
1660 string const shortcuts = toplevel_keymap->printbindings(cmd);
1662 if (!shortcuts.empty())
1663 comname += ": " + shortcuts;
1664 else if (!argsadded && !cmd.argument().empty())
1665 comname += ' ' + lyx::to_utf8(cmd.argument());
1667 if (!comname.empty()) {
1668 comname = rtrim(comname);
1669 dispatch_msg += lyx::from_utf8('(' + rtrim(comname) + ')');
1672 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1673 << lyx::to_utf8(dispatch_msg) << endl;
1674 if (!dispatch_msg.empty())
1675 owner->message(dispatch_msg);
1679 void LyXFunc::setupLocalKeymap()
1681 keyseq.stdmap = toplevel_keymap.get();
1682 keyseq.curmap = toplevel_keymap.get();
1683 cancel_meta_seq.stdmap = toplevel_keymap.get();
1684 cancel_meta_seq.curmap = toplevel_keymap.get();
1688 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1690 string initpath = lyxrc.document_path;
1691 string filename(name);
1693 if (view()->buffer()) {
1694 string const trypath = owner->buffer()->filePath();
1695 // If directory is writeable, use this as default.
1696 if (isDirWriteable(trypath))
1700 static int newfile_number;
1702 if (filename.empty()) {
1703 filename = addName(lyxrc.document_path,
1704 "newfile" + convert<string>(++newfile_number) + ".lyx");
1705 while (theApp->bufferList().exists(filename) || fs::is_readable(filename)) {
1707 filename = addName(lyxrc.document_path,
1708 "newfile" + convert<string>(newfile_number) +
1713 // The template stuff
1716 FileDialog fileDlg(lyx::to_utf8(_("Select template file")),
1717 LFUN_SELECT_FILE_SYNC,
1718 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1719 string(lyxrc.document_path)),
1720 make_pair(string(lyx::to_utf8(_("Templates|#T#t"))),
1721 string(lyxrc.template_path)));
1723 FileDialog::Result result =
1724 fileDlg.open(lyxrc.template_path,
1725 FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1728 if (result.first == FileDialog::Later)
1730 if (result.second.empty())
1732 templname = result.second;
1735 Buffer * const b = newFile(filename, templname, !name.empty());
1737 owner->setBuffer(b);
1741 void LyXFunc::open(string const & fname)
1743 string initpath = lyxrc.document_path;
1745 if (view()->buffer()) {
1746 string const trypath = owner->buffer()->filePath();
1747 // If directory is writeable, use this as default.
1748 if (isDirWriteable(trypath))
1754 if (fname.empty()) {
1755 FileDialog fileDlg(lyx::to_utf8(_("Select document to open")),
1757 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1758 string(lyxrc.document_path)),
1759 make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1760 string(addPath(package().system_support(), "examples"))));
1762 FileDialog::Result result =
1763 fileDlg.open(initpath,
1764 FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1767 if (result.first == FileDialog::Later)
1770 filename = result.second;
1772 // check selected filename
1773 if (filename.empty()) {
1774 owner->message(_("Canceled."));
1780 // get absolute path of file and add ".lyx" to the filename if
1782 string const fullpath = fileSearch(string(), filename, "lyx");
1783 if (!fullpath.empty()) {
1784 filename = fullpath;
1787 docstring const disp_fn = makeDisplayPath(filename);
1789 // if the file doesn't exist, let the user create one
1790 if (!fs::exists(filename)) {
1791 // the user specifically chose this name. Believe him.
1792 Buffer * const b = newFile(filename, string(), true);
1794 owner->setBuffer(b);
1798 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1801 if (owner->loadLyXFile(filename)) {
1802 str2 = bformat(_("Document %1$s opened."), disp_fn);
1804 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1806 owner->message(str2);
1810 void LyXFunc::doImport(string const & argument)
1813 string filename = split(argument, format, ' ');
1815 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1816 << " file: " << filename << endl;
1818 // need user interaction
1819 if (filename.empty()) {
1820 string initpath = lyxrc.document_path;
1822 if (view()->buffer()) {
1823 string const trypath = owner->buffer()->filePath();
1824 // If directory is writeable, use this as default.
1825 if (isDirWriteable(trypath))
1829 docstring const text = bformat(_("Select %1$s file to import"),
1830 formats.prettyName(format));
1832 FileDialog fileDlg(lyx::to_utf8(text),
1834 make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1835 string(lyxrc.document_path)),
1836 make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1837 string(addPath(package().system_support(), "examples"))));
1839 string const filter = lyx::to_utf8(formats.prettyName(format))
1840 + " (*." + formats.extension(format) + ')';
1842 FileDialog::Result result =
1843 fileDlg.open(initpath,
1844 FileFilterList(filter),
1847 if (result.first == FileDialog::Later)
1850 filename = result.second;
1852 // check selected filename
1853 if (filename.empty())
1854 owner->message(_("Canceled."));
1857 if (filename.empty())
1860 // get absolute path of file
1861 filename = makeAbsPath(filename);
1863 string const lyxfile = changeExtension(filename, ".lyx");
1865 // Check if the document already is open
1866 if (lyx_gui::use_gui && theApp->bufferList().exists(lyxfile)) {
1867 if (!theApp->bufferList().close(theApp->bufferList().getBuffer(lyxfile), true)) {
1868 owner->message(_("Canceled."));
1873 // if the file exists already, and we didn't do
1874 // -i lyx thefile.lyx, warn
1875 if (fs::exists(lyxfile) && filename != lyxfile) {
1876 docstring const file = makeDisplayPath(lyxfile, 30);
1878 docstring text = bformat(_("The document %1$s already exists.\n\n"
1879 "Do you want to over-write that document?"), file);
1880 int const ret = Alert::prompt(_("Over-write document?"),
1881 text, 0, 1, _("&Over-write"), _("&Cancel"));
1884 owner->message(_("Canceled."));
1889 ErrorList errorList;
1890 Importer::Import(owner, filename, format, errorList);
1891 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1895 void LyXFunc::closeBuffer()
1897 // save current cursor position
1898 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1899 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1900 if (theApp->bufferList().close(owner->buffer(), true) && !quitting) {
1901 if (theApp->bufferList().empty()) {
1902 // need this otherwise SEGV may occur while
1903 // trying to set variables that don't exist
1904 // since there's no current buffer
1905 owner->getDialogs().hideBufferDependent();
1907 owner->setBuffer(theApp->bufferList().first());
1913 // Each "owner" should have it's own message method. lyxview and
1914 // the minibuffer would use the minibuffer, but lyxserver would
1915 // send an ERROR signal to its client. Alejandro 970603
1916 // This function is bit problematic when it comes to NLS, to make the
1917 // lyx servers client be language indepenent we must not translate
1918 // strings sent to this func.
1919 void LyXFunc::setErrorMessage(docstring const & m) const
1921 dispatch_buffer = m;
1926 void LyXFunc::setMessage(docstring const & m) const
1928 dispatch_buffer = m;
1932 string const LyXFunc::viewStatusMessage()
1934 // When meta-fake key is pressed, show the key sequence so far + "M-".
1936 return keyseq.print() + "M-";
1938 // Else, when a non-complete key sequence is pressed,
1939 // show the available options.
1940 if (keyseq.length() > 0 && !keyseq.deleted())
1941 return keyseq.printOptions();
1943 if (!view()->buffer())
1944 return lyx::to_utf8(_("Welcome to LyX!"));
1946 return view()->cursor().currentState();
1950 BufferView * LyXFunc::view() const
1952 BOOST_ASSERT(owner);
1953 return owner->view();
1957 bool LyXFunc::wasMetaKey() const
1959 return (meta_fake_bit != key_modifier::none);
1965 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1967 // Why the switch you might ask. It is a trick to ensure that all
1968 // the elements in the LyXRCTags enum is handled. As you can see
1969 // there are no breaks at all. So it is just a huge fall-through.
1970 // The nice thing is that we will get a warning from the compiler
1971 // if we forget an element.
1972 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1974 case LyXRC::RC_ACCEPT_COMPOUND:
1975 case LyXRC::RC_ALT_LANG:
1976 case LyXRC::RC_ASCIIROFF_COMMAND:
1977 case LyXRC::RC_ASCII_LINELEN:
1978 case LyXRC::RC_AUTOREGIONDELETE:
1979 case LyXRC::RC_AUTORESET_OPTIONS:
1980 case LyXRC::RC_AUTOSAVE:
1981 case LyXRC::RC_AUTO_NUMBER:
1982 case LyXRC::RC_BACKUPDIR_PATH:
1983 case LyXRC::RC_BIBTEX_COMMAND:
1984 case LyXRC::RC_BINDFILE:
1985 case LyXRC::RC_CHECKLASTFILES:
1986 case LyXRC::RC_USELASTFILEPOS:
1987 case LyXRC::RC_LOADSESSION:
1988 case LyXRC::RC_CHKTEX_COMMAND:
1989 case LyXRC::RC_CONVERTER:
1990 case LyXRC::RC_COPIER:
1991 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1992 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1993 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1994 case LyXRC::RC_DATE_INSERT_FORMAT:
1995 case LyXRC::RC_DEFAULT_LANGUAGE:
1996 case LyXRC::RC_DEFAULT_PAPERSIZE:
1997 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1998 case LyXRC::RC_DISPLAY_GRAPHICS:
1999 case LyXRC::RC_DOCUMENTPATH:
2000 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2001 if (fs::exists(lyxrc_new.document_path) &&
2002 fs::is_directory(lyxrc_new.document_path)) {
2003 using lyx::support::package;
2004 package().document_dir() = lyxrc.document_path;
2007 case LyXRC::RC_ESC_CHARS:
2008 case LyXRC::RC_FONT_ENCODING:
2009 case LyXRC::RC_FORMAT:
2010 case LyXRC::RC_INDEX_COMMAND:
2011 case LyXRC::RC_INPUT:
2012 case LyXRC::RC_KBMAP:
2013 case LyXRC::RC_KBMAP_PRIMARY:
2014 case LyXRC::RC_KBMAP_SECONDARY:
2015 case LyXRC::RC_LABEL_INIT_LENGTH:
2016 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2017 case LyXRC::RC_LANGUAGE_AUTO_END:
2018 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2019 case LyXRC::RC_LANGUAGE_COMMAND_END:
2020 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2021 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2022 case LyXRC::RC_LANGUAGE_PACKAGE:
2023 case LyXRC::RC_LANGUAGE_USE_BABEL:
2024 case LyXRC::RC_MAKE_BACKUP:
2025 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2026 case LyXRC::RC_NUMLASTFILES:
2027 case LyXRC::RC_PATH_PREFIX:
2028 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2029 using lyx::support::prependEnvPath;
2030 prependEnvPath("PATH", lyxrc.path_prefix);
2032 case LyXRC::RC_PERS_DICT:
2033 case LyXRC::RC_POPUP_BOLD_FONT:
2034 case LyXRC::RC_POPUP_FONT_ENCODING:
2035 case LyXRC::RC_POPUP_NORMAL_FONT:
2036 case LyXRC::RC_PREVIEW:
2037 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2038 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2039 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2040 case LyXRC::RC_PRINTCOPIESFLAG:
2041 case LyXRC::RC_PRINTER:
2042 case LyXRC::RC_PRINTEVENPAGEFLAG:
2043 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2044 case LyXRC::RC_PRINTFILEEXTENSION:
2045 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2046 case LyXRC::RC_PRINTODDPAGEFLAG:
2047 case LyXRC::RC_PRINTPAGERANGEFLAG:
2048 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2049 case LyXRC::RC_PRINTPAPERFLAG:
2050 case LyXRC::RC_PRINTREVERSEFLAG:
2051 case LyXRC::RC_PRINTSPOOL_COMMAND:
2052 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2053 case LyXRC::RC_PRINTTOFILE:
2054 case LyXRC::RC_PRINTTOPRINTER:
2055 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2056 case LyXRC::RC_PRINT_COMMAND:
2057 case LyXRC::RC_RTL_SUPPORT:
2058 case LyXRC::RC_SCREEN_DPI:
2059 case LyXRC::RC_SCREEN_FONT_ENCODING:
2060 case LyXRC::RC_SCREEN_FONT_ROMAN:
2061 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2062 case LyXRC::RC_SCREEN_FONT_SANS:
2063 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2064 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2065 case LyXRC::RC_SCREEN_FONT_SIZES:
2066 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2067 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2068 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2069 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2070 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2071 case LyXRC::RC_SCREEN_ZOOM:
2072 case LyXRC::RC_SERVERPIPE:
2073 case LyXRC::RC_SET_COLOR:
2074 case LyXRC::RC_SHOW_BANNER:
2075 case LyXRC::RC_SPELL_COMMAND:
2076 case LyXRC::RC_TEMPDIRPATH:
2077 case LyXRC::RC_TEMPLATEPATH:
2078 case LyXRC::RC_TEX_ALLOWS_SPACES:
2079 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2080 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2081 namespace os = lyx::support::os;
2082 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2084 case LyXRC::RC_UIFILE:
2085 case LyXRC::RC_USER_EMAIL:
2086 case LyXRC::RC_USER_NAME:
2087 case LyXRC::RC_USETEMPDIR:
2088 case LyXRC::RC_USE_ALT_LANG:
2089 case LyXRC::RC_USE_ESC_CHARS:
2090 case LyXRC::RC_USE_INP_ENC:
2091 case LyXRC::RC_USE_PERS_DICT:
2092 case LyXRC::RC_USE_SPELL_LIB:
2093 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2094 case LyXRC::RC_VIEWER:
2095 case LyXRC::RC_LAST: