1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
14 #pragma implementation
21 #include "bufferlist.h"
22 #include "BufferView.h"
23 #include "lyxserver.h"
27 #include "LyXAction.h"
33 #include "trans_mgr.h"
35 #include "bufferview_funcs.h"
36 #include "frontends/MiniBuffer.h"
37 #include "frontends/LyXView.h"
38 #include "frontends/lyx_gui.h"
40 #include "FloatList.h"
41 #include "converter.h"
44 #include "TextCache.h"
46 #include "undo_funcs.h"
47 #include "ParagraphParameters.h"
49 #include "insets/inseturl.h"
50 #include "insets/insetlatexaccent.h"
51 #include "insets/insettoc.h"
52 #include "insets/insetref.h"
53 #include "insets/insetparent.h"
54 #include "insets/insetindex.h"
55 #include "insets/insetinclude.h"
56 #include "insets/insetbib.h"
57 #include "insets/insetcite.h"
58 #include "insets/insettext.h"
59 #include "insets/insetert.h"
60 #include "insets/insetexternal.h"
61 #include "insets/insetgraphics.h"
62 #include "insets/insetfoot.h"
63 #include "insets/insetmarginal.h"
64 #include "insets/insetminipage.h"
65 #include "insets/insetfloat.h"
67 #include "insets/insetlist.h"
68 #include "insets/insettheorem.h"
70 #include "insets/insettabular.h"
71 #include "insets/insetcaption.h"
73 #include "mathed/formulamacro.h"
74 #include "mathed/math_cursor.h"
75 #include "mathed/math_inset.h"
77 #include "frontends/FileDialog.h"
78 #include "frontends/Dialogs.h"
79 #include "frontends/Toolbar.h"
80 #include "frontends/Menubar.h"
81 #include "frontends/Alert.h"
83 #include "graphics/GraphicsCache.h"
85 #include "support/lyxalgo.h"
86 #include "support/LAssert.h"
87 #include "support/filetools.h"
88 #include "support/FileInfo.h"
89 #include "support/forkedcontr.h"
90 #include "support/lstrings.h"
91 #include "support/path.h"
92 #include "support/lyxfunctional.h"
103 using std::make_pair;
107 using std::transform;
108 using std::back_inserter;
110 extern BufferList bufferlist;
111 extern LyXServer * lyxserver;
112 extern bool selection_possible;
114 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
116 extern void show_symbols_form(LyXFunc *);
118 extern LyXAction lyxaction;
120 extern tex_accent_struct get_accent(kb_action action);
122 extern void ShowLatexLog();
125 /* === globals =========================================================== */
128 LyXFunc::LyXFunc(LyXView * o)
130 keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
131 cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get())
133 meta_fake_bit = key_modifier::none;
134 lyx_dead_action = LFUN_NOACTION;
135 lyx_calling_dead_action = LFUN_NOACTION;
140 LyXText * LyXFunc::TEXT(bool flag = true) const
143 return owner->view()->text;
144 return owner->view()->getLyXText();
148 // I changed this func slightly. I commented out the ...FinishUndo(),
149 // this means that all places that used to have a moveCursorUpdate, now
150 // have a ...FinishUndo() as the preceeding statement. I have also added
151 // a moveCursorUpdate to some of the functions that updated the cursor, but
152 // that did not show its new position.
154 void LyXFunc::moveCursorUpdate(bool flag, bool selecting)
156 if (selecting || TEXT(flag)->selection.mark()) {
157 TEXT(flag)->setSelection(owner->view());
158 if (TEXT(flag)->bv_owner)
159 owner->view()->toggleToggle();
161 owner->view()->update(TEXT(flag), BufferView::SELECT|BufferView::FITCUR);
162 owner->view()->showCursor();
164 /* ---> Everytime the cursor is moved, show the current font state. */
165 // should this too me moved out of this func?
166 owner->view()->setState();
170 void LyXFunc::handleKeyFunc(kb_action action)
172 char c = keyseq.getLastKeyEncoded();
174 if (keyseq.length() > 1) {
178 owner->getIntl()->getTransManager()
179 .deadkey(c, get_accent(action).accent, TEXT(false));
180 // Need to clear, in case the minibuffer calls these
183 // copied verbatim from do_accent_char
184 owner->view()->update(TEXT(false),
185 BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
186 TEXT(false)->selection.cursor = TEXT(false)->cursor;
190 void LyXFunc::processKeySym(LyXKeySymPtr keysym,
191 key_modifier::state state)
195 if (lyxerr.debugging(Debug::KEY)) {
196 lyxerr << "KeySym is "
197 << keysym->getSymbolName()
200 // Do nothing if we have nothing (JMarc)
201 if ( ! keysym->isOK() ) {
202 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
207 if (keysym->isModifier()) {
211 // Do a one-deep top-level lookup for
212 // cancel and meta-fake keys. RVDK_PATCH_5
213 cancel_meta_seq.reset();
215 int action = cancel_meta_seq.addkey(keysym, state);
216 if (lyxerr.debugging(Debug::KEY)) {
217 lyxerr << "action first set to [" << action << "]" << endl;
220 // When not cancel or meta-fake, do the normal lookup.
221 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
222 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
223 if ((action != LFUN_CANCEL) && (action != LFUN_META_FAKE)) {
224 // remove Caps Lock and Mod2 as a modifiers
225 action = keyseq.addkey(keysym, (state | meta_fake_bit));
226 if (lyxerr.debugging(Debug::KEY)) {
227 lyxerr << "action now set to ["
228 << action << "]" << endl;
231 // Dont remove this unless you know what you are doing.
232 meta_fake_bit = key_modifier::none;
234 // can this happen now ?
235 if (action == LFUN_NOACTION) {
236 action = LFUN_PREFIX;
239 if (lyxerr.debugging(Debug::KEY)) {
240 lyxerr << "Key [action="
242 << keyseq.print() << "]"
246 // already here we know if it any point in going further
247 // why not return already here if action == -1 and
248 // num_bytes == 0? (Lgb)
250 if (keyseq.length() > 1) {
251 owner->message(keyseq.print());
254 if (action == LFUN_UNKNOWN_ACTION) {
255 // It is unknown, but what if we remove all
256 // the modifiers? (Lgb)
257 action = keyseq.addkey(keysym, key_modifier::none);
259 if (lyxerr.debugging(Debug::KEY)) {
260 lyxerr << "Removing modifiers...\n"
261 << "Action now set to ["
262 << action << "]" << endl;
264 if (action == LFUN_UNKNOWN_ACTION) {
265 owner->message(_("Unknown function."));
270 if (action == LFUN_SELFINSERT) {
271 // This is very X dependent.
272 char c = keysym->getISOEncoded();
278 dispatch(LFUN_SELFINSERT, argument);
279 lyxerr[Debug::KEY] << "SelfInsert arg[`"
280 << argument << "']" << endl;
282 verboseDispatch(action, false);
287 FuncStatus LyXFunc::getStatus(int ac) const
291 action = lyxaction.retrieveActionArg(ac, argument);
292 return getStatus(action, argument);
296 FuncStatus LyXFunc::getStatus(kb_action action,
297 string const & argument) const
300 Buffer * buf = owner->buffer();
302 if (action == LFUN_NOACTION) {
303 setStatusMessage(N_("Nothing to do"));
304 return flag.disabled(true);
307 if (action == LFUN_UNKNOWN_ACTION) {
308 setStatusMessage(N_("Unknown action"));
309 return flag.unknown(true);
312 // the default error message if we disable the command
313 setStatusMessage(N_("Command disabled"));
315 // Check whether we need a buffer
316 if (!lyxaction.funcHasFlag(action, LyXAction::NoBuffer)) {
317 // Yes we need a buffer, do we have one?
320 // Can we use a readonly buffer?
321 if (buf->isReadonly() &&
322 !lyxaction.funcHasFlag(action,
323 LyXAction::ReadOnly)) {
325 setStatusMessage(N_("Document is read-only"));
330 setStatusMessage(N_("Command not allowed with"
331 "out any document open"));
332 return flag.disabled(true);
336 UpdatableInset * tli = owner->view()->theLockingInset();
338 // I would really like to avoid having this switch and rather try to
339 // encode this in the function itself.
340 bool disable = false;
343 disable = !Exporter::IsExportable(buf, "dvi")
344 || lyxrc.print_command == "none";
347 disable = argument == "fax" &&
348 !Exporter::IsExportable(buf, argument);
351 disable = buf->undostack.empty();
354 disable = buf->redostack.empty();
356 case LFUN_SPELLCHECK:
357 disable = lyxrc.isp_command == "none";
359 #ifndef HAVE_LIBAIKSAURUS
360 case LFUN_THESAURUS_ENTRY:
365 disable = lyxrc.chktex_command == "none";
368 disable = !Exporter::IsExportable(buf, "program");
371 case LFUN_LAYOUT_TABULAR:
373 || (tli->lyxCode() != Inset::TABULAR_CODE
374 && !tli->getFirstLockingInsetOfType(Inset::TABULAR_CODE));
378 case LFUN_LAYOUT_PARAGRAPH: {
379 Inset * inset = TEXT(false)->cursor.par()->inInset();
380 disable = inset && inset->forceDefaultParagraphs(inset);
384 case LFUN_TABULAR_FEATURE:
388 //ret.disabled(true);
389 if (tli->lyxCode() == Inset::TABULAR_CODE) {
390 ret = static_cast<InsetTabular *>(tli)
391 ->getStatus(argument);
392 } else if (tli->getFirstLockingInsetOfType(Inset::TABULAR_CODE)) {
393 ret = static_cast<InsetTabular *>
394 (tli->getFirstLockingInsetOfType(Inset::TABULAR_CODE))
395 ->getStatus(argument);
400 static InsetTabular inset(*owner->buffer(), 1, 1);
404 ret = inset.getStatus(argument);
405 if (ret.onoff(true) || ret.onoff(false))
406 flag.setOnOff(false);
410 case LFUN_VC_REGISTER:
411 disable = buf->lyxvc.inUse();
413 case LFUN_VC_CHECKIN:
414 disable = !buf->lyxvc.inUse() || buf->isReadonly();
416 case LFUN_VC_CHECKOUT:
417 disable = !buf->lyxvc.inUse() || !buf->isReadonly();
421 case LFUN_VC_HISTORY:
422 disable = !buf->lyxvc.inUse();
424 case LFUN_BOOKMARK_GOTO:
425 disable = !owner->view()->
426 isSavedPosition(strToUnsignedInt(argument));
430 case LFUN_INSET_TOGGLE: {
431 LyXText * lt = owner->view()->getLyXText();
432 disable = !(isEditableInset(lt->getInset())
434 && lt->inset_owner->owner()
435 && lt->inset_owner->owner()->isOpen()));
438 case LFUN_MATH_VALIGN:
439 if (mathcursor && mathcursor->formula()->hullType() != "simple") {
440 char align = mathcursor->valign();
445 if (argument.empty()) {
449 if (!contains("tcb", argument[0])) {
453 flag.setOnOff(argument[0] == align);
458 case LFUN_MATH_HALIGN:
459 if (mathcursor && mathcursor->formula()->hullType() != "simple") {
460 char align = mathcursor->halign();
465 if (argument.empty()) {
469 if (!contains("lcr", argument[0])) {
473 flag.setOnOff(argument[0] == align);
478 case LFUN_MATH_MUTATE:
479 if (tli && (tli->lyxCode() == Inset::MATH_CODE))
480 flag.setOnOff(mathcursor->formula()->hullType() == argument);
485 // we just need to be in math mode to enable that
487 case LFUN_MATH_SPACE:
488 case LFUN_MATH_LIMITS:
489 case LFUN_MATH_NONUMBER:
490 case LFUN_MATH_NUMBER:
491 disable = !mathcursor;
494 // we need to be math mode and a math array for that
495 // Hack: halign produces non-zero result iff we are in a math array
496 case LFUN_MATH_ROW_INSERT:
497 case LFUN_MATH_ROW_DELETE:
498 case LFUN_MATH_COLUMN_INSERT:
499 case LFUN_MATH_COLUMN_DELETE:
500 disable = !mathcursor || !mathcursor->halign() ||
501 mathcursor->formula()->hullType() == "simple";
508 // the functions which insert insets
509 Inset::Code code = Inset::NO_CODE;
512 code = Inset::ERT_CODE;
514 case LFUN_INSET_GRAPHICS:
515 code = Inset::GRAPHICS_CODE;
517 case LFUN_INSET_FOOTNOTE:
518 code = Inset::FOOT_CODE;
520 case LFUN_DIALOG_TABULAR_INSERT:
521 case LFUN_INSET_TABULAR:
522 code = Inset::TABULAR_CODE;
524 case LFUN_INSET_EXTERNAL:
525 code = Inset::EXTERNAL_CODE;
527 case LFUN_INSET_MARGINAL:
528 code = Inset::MARGIN_CODE;
530 case LFUN_INSET_MINIPAGE:
531 code = Inset::MINIPAGE_CODE;
533 case LFUN_INSET_FLOAT:
534 case LFUN_INSET_WIDE_FLOAT:
535 code = Inset::FLOAT_CODE;
537 case LFUN_FLOAT_LIST:
538 code = Inset::FLOAT_LIST_CODE;
541 case LFUN_INSET_LIST:
542 code = Inset::LIST_CODE;
544 case LFUN_INSET_THEOREM:
545 code = Inset::THEOREM_CODE;
548 case LFUN_INSET_CAPTION:
549 code = Inset::CAPTION_CODE;
551 case LFUN_INSERT_NOTE:
552 code = Inset::IGNORE_CODE;
554 case LFUN_INSERT_LABEL:
555 code = Inset::LABEL_CODE;
557 case LFUN_REF_INSERT:
558 code = Inset::REF_CODE;
560 case LFUN_CITATION_CREATE:
561 case LFUN_CITATION_INSERT:
562 code = Inset::CITE_CODE;
564 case LFUN_INSERT_BIBTEX:
565 code = Inset::BIBTEX_CODE;
567 case LFUN_INDEX_INSERT:
568 code = Inset::INDEX_CODE;
570 case LFUN_INDEX_PRINT:
571 code = Inset::INDEX_PRINT_CODE;
573 case LFUN_CHILD_INSERT:
574 code = Inset::INCLUDE_CODE;
576 case LFUN_TOC_INSERT:
577 code = Inset::TOC_CODE;
579 case LFUN_PARENTINSERT:
580 code = Inset::PARENT_CODE;
584 case LFUN_INSERT_URL:
585 code = Inset::URL_CODE;
588 // always allow this, since we will inset a raw quote
589 // if an inset is not allowed.
591 case LFUN_HYPHENATION:
592 case LFUN_LIGATURE_BREAK:
594 case LFUN_MENU_SEPARATOR:
596 case LFUN_END_OF_SENTENCE:
597 code = Inset::SPECIALCHAR_CODE;
599 case LFUN_PROTECTEDSPACE:
600 // slight hack: we know this is allowed in math mode
602 code = Inset::SPECIALCHAR_CODE;
607 if (code != Inset::NO_CODE && tli && !tli->insetAllowed(code)) {
614 // A few general toggles
616 case LFUN_TOOLTIPS_TOGGLE:
617 flag.setOnOff(owner->getDialogs()->tooltipsEnabled());
620 case LFUN_READ_ONLY_TOGGLE:
621 flag.setOnOff(buf->isReadonly());
624 flag.setOnOff(TEXT(false)->cursor.par()->params().startOfAppendix());
626 case LFUN_SWITCHBUFFER:
627 // toggle on the current buffer, but do not toggle off
628 // the other ones (is that a good idea?)
629 if (argument == buf->fileName())
636 // the font related toggles
638 LyXFont const & font = TEXT(false)->real_current_font;
641 flag.setOnOff(font.emph() == LyXFont::ON);
644 flag.setOnOff(font.noun() == LyXFont::ON);
647 flag.setOnOff(font.series() == LyXFont::BOLD_SERIES);
650 flag.setOnOff(font.family() == LyXFont::SANS_FAMILY);
653 flag.setOnOff(font.family() == LyXFont::ROMAN_FAMILY);
656 flag.setOnOff(font.family() == LyXFont::TYPEWRITER_FAMILY);
663 string tc = mathcursor->getLastCode();
666 flag.setOnOff(tc == "mathbf");
669 flag.setOnOff(tc == "mathsf");
672 flag.setOnOff(tc == "mathcal");
675 flag.setOnOff(tc == "mathrm");
678 flag.setOnOff(tc == "mathtt");
681 flag.setOnOff(tc == "mathbb");
684 flag.setOnOff(tc == "mathnormal");
695 // temporary dispatch method
696 void LyXFunc::miniDispatch(string const & s)
698 string s2(frontStrip(strip(s)));
701 verboseDispatch(s2, true);
706 void LyXFunc::verboseDispatch(string const & s, bool show_sc)
708 int action = lyxaction.LookupFunc(frontStrip(s));
710 if (action == LFUN_UNKNOWN_ACTION) {
711 string const msg = string(_("Unknown function ("))
715 verboseDispatch(action, show_sc);
720 void LyXFunc::verboseDispatch(int ac, bool show_sc)
725 // get the real action and argument
726 action = lyxaction.retrieveActionArg(ac, argument);
728 verboseDispatch(action, argument, show_sc);
733 void LyXFunc::verboseDispatch(kb_action action,
734 string const & argument, bool show_sc)
736 string res = dispatch(action, argument);
738 commandshortcut.erase();
740 if (show_sc && action != LFUN_SELFINSERT) {
741 // Put name of command and list of shortcuts
742 // for it in minibuffer
743 string comname = lyxaction.getActionName(action);
745 int pseudoaction = action;
746 bool argsadded = false;
748 if (!argument.empty()) {
749 // the pseudoaction is useful for the bindings
751 lyxaction.searchActionArg(action,
754 if (pseudoaction == LFUN_UNKNOWN_ACTION) {
755 pseudoaction = action;
757 comname += " " + argument;
762 string const shortcuts =
763 toplevel_keymap->findbinding(pseudoaction);
765 if (!shortcuts.empty()) {
766 comname += ": " + shortcuts;
767 } else if (!argsadded && !argument.empty()) {
768 comname += " " + argument;
771 if (!comname.empty()) {
772 comname = strip(comname);
773 commandshortcut = "(" + comname + ')';
778 if (!commandshortcut.empty()) {
779 owner->getMiniBuffer()->addSet(commandshortcut);
782 owner->getMiniBuffer()->addSet(' ' + commandshortcut);
787 string const LyXFunc::dispatch(kb_action action, string argument)
789 lyxerr[Debug::ACTION] << "LyXFunc::Dispatch: action[" << action
790 <<"] arg[" << argument << "]" << endl;
792 // we have not done anything wrong yet.
794 dispatch_buffer.erase();
796 #ifdef NEW_DISPATCHER
797 // We try do call the most specific dispatcher first:
798 // 1. the lockinginset's dispatch
799 // 2. the bufferview's dispatch
800 // 3. the lyxview's dispatch
803 selection_possible = false;
805 if (owner->view()->available())
806 owner->view()->hideCursor();
808 // We cannot use this function here
809 if (getStatus(action, argument).disabled()) {
810 lyxerr[Debug::ACTION] << "LyXFunc::Dispatch: "
811 << lyxaction.getActionName(action)
812 << " [" << action << "] is disabled at this location"
814 setErrorMessage(getStatusMessage());
815 goto exit_with_message;
818 if (owner->view()->available() && owner->view()->theLockingInset()) {
819 UpdatableInset::RESULT result;
820 if ((action > 1) || ((action == LFUN_UNKNOWN_ACTION) &&
821 (!keyseq.deleted())))
823 UpdatableInset * inset = owner->view()->theLockingInset();
827 inset->getCursorPos(owner->view(), inset_x, dummy_y);
829 if ((action == LFUN_UNKNOWN_ACTION)
830 && argument.empty()) {
831 argument = keyseq.getLastKeyEncoded();
833 // Undo/Redo is a bit tricky for insets.
834 if (action == LFUN_UNDO) {
835 owner->view()->menuUndo();
836 goto exit_with_message;
837 } else if (action == LFUN_REDO) {
838 owner->view()->menuRedo();
839 goto exit_with_message;
840 } else if (((result=inset->
841 // Hand-over to inset's own dispatch:
842 localDispatch(owner->view(), action, argument)) ==
843 UpdatableInset::DISPATCHED) ||
844 (result == UpdatableInset::DISPATCHED_NOUPDATE))
845 goto exit_with_message;
846 // If UNDISPATCHED, just soldier on
847 else if (result == UpdatableInset::FINISHED) {
848 goto exit_with_message;
849 // We do not need special RTL handling here:
850 // FINISHED means that the cursor should be
851 // one position after the inset.
852 } else if (result == UpdatableInset::FINISHED_RIGHT) {
853 TEXT()->cursorRight(owner->view());
854 moveCursorUpdate(true, false);
856 goto exit_with_message;
857 } else if (result == UpdatableInset::FINISHED_UP) {
858 if (TEXT()->cursor.irow()->previous()) {
860 TEXT()->setCursorFromCoordinates(
861 owner->view(), TEXT()->cursor.ix() + inset_x,
862 TEXT()->cursor.iy() -
863 TEXT()->cursor.irow()->baseline() - 1);
864 TEXT()->cursor.x_fix(TEXT()->cursor.x());
866 TEXT()->cursorUp(owner->view());
868 moveCursorUpdate(true, false);
871 owner->view()->update(TEXT(), BufferView::SELECT|BufferView::FITCUR);
873 goto exit_with_message;
874 } else if (result == UpdatableInset::FINISHED_DOWN) {
875 if (TEXT()->cursor.irow()->next()) {
877 TEXT()->setCursorFromCoordinates(
878 owner->view(), TEXT()->cursor.ix() + inset_x,
879 TEXT()->cursor.iy() -
880 TEXT()->cursor.irow()->baseline() +
881 TEXT()->cursor.irow()->height() + 1);
882 TEXT()->cursor.x_fix(TEXT()->cursor.x());
884 TEXT()->cursorDown(owner->view());
887 TEXT()->cursorRight(owner->view());
889 moveCursorUpdate(true, false);
891 goto exit_with_message;
893 #warning I am not sure this is still right, please have a look! (Jug 20020417)
894 else { // result == UNDISPATCHED
895 //setMessage(N_("Text mode"));
897 case LFUN_UNKNOWN_ACTION:
898 case LFUN_BREAKPARAGRAPH:
900 TEXT()->cursorRight(owner->view());
901 owner->view()->setState();
905 if (!TEXT()->cursor.par()->isRightToLeftPar(owner->buffer()->params)) {
906 TEXT()->cursorRight(owner->view());
907 moveCursorUpdate(true, false);
910 goto exit_with_message;
912 if (TEXT()->cursor.par()->isRightToLeftPar(owner->buffer()->params)) {
913 TEXT()->cursorRight(owner->view());
914 moveCursorUpdate(true, false);
917 goto exit_with_message;
919 if (TEXT()->cursor.row()->next())
920 TEXT()->cursorDown(owner->view());
922 TEXT()->cursorRight(owner->view());
923 moveCursorUpdate(true, false);
925 goto exit_with_message;
937 if (!owner->view()->available()) break;
938 // this function should be used always [asierra060396]
939 UpdatableInset * tli =
940 owner->view()->theLockingInset();
942 UpdatableInset * lock = tli->getLockingInset();
945 owner->view()->unlockInset(tli);
946 TEXT()->cursorRight(owner->view());
947 moveCursorUpdate(true, false);
950 tli->unlockInsetInInset(owner->view(),
955 // Tell the paragraph dialog that we changed paragraph
956 owner->getDialogs()->updateParagraph();
961 // --- Misc -------------------------------------------
962 case LFUN_WORDFINDFORWARD :
963 case LFUN_WORDFINDBACKWARD : {
964 static string last_search;
965 string searched_string;
967 if (!argument.empty()) {
968 last_search = argument;
969 searched_string = argument;
971 searched_string = last_search;
973 bool fw = (action == LFUN_WORDFINDBACKWARD);
974 if (!searched_string.empty()) {
975 lyxfind::LyXFind(owner->view(), searched_string, fw);
977 // owner->view()->showCursor();
983 if (owner->view()->available() && !owner->view()->theLockingInset()) {
984 owner->view()->update(TEXT(),
985 BufferView::SELECT|BufferView::FITCUR);
987 owner->message(keyseq.printOptions());
991 // --- Misc -------------------------------------------
992 case LFUN_EXEC_COMMAND:
994 vector<string> allCmds;
995 transform(lyxaction.func_begin(), lyxaction.func_end(),
996 back_inserter(allCmds), lyx::firster());
997 static vector<string> hist;
998 owner->getMiniBuffer()->prepareForInput(allCmds, hist);
1002 case LFUN_CANCEL: // RVDK_PATCH_5
1004 meta_fake_bit = key_modifier::none;
1005 if (owner->view()->available())
1006 // cancel any selection
1007 dispatch(LFUN_MARK_OFF);
1008 setMessage(N_("Cancel"));
1011 case LFUN_META_FAKE: // RVDK_PATCH_5
1013 meta_fake_bit = key_modifier::alt;
1014 setMessage(keyseq.print());
1018 case LFUN_READ_ONLY_TOGGLE:
1019 if (owner->buffer()->lyxvc.inUse()) {
1020 owner->buffer()->lyxvc.toggleReadOnly();
1022 owner->buffer()->setReadonly(
1023 !owner->buffer()->isReadonly());
1027 case LFUN_CENTER: // this is center and redraw.
1028 owner->view()->center();
1031 // --- Menus -----------------------------------------------
1033 menuNew(argument, false);
1036 case LFUN_MENUNEWTMPLT:
1037 menuNew(argument, true);
1040 case LFUN_CLOSEBUFFER:
1044 case LFUN_MENUWRITE:
1045 if (!owner->buffer()->isUnnamed()) {
1047 s1 << _("Saving document") << ' '
1048 << MakeDisplayPath(owner->buffer()->fileName() + "...");
1049 owner->message(s1.str().c_str());
1050 MenuWrite(owner->view(), owner->buffer());
1052 WriteAs(owner->view(), owner->buffer());
1056 WriteAs(owner->view(), owner->buffer(), argument);
1059 case LFUN_MENURELOAD:
1064 Exporter::Export(owner->buffer(), argument, true);
1068 Exporter::Preview(owner->buffer(), argument);
1071 case LFUN_BUILDPROG:
1072 Exporter::Export(owner->buffer(), "program", true);
1075 case LFUN_RUNCHKTEX:
1076 MenuRunChktex(owner->buffer());
1079 case LFUN_MENUPRINT:
1080 owner->getDialogs()->showPrint();
1084 if (argument == "custom")
1085 owner->getDialogs()->showSendto();
1087 Exporter::Export(owner->buffer(), argument, false);
1105 InsetCommandParams p;
1108 if (action == LFUN_TOCVIEW)
1110 p.setCmdName("tableofcontents");
1112 else if (action == LFUN_LOAVIEW)
1113 p.setCmdName("listof{algorithm}{List of Algorithms}");
1114 else if (action == LFUN_LOFVIEW)
1115 p.setCmdName("listoffigures");
1117 p.setCmdName("listoftables");
1119 owner->getDialogs()->createTOC(p.getAsString());
1123 case LFUN_DIALOG_TABULAR_INSERT:
1124 owner->getDialogs()->showTabularCreate();
1128 AutoSave(owner->view());
1132 owner->view()->menuUndo();
1136 owner->view()->menuRedo();
1139 case LFUN_MENUSEARCH:
1140 owner->getDialogs()->showSearch();
1143 case LFUN_REMOVEERRORS:
1144 if (owner->view()->removeAutoInsets()) {
1145 #warning repaint() or update() or nothing ?
1146 owner->view()->repaint();
1147 owner->view()->fitCursor();
1151 case LFUN_DEPTH_MIN:
1152 changeDepth(owner->view(), TEXT(false), -1);
1155 case LFUN_DEPTH_PLUS:
1156 changeDepth(owner->view(), TEXT(false), 1);
1160 owner->getDialogs()->setUserFreeFont();
1163 case LFUN_RECONFIGURE:
1164 Reconfigure(owner->view());
1168 case LFUN_FLOATSOPERATE:
1169 if (argument == "openfoot")
1170 owner->view()->allFloats(1,0);
1171 else if (argument == "closefoot")
1172 owner->view()->allFloats(0,0);
1173 else if (argument == "openfig")
1174 owner->view()->allFloats(1,1);
1175 else if (argument == "closefig")
1176 owner->view()->allFloats(0,1);
1179 #ifdef WITH_WARNINGS
1180 //#warning Find another implementation here (or another lyxfunc)!
1183 case LFUN_HELP_ABOUTLYX:
1184 owner->getDialogs()->showAboutlyx();
1187 case LFUN_HELP_TEXINFO:
1188 owner->getDialogs()->showTexinfo();
1191 case LFUN_HELP_OPEN:
1193 string const arg = argument;
1195 setErrorMessage(N_("Missing argument"));
1198 owner->prohibitInput();
1199 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1200 if (fname.empty()) {
1201 lyxerr << "LyX: unable to find documentation file `"
1202 << arg << "'. Bad installation?" << endl;
1203 owner->allowInput();
1207 str << _("Opening help file") << ' '
1208 << MakeDisplayPath(fname) << "...";
1209 owner->message(str.str().c_str());
1210 owner->view()->buffer(bufferlist.loadLyXFile(fname, false));
1211 owner->allowInput();
1215 // --- version control -------------------------------
1216 case LFUN_VC_REGISTER:
1218 if (!owner->buffer()->lyxvc.inUse())
1219 owner->buffer()->lyxvc.registrer();
1223 case LFUN_VC_CHECKIN:
1225 if (owner->buffer()->lyxvc.inUse()
1226 && !owner->buffer()->isReadonly())
1227 owner->buffer()->lyxvc.checkIn();
1231 case LFUN_VC_CHECKOUT:
1233 if (owner->buffer()->lyxvc.inUse()
1234 && owner->buffer()->isReadonly())
1235 owner->buffer()->lyxvc.checkOut();
1239 case LFUN_VC_REVERT:
1241 owner->buffer()->lyxvc.revert();
1247 owner->buffer()->lyxvc.undoLast();
1251 case LFUN_VC_HISTORY:
1253 owner->getDialogs()->showVCLogFile();
1257 // --- buffers ----------------------------------------
1259 case LFUN_SWITCHBUFFER:
1260 owner->view()->buffer(bufferlist.getBuffer(argument));
1265 // servercmd: argument must be <file>:<template>
1266 Buffer * tmpbuf = NewLyxFile(argument);
1268 owner->view()->buffer(tmpbuf);
1272 case LFUN_FILE_OPEN:
1276 case LFUN_LATEX_LOG:
1277 owner->getDialogs()->showLogFile();
1280 case LFUN_LAYOUT_DOCUMENT:
1281 owner->getDialogs()->showDocument();
1284 case LFUN_LAYOUT_PARAGRAPH:
1285 owner->getDialogs()->showParagraph();
1288 case LFUN_LAYOUT_CHARACTER:
1289 owner->getDialogs()->showCharacter();
1292 case LFUN_LAYOUT_TABULAR:
1293 if (owner->view()->theLockingInset()) {
1294 if (owner->view()->theLockingInset()->lyxCode()==Inset::TABULAR_CODE) {
1295 InsetTabular * inset = static_cast<InsetTabular *>
1296 (owner->view()->theLockingInset());
1297 inset->openLayoutDialog(owner->view());
1298 } else if (owner->view()->theLockingInset()->
1299 getFirstLockingInsetOfType(Inset::TABULAR_CODE)!=0) {
1300 InsetTabular * inset = static_cast<InsetTabular *>(
1301 owner->view()->theLockingInset()->getFirstLockingInsetOfType(Inset::TABULAR_CODE));
1302 inset->openLayoutDialog(owner->view());
1307 case LFUN_LAYOUT_PREAMBLE:
1308 owner->getDialogs()->showPreamble();
1311 case LFUN_DROP_LAYOUTS_CHOICE:
1312 owner->getToolbar()->openLayoutList();
1315 case LFUN_MENU_OPEN_BY_NAME:
1316 owner->getMenubar()->openByName(argument);
1317 break; // RVDK_PATCH_5
1319 case LFUN_SPELLCHECK:
1320 if (lyxrc.isp_command != "none")
1321 owner->getDialogs()->showSpellchecker();
1324 // --- lyxserver commands ----------------------------
1328 setMessage(owner->buffer()->fileName());
1329 lyxerr[Debug::INFO] << "FNAME["
1330 << owner->buffer()->fileName()
1336 dispatch_buffer = keyseq.print();
1337 lyxserver->notifyClient(dispatch_buffer);
1341 case LFUN_GOTOFILEROW:
1345 istringstream istr(argument.c_str());
1346 istr >> file_name >> row;
1347 // Must replace extension of the file to be .lyx and get full path
1348 string const s(ChangeExtension(file_name, ".lyx"));
1350 // Either change buffer or load the file
1351 if (bufferlist.exists(s)) {
1352 owner->view()->buffer(bufferlist.getBuffer(s));
1354 owner->view()->buffer(bufferlist.loadLyXFile(s));
1357 owner->view()->setCursorFromRow(row);
1359 owner->view()->center();
1360 // see BufferView_pimpl::center()
1361 owner->view()->updateScrollbar();
1365 case LFUN_GOTO_PARAGRAPH:
1367 istringstream istr(argument.c_str());
1371 Paragraph * par = owner->buffer()->getParFromID(id);
1373 lyxerr[Debug::INFO] << "No matching paragraph found! ["
1374 << id << "]" << endl;
1377 lyxerr[Debug::INFO] << "Paragraph " << par->id()
1378 << " found." << endl;
1381 if (owner->view()->theLockingInset())
1382 owner->view()->unlockInset(owner->view()->theLockingInset());
1383 if (par->inInset()) {
1384 par->inInset()->edit(owner->view());
1387 owner->view()->getLyXText()->setCursor(owner->view(), par, 0);
1388 owner->view()->setState();
1391 owner->view()->center();
1392 // see BufferView_pimpl::center()
1393 owner->view()->updateScrollbar();
1400 int const qa = lyxaction.LookupFunc(argument);
1401 setMessage(lyxaction.helpText(static_cast<kb_action>(qa)));
1405 // --- toolbar ----------------------------------
1406 case LFUN_PUSH_TOOLBAR:
1408 int nth = strToInt(argument);
1410 setErrorMessage(N_("Push-toolbar needs argument > 0"));
1412 owner->getToolbar()->push(nth);
1417 case LFUN_ADD_TO_TOOLBAR:
1419 if (lyxerr.debugging(Debug::GUI)) {
1420 lyxerr << "LFUN_ADD_TO_TOOLBAR:"
1421 "argument = `" << argument << '\'' << endl;
1423 string tmp(argument);
1424 //lyxerr <<string("Argument: ") + argument);
1425 //lyxerr <<string("Tmp : ") + tmp);
1427 setErrorMessage(N_("Usage: toolbar-add-to <LyX command>"));
1429 owner->getToolbar()->add(argument, false);
1430 owner->getToolbar()->set();
1435 // --- insert characters ----------------------------------------
1437 // --- Mathed stuff. If we are here, there is no locked inset yet.
1438 case LFUN_MATH_EXTERN:
1439 case LFUN_MATH_NUMBER:
1440 case LFUN_MATH_NONUMBER:
1441 case LFUN_MATH_LIMITS:
1443 setErrorMessage(N_("This is only allowed in math mode!"));
1447 // passthrough hat and underscore outside mathed:
1448 case LFUN_SUBSCRIPT:
1449 dispatch(LFUN_SELFINSERT, "_");
1451 case LFUN_SUPERSCRIPT:
1452 dispatch(LFUN_SELFINSERT, "^");
1455 case LFUN_MATH_PANEL:
1456 owner->getDialogs()->showMathPanel();
1459 case LFUN_CITATION_CREATE:
1461 InsetCommandParams p("cite");
1463 if (!argument.empty()) {
1464 // This should be set at source, ie when typing
1465 // "citation-insert foo" in the minibuffer.
1466 // Question: would pybibliographer also need to be
1467 // changed. Suspect so. Leave as-is therefore.
1468 if (contains(argument, "|")) {
1469 p.setContents(token(argument, '|', 0));
1470 p.setOptions( token(argument, '|', 1));
1472 p.setContents(argument);
1474 dispatch(LFUN_CITATION_INSERT, p.getAsString());
1476 owner->getDialogs()->createCitation(p.getAsString());
1480 case LFUN_CHILDOPEN:
1482 string const filename =
1483 MakeAbsPath(argument,
1484 owner->buffer()->filePath());
1485 setMessage(N_("Opening child document ") +
1486 MakeDisplayPath(filename) + "...");
1487 owner->view()->savePosition(0);
1488 if (bufferlist.exists(filename))
1489 owner->view()->buffer(bufferlist.getBuffer(filename));
1491 owner->view()->buffer(bufferlist.loadLyXFile(filename));
1495 case LFUN_TOGGLECURSORFOLLOW:
1496 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1499 case LFUN_KMAP_OFF: // keymap off
1500 owner->getIntl()->KeyMapOn(false);
1503 case LFUN_KMAP_PRIM: // primary keymap
1504 owner->getIntl()->KeyMapPrim();
1507 case LFUN_KMAP_SEC: // secondary keymap
1508 owner->getIntl()->KeyMapSec();
1511 case LFUN_KMAP_TOGGLE: // toggle keymap
1512 owner->getIntl()->ToggleKeyMap();
1517 // argument contains ';'-terminated commands
1518 while (!argument.empty()) {
1520 argument = split(argument, first, ';');
1521 verboseDispatch(first, false);
1526 case LFUN_DIALOG_PREFERENCES:
1527 owner->getDialogs()->showPreferences();
1530 case LFUN_SAVEPREFERENCES:
1532 Path p(user_lyxdir);
1533 lyxrc.write("preferences");
1537 case LFUN_SCREEN_FONT_UPDATE:
1539 // handle the screen font changes.
1541 lyxrc.set_font_norm_type();
1542 lyx_gui::update_fonts();
1543 // Of course we should only do the resize and the textcache.clear
1544 // if values really changed...but not very important right now. (Lgb)
1545 // All visible buffers will need resize
1546 owner->view()->resize();
1547 owner->view()->repaint();
1548 // We also need to empty the textcache so that
1549 // the buffer will be formatted correctly after
1555 case LFUN_SET_COLOR:
1558 string const x11_name = split(argument, lyx_name, ' ');
1559 if (lyx_name.empty() || x11_name.empty()) {
1560 setErrorMessage(N_("Syntax: set-color <lyx_name>"
1565 bool const graphicsbg_changed =
1566 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1567 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1569 if (!lcolor.setColor(lyx_name, x11_name)) {
1570 static string const err1 (N_("Set-color \""));
1571 static string const err2 (
1572 N_("\" failed - color is undefined "
1573 "or may not be redefined"));
1574 setErrorMessage(_(err1) + lyx_name + _(err2));
1578 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1580 if (graphicsbg_changed) {
1581 #ifdef WITH_WARNINGS
1582 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1585 grfx::GCache & gc = grfx::GCache::get();
1586 gc.changeDisplay(true);
1590 owner->view()->repaint();
1595 owner->message(argument);
1598 case LFUN_MESSAGE_PUSH:
1599 owner->messagePush(argument);
1602 case LFUN_MESSAGE_POP:
1603 owner->messagePop();
1606 case LFUN_FORKS_SHOW:
1607 owner->getDialogs()->showForks();
1610 case LFUN_FORKS_KILL:
1612 if (!isStrInt(argument))
1615 pid_t const pid = strToInt(argument);
1616 ForkedcallsController & fcc = ForkedcallsController::get();
1621 case LFUN_TOOLTIPS_TOGGLE:
1622 owner->getDialogs()->toggleTooltips();
1626 // Then if it was none of the above
1627 // Trying the BufferView::pimpl dispatch:
1628 if (!owner->view()->Dispatch(action, argument))
1629 lyxerr << "A truly unknown func ["
1630 << lyxaction.getActionName(action) << "]!"
1637 string const res = getMessage();
1640 owner->message(_(res));
1641 owner->updateMenubar();
1642 owner->updateToolbar();
1648 void LyXFunc::setupLocalKeymap()
1650 keyseq.stdmap = keyseq.curmap = toplevel_keymap.get();
1651 cancel_meta_seq.stdmap = cancel_meta_seq.curmap = toplevel_keymap.get();
1655 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1657 string initpath = lyxrc.document_path;
1658 string filename(name);
1660 if (owner->view()->available()) {
1661 string const trypath = owner->buffer()->filePath();
1662 // If directory is writeable, use this as default.
1663 if (IsDirWriteable(trypath))
1667 static int newfile_number;
1669 if (filename.empty()) {
1670 filename = AddName(lyxrc.document_path,
1671 "newfile" + tostr(++newfile_number) + ".lyx");
1672 FileInfo fi(filename);
1673 while (bufferlist.exists(filename) || fi.readable()) {
1675 filename = AddName(lyxrc.document_path,
1676 "newfile" + tostr(newfile_number) +
1678 fi.newFile(filename);
1682 // The template stuff
1685 FileDialog fileDlg(owner, _("Select template file"),
1686 LFUN_SELECT_FILE_SYNC,
1687 make_pair(string(_("Documents|#o#O")),
1688 string(lyxrc.document_path)),
1689 make_pair(string(_("Templates|#T#t")),
1690 string(lyxrc.template_path)));
1692 FileDialog::Result result =
1693 fileDlg.Select(lyxrc.template_path,
1694 _("*.lyx|LyX Documents (*.lyx)"));
1696 if (result.first == FileDialog::Later)
1699 string const fname = result.second;
1706 owner->view()->buffer(bufferlist.newFile(filename, templname, !name.empty()));
1710 void LyXFunc::open(string const & fname)
1712 string initpath = lyxrc.document_path;
1714 if (owner->view()->available()) {
1715 string const trypath = owner->buffer()->filePath();
1716 // If directory is writeable, use this as default.
1717 if (IsDirWriteable(trypath))
1723 if (fname.empty()) {
1724 FileDialog fileDlg(owner, _("Select document to open"),
1726 make_pair(string(_("Documents|#o#O")),
1727 string(lyxrc.document_path)),
1728 make_pair(string(_("Examples|#E#e")),
1729 string(AddPath(system_lyxdir, "examples"))));
1731 FileDialog::Result result =
1732 fileDlg.Select(initpath,
1733 "*.lyx|LyX Documents (*.lyx)");
1735 if (result.first == FileDialog::Later)
1738 filename = result.second;
1740 // check selected filename
1741 if (filename.empty()) {
1742 owner->message(_("Canceled."));
1748 // get absolute path of file and add ".lyx" to the filename if
1750 string const fullpath = FileSearch(string(), filename, "lyx");
1751 if (fullpath.empty()) {
1752 Alert::alert(_("Error"), _("Could not find file"), filename);
1756 filename = fullpath;
1759 string const disp_fn(MakeDisplayPath(filename));
1762 str << _("Opening document") << ' ' << disp_fn << "...";
1764 owner->message(str.str().c_str());
1766 Buffer * openbuf = bufferlist.loadLyXFile(filename);
1768 owner->view()->buffer(openbuf);
1770 str << _("Document") << ' ' << disp_fn << ' ' << _("opened.");
1771 owner->message(str.str().c_str());
1774 str << _("Could not open document") << ' ' << disp_fn;
1775 owner->message(str.str().c_str());
1780 void LyXFunc::doImport(string const & argument)
1783 string filename = split(argument, format, ' ');
1785 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1786 << " file: " << filename << endl;
1788 // need user interaction
1789 if (filename.empty()) {
1790 string initpath = lyxrc.document_path;
1792 if (owner->view()->available()) {
1793 string const trypath = owner->buffer()->filePath();
1794 // If directory is writeable, use this as default.
1795 if (IsDirWriteable(trypath))
1799 string const text = _("Select ") + formats.prettyName(format)
1800 + _(" file to import");
1802 FileDialog fileDlg(owner, text,
1804 make_pair(string(_("Documents|#o#O")),
1805 string(lyxrc.document_path)),
1806 make_pair(string(_("Examples|#E#e")),
1807 string(AddPath(system_lyxdir, "examples"))));
1809 string const extension = "*." + formats.extension(format)
1810 + "| " + formats.prettyName(format)
1811 + " (*." + formats.extension(format) + ")";
1813 FileDialog::Result result = fileDlg.Select(initpath,
1816 if (result.first == FileDialog::Later)
1819 filename = result.second;
1821 // check selected filename
1822 if (filename.empty())
1823 owner->message(_("Canceled."));
1826 if (filename.empty())
1829 // get absolute path of file
1830 filename = MakeAbsPath(filename);
1832 string const lyxfile = ChangeExtension(filename, ".lyx");
1834 // Check if the document already is open
1835 if (lyxrc.use_gui && bufferlist.exists(lyxfile)) {
1836 switch (Alert::askConfirmation(_("Document is already open:"),
1837 MakeDisplayPath(lyxfile, 50),
1838 _("Do you want to close that document now?\n"
1839 "('No' will just switch to the open version)")))
1842 // If close is canceled, we cancel here too.
1843 if (!bufferlist.close(bufferlist.getBuffer(lyxfile)))
1847 owner->view()->buffer(bufferlist.getBuffer(lyxfile));
1850 owner->message(_("Canceled."));
1855 // if the file exists already, and we didn't do
1856 // -i lyx thefile.lyx, warn
1857 if (FileInfo(lyxfile, true).exist() && filename != lyxfile) {
1858 if (!Alert::askQuestion(_("A document by the name"),
1859 MakeDisplayPath(lyxfile), _("already exists. Overwrite?"))) {
1860 owner->message(_("Canceled"));
1865 Importer::Import(owner, filename, format);
1869 void LyXFunc::reloadBuffer()
1871 string const fn = owner->buffer()->fileName();
1872 if (bufferlist.close(owner->buffer()))
1873 owner->view()->buffer(bufferlist.loadLyXFile(fn));
1877 void LyXFunc::closeBuffer()
1879 if (bufferlist.close(owner->buffer()) && !quitting) {
1880 if (bufferlist.empty()) {
1881 // need this otherwise SEGV may occur while trying to
1882 // set variables that don't exist
1883 // since there's no current buffer
1884 owner->getDialogs()->hideBufferDependent();
1886 owner->view()->buffer(bufferlist.first());
1892 // Each "owner" should have it's own message method. lyxview and
1893 // the minibuffer would use the minibuffer, but lyxserver would
1894 // send an ERROR signal to its client. Alejandro 970603
1895 // This func is bit problematic when it comes to NLS, to make the
1896 // lyx servers client be language indepenent we must not translate
1897 // strings sent to this func.
1898 void LyXFunc::setErrorMessage(string const & m) const
1900 dispatch_buffer = m;
1905 void LyXFunc::setMessage(string const & m) const
1907 dispatch_buffer = m;
1911 void LyXFunc::setStatusMessage(string const & m) const
1917 void LyXFunc::initMiniBuffer()
1919 string text = _("Welcome to LyX!");
1921 // When meta-fake key is pressed, show the key sequence so far + "M-".
1923 text = keyseq.print();
1927 // Else, when a non-complete key sequence is pressed,
1928 // show the available options.
1929 if (keyseq.length() > 0 && !keyseq.deleted()) {
1930 text = keyseq.printOptions();
1933 // Else, show the buffer state.
1934 else if (owner->view()->available()) {
1935 Buffer * tmpbuf = owner->buffer();
1937 string const nicename =
1938 MakeDisplayPath(tmpbuf->fileName());
1939 // Should we do this instead? (kindo like emacs)
1940 // leaves more room for other information
1943 if (tmpbuf->lyxvc.inUse()) {
1945 text += tmpbuf->lyxvc.versionString();
1947 text += tmpbuf->lyxvc.locker();
1948 if (tmpbuf->isReadonly())
1951 } else if (tmpbuf->isReadonly())
1953 if (!tmpbuf->isLyxClean())
1954 text += _(" (Changed)");
1956 if (text != _("Welcome to LyX!")) // this is a hack
1957 text = _("* No document open *");
1960 owner->message(text);