3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup
7 * \author Lars Gullik Bjønnes
8 * \author Alfredo Braunstein
9 * \author Angus Leeming
11 * \author André Pönitz
13 * Full author contact details are available in file CREDITS.
21 #include "BranchList.h"
22 #include "FloatList.h"
23 #include "FuncStatus.h"
25 #include "buffer_funcs.h"
26 #include "BufferParams.h"
27 #include "BufferView.h"
29 #include "CutAndPaste.h"
31 #include "DispatchResult.h"
32 #include "ErrorList.h"
34 #include "FuncRequest.h"
38 #include "LyXAction.h"
42 #include "Paragraph.h"
43 #include "paragraph_funcs.h"
44 #include "ParagraphParameters.h"
47 #include "ParIterator.h"
49 #include "frontends/Clipboard.h"
50 #include "frontends/Selection.h"
52 #include "insets/InsetCommand.h"
53 #include "insets/InsetFloatList.h"
54 #include "insets/InsetNewline.h"
55 #include "insets/InsetQuotes.h"
56 #include "insets/InsetSpecialChar.h"
57 #include "insets/InsetText.h"
59 #include "support/lstrings.h"
60 #include "support/lyxlib.h"
61 #include "support/convert.h"
62 #include "support/lyxtime.h"
64 #include "mathed/InsetMathHull.h"
65 #include "mathed/MathMacroTemplate.h"
67 #include <boost/current_function.hpp>
74 using std::istringstream;
75 using std::ostringstream;
79 using cap::copySelection;
80 using cap::cutSelection;
81 using cap::pasteFromStack;
82 using cap::pasteClipboard;
83 using cap::replaceSelection;
85 using support::isStrUnsignedInt;
89 extern docstring current_layout;
93 static Font freefont(Font::ALL_IGNORE);
94 static bool toggleall = false;
96 static void toggleAndShow(Cursor & cur, Text * text,
97 Font const & font, bool toggleall = true)
99 text->toggleFree(cur, font, toggleall);
101 if (font.language() != ignore_language ||
102 font.number() != Font::IGNORE) {
103 TextMetrics const & tm = cur.bv().textMetrics(text);
104 if (cur.boundary() != tm.isRTLBoundary(cur.pit(),
105 cur.pos(), cur.real_current_font))
106 text->setCursor(cur, cur.pit(), cur.pos(),
107 false, !cur.boundary());
112 static void moveCursor(Cursor & cur, bool selecting)
114 if (selecting || cur.mark())
119 static void finishChange(Cursor & cur, bool selecting)
122 moveCursor(cur, selecting);
126 static void mathDispatch(Cursor & cur, FuncRequest const & cmd, bool display)
129 docstring sel = cur.selectionAsString(false);
131 // It may happen that sel is empty but there is a selection
132 replaceSelection(cur);
135 #ifdef ENABLE_ASSERTIONS
136 const int old_pos = cur.pos();
138 cur.insert(new InsetMathHull(hullSimple));
139 BOOST_ASSERT(old_pos == cur.pos());
140 cur.nextInset()->edit(cur, true);
141 // don't do that also for LFUN_MATH_MODE
142 // unless you want end up with always changing
143 // to mathrm when opening an inlined inset --
144 // I really hate "LyXfunc overloading"...
146 cur.dispatch(FuncRequest(LFUN_MATH_DISPLAY));
147 // Avoid an unnecessary undo step if cmd.argument
149 if (!cmd.argument().empty())
150 cur.dispatch(FuncRequest(LFUN_MATH_INSERT,
153 // create a macro if we see "\\newcommand"
154 // somewhere, and an ordinary formula
156 if (sel.find(from_ascii("\\newcommand")) == string::npos
157 && sel.find(from_ascii("\\def")) == string::npos)
159 InsetMathHull * formula = new InsetMathHull;
160 istringstream is(to_utf8(sel));
163 formula->read(cur.buffer(), lex);
164 if (formula->getType() == hullNone)
165 // Don't create pseudo formulas if
166 // delimiters are left out
167 formula->mutate(hullSimple);
170 cur.insert(new MathMacroTemplate(sel));
173 cur.message(from_utf8(N_("Math editor mode")));
177 static void specialChar(Cursor & cur, InsetSpecialChar::Kind kind)
180 cap::replaceSelection(cur);
181 cur.insert(new InsetSpecialChar(kind));
186 static bool doInsertInset(Cursor & cur, Text * text,
187 FuncRequest const & cmd, bool edit, bool pastesel)
189 Inset * inset = createInset(&cur.bv(), cmd);
195 if (cur.selection()) {
196 lyx::dispatch(FuncRequest(LFUN_CUT));
199 text->insertInset(cur, inset);
202 inset->edit(cur, true);
204 if (gotsel && pastesel) {
205 lyx::dispatch(FuncRequest(LFUN_PASTE, "0"));
206 // reset first par to default
207 if (cur.lastpit() != 0 || cur.lastpos() != 0) {
208 LayoutPtr const layout =
209 cur.buffer().params().getTextClass().defaultLayout();
210 cur.text()->paragraphs().begin()->layout(layout);
217 string const freefont2string()
219 return freefont.toString(toggleall);
223 void Text::number(Cursor & cur)
225 Font font(Font::ALL_IGNORE);
226 font.setNumber(Font::TOGGLE);
227 toggleAndShow(cur, this, font);
231 bool Text::isRTL(Buffer const & buffer, Paragraph const & par) const
233 return par.isRTL(buffer.params());
237 void Text::dispatch(Cursor & cur, FuncRequest & cmd)
239 LYXERR(Debug::ACTION) << "Text::dispatch: cmd: " << cmd << endl;
241 // FIXME: We use the update flag to indicates wether a singlePar or a
242 // full screen update is needed. We reset it here but shall we restore it
246 BOOST_ASSERT(cur.text() == this);
247 BufferView * bv = &cur.bv();
248 TextMetrics & tm = cur.bv().textMetrics(this);
249 CursorSlice oldTopSlice = cur.top();
250 bool oldBoundary = cur.boundary();
251 bool sel = cur.selection();
252 // Signals that, even if needsUpdate == false, an update of the
253 // cursor paragraph is required
254 bool singleParUpdate = lyxaction.funcHasFlag(cmd.action,
255 LyXAction::SingleParUpdate);
256 // Signals that a full-screen update is required
257 bool needsUpdate = !(lyxaction.funcHasFlag(cmd.action,
258 LyXAction::NoUpdate) || singleParUpdate);
259 // Remember the old paragraph metric (_outer_ paragraph!)
260 ParagraphMetrics const & pm = cur.bv().parMetrics(
261 cur.bottom().text(), cur.bottom().pit());
262 Dimension olddim = pm.dim();
264 switch (cmd.action) {
266 case LFUN_PARAGRAPH_MOVE_DOWN: {
267 pit_type const pit = cur.pit();
268 recUndo(cur, pit, pit + 1);
270 std::swap(pars_[pit], pars_[pit + 1]);
271 updateLabels(cur.buffer());
277 case LFUN_PARAGRAPH_MOVE_UP: {
278 pit_type const pit = cur.pit();
279 recUndo(cur, pit - 1, pit);
281 std::swap(pars_[pit], pars_[pit - 1]);
282 updateLabels(cur.buffer());
288 case LFUN_APPENDIX: {
289 Paragraph & par = cur.paragraph();
290 bool start = !par.params().startOfAppendix();
292 // FIXME: The code below only makes sense at top level.
293 // Should LFUN_APPENDIX be restricted to top-level paragraphs?
294 // ensure that we have only one start_of_appendix in this document
295 // FIXME: this don't work for multipart document!
296 for (pit_type tmp = 0, end = pars_.size(); tmp != end; ++tmp) {
297 if (pars_[tmp].params().startOfAppendix()) {
299 pars_[tmp].params().startOfAppendix(false);
305 par.params().startOfAppendix(start);
307 // we can set the refreshing parameters now
308 updateLabels(cur.buffer());
312 case LFUN_WORD_DELETE_FORWARD:
313 if (cur.selection()) {
314 cutSelection(cur, true, false);
316 deleteWordForward(cur);
317 finishChange(cur, false);
320 case LFUN_WORD_DELETE_BACKWARD:
321 if (cur.selection()) {
322 cutSelection(cur, true, false);
324 deleteWordBackward(cur);
325 finishChange(cur, false);
328 case LFUN_LINE_DELETE:
329 if (cur.selection()) {
330 cutSelection(cur, true, false);
332 tm.deleteLineForward(cur);
333 finishChange(cur, false);
336 case LFUN_BUFFER_BEGIN:
337 case LFUN_BUFFER_BEGIN_SELECT:
338 needsUpdate |= cur.selHandle(cmd.action == LFUN_BUFFER_BEGIN_SELECT);
339 if (cur.depth() == 1) {
340 needsUpdate |= cursorTop(cur);
346 case LFUN_BUFFER_END:
347 case LFUN_BUFFER_END_SELECT:
348 needsUpdate |= cur.selHandle(cmd.action == LFUN_BUFFER_END_SELECT);
349 if (cur.depth() == 1) {
350 needsUpdate |= cursorBottom(cur);
356 case LFUN_CHAR_FORWARD:
357 case LFUN_CHAR_FORWARD_SELECT:
358 //lyxerr << BOOST_CURRENT_FUNCTION
359 // << " LFUN_CHAR_FORWARD[SEL]:\n" << cur << endl;
360 needsUpdate |= cur.selHandle(cmd.action == LFUN_CHAR_FORWARD_SELECT);
361 if (reverseDirectionNeeded(cur))
362 needsUpdate |= cursorLeft(cur);
364 needsUpdate |= cursorRight(cur);
366 if (!needsUpdate && oldTopSlice == cur.top()
367 && cur.boundary() == oldBoundary) {
369 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
373 case LFUN_CHAR_BACKWARD:
374 case LFUN_CHAR_BACKWARD_SELECT:
375 //lyxerr << "handle LFUN_CHAR_BACKWARD[_SELECT]:\n" << cur << endl;
376 needsUpdate |= cur.selHandle(cmd.action == LFUN_CHAR_BACKWARD_SELECT);
377 if (reverseDirectionNeeded(cur))
378 needsUpdate |= cursorRight(cur);
380 needsUpdate |= cursorLeft(cur);
382 if (!needsUpdate && oldTopSlice == cur.top()
383 && cur.boundary() == oldBoundary) {
385 cmd = FuncRequest(LFUN_FINISHED_LEFT);
390 case LFUN_DOWN_SELECT:
393 // stop/start the selection
394 bool select = cmd.action == LFUN_DOWN_SELECT ||
395 cmd.action == LFUN_UP_SELECT;
396 cur.selHandle(select);
398 // move cursor up/down
399 bool up = cmd.action == LFUN_UP_SELECT || cmd.action == LFUN_UP;
400 bool const successful = cur.upDownInText(up, needsUpdate);
402 // notify insets which were left and get their update flags
403 notifyCursorLeaves(cur.beforeDispatchCursor(), cur);
406 // redraw if you leave mathed (for the decorations)
407 needsUpdate |= cur.beforeDispatchCursor().inMathed();
414 case LFUN_PARAGRAPH_UP:
415 case LFUN_PARAGRAPH_UP_SELECT:
416 needsUpdate |= cur.selHandle(cmd.action == LFUN_PARAGRAPH_UP_SELECT);
417 needsUpdate |= cursorUpParagraph(cur);
420 case LFUN_PARAGRAPH_DOWN:
421 case LFUN_PARAGRAPH_DOWN_SELECT:
422 needsUpdate |= cur.selHandle(cmd.action == LFUN_PARAGRAPH_DOWN_SELECT);
423 needsUpdate |= cursorDownParagraph(cur);
426 case LFUN_SCREEN_UP_SELECT:
427 needsUpdate |= cur.selHandle(true);
428 if (cur.pit() == 0 && cur.textRow().pos() == 0)
431 tm.cursorPrevious(cur);
435 case LFUN_SCREEN_DOWN_SELECT:
436 needsUpdate |= cur.selHandle(true);
437 if (cur.pit() == cur.lastpit()
438 && cur.textRow().endpos() == cur.lastpos())
445 case LFUN_LINE_BEGIN:
446 case LFUN_LINE_BEGIN_SELECT:
447 needsUpdate |= cur.selHandle(cmd.action == LFUN_LINE_BEGIN_SELECT);
448 needsUpdate |= tm.cursorHome(cur);
452 case LFUN_LINE_END_SELECT:
453 needsUpdate |= cur.selHandle(cmd.action == LFUN_LINE_END_SELECT);
454 needsUpdate |= tm.cursorEnd(cur);
457 case LFUN_WORD_FORWARD:
458 case LFUN_WORD_FORWARD_SELECT:
459 needsUpdate |= cur.selHandle(cmd.action == LFUN_WORD_FORWARD_SELECT);
460 if (reverseDirectionNeeded(cur))
461 needsUpdate |= cursorLeftOneWord(cur);
463 needsUpdate |= cursorRightOneWord(cur);
466 case LFUN_WORD_BACKWARD:
467 case LFUN_WORD_BACKWARD_SELECT:
468 needsUpdate |= cur.selHandle(cmd.action == LFUN_WORD_BACKWARD_SELECT);
469 if (reverseDirectionNeeded(cur))
470 needsUpdate |= cursorRightOneWord(cur);
472 needsUpdate |= cursorLeftOneWord(cur);
475 case LFUN_WORD_SELECT: {
476 selectWord(cur, WHOLE_WORD);
477 finishChange(cur, true);
481 case LFUN_BREAK_LINE: {
482 // Not allowed by LaTeX (labels or empty par)
483 if (cur.pos() > cur.paragraph().beginOfBody()) {
484 // this avoids a double undo
485 // FIXME: should not be needed, ideally
486 if (!cur.selection())
488 cap::replaceSelection(cur);
489 cur.insert(new InsetNewline);
491 moveCursor(cur, false);
496 case LFUN_CHAR_DELETE_FORWARD:
497 if (!cur.selection()) {
498 if (cur.pos() == cur.paragraph().size())
499 // Par boundary, force full-screen update
500 singleParUpdate = false;
501 needsUpdate |= erase(cur);
503 // It is possible to make it a lot faster still
504 // just comment out the line below...
506 cutSelection(cur, true, false);
507 singleParUpdate = false;
509 moveCursor(cur, false);
512 case LFUN_DELETE_FORWARD_SKIP:
513 // Reverse the effect of LFUN_BREAK_PARAGRAPH_SKIP.
514 if (!cur.selection()) {
515 if (cur.pos() == cur.lastpos()) {
522 cutSelection(cur, true, false);
527 case LFUN_CHAR_DELETE_BACKWARD:
528 if (!cur.selection()) {
529 if (bv->getIntl().getTransManager().backspace()) {
530 // Par boundary, full-screen update
532 singleParUpdate = false;
533 needsUpdate |= backspace(cur);
535 // It is possible to make it a lot faster still
536 // just comment out the line below...
539 cutSelection(cur, true, false);
540 singleParUpdate = false;
544 case LFUN_DELETE_BACKWARD_SKIP:
545 // Reverse the effect of LFUN_BREAK_PARAGRAPH_SKIP.
546 if (!cur.selection()) {
548 //CursorSlice cur = cursor();
552 cutSelection(cur, true, false);
556 case LFUN_BREAK_PARAGRAPH:
557 cap::replaceSelection(cur);
558 breakParagraph(cur, false);
562 case LFUN_BREAK_PARAGRAPH_KEEP_LAYOUT:
563 cap::replaceSelection(cur);
564 breakParagraph(cur, true);
568 case LFUN_BREAK_PARAGRAPH_SKIP: {
569 // When at the beginning of a paragraph, remove
570 // indentation. Otherwise, do the same as LFUN_BREAK_PARAGRAPH.
571 cap::replaceSelection(cur);
573 cur.paragraph().params().labelWidthString(docstring());
575 breakParagraph(cur, false);
581 // With the creation of LFUN_PARAGRAPH_PARAMS, this is now redundant,
582 // as its duties can be performed there. Should it be removed??
583 // FIXME For now, it can just dispatch LFUN_PARAGRAPH_PARAMS...
584 case LFUN_PARAGRAPH_SPACING: {
585 Paragraph & par = cur.paragraph();
586 Spacing::Space cur_spacing = par.params().spacing().getSpace();
587 string cur_value = "1.0";
588 if (cur_spacing == Spacing::Other)
589 cur_value = par.params().spacing().getValueAsString();
591 istringstream is(to_utf8(cmd.argument()));
594 Spacing::Space new_spacing = cur_spacing;
595 string new_value = cur_value;
597 lyxerr << "Missing argument to `paragraph-spacing'"
599 } else if (tmp == "single") {
600 new_spacing = Spacing::Single;
601 } else if (tmp == "onehalf") {
602 new_spacing = Spacing::Onehalf;
603 } else if (tmp == "double") {
604 new_spacing = Spacing::Double;
605 } else if (tmp == "other") {
606 new_spacing = Spacing::Other;
607 string tmpval = "0.0";
609 lyxerr << "new_value = " << tmpval << endl;
612 } else if (tmp == "default") {
613 new_spacing = Spacing::Default;
615 lyxerr << to_utf8(_("Unknown spacing argument: "))
616 << to_utf8(cmd.argument()) << endl;
618 if (cur_spacing != new_spacing || cur_value != new_value)
619 par.params().spacing(Spacing(new_spacing, new_value));
623 case LFUN_INSET_INSERT: {
625 Inset * inset = createInset(bv, cmd);
627 // FIXME (Abdel 01/02/2006):
628 // What follows would be a partial fix for bug 2154:
629 // http://bugzilla.lyx.org/show_bug.cgi?id=2154
630 // This automatically put the label inset _after_ a
631 // numbered section. It should be possible to extend the mechanism
632 // to any kind of LateX environement.
633 // The correct way to fix that bug would be at LateX generation.
634 // I'll let the code here for reference as it could be used for some
635 // other feature like "automatic labelling".
637 Paragraph & par = pars_[cur.pit()];
638 if (inset->lyxCode() == Inset::LABEL_CODE
639 && par.layout()->labeltype == LABEL_COUNTER) {
640 // Go to the end of the paragraph
641 // Warning: Because of Change-Tracking, the last
642 // position is 'size()' and not 'size()-1':
643 cur.pos() = par.size();
644 // Insert a new paragraph
645 FuncRequest fr(LFUN_BREAK_PARAGRAPH);
650 cutSelection(cur, true, false);
651 insertInset(cur, inset);
657 case LFUN_INSET_DISSOLVE:
658 needsUpdate |= dissolveInset(cur);
661 case LFUN_INSET_SETTINGS:
662 cur.inset().showInsetDialog(bv);
665 case LFUN_SPACE_INSERT:
666 if (cur.paragraph().layout()->free_spacing)
667 insertChar(cur, ' ');
669 doInsertInset(cur, this, cmd, false, false);
672 moveCursor(cur, false);
675 case LFUN_HYPHENATION_POINT_INSERT:
676 specialChar(cur, InsetSpecialChar::HYPHENATION);
679 case LFUN_LIGATURE_BREAK_INSERT:
680 specialChar(cur, InsetSpecialChar::LIGATURE_BREAK);
683 case LFUN_DOTS_INSERT:
684 specialChar(cur, InsetSpecialChar::LDOTS);
687 case LFUN_END_OF_SENTENCE_PERIOD_INSERT:
688 specialChar(cur, InsetSpecialChar::END_OF_SENTENCE);
691 case LFUN_MENU_SEPARATOR_INSERT:
692 specialChar(cur, InsetSpecialChar::MENU_SEPARATOR);
695 case LFUN_WORD_UPCASE:
696 changeCase(cur, Text::text_uppercase);
699 case LFUN_WORD_LOWCASE:
700 changeCase(cur, Text::text_lowercase);
703 case LFUN_WORD_CAPITALIZE:
704 changeCase(cur, Text::text_capitalization);
707 case LFUN_CHARS_TRANSPOSE:
712 cur.message(_("Paste"));
713 cap::replaceSelection(cur);
714 if (cmd.argument().empty() && !theClipboard().isInternal())
715 pasteClipboard(cur, bv->buffer().errorList("Paste"));
717 string const arg(to_utf8(cmd.argument()));
718 pasteFromStack(cur, bv->buffer().errorList("Paste"),
719 isStrUnsignedInt(arg) ?
720 convert<unsigned int>(arg) :
723 bv->buffer().errors("Paste");
724 cur.clearSelection(); // bug 393
729 cutSelection(cur, true, true);
730 cur.message(_("Cut"));
735 cur.message(_("Copy"));
738 case LFUN_SERVER_GET_XY:
739 cur.message(from_utf8(
740 convert<string>(tm.cursorX(cur.top(), cur.boundary()))
741 + ' ' + convert<string>(tm.cursorY(cur.top(), cur.boundary()))));
744 case LFUN_SERVER_SET_XY: {
747 istringstream is(to_utf8(cmd.argument()));
750 lyxerr << "SETXY: Could not parse coordinates in '"
751 << to_utf8(cmd.argument()) << std::endl;
753 tm.setCursorFromCoordinates(cur, x, y);
757 case LFUN_SERVER_GET_FONT:
758 if (cur.current_font.shape() == Font::ITALIC_SHAPE)
759 cur.message(from_ascii("E"));
760 else if (cur.current_font.shape() == Font::SMALLCAPS_SHAPE)
761 cur.message(from_ascii("N"));
763 cur.message(from_ascii("0"));
766 case LFUN_SERVER_GET_LAYOUT:
767 cur.message(cur.paragraph().layout()->name());
771 docstring layout = cmd.argument();
772 LYXERR(Debug::INFO) << "LFUN_LAYOUT: (arg) " << to_utf8(layout) << endl;
774 // Derive layout number from given argument (string)
775 // and current buffer's textclass (number)
776 TextClass const & tclass = bv->buffer().params().getTextClass();
778 layout = tclass.defaultLayoutName();
779 bool hasLayout = tclass.hasLayout(layout);
781 // If the entry is obsolete, use the new one instead.
783 docstring const & obs = tclass[layout]->obsoleted_by();
789 cur.errorMessage(from_utf8(N_("Layout ")) + cmd.argument() +
790 from_utf8(N_(" not known")));
794 bool change_layout = (frontend::current_layout != layout);
796 if (!change_layout && cur.selection() &&
797 cur.selBegin().pit() != cur.selEnd().pit())
799 pit_type spit = cur.selBegin().pit();
800 pit_type epit = cur.selEnd().pit() + 1;
801 while (spit != epit) {
802 if (pars_[spit].layout()->name() != frontend::current_layout) {
803 change_layout = true;
811 setLayout(cur, layout);
812 // inform the GUI that the layout has changed.
813 bv->layoutChanged(layout);
818 case LFUN_CLIPBOARD_PASTE:
819 cur.clearSelection();
820 pasteClipboard(cur, bv->buffer().errorList("Paste"),
821 cmd.argument() == "paragraph");
822 bv->buffer().errors("Paste");
825 case LFUN_PRIMARY_SELECTION_PASTE:
826 pasteString(cur, theSelection().get(),
827 cmd.argument() == "paragraph");
830 case LFUN_UNICODE_INSERT: {
831 if (cmd.argument().empty())
833 docstring hexstring = cmd.argument();
834 if (lyx::support::isHex(hexstring)) {
835 char_type c = lyx::support::hexToInt(hexstring);
836 if (c >= 32 && c < 0x10ffff) {
837 lyxerr << "Inserting c: " << c << endl;
838 docstring s = docstring(1, c);
839 lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, s));
845 case LFUN_QUOTE_INSERT: {
846 Paragraph & par = cur.paragraph();
847 pos_type pos = cur.pos();
848 BufferParams const & bufparams = bv->buffer().params();
849 LayoutPtr const & style = par.layout();
850 if (!style->pass_thru
851 && par.getFontSettings(bufparams, pos).language()->lang() != "hebrew") {
852 // this avoids a double undo
853 // FIXME: should not be needed, ideally
854 if (!cur.selection())
856 cap::replaceSelection(cur);
861 else if (cur.prevInset() && cur.prevInset()->isSpace())
864 c = par.getChar(pos - 1);
865 string arg = to_utf8(cmd.argument());
867 cur.insert(new InsetQuotes(c,
868 bufparams.quotes_language,
869 InsetQuotes::SingleQ));
871 cur.insert(new InsetQuotes(c,
872 bufparams.quotes_language,
873 InsetQuotes::DoubleQ));
877 lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, "\""));
881 case LFUN_DATE_INSERT: {
882 string const format = cmd.argument().empty()
883 ? lyxrc.date_insert_format : to_utf8(cmd.argument());
884 string const time = formatted_time(current_time(), format);
885 lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, time));
889 case LFUN_MOUSE_TRIPLE:
890 if (cmd.button() == mouse_button::button1) {
899 case LFUN_MOUSE_DOUBLE:
900 if (cmd.button() == mouse_button::button1) {
901 selectWord(cur, WHOLE_WORD_STRICT);
906 // Single-click on work area
907 case LFUN_MOUSE_PRESS: {
908 // Right click on a footnote flag opens float menu
909 if (cmd.button() == mouse_button::button3)
910 cur.clearSelection();
913 bool update = bv->mouseSetCursor(cur);
915 // Insert primary selection with middle mouse
916 // if there is a local selection in the current buffer,
918 if (cmd.button() == mouse_button::button2) {
919 if (cap::selection()) {
920 // Copy the selection buffer to the clipboard
921 // stack, because we want it to appear in the
922 // "Edit->Paste recent" menu.
923 cap::copySelectionToStack();
925 cap::pasteSelection(bv->cursor(),
926 bv->buffer().errorList("Paste"));
927 bv->buffer().errors("Paste");
928 bv->buffer().markDirty();
931 lyx::dispatch(FuncRequest(LFUN_PRIMARY_SELECTION_PASTE, "paragraph"));
935 // we have to update after dEPM triggered
936 if (!update && cmd.button() == mouse_button::button1) {
944 case LFUN_MOUSE_MOTION: {
945 // Only use motion with button 1
946 //if (cmd.button() != mouse_button::button1)
949 // ignore motions deeper nested than the real anchor
950 Cursor & bvcur = cur.bv().cursor();
951 if (bvcur.anchor_.hasPart(cur)) {
952 CursorSlice old = bvcur.top();
954 int const wh = bv->workHeight();
955 int const y = std::max(0, std::min(wh - 1, cmd.y));
957 tm.setCursorFromCoordinates(cur, cmd.x, y);
958 cur.setTargetX(cmd.x);
960 lyx::dispatch(FuncRequest(LFUN_DOWN_SELECT));
962 lyx::dispatch(FuncRequest(LFUN_UP_SELECT));
963 // This is to allow jumping over large insets
964 if (cur.top() == old) {
966 lyx::dispatch(FuncRequest(LFUN_DOWN_SELECT));
968 lyx::dispatch(FuncRequest(LFUN_UP_SELECT));
971 if (cur.top() == old)
975 bvcur.setCursor(cur);
976 bvcur.selection() = true;
977 //lyxerr << "MOTION: " << bv->cursor() << endl;
985 case LFUN_MOUSE_RELEASE: {
986 if (cmd.button() == mouse_button::button2)
989 if (cmd.button() == mouse_button::button1) {
990 // if there is new selection, update persistent
991 // selection, otherwise, single click does not
992 // clear persistent selection buffer
993 if (cur.selection()) {
995 // if double click, cur is moved to the end of word by selectWord
996 // but bvcur is current mouse position
997 Cursor & bvcur = cur.bv().cursor();
998 bvcur.selection() = true;
1000 needsUpdate = false;
1007 case LFUN_SELF_INSERT: {
1008 if (cmd.argument().empty())
1011 // Automatically delete the currently selected
1012 // text and replace it with what is being
1013 // typed in now. Depends on lyxrc settings
1014 // "auto_region_delete", which defaults to
1017 if (lyxrc.auto_region_delete && cur.selection()) {
1018 cutSelection(cur, false, false);
1019 // When change tracking is set to off, the metrics update
1020 // mechanism correctly detects if a full update is needed or not.
1021 // This detection fails when a selection spans multiple rows and
1022 // change tracking is enabled because the paragraph metrics stays
1023 // the same. In this case, we force the full update:
1024 // (see http://bugzilla.lyx.org/show_bug.cgi?id=3992)
1025 if (cur.buffer().params().trackChanges)
1026 cur.updateFlags(Update::Force);
1029 cur.clearSelection();
1030 Font const old_font = cur.real_current_font;
1032 docstring::const_iterator cit = cmd.argument().begin();
1033 docstring::const_iterator end = cmd.argument().end();
1034 for (; cit != end; ++cit)
1035 bv->translateAndInsert(*cit, this, cur);
1038 moveCursor(cur, false);
1042 case LFUN_URL_INSERT: {
1043 InsetCommandParams p("url");
1045 if (cur.selection()) {
1046 content = cur.selectionAsString(false);
1047 cutSelection(cur, true, false);
1049 p["target"] = (cmd.argument().empty()) ?
1050 content : cmd.argument();
1051 string const data = InsetCommandMailer::params2string("url", p);
1052 if (p["target"].empty()) {
1053 bv->showInsetDialog("url", data, 0);
1055 FuncRequest fr(LFUN_INSET_INSERT, data);
1061 case LFUN_HTML_INSERT: {
1062 InsetCommandParams p("htmlurl");
1064 if (cur.selection()) {
1065 content = cur.selectionAsString(false);
1066 cutSelection(cur, true, false);
1068 p["target"] = (cmd.argument().empty()) ?
1069 content : cmd.argument();
1070 string const data = InsetCommandMailer::params2string("url", p);
1071 if (p["target"].empty()) {
1072 bv->showInsetDialog("url", data, 0);
1074 FuncRequest fr(LFUN_INSET_INSERT, data);
1080 case LFUN_LABEL_INSERT: {
1081 InsetCommandParams p("label");
1082 // Try to generate a valid label
1083 p["name"] = (cmd.argument().empty()) ?
1084 cur.getPossibleLabel() :
1086 string const data = InsetCommandMailer::params2string("label", p);
1088 if (cmd.argument().empty()) {
1089 bv->showInsetDialog("label", data, 0);
1091 FuncRequest fr(LFUN_INSET_INSERT, data);
1099 case LFUN_LIST_INSERT:
1100 case LFUN_THEOREM_INSERT:
1102 case LFUN_CAPTION_INSERT:
1103 // Open the inset, and move the current selection
1105 doInsertInset(cur, this, cmd, true, true);
1107 updateLabels(bv->buffer());
1109 case LFUN_NOTE_INSERT:
1110 case LFUN_FLEX_INSERT:
1111 case LFUN_BOX_INSERT:
1112 case LFUN_BRANCH_INSERT:
1113 case LFUN_BIBITEM_INSERT:
1114 case LFUN_ERT_INSERT:
1115 case LFUN_LISTING_INSERT:
1116 case LFUN_FOOTNOTE_INSERT:
1117 case LFUN_MARGINALNOTE_INSERT:
1118 case LFUN_OPTIONAL_INSERT:
1119 case LFUN_ENVIRONMENT_INSERT:
1120 // Open the inset, and move the current selection
1122 doInsertInset(cur, this, cmd, true, true);
1126 case LFUN_TABULAR_INSERT:
1127 // if there were no arguments, just open the dialog
1128 if (doInsertInset(cur, this, cmd, false, true))
1131 bv->showDialog("tabularcreate");
1135 case LFUN_FLOAT_INSERT:
1136 case LFUN_FLOAT_WIDE_INSERT:
1137 case LFUN_WRAP_INSERT: {
1138 bool content = cur.selection(); // will some text be moved into the inset?
1140 doInsertInset(cur, this, cmd, true, true);
1142 ParagraphList & pars = cur.text()->paragraphs();
1144 TextClass const & tclass = bv->buffer().params().getTextClass();
1146 // add a separate paragraph for the caption inset
1147 pars.push_back(Paragraph());
1148 pars.back().setInsetOwner(pars[0].inInset());
1149 pars.back().layout(tclass.defaultLayout());
1151 int cap_pit = pars.size() - 1;
1153 // if an empty inset was created, we create an additional empty
1154 // paragraph at the bottom so that the user can choose where to put
1155 // the graphics (or table).
1157 pars.push_back(Paragraph());
1158 pars.back().setInsetOwner(pars[0].inInset());
1159 pars.back().layout(tclass.defaultLayout());
1163 // reposition the cursor to the caption
1164 cur.pit() = cap_pit;
1166 // FIXME: This Text/Cursor dispatch handling is a mess!
1167 // We cannot use Cursor::dispatch here it needs access to up to
1169 FuncRequest cmd_caption(LFUN_CAPTION_INSERT);
1170 cur.text()->dispatch(cur, cmd_caption);
1171 cur.updateFlags(Update::Force);
1172 // FIXME: When leaving the Float (or Wrap) inset we should
1173 // delete any empty paragraph left above or below the
1178 case LFUN_INDEX_INSERT:
1179 case LFUN_NOMENCL_INSERT: {
1180 Inset * inset = createInset(&cur.bv(), cmd);
1184 cur.clearSelection();
1185 insertInset(cur, inset);
1186 // Show the dialog for the nomenclature entry, since the
1187 // description entry still needs to be filled in.
1188 if (cmd.action == LFUN_NOMENCL_INSERT)
1189 inset->edit(cur, true);
1194 case LFUN_INDEX_PRINT:
1195 case LFUN_NOMENCL_PRINT:
1196 case LFUN_TOC_INSERT:
1197 case LFUN_HFILL_INSERT:
1198 case LFUN_LINE_INSERT:
1199 case LFUN_PAGEBREAK_INSERT:
1200 case LFUN_CLEARPAGE_INSERT:
1201 case LFUN_CLEARDOUBLEPAGE_INSERT:
1203 doInsertInset(cur, this, cmd, false, false);
1207 case LFUN_DEPTH_DECREMENT:
1208 changeDepth(cur, DEC_DEPTH);
1211 case LFUN_DEPTH_INCREMENT:
1212 changeDepth(cur, INC_DEPTH);
1215 case LFUN_MATH_DISPLAY:
1216 mathDispatch(cur, cmd, true);
1219 case LFUN_MATH_IMPORT_SELECTION:
1220 case LFUN_MATH_MODE:
1221 if (cmd.argument() == "on")
1222 // don't pass "on" as argument
1223 mathDispatch(cur, FuncRequest(LFUN_MATH_MODE), false);
1225 mathDispatch(cur, cmd, false);
1228 case LFUN_MATH_MACRO:
1229 if (cmd.argument().empty())
1230 cur.errorMessage(from_utf8(N_("Missing argument")));
1232 string s = to_utf8(cmd.argument());
1233 string const s1 = token(s, ' ', 1);
1234 int const nargs = s1.empty() ? 0 : convert<int>(s1);
1235 string const s2 = token(s, ' ', 2);
1236 string const type = s2.empty() ? "newcommand" : s2;
1237 cur.insert(new MathMacroTemplate(from_utf8(token(s, ' ', 0)), nargs, from_utf8(type)));
1238 //cur.nextInset()->edit(cur, true);
1242 // passthrough hat and underscore outside mathed:
1243 case LFUN_MATH_SUBSCRIPT:
1244 mathDispatch(cur, FuncRequest(LFUN_SELF_INSERT, "_"), false);
1246 case LFUN_MATH_SUPERSCRIPT:
1247 mathDispatch(cur, FuncRequest(LFUN_SELF_INSERT, "^"), false);
1250 case LFUN_MATH_INSERT:
1251 case LFUN_MATH_MATRIX:
1252 case LFUN_MATH_DELIM:
1253 case LFUN_MATH_BIGDELIM: {
1254 if (cur.selection())
1255 cur.clearSelection();
1256 // FIXME: instead of the above, this one
1257 // should be used (but it asserts with Bidi enabled)
1258 // cf. http://bugzilla.lyx.org/show_bug.cgi?id=4055
1259 // cap::replaceSelection(cur);
1260 cur.insert(new InsetMathHull(hullSimple));
1261 checkAndActivateInset(cur, true);
1262 BOOST_ASSERT(cur.inMathed());
1267 case LFUN_FONT_EMPH: {
1268 Font font(Font::ALL_IGNORE);
1269 font.setEmph(Font::TOGGLE);
1270 toggleAndShow(cur, this, font);
1274 case LFUN_FONT_BOLD: {
1275 Font font(Font::ALL_IGNORE);
1276 font.setSeries(Font::BOLD_SERIES);
1277 toggleAndShow(cur, this, font);
1281 case LFUN_FONT_NOUN: {
1282 Font font(Font::ALL_IGNORE);
1283 font.setNoun(Font::TOGGLE);
1284 toggleAndShow(cur, this, font);
1288 case LFUN_FONT_TYPEWRITER: {
1289 Font font(Font::ALL_IGNORE);
1290 font.setFamily(Font::TYPEWRITER_FAMILY); // no good
1291 toggleAndShow(cur, this, font);
1295 case LFUN_FONT_SANS: {
1296 Font font(Font::ALL_IGNORE);
1297 font.setFamily(Font::SANS_FAMILY);
1298 toggleAndShow(cur, this, font);
1302 case LFUN_FONT_ROMAN: {
1303 Font font(Font::ALL_IGNORE);
1304 font.setFamily(Font::ROMAN_FAMILY);
1305 toggleAndShow(cur, this, font);
1309 case LFUN_FONT_DEFAULT: {
1310 Font font(Font::ALL_INHERIT, ignore_language);
1311 toggleAndShow(cur, this, font);
1315 case LFUN_FONT_UNDERLINE: {
1316 Font font(Font::ALL_IGNORE);
1317 font.setUnderbar(Font::TOGGLE);
1318 toggleAndShow(cur, this, font);
1322 case LFUN_FONT_SIZE: {
1323 Font font(Font::ALL_IGNORE);
1324 font.setLyXSize(to_utf8(cmd.argument()));
1325 toggleAndShow(cur, this, font);
1329 case LFUN_LANGUAGE: {
1330 Language const * lang = languages.getLanguage(to_utf8(cmd.argument()));
1333 Font font(Font::ALL_IGNORE);
1334 font.setLanguage(lang);
1335 toggleAndShow(cur, this, font);
1339 case LFUN_FONT_FREE_APPLY:
1340 toggleAndShow(cur, this, freefont, toggleall);
1341 cur.message(_("Character set"));
1344 // Set the freefont using the contents of \param data dispatched from
1345 // the frontends and apply it at the current cursor location.
1346 case LFUN_FONT_FREE_UPDATE: {
1349 if (font.fromString(to_utf8(cmd.argument()), toggle)) {
1352 toggleAndShow(cur, this, freefont, toggleall);
1353 cur.message(_("Character set"));
1358 case LFUN_FINISHED_LEFT:
1359 LYXERR(Debug::DEBUG) << "handle LFUN_FINISHED_LEFT:\n" << cur << endl;
1360 if (reverseDirectionNeeded(cur)) {
1362 cur.setCurrentFont();
1366 case LFUN_FINISHED_RIGHT:
1367 LYXERR(Debug::DEBUG) << "handle LFUN_FINISHED_RIGHT:\n" << cur << endl;
1368 if (!reverseDirectionNeeded(cur)) {
1370 cur.setCurrentFont();
1374 case LFUN_LAYOUT_PARAGRAPH: {
1376 params2string(cur.paragraph(), data);
1377 data = "show\n" + data;
1378 bv->showDialogWithData("paragraph", data);
1382 case LFUN_PARAGRAPH_UPDATE: {
1384 params2string(cur.paragraph(), data);
1386 // Will the paragraph accept changes from the dialog?
1387 bool const accept = !cur.inset().forceDefaultParagraphs(cur.idx());
1389 data = "update " + convert<string>(accept) + '\n' + data;
1390 bv->updateDialog("paragraph", data);
1394 case LFUN_ACCENT_UMLAUT:
1395 case LFUN_ACCENT_CIRCUMFLEX:
1396 case LFUN_ACCENT_GRAVE:
1397 case LFUN_ACCENT_ACUTE:
1398 case LFUN_ACCENT_TILDE:
1399 case LFUN_ACCENT_CEDILLA:
1400 case LFUN_ACCENT_MACRON:
1401 case LFUN_ACCENT_DOT:
1402 case LFUN_ACCENT_UNDERDOT:
1403 case LFUN_ACCENT_UNDERBAR:
1404 case LFUN_ACCENT_CARON:
1405 case LFUN_ACCENT_SPECIAL_CARON:
1406 case LFUN_ACCENT_BREVE:
1407 case LFUN_ACCENT_TIE:
1408 case LFUN_ACCENT_HUNGARIAN_UMLAUT:
1409 case LFUN_ACCENT_CIRCLE:
1410 case LFUN_ACCENT_OGONEK:
1411 theLyXFunc().handleKeyFunc(cmd.action);
1412 if (!cmd.argument().empty())
1413 // FIXME: Are all these characters encoded in one byte in utf8?
1414 bv->translateAndInsert(cmd.argument()[0], this, cur);
1417 case LFUN_FLOAT_LIST: {
1418 TextClass const & tclass = bv->buffer().params().getTextClass();
1419 if (tclass.floats().typeExist(to_utf8(cmd.argument()))) {
1421 if (cur.selection())
1422 cutSelection(cur, true, false);
1423 breakParagraph(cur);
1425 if (cur.lastpos() != 0) {
1427 breakParagraph(cur);
1430 setLayout(cur, tclass.defaultLayoutName());
1431 ParagraphParameters p;
1432 setParagraphs(cur, p);
1433 insertInset(cur, new InsetFloatList(to_utf8(cmd.argument())));
1436 lyxerr << "Non-existent float type: "
1437 << to_utf8(cmd.argument()) << endl;
1442 case LFUN_CHANGE_ACCEPT: {
1443 acceptOrRejectChanges(cur, ACCEPT);
1447 case LFUN_CHANGE_REJECT: {
1448 acceptOrRejectChanges(cur, REJECT);
1452 case LFUN_THESAURUS_ENTRY: {
1453 docstring arg = cmd.argument();
1455 arg = cur.selectionAsString(false);
1457 if (arg.size() > 100 || arg.empty()) {
1458 // Get word or selection
1459 selectWordWhenUnderCursor(cur, WHOLE_WORD);
1460 arg = cur.selectionAsString(false);
1463 bv->showDialogWithData("thesaurus", to_utf8(arg));
1467 case LFUN_PARAGRAPH_PARAMS_APPLY: {
1468 // Given data, an encoding of the ParagraphParameters
1469 // generated in the Paragraph dialog, this function sets
1470 // the current paragraph, or currently selected paragraphs,
1472 // NOTE: This function overrides all existing settings.
1473 setParagraphs(cur, cmd.argument());
1474 cur.message(_("Paragraph layout set"));
1478 case LFUN_PARAGRAPH_PARAMS: {
1479 // Given data, an encoding of the ParagraphParameters as we'd
1480 // find them in a LyX file, this function modifies the current paragraph,
1481 // or currently selected paragraphs.
1482 // NOTE: This function only modifies, and does not override, existing
1484 setParagraphs(cur, cmd.argument(), true);
1485 cur.message(_("Paragraph layout set"));
1490 if (cur.selection()) {
1491 cur.selection() = false;
1494 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
1499 LYXERR(Debug::ACTION)
1500 << BOOST_CURRENT_FUNCTION
1501 << ": Command " << cmd
1502 << " not DISPATCHED by Text" << endl;
1507 needsUpdate |= (cur.pos() != cur.lastpos()) && cur.selection();
1509 // FIXME: The cursor flag is reset two lines below
1510 // so we need to check here if some of the LFUN did touch that.
1511 // for now only Text::erase() and Text::backspace() do that.
1512 // The plan is to verify all the LFUNs and then to remove this
1513 // singleParUpdate boolean altogether.
1514 if (cur.result().update() & Update::Force) {
1515 singleParUpdate = false;
1519 // FIXME: the following code should go in favor of fine grained
1520 // update flag treatment.
1521 if (singleParUpdate) {
1522 // Inserting characters does not change par height
1523 ParagraphMetrics const & pms
1524 = cur.bv().parMetrics(cur.bottom().text(), cur.bottom().pit());
1525 if (pms.dim().height()
1526 == olddim.height()) {
1527 // if so, update _only_ this paragraph
1528 cur.updateFlags(Update::SinglePar |
1530 Update::MultiParSel);
1537 && &oldTopSlice.inset() == &cur.inset()
1538 && oldTopSlice.idx() == cur.idx()
1539 && !sel // sel is a backup of cur.selection() at the biginning of the function.
1540 && !cur.selection())
1541 // FIXME: it would be better if we could just do this
1543 //if (cur.result().update() != Update::FitCursor)
1546 // But some LFUNs do not set Update::FitCursor when needed, so we
1547 // do it for all. This is not very harmfull as FitCursor will provoke
1548 // a full redraw only if needed but still, a proper review of all LFUN
1549 // should be done and this needsUpdate boolean can then be removed.
1550 cur.updateFlags(Update::FitCursor);
1552 cur.updateFlags(Update::Force | Update::FitCursor);
1556 bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
1557 FuncStatus & flag) const
1559 BOOST_ASSERT(cur.text() == this);
1561 Font const & font = cur.real_current_font;
1563 Inset::Code code = Inset::NO_CODE;
1565 switch (cmd.action) {
1567 case LFUN_DEPTH_DECREMENT:
1568 enable = changeDepthAllowed(cur, DEC_DEPTH);
1571 case LFUN_DEPTH_INCREMENT:
1572 enable = changeDepthAllowed(cur, INC_DEPTH);
1576 flag.setOnOff(cur.paragraph().params().startOfAppendix());
1579 case LFUN_BIBITEM_INSERT:
1580 enable = (cur.paragraph().layout()->labeltype == LABEL_BIBLIO
1584 case LFUN_DIALOG_SHOW_NEW_INSET:
1585 if (cmd.argument() == "bibitem")
1586 code = Inset::BIBITEM_CODE;
1587 else if (cmd.argument() == "bibtex")
1588 code = Inset::BIBTEX_CODE;
1589 else if (cmd.argument() == "box")
1590 code = Inset::BOX_CODE;
1591 else if (cmd.argument() == "branch")
1592 code = Inset::BRANCH_CODE;
1593 else if (cmd.argument() == "citation")
1594 code = Inset::CITE_CODE;
1595 else if (cmd.argument() == "ert")
1596 code = Inset::ERT_CODE;
1597 else if (cmd.argument() == "external")
1598 code = Inset::EXTERNAL_CODE;
1599 else if (cmd.argument() == "float")
1600 code = Inset::FLOAT_CODE;
1601 else if (cmd.argument() == "graphics")
1602 code = Inset::GRAPHICS_CODE;
1603 else if (cmd.argument() == "include")
1604 code = Inset::INCLUDE_CODE;
1605 else if (cmd.argument() == "index")
1606 code = Inset::INDEX_CODE;
1607 else if (cmd.argument() == "nomenclature")
1608 code = Inset::NOMENCL_CODE;
1609 else if (cmd.argument() == "label")
1610 code = Inset::LABEL_CODE;
1611 else if (cmd.argument() == "note")
1612 code = Inset::NOTE_CODE;
1613 else if (cmd.argument() == "ref")
1614 code = Inset::REF_CODE;
1615 else if (cmd.argument() == "toc")
1616 code = Inset::TOC_CODE;
1617 else if (cmd.argument() == "url")
1618 code = Inset::URL_CODE;
1619 else if (cmd.argument() == "vspace")
1620 code = Inset::VSPACE_CODE;
1621 else if (cmd.argument() == "wrap")
1622 code = Inset::WRAP_CODE;
1623 else if (cmd.argument() == "listings")
1624 code = Inset::LISTINGS_CODE;
1627 case LFUN_ERT_INSERT:
1628 code = Inset::ERT_CODE;
1630 case LFUN_LISTING_INSERT:
1631 code = Inset::LISTINGS_CODE;
1633 case LFUN_FOOTNOTE_INSERT:
1634 code = Inset::FOOT_CODE;
1636 case LFUN_TABULAR_INSERT:
1637 code = Inset::TABULAR_CODE;
1639 case LFUN_MARGINALNOTE_INSERT:
1640 code = Inset::MARGIN_CODE;
1642 case LFUN_FLOAT_INSERT:
1643 case LFUN_FLOAT_WIDE_INSERT:
1644 code = Inset::FLOAT_CODE;
1646 case LFUN_WRAP_INSERT:
1647 code = Inset::WRAP_CODE;
1649 case LFUN_FLOAT_LIST:
1650 code = Inset::FLOAT_LIST_CODE;
1653 case LFUN_LIST_INSERT:
1654 code = Inset::LIST_CODE;
1656 case LFUN_THEOREM_INSERT:
1657 code = Inset::THEOREM_CODE;
1660 case LFUN_CAPTION_INSERT:
1661 code = Inset::CAPTION_CODE;
1663 case LFUN_NOTE_INSERT:
1664 code = Inset::NOTE_CODE;
1666 case LFUN_FLEX_INSERT: {
1667 code = Inset::FLEX_CODE;
1668 string s = cmd.getArg(0);
1669 InsetLayout il = cur.buffer().params().getTextClass().insetlayout(from_utf8(s));
1670 if (il.lyxtype != "charstyle" &&
1671 il.lyxtype != "custom" &&
1672 il.lyxtype != "element")
1676 case LFUN_BOX_INSERT:
1677 code = Inset::BOX_CODE;
1679 case LFUN_BRANCH_INSERT:
1680 code = Inset::BRANCH_CODE;
1681 if (cur.buffer().getMasterBuffer()->params().branchlist().empty())
1684 case LFUN_LABEL_INSERT:
1685 code = Inset::LABEL_CODE;
1687 case LFUN_OPTIONAL_INSERT:
1688 code = Inset::OPTARG_CODE;
1689 enable = numberOfOptArgs(cur.paragraph())
1690 < cur.paragraph().layout()->optionalargs;
1692 case LFUN_ENVIRONMENT_INSERT:
1693 code = Inset::BOX_CODE;
1695 case LFUN_INDEX_INSERT:
1696 code = Inset::INDEX_CODE;
1698 case LFUN_INDEX_PRINT:
1699 code = Inset::INDEX_PRINT_CODE;
1701 case LFUN_NOMENCL_INSERT:
1702 code = Inset::NOMENCL_CODE;
1704 case LFUN_NOMENCL_PRINT:
1705 code = Inset::NOMENCL_PRINT_CODE;
1707 case LFUN_TOC_INSERT:
1708 code = Inset::TOC_CODE;
1710 case LFUN_HTML_INSERT:
1711 case LFUN_URL_INSERT:
1712 code = Inset::URL_CODE;
1714 case LFUN_QUOTE_INSERT:
1715 // always allow this, since we will inset a raw quote
1716 // if an inset is not allowed.
1718 case LFUN_HYPHENATION_POINT_INSERT:
1719 case LFUN_LIGATURE_BREAK_INSERT:
1720 case LFUN_HFILL_INSERT:
1721 case LFUN_MENU_SEPARATOR_INSERT:
1722 case LFUN_DOTS_INSERT:
1723 case LFUN_END_OF_SENTENCE_PERIOD_INSERT:
1724 code = Inset::SPECIALCHAR_CODE;
1726 case LFUN_SPACE_INSERT:
1727 // slight hack: we know this is allowed in math mode
1729 code = Inset::SPACE_CODE;
1732 case LFUN_INSET_MODIFY:
1733 // We need to disable this, because we may get called for a
1735 // InsetTabular::getStatus() -> InsetText::getStatus()
1736 // and we don't handle LFUN_INSET_MODIFY.
1740 case LFUN_FONT_EMPH:
1741 flag.setOnOff(font.emph() == Font::ON);
1744 case LFUN_FONT_NOUN:
1745 flag.setOnOff(font.noun() == Font::ON);
1748 case LFUN_FONT_BOLD:
1749 flag.setOnOff(font.series() == Font::BOLD_SERIES);
1752 case LFUN_FONT_SANS:
1753 flag.setOnOff(font.family() == Font::SANS_FAMILY);
1756 case LFUN_FONT_ROMAN:
1757 flag.setOnOff(font.family() == Font::ROMAN_FAMILY);
1760 case LFUN_FONT_TYPEWRITER:
1761 flag.setOnOff(font.family() == Font::TYPEWRITER_FAMILY);
1766 enable = cur.selection();
1770 if (cmd.argument().empty()) {
1771 if (theClipboard().isInternal())
1772 enable = cap::numberOfSelections() > 0;
1774 enable = !theClipboard().empty();
1776 string const arg = to_utf8(cmd.argument());
1777 if (isStrUnsignedInt(arg)) {
1778 unsigned int n = convert<unsigned int>(arg);
1779 enable = cap::numberOfSelections() > n;
1786 case LFUN_CLIPBOARD_PASTE:
1787 enable = !theClipboard().empty();
1790 case LFUN_PRIMARY_SELECTION_PASTE:
1791 enable = cur.selection() || !theSelection().empty();
1794 case LFUN_PARAGRAPH_MOVE_UP:
1795 enable = cur.pit() > 0 && !cur.selection();
1798 case LFUN_PARAGRAPH_MOVE_DOWN:
1799 enable = cur.pit() < cur.lastpit() && !cur.selection();
1802 case LFUN_INSET_DISSOLVE:
1803 enable = !isMainText(cur.bv().buffer()) && cur.inset().nargs() == 1;
1806 case LFUN_CHANGE_ACCEPT:
1807 case LFUN_CHANGE_REJECT:
1808 // TODO: context-sensitive enabling of LFUN_CHANGE_ACCEPT/REJECT
1809 // In principle, these LFUNs should only be enabled if there
1810 // is a change at the current position/in the current selection.
1811 // However, without proper optimizations, this will inevitably
1812 // result in unacceptable performance - just imagine a user who
1813 // wants to select the complete content of a long document.
1817 case LFUN_WORD_DELETE_FORWARD:
1818 case LFUN_WORD_DELETE_BACKWARD:
1819 case LFUN_LINE_DELETE:
1820 case LFUN_WORD_FORWARD:
1821 case LFUN_WORD_BACKWARD:
1822 case LFUN_CHAR_FORWARD:
1823 case LFUN_CHAR_FORWARD_SELECT:
1824 case LFUN_CHAR_BACKWARD:
1825 case LFUN_CHAR_BACKWARD_SELECT:
1827 case LFUN_UP_SELECT:
1829 case LFUN_DOWN_SELECT:
1830 case LFUN_PARAGRAPH_UP_SELECT:
1831 case LFUN_PARAGRAPH_DOWN_SELECT:
1832 case LFUN_SCREEN_UP_SELECT:
1833 case LFUN_SCREEN_DOWN_SELECT:
1834 case LFUN_LINE_BEGIN_SELECT:
1835 case LFUN_LINE_END_SELECT:
1836 case LFUN_WORD_FORWARD_SELECT:
1837 case LFUN_WORD_BACKWARD_SELECT:
1838 case LFUN_WORD_SELECT:
1839 case LFUN_PARAGRAPH_UP:
1840 case LFUN_PARAGRAPH_DOWN:
1841 case LFUN_LINE_BEGIN:
1843 case LFUN_BREAK_LINE:
1844 case LFUN_CHAR_DELETE_FORWARD:
1845 case LFUN_DELETE_FORWARD_SKIP:
1846 case LFUN_CHAR_DELETE_BACKWARD:
1847 case LFUN_DELETE_BACKWARD_SKIP:
1848 case LFUN_BREAK_PARAGRAPH:
1849 case LFUN_BREAK_PARAGRAPH_KEEP_LAYOUT:
1850 case LFUN_BREAK_PARAGRAPH_SKIP:
1851 case LFUN_PARAGRAPH_SPACING:
1852 case LFUN_INSET_INSERT:
1853 case LFUN_WORD_UPCASE:
1854 case LFUN_WORD_LOWCASE:
1855 case LFUN_WORD_CAPITALIZE:
1856 case LFUN_CHARS_TRANSPOSE:
1857 case LFUN_SERVER_GET_XY:
1858 case LFUN_SERVER_SET_XY:
1859 case LFUN_SERVER_GET_FONT:
1860 case LFUN_SERVER_GET_LAYOUT:
1862 case LFUN_DATE_INSERT:
1863 case LFUN_SELF_INSERT:
1864 case LFUN_LINE_INSERT:
1865 case LFUN_PAGEBREAK_INSERT:
1866 case LFUN_CLEARPAGE_INSERT:
1867 case LFUN_CLEARDOUBLEPAGE_INSERT:
1868 case LFUN_MATH_DISPLAY:
1869 case LFUN_MATH_IMPORT_SELECTION:
1870 case LFUN_MATH_MODE:
1871 case LFUN_MATH_MACRO:
1872 case LFUN_MATH_MATRIX:
1873 case LFUN_MATH_DELIM:
1874 case LFUN_MATH_BIGDELIM:
1875 case LFUN_MATH_INSERT:
1876 case LFUN_MATH_SUBSCRIPT:
1877 case LFUN_MATH_SUPERSCRIPT:
1878 case LFUN_FONT_DEFAULT:
1879 case LFUN_FONT_UNDERLINE:
1880 case LFUN_FONT_SIZE:
1882 case LFUN_FONT_FREE_APPLY:
1883 case LFUN_FONT_FREE_UPDATE:
1884 case LFUN_LAYOUT_PARAGRAPH:
1885 case LFUN_PARAGRAPH_UPDATE:
1886 case LFUN_ACCENT_UMLAUT:
1887 case LFUN_ACCENT_CIRCUMFLEX:
1888 case LFUN_ACCENT_GRAVE:
1889 case LFUN_ACCENT_ACUTE:
1890 case LFUN_ACCENT_TILDE:
1891 case LFUN_ACCENT_CEDILLA:
1892 case LFUN_ACCENT_MACRON:
1893 case LFUN_ACCENT_DOT:
1894 case LFUN_ACCENT_UNDERDOT:
1895 case LFUN_ACCENT_UNDERBAR:
1896 case LFUN_ACCENT_CARON:
1897 case LFUN_ACCENT_SPECIAL_CARON:
1898 case LFUN_ACCENT_BREVE:
1899 case LFUN_ACCENT_TIE:
1900 case LFUN_ACCENT_HUNGARIAN_UMLAUT:
1901 case LFUN_ACCENT_CIRCLE:
1902 case LFUN_ACCENT_OGONEK:
1903 case LFUN_THESAURUS_ENTRY:
1904 case LFUN_PARAGRAPH_PARAMS_APPLY:
1905 case LFUN_PARAGRAPH_PARAMS:
1907 case LFUN_BUFFER_END:
1908 case LFUN_BUFFER_BEGIN:
1909 case LFUN_BUFFER_BEGIN_SELECT:
1910 case LFUN_BUFFER_END_SELECT:
1911 case LFUN_UNICODE_INSERT:
1912 // these are handled in our dispatch()
1920 if (code != Inset::NO_CODE
1921 && (cur.empty() || !cur.inset().insetAllowed(code)))
1924 flag.enabled(enable);
1929 void Text::pasteString(Cursor & cur, docstring const & clip,
1932 cur.clearSelection();
1933 if (!clip.empty()) {
1936 insertStringAsParagraphs(cur, clip);
1938 insertStringAsLines(cur, clip);