3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
9 * \author Angus Leeming
11 * \author André Pönitz
14 * \author Martin Vermeer
15 * \author Jürgen Vigna
17 * Full author contact details are available in file CREDITS.
24 #include "BranchList.h"
26 #include "buffer_funcs.h"
27 #include "bufferlist.h"
28 #include "bufferparams.h"
29 #include "BufferView.h"
31 #include "CutAndPaste.h"
33 #include "dispatchresult.h"
35 #include "errorlist.h"
38 #include "funcrequest.h"
41 #include "insetiterator.h"
49 #include "LyXAction.h"
54 #include "lyxserver.h"
55 #include "lyxtextclasslist.h"
57 #include "paragraph.h"
58 #include "pariterator.h"
59 #include "ParagraphParameters.h"
62 #include "insets/insetbox.h"
63 #include "insets/insetbranch.h"
64 #include "insets/insetcommand.h"
65 #include "insets/insetert.h"
66 #include "insets/insetexternal.h"
67 #include "insets/insetfloat.h"
68 #include "insets/insetgraphics.h"
69 #include "insets/insetinclude.h"
70 #include "insets/insetnote.h"
71 #include "insets/insettabular.h"
72 #include "insets/insetvspace.h"
73 #include "insets/insetwrap.h"
75 #include "frontends/Alert.h"
76 #include "frontends/Dialogs.h"
77 #include "frontends/FileDialog.h"
78 #include "frontends/lyx_gui.h"
79 #include "frontends/LyXKeySym.h"
80 #include "frontends/LyXView.h"
81 #include "frontends/Menubar.h"
82 #include "frontends/Toolbars.h"
84 #include "support/environment.h"
85 #include "support/filefilterlist.h"
86 #include "support/filetools.h"
87 #include "support/forkedcontr.h"
88 #include "support/fs_extras.h"
89 #include "support/lstrings.h"
90 #include "support/path.h"
91 #include "support/package.h"
92 #include "support/systemcall.h"
93 #include "support/convert.h"
94 #include "support/os.h"
96 #include <boost/current_function.hpp>
97 #include <boost/filesystem/operations.hpp>
101 using bv_funcs::freefont2string;
103 using lyx::support::absolutePath;
104 using lyx::support::addName;
105 using lyx::support::addPath;
106 using lyx::support::bformat;
107 using lyx::support::changeExtension;
108 using lyx::support::contains;
109 using lyx::support::FileFilterList;
110 using lyx::support::fileSearch;
111 using lyx::support::ForkedcallsController;
112 using lyx::support::i18nLibFileSearch;
113 using lyx::support::isDirWriteable;
114 using lyx::support::isFileReadable;
115 using lyx::support::isStrInt;
116 using lyx::support::makeAbsPath;
117 using lyx::support::makeDisplayPath;
118 using lyx::support::package;
119 using lyx::support::Path;
120 using lyx::support::quoteName;
121 using lyx::support::rtrim;
122 using lyx::support::split;
123 using lyx::support::subst;
124 using lyx::support::Systemcall;
125 using lyx::support::token;
126 using lyx::support::trim;
127 using lyx::support::prefixIs;
130 using std::make_pair;
133 using std::istringstream;
134 using std::ostringstream;
136 namespace biblio = lyx::biblio;
137 namespace fs = boost::filesystem;
140 extern BufferList bufferlist;
141 extern LyXServer * lyxserver;
143 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
146 extern tex_accent_struct get_accent(kb_action action);
151 bool getStatus(LCursor cursor,
152 FuncRequest const & cmd, FuncStatus & status)
154 // Try to fix cursor in case it is broken.
155 cursor.fixIfBroken();
157 // This is, of course, a mess. Better create a new doc iterator and use
158 // this in Inset::getStatus. This might require an additional
159 // BufferView * arg, though (which should be avoided)
160 //LCursor safe = *this;
162 for ( ; cursor.depth(); cursor.pop()) {
163 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
164 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
165 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
166 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
168 // The inset's getStatus() will return 'true' if it made
169 // a definitive decision on whether it want to handle the
170 // request or not. The result of this decision is put into
171 // the 'status' parameter.
172 if (cursor.inset().getStatus(cursor, cmd, status)) {
181 /** Return the change status at cursor position, taking in account the
182 * status at each level of the document iterator (a table in a deleted
183 * footnote is deleted).
184 * When \param outer is true, the top slice is not looked at.
186 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
188 size_t const depth = dit.depth() - (outer ? 1 : 0);
190 for (size_t i = 0 ; i < depth ; ++i) {
191 CursorSlice const & slice = dit[i];
192 if (!slice.inset().inMathed()
193 && slice.pos() < slice.paragraph().size()) {
194 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
195 if (ch != Change::UNCHANGED)
199 return Change::UNCHANGED;
204 LyXFunc::LyXFunc(LyXView * lv)
207 keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
208 cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
209 meta_fake_bit(key_modifier::none)
214 void LyXFunc::handleKeyFunc(kb_action action)
216 char c = encoded_last_key;
218 if (keyseq.length()) {
222 owner->getIntl().getTransManager()
223 .deadkey(c, get_accent(action).accent, view()->getLyXText());
224 // Need to clear, in case the minibuffer calls these
227 // copied verbatim from do_accent_char
228 view()->cursor().resetAnchor();
233 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
235 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
237 // Do nothing if we have nothing (JMarc)
238 if (!keysym->isOK()) {
239 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
244 if (keysym->isModifier()) {
245 lyxerr[Debug::KEY] << "isModifier true" << endl;
249 Encoding const * encoding = view()->cursor().getEncoding();
251 encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
253 // Do a one-deep top-level lookup for
254 // cancel and meta-fake keys. RVDK_PATCH_5
255 cancel_meta_seq.reset();
257 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
258 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
259 << " action first set to [" << func.action << ']'
262 // When not cancel or meta-fake, do the normal lookup.
263 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
264 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
265 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
266 // remove Caps Lock and Mod2 as a modifiers
267 func = keyseq.addkey(keysym, (state | meta_fake_bit));
268 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
269 << "action now set to ["
270 << func.action << ']' << endl;
273 // Dont remove this unless you know what you are doing.
274 meta_fake_bit = key_modifier::none;
276 // Can this happen now ?
277 if (func.action == LFUN_NOACTION) {
278 func = FuncRequest(LFUN_COMMAND_PREFIX);
281 if (lyxerr.debugging(Debug::KEY)) {
282 lyxerr << BOOST_CURRENT_FUNCTION
284 << func.action << "]["
285 << keyseq.print() << ']'
289 // already here we know if it any point in going further
290 // why not return already here if action == -1 and
291 // num_bytes == 0? (Lgb)
293 if (keyseq.length() > 1) {
294 owner->message(keyseq.print());
298 // Maybe user can only reach the key via holding down shift.
299 // Let's see. But only if shift is the only modifier
300 if (func.action == LFUN_UNKNOWN_ACTION &&
301 state == key_modifier::shift) {
302 lyxerr[Debug::KEY] << "Trying without shift" << endl;
303 func = keyseq.addkey(keysym, key_modifier::none);
304 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
307 if (func.action == LFUN_UNKNOWN_ACTION) {
308 // Hmm, we didn't match any of the keysequences. See
309 // if it's normal insertable text not already covered
311 if (keysym->isText() && keyseq.length() == 1) {
312 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
313 func = FuncRequest(LFUN_SELF_INSERT,
314 FuncRequest::KEYBOARD);
316 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
317 owner->message(_("Unknown function."));
322 if (func.action == LFUN_SELF_INSERT) {
323 if (encoded_last_key != 0) {
324 string const arg(1, encoded_last_key);
325 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
326 FuncRequest::KEYBOARD));
328 << "SelfInsert arg[`" << arg << "']" << endl;
336 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
338 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
340 LCursor & cur = view()->cursor();
342 /* In LyX/Mac, when a dialog is open, the menus of the
343 application can still be accessed without giving focus to
344 the main window. In this case, we want to disable the menu
345 entries that are buffer-related.
347 Note that this code is not perfect, as bug 1941 attests:
348 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
351 if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
354 buf = owner->buffer();
356 if (cmd.action == LFUN_NOACTION) {
357 flag.message(N_("Nothing to do"));
362 switch (cmd.action) {
363 case LFUN_UNKNOWN_ACTION:
364 #ifndef HAVE_LIBAIKSAURUS
365 case LFUN_THESAURUS_ENTRY:
371 flag |= lyx_gui::getStatus(cmd);
374 if (flag.unknown()) {
375 flag.message(N_("Unknown action"));
379 if (!flag.enabled()) {
380 if (flag.message().empty())
381 flag.message(N_("Command disabled"));
385 // Check whether we need a buffer
386 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
388 flag.message(N_("Command not allowed with"
389 "out any document open"));
394 // I would really like to avoid having this switch and rather try to
395 // encode this in the function itself.
396 // -- And I'd rather let an inset decide which LFUNs it is willing
397 // to handle (Andre')
399 switch (cmd.action) {
400 case LFUN_TOOLTIPS_TOGGLE:
401 flag.setOnOff(owner->getDialogs().tooltipsEnabled());
404 case LFUN_BUFFER_TOGGLE_READ_ONLY:
405 flag.setOnOff(buf->isReadonly());
408 case LFUN_BUFFER_SWITCH:
409 // toggle on the current buffer, but do not toggle off
410 // the other ones (is that a good idea?)
411 if (cmd.argument == buf->fileName())
415 case LFUN_BUFFER_EXPORT:
416 enable = cmd.argument == "custom"
417 || Exporter::isExportable(*buf, cmd.argument);
420 case LFUN_BUFFER_CHKTEX:
421 enable = buf->isLatex() && lyxrc.chktex_command != "none";
424 case LFUN_BUILD_PROGRAM:
425 enable = Exporter::isExportable(*buf, "program");
428 case LFUN_LAYOUT_TABULAR:
429 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
433 case LFUN_LAYOUT_PARAGRAPH:
434 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
437 case LFUN_VC_REGISTER:
438 enable = !buf->lyxvc().inUse();
440 case LFUN_VC_CHECK_IN:
441 enable = buf->lyxvc().inUse() && !buf->isReadonly();
443 case LFUN_VC_CHECK_OUT:
444 enable = buf->lyxvc().inUse() && buf->isReadonly();
447 case LFUN_VC_UNDO_LAST:
448 enable = buf->lyxvc().inUse();
450 case LFUN_BUFFER_RELOAD:
451 enable = !buf->isUnnamed() && !buf->isClean();
454 case LFUN_INSET_SETTINGS: {
458 InsetBase::Code code = cur.inset().lyxCode();
460 case InsetBase::TABULAR_CODE:
461 enable = cmd.argument == "tabular";
463 case InsetBase::ERT_CODE:
464 enable = cmd.argument == "ert";
466 case InsetBase::FLOAT_CODE:
467 enable = cmd.argument == "float";
469 case InsetBase::WRAP_CODE:
470 enable = cmd.argument == "wrap";
472 case InsetBase::NOTE_CODE:
473 enable = cmd.argument == "note";
475 case InsetBase::BRANCH_CODE:
476 enable = cmd.argument == "branch";
478 case InsetBase::BOX_CODE:
479 enable = cmd.argument == "box";
487 case LFUN_INSET_APPLY: {
488 string const name = cmd.getArg(0);
489 InsetBase * inset = owner->getDialogs().getOpenInset(name);
491 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument);
493 bool const success = inset->getStatus(cur, fr, fs);
494 // Every inset is supposed to handle this
495 BOOST_ASSERT(success);
498 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument);
499 flag |= getStatus(fr);
501 enable = flag.enabled();
505 case LFUN_DIALOG_SHOW: {
506 string const name = cmd.getArg(0);
508 enable = name == "aboutlyx"
512 || name == "texinfo";
513 else if (name == "print")
514 enable = Exporter::isExportable(*buf, "dvi")
515 && lyxrc.print_command != "none";
516 else if (name == "character" || name == "mathpanel")
517 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
518 else if (name == "latexlog")
519 enable = isFileReadable(buf->getLogName().second);
520 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
521 else if (name == "spellchecker")
524 else if (name == "vclog")
525 enable = buf->lyxvc().inUse();
526 else if (name == "view-source")
531 case LFUN_DIALOG_SHOW_NEW_INSET:
532 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
535 case LFUN_DIALOG_UPDATE: {
536 string const name = cmd.getArg(0);
538 enable = name == "prefs";
542 case LFUN_CITATION_INSERT: {
543 FuncRequest fr(LFUN_INSET_INSERT, "citation");
544 enable = getStatus(fr).enabled();
548 case LFUN_BUFFER_WRITE: {
549 enable = view()->buffer()->isUnnamed()
550 || !view()->buffer()->isClean();
554 // this one is difficult to get right. As a half-baked
555 // solution, we consider only the first action of the sequence
556 case LFUN_COMMAND_SEQUENCE: {
557 // argument contains ';'-terminated commands
558 string const firstcmd = token(cmd.argument, ';', 0);
559 FuncRequest func(lyxaction.lookupFunc(firstcmd));
560 func.origin = cmd.origin;
561 flag = getStatus(func);
564 case LFUN_BUFFER_NEW:
565 case LFUN_BUFFER_NEW_TEMPLATE:
566 case LFUN_WORD_FIND_FORWARD:
567 case LFUN_WORD_FIND_BACKWARD:
568 case LFUN_COMMAND_PREFIX:
569 case LFUN_COMMAND_EXECUTE:
571 case LFUN_META_PREFIX:
572 case LFUN_BUFFER_CLOSE:
573 case LFUN_BUFFER_WRITE_AS:
574 case LFUN_BUFFER_UPDATE:
575 case LFUN_BUFFER_VIEW:
576 case LFUN_BUFFER_IMPORT:
579 case LFUN_BUFFER_AUTO_SAVE:
580 case LFUN_RECONFIGURE:
584 case LFUN_DROP_LAYOUTS_CHOICE:
586 case LFUN_SERVER_GET_NAME:
587 case LFUN_SERVER_NOTIFY:
588 case LFUN_SERVER_GOTO_FILE_ROW:
589 case LFUN_DIALOG_SHOW_NEXT_INSET:
590 case LFUN_DIALOG_HIDE:
591 case LFUN_DIALOG_DISCONNECT_INSET:
592 case LFUN_BUFFER_CHILD_OPEN:
593 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
594 case LFUN_KEYMAP_OFF:
595 case LFUN_KEYMAP_PRIMARY:
596 case LFUN_KEYMAP_SECONDARY:
597 case LFUN_KEYMAP_TOGGLE:
599 case LFUN_BUFFER_EXPORT_CUSTOM:
600 case LFUN_BUFFER_PRINT:
601 case LFUN_PREFERENCES_SAVE:
602 case LFUN_SCREEN_FONT_UPDATE:
605 case LFUN_EXTERNAL_EDIT:
606 case LFUN_GRAPHICS_EDIT:
607 case LFUN_ALL_INSETS_TOGGLE:
608 case LFUN_BUFFER_LANGUAGE:
609 case LFUN_TEXTCLASS_APPLY:
610 case LFUN_TEXTCLASS_LOAD:
611 case LFUN_BUFFER_SAVE_AS_DEFAULT:
612 case LFUN_BUFFER_PARAMS_APPLY:
613 case LFUN_LYXRC_APPLY:
614 case LFUN_BUFFER_NEXT:
615 case LFUN_BUFFER_PREVIOUS:
616 // these are handled in our dispatch()
621 if (!::getStatus(cur, cmd, flag))
622 flag = view()->getStatus(cmd);
628 // Can we use a readonly buffer?
629 if (buf && buf->isReadonly()
630 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
631 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
632 flag.message(N_("Document is read-only"));
636 // Are we in a DELETED change-tracking region?
637 if (buf && buf->params().tracking_changes
638 && lookupChangeType(cur, true) == Change::DELETED
639 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
640 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
641 flag.message(N_("This portion of the document is deleted."));
645 // the default error message if we disable the command
646 if (!flag.enabled() && flag.message().empty())
647 flag.message(N_("Command disabled"));
655 bool ensureBufferClean(BufferView * bv)
657 Buffer & buf = *bv->buffer();
661 string const file = makeDisplayPath(buf.fileName(), 30);
662 string text = bformat(_("The document %1$s has unsaved "
663 "changes.\n\nDo you want to save "
664 "the document?"), file);
665 int const ret = Alert::prompt(_("Save changed document?"),
666 text, 0, 1, _("&Save"),
670 bv->owner()->dispatch(FuncRequest(LFUN_BUFFER_WRITE));
672 return buf.isClean();
676 void showPrintError(string const & name)
678 string str = bformat(_("Could not print the document %1$s.\n"
679 "Check that your printer is set up correctly."),
680 makeDisplayPath(name, 50));
681 Alert::error(_("Print document failed"), str);
685 void loadTextclass(string const & name)
687 std::pair<bool, lyx::textclass_type> const tc_pair =
688 textclasslist.numberOfClass(name);
690 if (!tc_pair.first) {
691 lyxerr << "Document class \"" << name
692 << "\" does not exist."
697 lyx::textclass_type const tc = tc_pair.second;
699 if (!textclasslist[tc].load()) {
700 string s = bformat(_("The document could not be converted\n"
701 "into the document class %1$s."),
702 textclasslist[tc].name());
703 Alert::error(_("Could not change class"), s);
708 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
713 void LyXFunc::dispatch(FuncRequest const & cmd)
715 BOOST_ASSERT(view());
716 string const argument = cmd.argument;
717 kb_action const action = cmd.action;
719 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
720 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
722 // we have not done anything wrong yet.
724 dispatch_buffer.erase();
726 // redraw the screen at the end (first of the two drawing steps).
727 //This is done unless explicitely requested otherwise
729 // also do the second redrawing step. Only done if requested.
730 bool updateforce = false;
732 FuncStatus const flag = getStatus(cmd);
733 if (!flag.enabled()) {
734 // We cannot use this function here
735 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
736 << lyxaction.getActionName(action)
737 << " [" << action << "] is disabled at this location"
739 setErrorMessage(flag.message());
743 case LFUN_WORD_FIND_FORWARD:
744 case LFUN_WORD_FIND_BACKWARD: {
745 static string last_search;
746 string searched_string;
748 if (!argument.empty()) {
749 last_search = argument;
750 searched_string = argument;
752 searched_string = last_search;
755 if (searched_string.empty())
758 bool const fw = action == LFUN_WORD_FIND_FORWARD;
760 lyx::find::find2string(searched_string, true, false, fw);
761 lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
765 case LFUN_COMMAND_PREFIX:
766 owner->message(keyseq.printOptions());
769 case LFUN_COMMAND_EXECUTE:
770 owner->getToolbars().display("minibuffer", true);
771 owner->focus_command_buffer();
776 meta_fake_bit = key_modifier::none;
777 if (view()->available())
778 // cancel any selection
779 dispatch(FuncRequest(LFUN_MARK_OFF));
780 setMessage(N_("Cancel"));
783 case LFUN_META_PREFIX:
784 meta_fake_bit = key_modifier::alt;
785 setMessage(keyseq.print());
788 case LFUN_BUFFER_TOGGLE_READ_ONLY:
789 if (owner->buffer()->lyxvc().inUse())
790 owner->buffer()->lyxvc().toggleReadOnly();
792 owner->buffer()->setReadonly(
793 !owner->buffer()->isReadonly());
796 // --- Menus -----------------------------------------------
797 case LFUN_BUFFER_NEW:
798 menuNew(argument, false);
801 case LFUN_BUFFER_NEW_TEMPLATE:
802 menuNew(argument, true);
805 case LFUN_BUFFER_CLOSE:
809 case LFUN_BUFFER_WRITE:
810 if (!owner->buffer()->isUnnamed()) {
811 string const str = bformat(_("Saving document %1$s..."),
812 makeDisplayPath(owner->buffer()->fileName()));
814 menuWrite(owner->buffer());
815 owner->message(str + _(" done."));
817 writeAs(owner->buffer());
821 case LFUN_BUFFER_WRITE_AS:
822 writeAs(owner->buffer(), argument);
826 case LFUN_BUFFER_RELOAD: {
827 string const file = makeDisplayPath(view()->buffer()->fileName(), 20);
828 string text = bformat(_("Any changes will be lost. Are you sure "
829 "you want to revert to the saved version of the document %1$s?"), file);
830 int const ret = Alert::prompt(_("Revert to saved document?"),
831 text, 0, 1, _("&Revert"), _("&Cancel"));
838 case LFUN_BUFFER_UPDATE:
839 Exporter::Export(owner->buffer(), argument, true);
842 case LFUN_BUFFER_VIEW:
843 Exporter::preview(owner->buffer(), argument);
846 case LFUN_BUILD_PROGRAM:
847 Exporter::Export(owner->buffer(), "program", true);
850 case LFUN_BUFFER_CHKTEX:
851 owner->buffer()->runChktex();
854 case LFUN_BUFFER_EXPORT:
855 if (argument == "custom")
856 owner->getDialogs().show("sendto");
858 Exporter::Export(owner->buffer(), argument, false);
862 case LFUN_BUFFER_EXPORT_CUSTOM: {
864 string command = split(argument, format_name, ' ');
865 Format const * format = formats.getFormat(format_name);
867 lyxerr << "Format \"" << format_name
868 << "\" not recognized!"
873 Buffer * buffer = owner->buffer();
875 // The name of the file created by the conversion process
878 // Output to filename
879 if (format->name() == "lyx") {
880 string const latexname =
881 buffer->getLatexName(false);
882 filename = changeExtension(latexname,
883 format->extension());
884 filename = addName(buffer->temppath(), filename);
886 if (!buffer->writeFile(filename))
890 Exporter::Export(buffer, format_name, true, filename);
893 // Substitute $$FName for filename
894 if (!contains(command, "$$FName"))
895 command = "( " + command + " ) < $$FName";
896 command = subst(command, "$$FName", filename);
898 // Execute the command in the background
900 call.startscript(Systemcall::DontWait, command);
904 case LFUN_BUFFER_PRINT: {
907 string command = split(split(argument, target, ' '),
911 || target_name.empty()
912 || command.empty()) {
913 lyxerr << "Unable to parse \""
914 << argument << '"' << std::endl;
917 if (target != "printer" && target != "file") {
918 lyxerr << "Unrecognized target \""
919 << target << '"' << std::endl;
923 Buffer * buffer = owner->buffer();
925 if (!Exporter::Export(buffer, "dvi", true)) {
926 showPrintError(buffer->fileName());
930 // Push directory path.
931 string const path = buffer->temppath();
934 // there are three cases here:
935 // 1. we print to a file
936 // 2. we print directly to a printer
937 // 3. we print using a spool command (print to file first)
940 string const dviname =
941 changeExtension(buffer->getLatexName(true),
944 if (target == "printer") {
945 if (!lyxrc.print_spool_command.empty()) {
946 // case 3: print using a spool
947 string const psname =
948 changeExtension(dviname,".ps");
949 command += lyxrc.print_to_file
952 + quoteName(dviname);
955 lyxrc.print_spool_command +' ';
956 if (target_name != "default") {
957 command2 += lyxrc.print_spool_printerprefix
961 command2 += quoteName(psname);
963 // If successful, then spool command
964 res = one.startscript(
969 res = one.startscript(
970 Systemcall::DontWait,
973 // case 2: print directly to a printer
974 res = one.startscript(
975 Systemcall::DontWait,
976 command + quoteName(dviname));
980 // case 1: print to a file
981 command += lyxrc.print_to_file
982 + quoteName(makeAbsPath(target_name,
985 + quoteName(dviname);
986 res = one.startscript(Systemcall::DontWait,
991 showPrintError(buffer->fileName());
995 case LFUN_BUFFER_IMPORT:
1000 if (view()->available()) {
1001 // save cursor Position for opened files to .lyx/session
1002 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1003 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1004 // save bookmarks to .lyx/session
1005 view()->saveSavedPositions();
1007 quitLyX(argument == "force");
1010 case LFUN_TOC_VIEW: {
1011 InsetCommandParams p("tableofcontents");
1012 string const data = InsetCommandMailer::params2string("toc", p);
1013 owner->getDialogs().show("toc", data, 0);
1017 case LFUN_BUFFER_AUTO_SAVE:
1021 case LFUN_RECONFIGURE:
1022 reconfigure(view());
1025 case LFUN_HELP_OPEN: {
1026 string const arg = argument;
1028 setErrorMessage(N_("Missing argument"));
1031 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1032 if (fname.empty()) {
1033 lyxerr << "LyX: unable to find documentation file `"
1034 << arg << "'. Bad installation?" << endl;
1037 owner->message(bformat(_("Opening help file %1$s..."),
1038 makeDisplayPath(fname)));
1039 owner->loadLyXFile(fname, false);
1043 // --- version control -------------------------------
1044 case LFUN_VC_REGISTER:
1045 if (!ensureBufferClean(view()))
1047 if (!owner->buffer()->lyxvc().inUse()) {
1048 owner->buffer()->lyxvc().registrer();
1053 case LFUN_VC_CHECK_IN:
1054 if (!ensureBufferClean(view()))
1056 if (owner->buffer()->lyxvc().inUse()
1057 && !owner->buffer()->isReadonly()) {
1058 owner->buffer()->lyxvc().checkIn();
1063 case LFUN_VC_CHECK_OUT:
1064 if (!ensureBufferClean(view()))
1066 if (owner->buffer()->lyxvc().inUse()
1067 && owner->buffer()->isReadonly()) {
1068 owner->buffer()->lyxvc().checkOut();
1073 case LFUN_VC_REVERT:
1074 owner->buffer()->lyxvc().revert();
1078 case LFUN_VC_UNDO_LAST:
1079 owner->buffer()->lyxvc().undoLast();
1083 // --- buffers ----------------------------------------
1084 case LFUN_BUFFER_SWITCH:
1085 owner->setBuffer(bufferlist.getBuffer(argument));
1088 case LFUN_BUFFER_NEXT:
1089 owner->setBuffer(bufferlist.next(view()->buffer()));
1092 case LFUN_BUFFER_PREVIOUS:
1093 owner->setBuffer(bufferlist.previous(view()->buffer()));
1097 newFile(view(), argument);
1100 case LFUN_FILE_OPEN:
1104 case LFUN_DROP_LAYOUTS_CHOICE:
1105 owner->getToolbars().openLayoutList();
1108 case LFUN_MENU_OPEN:
1109 owner->getMenubar().openByName(argument);
1112 // --- lyxserver commands ----------------------------
1113 case LFUN_SERVER_GET_NAME:
1114 setMessage(owner->buffer()->fileName());
1115 lyxerr[Debug::INFO] << "FNAME["
1116 << owner->buffer()->fileName()
1120 case LFUN_SERVER_NOTIFY:
1121 dispatch_buffer = keyseq.print();
1122 lyxserver->notifyClient(dispatch_buffer);
1125 case LFUN_SERVER_GOTO_FILE_ROW: {
1128 istringstream is(argument);
1129 is >> file_name >> row;
1130 if (prefixIs(file_name, package().temp_dir())) {
1131 // Needed by inverse dvi search. If it is a file
1132 // in tmpdir, call the apropriated function
1133 owner->setBuffer(bufferlist.getBufferFromTmp(file_name));
1135 // Must replace extension of the file to be .lyx
1136 // and get full path
1137 string const s = changeExtension(file_name, ".lyx");
1138 // Either change buffer or load the file
1139 if (bufferlist.exists(s)) {
1140 owner->setBuffer(bufferlist.getBuffer(s));
1142 owner->loadLyXFile(s);
1146 view()->setCursorFromRow(row);
1149 // see BufferView_pimpl::center()
1153 case LFUN_DIALOG_SHOW: {
1154 string const name = cmd.getArg(0);
1155 string data = trim(cmd.argument.substr(name.size()));
1157 if (name == "character") {
1158 data = freefont2string();
1160 owner->getDialogs().show("character", data);
1161 } else if (name == "latexlog") {
1162 pair<Buffer::LogType, string> const logfile =
1163 owner->buffer()->getLogName();
1164 switch (logfile.first) {
1165 case Buffer::latexlog:
1168 case Buffer::buildlog:
1172 data += LyXLex::quoteString(logfile.second);
1173 owner->getDialogs().show("log", data);
1174 } else if (name == "vclog") {
1175 string const data = "vc " +
1176 LyXLex::quoteString(owner->buffer()->lyxvc().getLogFile());
1177 owner->getDialogs().show("log", data);
1179 owner->getDialogs().show(name, data);
1183 case LFUN_DIALOG_SHOW_NEW_INSET: {
1184 string const name = cmd.getArg(0);
1185 string data = trim(cmd.argument.substr(name.size()));
1186 if (name == "bibitem" ||
1193 InsetCommandParams p(name);
1194 data = InsetCommandMailer::params2string(name, p);
1195 } else if (name == "include") {
1196 InsetCommandParams p(data);
1197 data = InsetIncludeMailer::params2string(p);
1198 } else if (name == "box") {
1199 // \c data == "Boxed" || "Frameless" etc
1200 InsetBoxParams p(data);
1201 data = InsetBoxMailer::params2string(p);
1202 } else if (name == "branch") {
1203 InsetBranchParams p;
1204 data = InsetBranchMailer::params2string(p);
1205 } else if (name == "citation") {
1206 InsetCommandParams p("cite");
1207 data = InsetCommandMailer::params2string(name, p);
1208 } else if (name == "ert") {
1209 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1210 } else if (name == "external") {
1211 InsetExternalParams p;
1212 Buffer const & buffer = *owner->buffer();
1213 data = InsetExternalMailer::params2string(p, buffer);
1214 } else if (name == "float") {
1216 data = InsetFloatMailer::params2string(p);
1217 } else if (name == "graphics") {
1218 InsetGraphicsParams p;
1219 Buffer const & buffer = *owner->buffer();
1220 data = InsetGraphicsMailer::params2string(p, buffer);
1221 } else if (name == "note") {
1223 data = InsetNoteMailer::params2string(p);
1224 } else if (name == "vspace") {
1226 data = InsetVSpaceMailer::params2string(space);
1227 } else if (name == "wrap") {
1229 data = InsetWrapMailer::params2string(p);
1231 owner->getDialogs().show(name, data, 0);
1235 case LFUN_DIALOG_SHOW_NEXT_INSET:
1238 case LFUN_DIALOG_UPDATE: {
1239 string const & name = argument;
1240 // Can only update a dialog connected to an existing inset
1241 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1243 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument);
1244 inset->dispatch(view()->cursor(), fr);
1245 } else if (name == "paragraph") {
1246 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1247 } else if (name == "prefs") {
1248 owner->getDialogs().update(name, string());
1253 case LFUN_DIALOG_HIDE:
1254 Dialogs::hide(argument, 0);
1257 case LFUN_DIALOG_DISCONNECT_INSET:
1258 owner->getDialogs().disconnect(argument);
1262 case LFUN_CITATION_INSERT: {
1263 if (!argument.empty()) {
1264 // we can have one optional argument, delimited by '|'
1265 // citation-insert <key>|<text_before>
1266 // this should be enhanced to also support text_after
1267 // and citation style
1268 string arg = argument;
1270 if (contains(argument, "|")) {
1271 arg = token(argument, '|', 0);
1272 opt1 = '[' + token(argument, '|', 1) + ']';
1274 std::ostringstream os;
1275 os << "citation LatexCommand\n"
1276 << "\\cite" << opt1 << "{" << arg << "}\n"
1278 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1281 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1285 case LFUN_BUFFER_CHILD_OPEN: {
1286 string const filename =
1287 makeAbsPath(argument, owner->buffer()->filePath());
1288 setMessage(N_("Opening child document ") +
1289 makeDisplayPath(filename) + "...");
1290 view()->savePosition(0);
1291 string const parentfilename = owner->buffer()->fileName();
1292 if (bufferlist.exists(filename))
1293 owner->setBuffer(bufferlist.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->getIntl().keyMapOn(false);
1311 case LFUN_KEYMAP_PRIMARY:
1312 owner->getIntl().keyMapPrim();
1315 case LFUN_KEYMAP_SECONDARY:
1316 owner->getIntl().keyMapSec();
1319 case LFUN_KEYMAP_TOGGLE:
1320 owner->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 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 lyx_gui::update_fonts();
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(N_("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"), lyx_name));
1384 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1386 if (graphicsbg_changed) {
1387 #ifdef WITH_WARNINGS
1388 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1391 lyx::graphics::GCache::get().changeDisplay(true);
1398 owner->message(argument);
1401 case LFUN_TOOLTIPS_TOGGLE:
1402 owner->getDialogs().toggleTooltips();
1405 case LFUN_EXTERNAL_EDIT: {
1406 FuncRequest fr(action, argument);
1407 InsetExternal().dispatch(view()->cursor(), fr);
1411 case LFUN_GRAPHICS_EDIT: {
1412 FuncRequest fr(action, argument);
1413 InsetGraphics().dispatch(view()->cursor(), fr);
1417 case LFUN_INSET_APPLY: {
1418 string const name = cmd.getArg(0);
1419 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1421 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1422 inset->dispatch(view()->cursor(), fr);
1424 FuncRequest fr(LFUN_INSET_INSERT, argument);
1427 // ideally, the update flag should be set by the insets,
1428 // but this is not possible currently
1433 case LFUN_ALL_INSETS_TOGGLE: {
1435 string const name = split(argument, action, ' ');
1436 InsetBase::Code const inset_code =
1437 InsetBase::translate(name);
1439 LCursor & cur = view()->cursor();
1440 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1442 InsetBase & inset = owner->buffer()->inset();
1443 InsetIterator it = inset_iterator_begin(inset);
1444 InsetIterator const end = inset_iterator_end(inset);
1445 for (; it != end; ++it) {
1446 if (inset_code == InsetBase::NO_CODE
1447 || inset_code == it->lyxCode()) {
1448 LCursor tmpcur = cur;
1449 tmpcur.pushLeft(*it);
1450 it->dispatch(tmpcur, fr);
1457 case LFUN_BUFFER_LANGUAGE: {
1458 Buffer & buffer = *owner->buffer();
1459 Language const * oldL = buffer.params().language;
1460 Language const * newL = languages.getLanguage(argument);
1461 if (!newL || oldL == newL)
1464 if (oldL->rightToLeft() == newL->rightToLeft()
1465 && !buffer.isMultiLingual())
1466 buffer.changeLanguage(oldL, newL);
1468 buffer.updateDocLang(newL);
1472 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1473 string const fname =
1474 addName(addPath(package().user_support(), "templates/"),
1476 Buffer defaults(fname);
1478 istringstream ss(argument);
1481 int const unknown_tokens = defaults.readHeader(lex);
1483 if (unknown_tokens != 0) {
1484 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1485 << unknown_tokens << " unknown token"
1486 << (unknown_tokens == 1 ? "" : "s")
1490 if (defaults.writeFile(defaults.fileName()))
1491 setMessage(_("Document defaults saved in ")
1492 + makeDisplayPath(fname));
1494 setErrorMessage(_("Unable to save document defaults"));
1498 case LFUN_BUFFER_PARAMS_APPLY: {
1499 biblio::CiteEngine const engine =
1500 owner->buffer()->params().cite_engine;
1502 istringstream ss(argument);
1505 int const unknown_tokens =
1506 owner->buffer()->readHeader(lex);
1508 if (unknown_tokens != 0) {
1509 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1510 << unknown_tokens << " unknown token"
1511 << (unknown_tokens == 1 ? "" : "s")
1514 if (engine == owner->buffer()->params().cite_engine)
1517 LCursor & cur = view()->cursor();
1518 FuncRequest fr(LFUN_INSET_REFRESH);
1520 InsetBase & inset = owner->buffer()->inset();
1521 InsetIterator it = inset_iterator_begin(inset);
1522 InsetIterator const end = inset_iterator_end(inset);
1523 for (; it != end; ++it)
1524 if (it->lyxCode() == InsetBase::CITE_CODE)
1525 it->dispatch(cur, fr);
1529 case LFUN_TEXTCLASS_APPLY: {
1530 Buffer * buffer = owner->buffer();
1532 lyx::textclass_type const old_class =
1533 buffer->params().textclass;
1535 loadTextclass(argument);
1537 std::pair<bool, lyx::textclass_type> const tc_pair =
1538 textclasslist.numberOfClass(argument);
1543 lyx::textclass_type const new_class = tc_pair.second;
1544 if (old_class == new_class)
1548 owner->message(_("Converting document to new document class..."));
1549 recordUndoFullDocument(view());
1550 buffer->params().textclass = new_class;
1551 StableDocIterator backcur(view()->cursor());
1552 ErrorList & el = buffer->errorList("Class Switch");
1553 lyx::cap::switchBetweenClasses(
1554 old_class, new_class,
1555 static_cast<InsetText &>(buffer->inset()), el);
1557 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1559 buffer->errors("Class Switch");
1560 updateLabels(*buffer);
1565 case LFUN_TEXTCLASS_LOAD:
1566 loadTextclass(argument);
1569 case LFUN_LYXRC_APPLY: {
1570 LyXRC const lyxrc_orig = lyxrc;
1572 istringstream ss(argument);
1573 bool const success = lyxrc.read(ss) == 0;
1576 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1577 << "Unable to read lyxrc data"
1582 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1587 view()->cursor().dispatch(cmd);
1588 updateforce |= view()->cursor().result().update();
1589 if (!view()->cursor().result().dispatched())
1590 updateforce |= view()->dispatch(cmd);
1595 if (view()->available()) {
1596 // Redraw screen unless explicitly told otherwise.
1597 // This also initializes the position cache for all insets
1598 // in (at least partially) visible top-level paragraphs.
1600 view()->update(Update::FitCursor | Update::Force);
1602 view()->update(Update::FitCursor);
1604 owner->redrawWorkArea();
1606 // if we executed a mutating lfun, mark the buffer as dirty
1608 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1609 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1610 view()->buffer()->markDirty();
1613 if (view()->cursor().inTexted()) {
1614 view()->owner()->updateLayoutChoice();
1617 sendDispatchMessage(_(getMessage()), cmd);
1621 void LyXFunc::sendDispatchMessage(string const & msg, FuncRequest const & cmd)
1623 /* When an action did not originate from the UI/kbd, it makes
1624 * sense to avoid updating the GUI. It turns out that this
1625 * fixes bug 1941, for reasons that are described here:
1626 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1628 if (cmd.origin != FuncRequest::INTERNAL) {
1629 owner->updateMenubar();
1630 owner->updateToolbars();
1633 const bool verbose = (cmd.origin == FuncRequest::UI
1634 || cmd.origin == FuncRequest::COMMANDBUFFER);
1636 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1637 lyxerr[Debug::ACTION] << "dispatch msg is " << msg << endl;
1639 owner->message(msg);
1643 string dispatch_msg = msg;
1644 if (!dispatch_msg.empty())
1645 dispatch_msg += ' ';
1647 string comname = lyxaction.getActionName(cmd.action);
1649 bool argsadded = false;
1651 if (!cmd.argument.empty()) {
1652 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1653 comname += ' ' + cmd.argument;
1658 string const shortcuts = toplevel_keymap->printbindings(cmd);
1660 if (!shortcuts.empty())
1661 comname += ": " + shortcuts;
1662 else if (!argsadded && !cmd.argument.empty())
1663 comname += ' ' + cmd.argument;
1665 if (!comname.empty()) {
1666 comname = rtrim(comname);
1667 dispatch_msg += '(' + rtrim(comname) + ')';
1670 lyxerr[Debug::ACTION] << "verbose dispatch msg " << dispatch_msg << endl;
1671 if (!dispatch_msg.empty())
1672 owner->message(dispatch_msg);
1676 void LyXFunc::setupLocalKeymap()
1678 keyseq.stdmap = toplevel_keymap.get();
1679 keyseq.curmap = toplevel_keymap.get();
1680 cancel_meta_seq.stdmap = toplevel_keymap.get();
1681 cancel_meta_seq.curmap = toplevel_keymap.get();
1685 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1687 string initpath = lyxrc.document_path;
1688 string filename(name);
1690 if (view()->available()) {
1691 string const trypath = owner->buffer()->filePath();
1692 // If directory is writeable, use this as default.
1693 if (isDirWriteable(trypath))
1697 static int newfile_number;
1699 if (filename.empty()) {
1700 filename = addName(lyxrc.document_path,
1701 "newfile" + convert<string>(++newfile_number) + ".lyx");
1702 while (bufferlist.exists(filename) || fs::is_readable(filename)) {
1704 filename = addName(lyxrc.document_path,
1705 "newfile" + convert<string>(newfile_number) +
1710 // The template stuff
1713 FileDialog fileDlg(_("Select template file"),
1714 LFUN_SELECT_FILE_SYNC,
1715 make_pair(string(_("Documents|#o#O")),
1716 string(lyxrc.document_path)),
1717 make_pair(string(_("Templates|#T#t")),
1718 string(lyxrc.template_path)));
1720 FileDialog::Result result =
1721 fileDlg.open(lyxrc.template_path,
1722 FileFilterList(_("LyX Documents (*.lyx)")),
1725 if (result.first == FileDialog::Later)
1727 if (result.second.empty())
1729 templname = 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()->available()) {
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(string(_("Documents|#o#O")),
1755 string(lyxrc.document_path)),
1756 make_pair(string(_("Examples|#E#e")),
1757 string(addPath(package().system_support(), "examples"))));
1759 FileDialog::Result result =
1760 fileDlg.open(initpath,
1761 FileFilterList(_("LyX Documents (*.lyx)")),
1764 if (result.first == FileDialog::Later)
1767 filename = result.second;
1769 // check selected filename
1770 if (filename.empty()) {
1771 owner->message(_("Canceled."));
1777 // get absolute path of file and add ".lyx" to the filename if
1779 string const fullpath = fileSearch(string(), filename, "lyx");
1780 if (!fullpath.empty()) {
1781 filename = fullpath;
1784 string const disp_fn(makeDisplayPath(filename));
1786 // if the file doesn't exist, let the user create one
1787 if (!fs::exists(filename)) {
1788 // the user specifically chose this name. Believe him.
1789 Buffer * const b = newFile(filename, string(), true);
1791 owner->setBuffer(b);
1795 owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1798 if (owner->loadLyXFile(filename)) {
1799 str2 = bformat(_("Document %1$s opened."), disp_fn);
1801 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1803 owner->message(str2);
1807 void LyXFunc::doImport(string const & argument)
1810 string filename = split(argument, format, ' ');
1812 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1813 << " file: " << filename << endl;
1815 // need user interaction
1816 if (filename.empty()) {
1817 string initpath = lyxrc.document_path;
1819 if (view()->available()) {
1820 string const trypath = owner->buffer()->filePath();
1821 // If directory is writeable, use this as default.
1822 if (isDirWriteable(trypath))
1826 string const text = bformat(_("Select %1$s file to import"),
1827 formats.prettyName(format));
1829 FileDialog fileDlg(text,
1831 make_pair(string(_("Documents|#o#O")),
1832 string(lyxrc.document_path)),
1833 make_pair(string(_("Examples|#E#e")),
1834 string(addPath(package().system_support(), "examples"))));
1836 string const filter = formats.prettyName(format)
1837 + " (*." + formats.extension(format) + ')';
1839 FileDialog::Result result =
1840 fileDlg.open(initpath,
1841 FileFilterList(filter),
1844 if (result.first == FileDialog::Later)
1847 filename = 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_gui::use_gui && bufferlist.exists(lyxfile)) {
1864 if (!bufferlist.close(bufferlist.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 string const file = makeDisplayPath(lyxfile, 30);
1875 string 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 (bufferlist.close(owner->buffer(), true) && !quitting) {
1898 if (bufferlist.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(bufferlist.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(string const & m) const
1918 dispatch_buffer = m;
1923 void LyXFunc::setMessage(string 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()->available())
1941 return _("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: