scramble email addresses
[lyx.git] / src / Text3.cpp
blobaa31f32932d26cfc176c226a550d247fa833ec64
1 /**
2 * \file text3.cpp
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
10 * \author John Levon
11 * \author André Pönitz
13 * Full author contact details are available in file CREDITS.
16 #include <config.h>
18 #include "Text.h"
20 #include "Bidi.h"
21 #include "BranchList.h"
22 #include "FloatList.h"
23 #include "FuncStatus.h"
24 #include "Buffer.h"
25 #include "buffer_funcs.h"
26 #include "BufferParams.h"
27 #include "BufferView.h"
28 #include "Cursor.h"
29 #include "CutAndPaste.h"
30 #include "debug.h"
31 #include "DispatchResult.h"
32 #include "ErrorList.h"
33 #include "factory.h"
34 #include "FuncRequest.h"
35 #include "gettext.h"
36 #include "Intl.h"
37 #include "Language.h"
38 #include "Layout.h"
39 #include "LyXAction.h"
40 #include "LyXFunc.h"
41 #include "Lexer.h"
42 #include "LyXRC.h"
43 #include "Paragraph.h"
44 #include "paragraph_funcs.h"
45 #include "ParagraphParameters.h"
46 #include "TextMetrics.h"
47 #include "VSpace.h"
48 #include "ParIterator.h"
50 #include "frontends/Clipboard.h"
51 #include "frontends/Selection.h"
53 #include "insets/InsetCollapsable.h"
54 #include "insets/InsetCommand.h"
55 #include "insets/InsetFloatList.h"
56 #include "insets/InsetNewline.h"
57 #include "insets/InsetQuotes.h"
58 #include "insets/InsetSpecialChar.h"
59 #include "insets/InsetText.h"
60 #include "insets/InsetInfo.h"
62 #include "support/lstrings.h"
63 #include "support/lyxlib.h"
64 #include "support/convert.h"
65 #include "support/lyxtime.h"
67 #include "mathed/InsetMathHull.h"
68 #include "mathed/MathMacroTemplate.h"
70 #include <boost/current_function.hpp>
72 #include <clocale>
73 #include <sstream>
75 using std::endl;
76 using std::string;
77 using std::istringstream;
78 using std::ostringstream;
80 namespace lyx {
82 using cap::copySelection;
83 using cap::cutSelection;
84 using cap::pasteFromStack;
85 using cap::pasteClipboard;
86 using cap::replaceSelection;
88 using support::isStrUnsignedInt;
89 using support::token;
91 // globals...
92 static Font freefont(Font::ALL_IGNORE);
93 static bool toggleall = false;
95 static void toggleAndShow(Cursor & cur, Text * text,
96 Font const & font, bool toggleall = true)
98 text->toggleFree(cur, font, toggleall);
100 if (font.language() != ignore_language ||
101 font.number() != Font::IGNORE) {
102 TextMetrics const & tm = cur.bv().textMetrics(text);
103 if (cur.boundary() != tm.isRTLBoundary(cur.pit(),
104 cur.pos(), cur.real_current_font))
105 text->setCursor(cur, cur.pit(), cur.pos(),
106 false, !cur.boundary());
111 static void moveCursor(Cursor & cur, bool selecting)
113 if (selecting || cur.mark())
114 cur.setSelection();
118 static void finishChange(Cursor & cur, bool selecting)
120 cur.finishUndo();
121 moveCursor(cur, selecting);
125 static void mathDispatch(Cursor & cur, FuncRequest const & cmd, bool display)
127 cur.recordUndo();
128 docstring sel = cur.selectionAsString(false);
130 // It may happen that sel is empty but there is a selection
131 replaceSelection(cur);
133 if (sel.empty()) {
134 #ifdef ENABLE_ASSERTIONS
135 const int old_pos = cur.pos();
136 #endif
137 cur.insert(new InsetMathHull(hullSimple));
138 BOOST_ASSERT(old_pos == cur.pos());
139 cur.nextInset()->edit(cur, true);
140 // don't do that also for LFUN_MATH_MODE
141 // unless you want end up with always changing
142 // to mathrm when opening an inlined inset --
143 // I really hate "LyXfunc overloading"...
144 if (display)
145 cur.dispatch(FuncRequest(LFUN_MATH_DISPLAY));
146 // Avoid an unnecessary undo step if cmd.argument
147 // is empty
148 if (!cmd.argument().empty())
149 cur.dispatch(FuncRequest(LFUN_MATH_INSERT,
150 cmd.argument()));
151 } else {
152 // create a macro if we see "\\newcommand"
153 // somewhere, and an ordinary formula
154 // otherwise
155 if (sel.find(from_ascii("\\newcommand")) == string::npos
156 && sel.find(from_ascii("\\def")) == string::npos)
158 InsetMathHull * formula = new InsetMathHull;
159 istringstream is(to_utf8(sel));
160 Lexer lex(0, 0);
161 lex.setStream(is);
162 formula->read(cur.buffer(), lex);
163 if (formula->getType() == hullNone)
164 // Don't create pseudo formulas if
165 // delimiters are left out
166 formula->mutate(hullSimple);
167 cur.insert(formula);
168 } else {
169 cur.insert(new MathMacroTemplate(sel));
172 cur.message(from_utf8(N_("Math editor mode")));
176 static void specialChar(Cursor & cur, InsetSpecialChar::Kind kind)
178 cur.recordUndo();
179 cap::replaceSelection(cur);
180 cur.insert(new InsetSpecialChar(kind));
181 cur.posRight();
185 static bool doInsertInset(Cursor & cur, Text * text,
186 FuncRequest const & cmd, bool edit, bool pastesel)
188 Inset * inset = createInset(&cur.bv(), cmd);
189 if (!inset)
190 return false;
192 cur.recordUndo();
193 if (cmd.action == LFUN_INDEX_INSERT) {
194 docstring ds = support::subst(text->getStringToIndex(cur), '\n', ' ');
195 text->insertInset(cur, inset);
196 if (edit)
197 inset->edit(cur, true);
198 // Now put this into inset
199 static_cast<InsetCollapsable *>(inset)->text_.insertStringAsParagraphs(cur, ds);
200 } else {
201 bool gotsel = false;
202 if (cur.selection()) {
203 lyx::dispatch(FuncRequest(LFUN_CUT));
204 gotsel = true;
206 text->insertInset(cur, inset);
208 if (edit)
209 inset->edit(cur, true);
211 if (gotsel && pastesel) {
212 lyx::dispatch(FuncRequest(LFUN_PASTE, "0"));
213 InsetText * insetText = dynamic_cast<InsetText *>(inset);
214 if (insetText && !insetText->allowMultiPar()
215 || cur.lastpit() == 0) {
216 // reset first par to default
217 LayoutPtr const layout =
218 cur.buffer().params().getTextClass().defaultLayout();
219 cur.text()->paragraphs().begin()->layout(layout);
220 cur.pos() = 0;
221 cur.pit() = 0;
222 // Merge multiple paragraphs -- hack
223 while (cur.lastpit() > 0) {
224 mergeParagraph(cur.buffer().params(),
225 cur.text()->paragraphs(), 0);
227 } else {
228 // reset surrounding par to default
229 docstring const layoutname =
230 cur.buffer().params().getTextClass().defaultLayoutName();
231 cur.leaveInset(*inset);
232 text->setLayout(cur, layoutname);
236 return true;
240 string const freefont2string()
242 return freefont.toString(toggleall);
246 void Text::number(Cursor & cur)
248 Font font(Font::ALL_IGNORE);
249 font.setNumber(Font::TOGGLE);
250 toggleAndShow(cur, this, font);
254 bool Text::isRTL(Buffer const & buffer, Paragraph const & par) const
256 return par.isRTL(buffer.params());
260 void Text::dispatch(Cursor & cur, FuncRequest & cmd)
262 LYXERR(Debug::ACTION) << "Text::dispatch: cmd: " << cmd << endl;
264 // FIXME: We use the update flag to indicates wether a singlePar or a
265 // full screen update is needed. We reset it here but shall we restore it
266 // at the end?
267 cur.noUpdate();
269 BOOST_ASSERT(cur.text() == this);
270 BufferView * bv = &cur.bv();
271 TextMetrics & tm = cur.bv().textMetrics(this);
272 CursorSlice oldTopSlice = cur.top();
273 bool oldBoundary = cur.boundary();
274 bool sel = cur.selection();
275 // Signals that, even if needsUpdate == false, an update of the
276 // cursor paragraph is required
277 bool singleParUpdate = lyxaction.funcHasFlag(cmd.action,
278 LyXAction::SingleParUpdate);
279 // Signals that a full-screen update is required
280 bool needsUpdate = !(lyxaction.funcHasFlag(cmd.action,
281 LyXAction::NoUpdate) || singleParUpdate);
282 // Remember the old paragraph metric (_outer_ paragraph!)
283 ParagraphMetrics const & pm = cur.bv().parMetrics(
284 cur.bottom().text(), cur.bottom().pit());
285 Dimension olddim = pm.dim();
287 switch (cmd.action) {
289 case LFUN_PARAGRAPH_MOVE_DOWN: {
290 pit_type const pit = cur.pit();
291 recUndo(cur, pit, pit + 1);
292 cur.finishUndo();
293 std::swap(pars_[pit], pars_[pit + 1]);
294 updateLabels(cur.buffer());
295 needsUpdate = true;
296 ++cur.pit();
297 break;
300 case LFUN_PARAGRAPH_MOVE_UP: {
301 pit_type const pit = cur.pit();
302 recUndo(cur, pit - 1, pit);
303 cur.finishUndo();
304 std::swap(pars_[pit], pars_[pit - 1]);
305 updateLabels(cur.buffer());
306 --cur.pit();
307 needsUpdate = true;
308 break;
311 case LFUN_APPENDIX: {
312 Paragraph & par = cur.paragraph();
313 bool start = !par.params().startOfAppendix();
315 // FIXME: The code below only makes sense at top level.
316 // Should LFUN_APPENDIX be restricted to top-level paragraphs?
317 // ensure that we have only one start_of_appendix in this document
318 // FIXME: this don't work for multipart document!
319 for (pit_type tmp = 0, end = pars_.size(); tmp != end; ++tmp) {
320 if (pars_[tmp].params().startOfAppendix()) {
321 recUndo(cur, tmp);
322 pars_[tmp].params().startOfAppendix(false);
323 break;
327 cur.recordUndo();
328 par.params().startOfAppendix(start);
330 // we can set the refreshing parameters now
331 updateLabels(cur.buffer());
332 break;
335 case LFUN_WORD_DELETE_FORWARD:
336 if (cur.selection()) {
337 cutSelection(cur, true, false);
338 } else
339 deleteWordForward(cur);
340 finishChange(cur, false);
341 break;
343 case LFUN_WORD_DELETE_BACKWARD:
344 if (cur.selection()) {
345 cutSelection(cur, true, false);
346 } else
347 deleteWordBackward(cur);
348 finishChange(cur, false);
349 break;
351 case LFUN_LINE_DELETE:
352 if (cur.selection()) {
353 cutSelection(cur, true, false);
354 } else
355 tm.deleteLineForward(cur);
356 finishChange(cur, false);
357 break;
359 case LFUN_BUFFER_BEGIN:
360 case LFUN_BUFFER_BEGIN_SELECT:
361 needsUpdate |= cur.selHandle(cmd.action == LFUN_BUFFER_BEGIN_SELECT);
362 if (cur.depth() == 1) {
363 needsUpdate |= cursorTop(cur);
364 } else {
365 cur.undispatched();
367 break;
369 case LFUN_BUFFER_END:
370 case LFUN_BUFFER_END_SELECT:
371 needsUpdate |= cur.selHandle(cmd.action == LFUN_BUFFER_END_SELECT);
372 if (cur.depth() == 1) {
373 needsUpdate |= cursorBottom(cur);
374 } else {
375 cur.undispatched();
377 break;
379 case LFUN_CHAR_FORWARD:
380 case LFUN_CHAR_FORWARD_SELECT:
381 //lyxerr << BOOST_CURRENT_FUNCTION
382 // << " LFUN_CHAR_FORWARD[SEL]:\n" << cur << endl;
383 needsUpdate |= cur.selHandle(cmd.action == LFUN_CHAR_FORWARD_SELECT);
384 needsUpdate |= cursorForward(cur);
386 if (!needsUpdate && oldTopSlice == cur.top()
387 && cur.boundary() == oldBoundary) {
388 cur.undispatched();
389 cmd = FuncRequest(LFUN_FINISHED_FORWARD);
391 break;
393 case LFUN_CHAR_BACKWARD:
394 case LFUN_CHAR_BACKWARD_SELECT:
395 //lyxerr << "handle LFUN_CHAR_BACKWARD[_SELECT]:\n" << cur << endl;
396 needsUpdate |= cur.selHandle(cmd.action == LFUN_CHAR_BACKWARD_SELECT);
397 needsUpdate |= cursorBackward(cur);
399 if (!needsUpdate && oldTopSlice == cur.top()
400 && cur.boundary() == oldBoundary) {
401 cur.undispatched();
402 cmd = FuncRequest(LFUN_FINISHED_BACKWARD);
404 break;
406 case LFUN_CHAR_LEFT:
407 case LFUN_CHAR_LEFT_SELECT:
408 //FIXME: for visual cursor, really move left
409 if (reverseDirectionNeeded(cur)) {
410 lyx::dispatch(FuncRequest(
411 cmd.action == LFUN_CHAR_LEFT_SELECT ?
412 LFUN_CHAR_FORWARD_SELECT : LFUN_CHAR_FORWARD));
413 } else {
414 lyx::dispatch(FuncRequest(
415 cmd.action == LFUN_CHAR_LEFT_SELECT ?
416 LFUN_CHAR_BACKWARD_SELECT : LFUN_CHAR_BACKWARD));
418 break;
420 case LFUN_CHAR_RIGHT:
421 case LFUN_CHAR_RIGHT_SELECT:
422 //FIXME: for visual cursor, really move right
423 if (reverseDirectionNeeded(cur)) {
424 lyx::dispatch(FuncRequest(
425 cmd.action == LFUN_CHAR_RIGHT_SELECT ?
426 LFUN_CHAR_BACKWARD_SELECT : LFUN_CHAR_BACKWARD));
427 } else {
428 lyx::dispatch(FuncRequest(
429 cmd.action == LFUN_CHAR_RIGHT_SELECT ?
430 LFUN_CHAR_FORWARD_SELECT : LFUN_CHAR_FORWARD));
432 break;
434 case LFUN_UP_SELECT:
435 case LFUN_DOWN_SELECT:
436 case LFUN_UP:
437 case LFUN_DOWN: {
438 // stop/start the selection
439 bool select = cmd.action == LFUN_DOWN_SELECT ||
440 cmd.action == LFUN_UP_SELECT;
441 cur.selHandle(select);
443 // move cursor up/down
444 bool up = cmd.action == LFUN_UP_SELECT || cmd.action == LFUN_UP;
445 bool const successful = cur.upDownInText(up, needsUpdate);
446 if (successful) {
447 // notify insets which were left and get their update flags
448 notifyCursorLeaves(cur.beforeDispatchCursor(), cur);
449 cur.fixIfBroken();
451 // redraw if you leave mathed (for the decorations)
452 needsUpdate |= cur.beforeDispatchCursor().inMathed();
453 } else
454 cur.undispatched();
456 break;
459 case LFUN_PARAGRAPH_UP:
460 case LFUN_PARAGRAPH_UP_SELECT:
461 needsUpdate |= cur.selHandle(cmd.action == LFUN_PARAGRAPH_UP_SELECT);
462 needsUpdate |= cursorUpParagraph(cur);
463 break;
465 case LFUN_PARAGRAPH_DOWN:
466 case LFUN_PARAGRAPH_DOWN_SELECT:
467 needsUpdate |= cur.selHandle(cmd.action == LFUN_PARAGRAPH_DOWN_SELECT);
468 needsUpdate |= cursorDownParagraph(cur);
469 break;
471 case LFUN_SCREEN_UP_SELECT:
472 needsUpdate |= cur.selHandle(true);
473 if (cur.pit() == 0 && cur.textRow().pos() == 0)
474 cur.undispatched();
475 else {
476 tm.cursorPrevious(cur);
478 break;
480 case LFUN_SCREEN_DOWN_SELECT:
481 needsUpdate |= cur.selHandle(true);
482 if (cur.pit() == cur.lastpit()
483 && cur.textRow().endpos() == cur.lastpos())
484 cur.undispatched();
485 else {
486 tm.cursorNext(cur);
488 break;
490 case LFUN_LINE_BEGIN:
491 case LFUN_LINE_BEGIN_SELECT:
492 needsUpdate |= cur.selHandle(cmd.action == LFUN_LINE_BEGIN_SELECT);
493 needsUpdate |= tm.cursorHome(cur);
494 break;
496 case LFUN_LINE_END:
497 case LFUN_LINE_END_SELECT:
498 needsUpdate |= cur.selHandle(cmd.action == LFUN_LINE_END_SELECT);
499 needsUpdate |= tm.cursorEnd(cur);
500 break;
502 case LFUN_WORD_FORWARD:
503 case LFUN_WORD_FORWARD_SELECT:
504 needsUpdate |= cur.selHandle(cmd.action == LFUN_WORD_FORWARD_SELECT);
505 if (reverseDirectionNeeded(cur))
506 needsUpdate |= cursorLeftOneWord(cur);
507 else
508 needsUpdate |= cursorRightOneWord(cur);
509 break;
511 case LFUN_WORD_BACKWARD:
512 case LFUN_WORD_BACKWARD_SELECT:
513 needsUpdate |= cur.selHandle(cmd.action == LFUN_WORD_BACKWARD_SELECT);
514 if (reverseDirectionNeeded(cur))
515 needsUpdate |= cursorRightOneWord(cur);
516 else
517 needsUpdate |= cursorLeftOneWord(cur);
518 break;
520 case LFUN_WORD_SELECT: {
521 selectWord(cur, WHOLE_WORD);
522 finishChange(cur, true);
523 break;
526 case LFUN_BREAK_LINE: {
527 // Not allowed by LaTeX (labels or empty par)
528 if (cur.pos() > cur.paragraph().beginOfBody()) {
529 // this avoids a double undo
530 // FIXME: should not be needed, ideally
531 if (!cur.selection())
532 cur.recordUndo();
533 cap::replaceSelection(cur);
534 cur.insert(new InsetNewline);
535 cur.posRight();
536 moveCursor(cur, false);
538 break;
541 case LFUN_CHAR_DELETE_FORWARD:
542 if (!cur.selection()) {
543 if (cur.pos() == cur.paragraph().size())
544 // Par boundary, force full-screen update
545 singleParUpdate = false;
546 needsUpdate |= erase(cur);
547 cur.resetAnchor();
548 // It is possible to make it a lot faster still
549 // just comment out the line below...
550 } else {
551 cutSelection(cur, true, false);
552 singleParUpdate = false;
554 moveCursor(cur, false);
555 break;
557 case LFUN_DELETE_FORWARD_SKIP:
558 // Reverse the effect of LFUN_BREAK_PARAGRAPH_SKIP.
559 if (!cur.selection()) {
560 if (cur.pos() == cur.lastpos()) {
561 cursorForward(cur);
562 cursorBackward(cur);
564 erase(cur);
565 cur.resetAnchor();
566 } else {
567 cutSelection(cur, true, false);
569 break;
572 case LFUN_CHAR_DELETE_BACKWARD:
573 if (!cur.selection()) {
574 if (bv->getIntl().getTransManager().backspace()) {
575 // Par boundary, full-screen update
576 if (cur.pos() == 0)
577 singleParUpdate = false;
578 needsUpdate |= backspace(cur);
579 cur.resetAnchor();
580 // It is possible to make it a lot faster still
581 // just comment out the line below...
583 } else {
584 cutSelection(cur, true, false);
585 singleParUpdate = false;
587 break;
589 case LFUN_DELETE_BACKWARD_SKIP:
590 // Reverse the effect of LFUN_BREAK_PARAGRAPH_SKIP.
591 if (!cur.selection()) {
592 // FIXME: look here
593 //CursorSlice cur = cursor();
594 backspace(cur);
595 //anchor() = cur;
596 } else {
597 cutSelection(cur, true, false);
599 break;
601 case LFUN_BREAK_PARAGRAPH:
602 cap::replaceSelection(cur);
603 breakParagraph(cur, cmd.argument() == "inverse");
604 cur.resetAnchor();
605 break;
607 case LFUN_BREAK_PARAGRAPH_SKIP: {
608 // When at the beginning of a paragraph, remove
609 // indentation. Otherwise, do the same as LFUN_BREAK_PARAGRAPH.
610 cap::replaceSelection(cur);
611 if (cur.pos() == 0)
612 cur.paragraph().params().labelWidthString(docstring());
613 else
614 breakParagraph(cur, false);
615 cur.resetAnchor();
616 break;
619 // TODO
620 // With the creation of LFUN_PARAGRAPH_PARAMS, this is now redundant,
621 // as its duties can be performed there. Should it be removed??
622 // FIXME For now, it can just dispatch LFUN_PARAGRAPH_PARAMS...
623 case LFUN_PARAGRAPH_SPACING: {
624 Paragraph & par = cur.paragraph();
625 Spacing::Space cur_spacing = par.params().spacing().getSpace();
626 string cur_value = "1.0";
627 if (cur_spacing == Spacing::Other)
628 cur_value = par.params().spacing().getValueAsString();
630 istringstream is(to_utf8(cmd.argument()));
631 string tmp;
632 is >> tmp;
633 Spacing::Space new_spacing = cur_spacing;
634 string new_value = cur_value;
635 if (tmp.empty()) {
636 lyxerr << "Missing argument to `paragraph-spacing'"
637 << endl;
638 } else if (tmp == "single") {
639 new_spacing = Spacing::Single;
640 } else if (tmp == "onehalf") {
641 new_spacing = Spacing::Onehalf;
642 } else if (tmp == "double") {
643 new_spacing = Spacing::Double;
644 } else if (tmp == "other") {
645 new_spacing = Spacing::Other;
646 string tmpval = "0.0";
647 is >> tmpval;
648 lyxerr << "new_value = " << tmpval << endl;
649 if (tmpval != "0.0")
650 new_value = tmpval;
651 } else if (tmp == "default") {
652 new_spacing = Spacing::Default;
653 } else {
654 lyxerr << to_utf8(_("Unknown spacing argument: "))
655 << to_utf8(cmd.argument()) << endl;
657 if (cur_spacing != new_spacing || cur_value != new_value)
658 par.params().spacing(Spacing(new_spacing, new_value));
659 break;
662 case LFUN_INSET_INSERT: {
663 cur.recordUndo();
664 Inset * inset = createInset(bv, cmd);
665 if (inset) {
666 // FIXME (Abdel 01/02/2006):
667 // What follows would be a partial fix for bug 2154:
668 // http://bugzilla.lyx.org/show_bug.cgi?id=2154
669 // This automatically put the label inset _after_ a
670 // numbered section. It should be possible to extend the mechanism
671 // to any kind of LateX environement.
672 // The correct way to fix that bug would be at LateX generation.
673 // I'll let the code here for reference as it could be used for some
674 // other feature like "automatic labelling".
676 Paragraph & par = pars_[cur.pit()];
677 if (inset->lyxCode() == LABEL_CODE
678 && par.layout()->labeltype == LABEL_COUNTER) {
679 // Go to the end of the paragraph
680 // Warning: Because of Change-Tracking, the last
681 // position is 'size()' and not 'size()-1':
682 cur.pos() = par.size();
683 // Insert a new paragraph
684 FuncRequest fr(LFUN_BREAK_PARAGRAPH);
685 dispatch(cur, fr);
688 if (cur.selection())
689 cutSelection(cur, true, false);
690 insertInset(cur, inset);
691 cur.posRight();
693 break;
696 case LFUN_INSET_DISSOLVE:
697 needsUpdate |= dissolveInset(cur);
698 break;
700 case LFUN_INSET_SETTINGS:
701 cur.inset().showInsetDialog(bv);
702 break;
704 case LFUN_SPACE_INSERT:
705 if (cur.paragraph().layout()->free_spacing)
706 insertChar(cur, ' ');
707 else {
708 doInsertInset(cur, this, cmd, false, false);
709 cur.posRight();
711 moveCursor(cur, false);
712 break;
714 case LFUN_HYPHENATION_POINT_INSERT:
715 specialChar(cur, InsetSpecialChar::HYPHENATION);
716 break;
718 case LFUN_LIGATURE_BREAK_INSERT:
719 specialChar(cur, InsetSpecialChar::LIGATURE_BREAK);
720 break;
722 case LFUN_DOTS_INSERT:
723 specialChar(cur, InsetSpecialChar::LDOTS);
724 break;
726 case LFUN_END_OF_SENTENCE_PERIOD_INSERT:
727 specialChar(cur, InsetSpecialChar::END_OF_SENTENCE);
728 break;
730 case LFUN_MENU_SEPARATOR_INSERT:
731 specialChar(cur, InsetSpecialChar::MENU_SEPARATOR);
732 break;
734 case LFUN_WORD_UPCASE:
735 changeCase(cur, Text::text_uppercase);
736 break;
738 case LFUN_WORD_LOWCASE:
739 changeCase(cur, Text::text_lowercase);
740 break;
742 case LFUN_WORD_CAPITALIZE:
743 changeCase(cur, Text::text_capitalization);
744 break;
746 case LFUN_CHARS_TRANSPOSE:
747 charsTranspose(cur);
748 break;
750 case LFUN_PASTE:
751 cur.message(_("Paste"));
752 cap::replaceSelection(cur);
753 if (cmd.argument().empty() && !theClipboard().isInternal())
754 pasteClipboard(cur, bv->buffer().errorList("Paste"));
755 else {
756 string const arg(to_utf8(cmd.argument()));
757 pasteFromStack(cur, bv->buffer().errorList("Paste"),
758 isStrUnsignedInt(arg) ?
759 convert<unsigned int>(arg) :
762 bv->buffer().errors("Paste");
763 cur.clearSelection(); // bug 393
764 cur.finishUndo();
765 break;
767 case LFUN_CUT:
768 cutSelection(cur, true, true);
769 cur.message(_("Cut"));
770 break;
772 case LFUN_COPY:
773 copySelection(cur);
774 cur.message(_("Copy"));
775 break;
777 case LFUN_SERVER_GET_XY:
778 cur.message(from_utf8(
779 convert<string>(tm.cursorX(cur.top(), cur.boundary()))
780 + ' ' + convert<string>(tm.cursorY(cur.top(), cur.boundary()))));
781 break;
783 case LFUN_SERVER_SET_XY: {
784 int x = 0;
785 int y = 0;
786 istringstream is(to_utf8(cmd.argument()));
787 is >> x >> y;
788 if (!is)
789 lyxerr << "SETXY: Could not parse coordinates in '"
790 << to_utf8(cmd.argument()) << std::endl;
791 else
792 tm.setCursorFromCoordinates(cur, x, y);
793 break;
796 case LFUN_SERVER_GET_FONT:
797 if (cur.current_font.shape() == Font::ITALIC_SHAPE)
798 cur.message(from_ascii("E"));
799 else if (cur.current_font.shape() == Font::SMALLCAPS_SHAPE)
800 cur.message(from_ascii("N"));
801 else
802 cur.message(from_ascii("0"));
803 break;
805 case LFUN_SERVER_GET_LAYOUT:
806 cur.message(cur.paragraph().layout()->name());
807 break;
809 case LFUN_LAYOUT: {
810 docstring layout = cmd.argument();
811 LYXERR(Debug::INFO) << "LFUN_LAYOUT: (arg) " << to_utf8(layout) << endl;
813 docstring const old_layout = cur.paragraph().layout()->name();
815 // Derive layout number from given argument (string)
816 // and current buffer's textclass (number)
817 TextClass const & tclass = bv->buffer().params().getTextClass();
818 if (layout.empty())
819 layout = tclass.defaultLayoutName();
820 bool hasLayout = tclass.hasLayout(layout);
822 // If the entry is obsolete, use the new one instead.
823 if (hasLayout) {
824 docstring const & obs = tclass[layout]->obsoleted_by();
825 if (!obs.empty())
826 layout = obs;
829 if (!hasLayout) {
830 cur.errorMessage(from_utf8(N_("Layout ")) + cmd.argument() +
831 from_utf8(N_(" not known")));
832 break;
835 bool change_layout = (old_layout != layout);
837 if (!change_layout && cur.selection() &&
838 cur.selBegin().pit() != cur.selEnd().pit())
840 pit_type spit = cur.selBegin().pit();
841 pit_type epit = cur.selEnd().pit() + 1;
842 while (spit != epit) {
843 if (pars_[spit].layout()->name() != old_layout) {
844 change_layout = true;
845 break;
847 ++spit;
851 if (change_layout)
852 setLayout(cur, layout);
854 break;
857 case LFUN_CLIPBOARD_PASTE:
858 cur.clearSelection();
859 pasteClipboard(cur, bv->buffer().errorList("Paste"),
860 cmd.argument() == "paragraph");
861 bv->buffer().errors("Paste");
862 break;
864 case LFUN_PRIMARY_SELECTION_PASTE:
865 pasteString(cur, theSelection().get(),
866 cmd.argument() == "paragraph");
867 break;
869 case LFUN_UNICODE_INSERT: {
870 if (cmd.argument().empty())
871 break;
872 docstring hexstring = cmd.argument();
873 if (lyx::support::isHex(hexstring)) {
874 char_type c = lyx::support::hexToInt(hexstring);
875 if (c >= 32 && c < 0x10ffff) {
876 lyxerr << "Inserting c: " << c << endl;
877 docstring s = docstring(1, c);
878 lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, s));
881 break;
884 case LFUN_QUOTE_INSERT: {
885 Paragraph & par = cur.paragraph();
886 pos_type pos = cur.pos();
887 BufferParams const & bufparams = bv->buffer().params();
888 LayoutPtr const & style = par.layout();
889 if (!style->pass_thru
890 && par.getFontSettings(bufparams, pos).language()->lang() != "hebrew") {
891 // this avoids a double undo
892 // FIXME: should not be needed, ideally
893 if (!cur.selection())
894 cur.recordUndo();
895 cap::replaceSelection(cur);
896 pos = cur.pos();
897 char_type c;
898 if (pos == 0)
899 c = ' ';
900 else if (cur.prevInset() && cur.prevInset()->isSpace())
901 c = ' ';
902 else
903 c = par.getChar(pos - 1);
904 string arg = to_utf8(cmd.argument());
905 if (arg == "single")
906 cur.insert(new InsetQuotes(c,
907 bufparams.quotes_language,
908 InsetQuotes::SingleQ));
909 else
910 cur.insert(new InsetQuotes(c,
911 bufparams.quotes_language,
912 InsetQuotes::DoubleQ));
913 cur.posRight();
915 else
916 lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, "\""));
917 break;
920 case LFUN_DATE_INSERT: {
921 string const format = cmd.argument().empty()
922 ? lyxrc.date_insert_format : to_utf8(cmd.argument());
923 string const time = formatted_time(current_time(), format);
924 lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, time));
925 break;
928 case LFUN_MOUSE_TRIPLE:
929 if (cmd.button() == mouse_button::button1) {
930 tm.cursorHome(cur);
931 cur.resetAnchor();
932 tm.cursorEnd(cur);
933 cur.setSelection();
934 bv->cursor() = cur;
936 break;
938 case LFUN_MOUSE_DOUBLE:
939 if (cmd.button() == mouse_button::button1) {
940 selectWord(cur, WHOLE_WORD_STRICT);
941 bv->cursor() = cur;
943 break;
945 // Single-click on work area
946 case LFUN_MOUSE_PRESS: {
947 // Right click on a footnote flag opens float menu
948 // FIXME: Why should we clear the selection in this case?
949 if (cmd.button() == mouse_button::button3)
950 cur.clearSelection();
952 bool do_selection = cmd.button() == mouse_button::button1
953 && cmd.argument() == "region-select";
954 // Set the cursor
955 bool update = bv->mouseSetCursor(cur, do_selection);
957 // Insert primary selection with middle mouse
958 // if there is a local selection in the current buffer,
959 // insert this
960 if (cmd.button() == mouse_button::button2) {
961 if (cap::selection()) {
962 // Copy the selection buffer to the clipboard
963 // stack, because we want it to appear in the
964 // "Edit->Paste recent" menu.
965 cap::copySelectionToStack();
967 cap::pasteSelection(bv->cursor(),
968 bv->buffer().errorList("Paste"));
969 bv->buffer().errors("Paste");
970 bv->buffer().markDirty();
971 bv->cursor().finishUndo();
972 } else {
973 lyx::dispatch(FuncRequest(LFUN_PRIMARY_SELECTION_PASTE, "paragraph"));
977 // we have to update after dEPM triggered
978 if (!update && cmd.button() == mouse_button::button1) {
979 needsUpdate = false;
980 cur.noUpdate();
983 break;
986 case LFUN_MOUSE_MOTION: {
987 // Only use motion with button 1
988 //if (cmd.button() != mouse_button::button1)
989 // return false;
991 // ignore motions deeper nested than the real anchor
992 Cursor & bvcur = cur.bv().cursor();
993 if (bvcur.anchor_.hasPart(cur)) {
994 CursorSlice old = bvcur.top();
996 int const wh = bv->workHeight();
997 int const y = std::max(0, std::min(wh - 1, cmd.y));
999 tm.setCursorFromCoordinates(cur, cmd.x, y);
1000 cur.setTargetX(cmd.x);
1001 if (cmd.y >= wh)
1002 lyx::dispatch(FuncRequest(LFUN_DOWN_SELECT));
1003 else if (cmd.y < 0)
1004 lyx::dispatch(FuncRequest(LFUN_UP_SELECT));
1005 // This is to allow jumping over large insets
1006 if (cur.top() == old) {
1007 if (cmd.y >= wh)
1008 lyx::dispatch(FuncRequest(LFUN_DOWN_SELECT));
1009 else if (cmd.y < 0)
1010 lyx::dispatch(FuncRequest(LFUN_UP_SELECT));
1013 if (cur.top() == old)
1014 cur.noUpdate();
1015 else {
1016 // don't set anchor_
1017 bvcur.setCursor(cur);
1018 bvcur.selection() = true;
1019 //lyxerr << "MOTION: " << bv->cursor() << endl;
1022 } else
1023 cur.undispatched();
1024 break;
1027 case LFUN_MOUSE_RELEASE: {
1028 if (cmd.button() == mouse_button::button2)
1029 break;
1031 if (cmd.button() == mouse_button::button1) {
1032 // if there is new selection, update persistent
1033 // selection, otherwise, single click does not
1034 // clear persistent selection buffer
1035 if (cur.selection()) {
1036 // finish selection
1037 // if double click, cur is moved to the end of word by selectWord
1038 // but bvcur is current mouse position
1039 Cursor & bvcur = cur.bv().cursor();
1040 bvcur.selection() = true;
1042 needsUpdate = false;
1043 cur.noUpdate();
1046 break;
1049 case LFUN_SELF_INSERT: {
1050 if (cmd.argument().empty())
1051 break;
1053 // Automatically delete the currently selected
1054 // text and replace it with what is being
1055 // typed in now. Depends on lyxrc settings
1056 // "auto_region_delete", which defaults to
1057 // true (on).
1059 if (lyxrc.auto_region_delete && cur.selection()) {
1060 cutSelection(cur, false, false);
1061 // When change tracking is set to off, the metrics update
1062 // mechanism correctly detects if a full update is needed or not.
1063 // This detection fails when a selection spans multiple rows and
1064 // change tracking is enabled because the paragraph metrics stays
1065 // the same. In this case, we force the full update:
1066 // (see http://bugzilla.lyx.org/show_bug.cgi?id=3992)
1067 if (cur.buffer().params().trackChanges)
1068 cur.updateFlags(Update::Force);
1071 cur.clearSelection();
1072 Font const old_font = cur.real_current_font;
1074 docstring::const_iterator cit = cmd.argument().begin();
1075 docstring::const_iterator end = cmd.argument().end();
1076 for (; cit != end; ++cit)
1077 bv->translateAndInsert(*cit, this, cur);
1079 cur.resetAnchor();
1080 moveCursor(cur, false);
1081 break;
1084 case LFUN_HYPERLINK_INSERT: {
1085 InsetCommandParams p(HYPERLINK_CODE);
1086 docstring content;
1087 if (cur.selection()) {
1088 content = cur.selectionAsString(false);
1089 cutSelection(cur, true, false);
1091 p["target"] = (cmd.argument().empty()) ?
1092 content : cmd.argument();
1093 string const data = InsetCommandMailer::params2string("href", p);
1094 if (p["target"].empty()) {
1095 bv->showInsetDialog("href", data, 0);
1096 } else {
1097 FuncRequest fr(LFUN_INSET_INSERT, data);
1098 dispatch(cur, fr);
1100 break;
1103 case LFUN_LABEL_INSERT: {
1104 InsetCommandParams p(LABEL_CODE);
1105 // Try to generate a valid label
1106 p["name"] = (cmd.argument().empty()) ?
1107 cur.getPossibleLabel() :
1108 cmd.argument();
1109 string const data = InsetCommandMailer::params2string("label", p);
1111 if (cmd.argument().empty()) {
1112 bv->showInsetDialog("label", data, 0);
1113 } else {
1114 FuncRequest fr(LFUN_INSET_INSERT, data);
1115 dispatch(cur, fr);
1117 break;
1120 case LFUN_INFO_INSERT: {
1121 Inset * inset = createInset(&cur.bv(), cmd);
1122 if (!inset)
1123 break;
1124 // if an empty inset is created (cmd.argument() is empty)
1125 // use current selection as parameter.
1126 if (cmd.argument().empty() && cur.selection()) {
1127 // use selected text as info to avoid a separate UI
1128 docstring ds = cur.selectionAsString(false);
1129 cutSelection(cur, true, false);
1130 static_cast<InsetInfo *>(inset)->setInfo(to_utf8(ds));
1132 insertInset(cur, inset);
1133 cur.posRight();
1134 break;
1136 #if 0
1137 case LFUN_LIST_INSERT:
1138 case LFUN_THEOREM_INSERT:
1139 #endif
1140 case LFUN_CAPTION_INSERT:
1141 // Open the inset, and move the current selection
1142 // inside it.
1143 doInsertInset(cur, this, cmd, true, true);
1144 cur.posRight();
1145 updateLabels(bv->buffer());
1146 break;
1147 case LFUN_NOTE_INSERT:
1148 case LFUN_FLEX_INSERT:
1149 case LFUN_BOX_INSERT:
1150 case LFUN_BRANCH_INSERT:
1151 case LFUN_BIBITEM_INSERT:
1152 case LFUN_ERT_INSERT:
1153 case LFUN_LISTING_INSERT:
1154 case LFUN_FOOTNOTE_INSERT:
1155 case LFUN_MARGINALNOTE_INSERT:
1156 case LFUN_OPTIONAL_INSERT:
1157 case LFUN_ENVIRONMENT_INSERT:
1158 // Open the inset, and move the current selection
1159 // inside it.
1160 doInsertInset(cur, this, cmd, true, true);
1161 cur.posRight();
1162 break;
1164 case LFUN_TABULAR_INSERT:
1165 // if there were no arguments, just open the dialog
1166 if (doInsertInset(cur, this, cmd, false, true))
1167 cur.posRight();
1168 else
1169 bv->showDialog("tabularcreate");
1171 break;
1173 case LFUN_FLOAT_INSERT:
1174 case LFUN_FLOAT_WIDE_INSERT:
1175 case LFUN_WRAP_INSERT: {
1176 bool content = cur.selection(); // will some text be moved into the inset?
1178 doInsertInset(cur, this, cmd, true, true);
1179 cur.posRight();
1180 ParagraphList & pars = cur.text()->paragraphs();
1182 TextClass const & tclass = bv->buffer().params().getTextClass();
1184 // add a separate paragraph for the caption inset
1185 pars.push_back(Paragraph());
1186 pars.back().setInsetOwner(pars[0].inInset());
1187 pars.back().layout(tclass.defaultLayout());
1189 int cap_pit = pars.size() - 1;
1191 // if an empty inset was created, we create an additional empty
1192 // paragraph at the bottom so that the user can choose where to put
1193 // the graphics (or table).
1194 if (!content) {
1195 pars.push_back(Paragraph());
1196 pars.back().setInsetOwner(pars[0].inInset());
1197 pars.back().layout(tclass.defaultLayout());
1201 // reposition the cursor to the caption
1202 cur.pit() = cap_pit;
1203 cur.pos() = 0;
1204 // FIXME: This Text/Cursor dispatch handling is a mess!
1205 // We cannot use Cursor::dispatch here it needs access to up to
1206 // date metrics.
1207 FuncRequest cmd_caption(LFUN_CAPTION_INSERT);
1208 cur.text()->dispatch(cur, cmd_caption);
1209 cur.updateFlags(Update::Force);
1210 // FIXME: When leaving the Float (or Wrap) inset we should
1211 // delete any empty paragraph left above or below the
1212 // caption.
1213 break;
1216 case LFUN_INDEX_INSERT:
1217 doInsertInset(cur, this, cmd, true, true);
1218 cur.posRight();
1219 break;
1220 case LFUN_NOMENCL_INSERT: {
1221 Inset * inset = createInset(&cur.bv(), cmd);
1222 if (!inset)
1223 break;
1224 cur.recordUndo();
1225 cur.clearSelection();
1226 insertInset(cur, inset);
1227 // Show the dialog for the nomenclature entry, since the
1228 // description entry still needs to be filled in.
1229 if (cmd.action == LFUN_NOMENCL_INSERT)
1230 inset->edit(cur, true);
1231 cur.posRight();
1232 break;
1235 case LFUN_INDEX_PRINT:
1236 case LFUN_NOMENCL_PRINT:
1237 case LFUN_TOC_INSERT:
1238 case LFUN_HFILL_INSERT:
1239 case LFUN_LINE_INSERT:
1240 case LFUN_PAGEBREAK_INSERT:
1241 case LFUN_CLEARPAGE_INSERT:
1242 case LFUN_CLEARDOUBLEPAGE_INSERT:
1243 // do nothing fancy
1244 doInsertInset(cur, this, cmd, false, false);
1245 cur.posRight();
1246 break;
1248 case LFUN_DEPTH_DECREMENT:
1249 changeDepth(cur, DEC_DEPTH);
1250 break;
1252 case LFUN_DEPTH_INCREMENT:
1253 changeDepth(cur, INC_DEPTH);
1254 break;
1256 case LFUN_MATH_DISPLAY:
1257 mathDispatch(cur, cmd, true);
1258 break;
1260 case LFUN_MATH_IMPORT_SELECTION:
1261 case LFUN_MATH_MODE:
1262 if (cmd.argument() == "on")
1263 // don't pass "on" as argument
1264 mathDispatch(cur, FuncRequest(LFUN_MATH_MODE), false);
1265 else
1266 mathDispatch(cur, cmd, false);
1267 break;
1269 case LFUN_MATH_MACRO:
1270 if (cmd.argument().empty())
1271 cur.errorMessage(from_utf8(N_("Missing argument")));
1272 else {
1273 string s = to_utf8(cmd.argument());
1274 string const s1 = token(s, ' ', 1);
1275 int const nargs = s1.empty() ? 0 : convert<int>(s1);
1276 string const s2 = token(s, ' ', 2);
1277 string const type = s2.empty() ? "newcommand" : s2;
1278 cur.insert(new MathMacroTemplate(from_utf8(token(s, ' ', 0)), nargs, from_utf8(type)));
1279 //cur.nextInset()->edit(cur, true);
1281 break;
1283 // passthrough hat and underscore outside mathed:
1284 case LFUN_MATH_SUBSCRIPT:
1285 mathDispatch(cur, FuncRequest(LFUN_SELF_INSERT, "_"), false);
1286 break;
1287 case LFUN_MATH_SUPERSCRIPT:
1288 mathDispatch(cur, FuncRequest(LFUN_SELF_INSERT, "^"), false);
1289 break;
1291 case LFUN_MATH_INSERT:
1292 case LFUN_MATH_MATRIX:
1293 case LFUN_MATH_DELIM:
1294 case LFUN_MATH_BIGDELIM: {
1295 if (cur.selection())
1296 cur.clearSelection();
1297 // FIXME: instead of the above, this one
1298 // should be used (but it asserts with Bidi enabled)
1299 // cf. http://bugzilla.lyx.org/show_bug.cgi?id=4055
1300 // cap::replaceSelection(cur);
1301 cur.insert(new InsetMathHull(hullSimple));
1302 checkAndActivateInset(cur, true);
1303 BOOST_ASSERT(cur.inMathed());
1304 cur.dispatch(cmd);
1305 break;
1308 case LFUN_FONT_EMPH: {
1309 Font font(Font::ALL_IGNORE);
1310 font.setEmph(Font::TOGGLE);
1311 toggleAndShow(cur, this, font);
1312 break;
1315 case LFUN_FONT_BOLD: {
1316 Font font(Font::ALL_IGNORE);
1317 font.setSeries(Font::BOLD_SERIES);
1318 toggleAndShow(cur, this, font);
1319 break;
1322 case LFUN_FONT_NOUN: {
1323 Font font(Font::ALL_IGNORE);
1324 font.setNoun(Font::TOGGLE);
1325 toggleAndShow(cur, this, font);
1326 break;
1329 case LFUN_FONT_TYPEWRITER: {
1330 Font font(Font::ALL_IGNORE);
1331 font.setFamily(Font::TYPEWRITER_FAMILY); // no good
1332 toggleAndShow(cur, this, font);
1333 break;
1336 case LFUN_FONT_SANS: {
1337 Font font(Font::ALL_IGNORE);
1338 font.setFamily(Font::SANS_FAMILY);
1339 toggleAndShow(cur, this, font);
1340 break;
1343 case LFUN_FONT_ROMAN: {
1344 Font font(Font::ALL_IGNORE);
1345 font.setFamily(Font::ROMAN_FAMILY);
1346 toggleAndShow(cur, this, font);
1347 break;
1350 case LFUN_FONT_DEFAULT: {
1351 Font font(Font::ALL_INHERIT, ignore_language);
1352 toggleAndShow(cur, this, font);
1353 break;
1356 case LFUN_FONT_UNDERLINE: {
1357 Font font(Font::ALL_IGNORE);
1358 font.setUnderbar(Font::TOGGLE);
1359 toggleAndShow(cur, this, font);
1360 break;
1363 case LFUN_FONT_SIZE: {
1364 Font font(Font::ALL_IGNORE);
1365 font.setLyXSize(to_utf8(cmd.argument()));
1366 toggleAndShow(cur, this, font);
1367 break;
1370 case LFUN_LANGUAGE: {
1371 Language const * lang = languages.getLanguage(to_utf8(cmd.argument()));
1372 if (!lang)
1373 break;
1374 Font font(Font::ALL_IGNORE);
1375 font.setLanguage(lang);
1376 toggleAndShow(cur, this, font);
1377 break;
1380 case LFUN_FONT_FREE_APPLY:
1381 toggleAndShow(cur, this, freefont, toggleall);
1382 cur.message(_("Character set"));
1383 break;
1385 // Set the freefont using the contents of \param data dispatched from
1386 // the frontends and apply it at the current cursor location.
1387 case LFUN_FONT_FREE_UPDATE: {
1388 Font font;
1389 bool toggle;
1390 if (font.fromString(to_utf8(cmd.argument()), toggle)) {
1391 freefont = font;
1392 toggleall = toggle;
1393 toggleAndShow(cur, this, freefont, toggleall);
1394 cur.message(_("Character set"));
1396 break;
1399 case LFUN_FINISHED_LEFT:
1400 LYXERR(Debug::DEBUG) << "handle LFUN_FINISHED_LEFT:\n" << cur << endl;
1401 if (reverseDirectionNeeded(cur)) {
1402 ++cur.pos();
1403 cur.setCurrentFont();
1405 break;
1407 case LFUN_FINISHED_RIGHT:
1408 LYXERR(Debug::DEBUG) << "handle LFUN_FINISHED_RIGHT:\n" << cur << endl;
1409 if (!reverseDirectionNeeded(cur)) {
1410 ++cur.pos();
1411 cur.setCurrentFont();
1413 break;
1415 case LFUN_FINISHED_BACKWARD:
1416 LYXERR(Debug::DEBUG) << "handle LFUN_FINISHED_BACKWARD:\n" << cur << endl;
1417 break;
1419 case LFUN_FINISHED_FORWARD:
1420 LYXERR(Debug::DEBUG) << "handle LFUN_FINISHED_FORWARD:\n" << cur << endl;
1421 ++cur.pos();
1422 cur.setCurrentFont();
1423 break;
1425 case LFUN_LAYOUT_PARAGRAPH: {
1426 string data;
1427 params2string(cur.paragraph(), data);
1428 data = "show\n" + data;
1429 bv->showDialogWithData("paragraph", data);
1430 break;
1433 case LFUN_PARAGRAPH_UPDATE: {
1434 string data;
1435 params2string(cur.paragraph(), data);
1437 // Will the paragraph accept changes from the dialog?
1438 bool const accept = !cur.inset().forceDefaultParagraphs(cur.idx());
1440 data = "update " + convert<string>(accept) + '\n' + data;
1441 bv->updateDialog("paragraph", data);
1442 break;
1445 case LFUN_ACCENT_UMLAUT:
1446 case LFUN_ACCENT_CIRCUMFLEX:
1447 case LFUN_ACCENT_GRAVE:
1448 case LFUN_ACCENT_ACUTE:
1449 case LFUN_ACCENT_TILDE:
1450 case LFUN_ACCENT_CEDILLA:
1451 case LFUN_ACCENT_MACRON:
1452 case LFUN_ACCENT_DOT:
1453 case LFUN_ACCENT_UNDERDOT:
1454 case LFUN_ACCENT_UNDERBAR:
1455 case LFUN_ACCENT_CARON:
1456 case LFUN_ACCENT_SPECIAL_CARON:
1457 case LFUN_ACCENT_BREVE:
1458 case LFUN_ACCENT_TIE:
1459 case LFUN_ACCENT_HUNGARIAN_UMLAUT:
1460 case LFUN_ACCENT_CIRCLE:
1461 case LFUN_ACCENT_OGONEK:
1462 theLyXFunc().handleKeyFunc(cmd.action);
1463 if (!cmd.argument().empty())
1464 // FIXME: Are all these characters encoded in one byte in utf8?
1465 bv->translateAndInsert(cmd.argument()[0], this, cur);
1466 break;
1468 case LFUN_FLOAT_LIST: {
1469 TextClass const & tclass = bv->buffer().params().getTextClass();
1470 if (tclass.floats().typeExist(to_utf8(cmd.argument()))) {
1471 cur.recordUndo();
1472 if (cur.selection())
1473 cutSelection(cur, true, false);
1474 breakParagraph(cur);
1476 if (cur.lastpos() != 0) {
1477 cursorBackward(cur);
1478 breakParagraph(cur);
1481 setLayout(cur, tclass.defaultLayoutName());
1482 ParagraphParameters p;
1483 setParagraphs(cur, p);
1484 insertInset(cur, new InsetFloatList(to_utf8(cmd.argument())));
1485 cur.posRight();
1486 } else {
1487 lyxerr << "Non-existent float type: "
1488 << to_utf8(cmd.argument()) << endl;
1490 break;
1493 case LFUN_CHANGE_ACCEPT: {
1494 acceptOrRejectChanges(cur, ACCEPT);
1495 break;
1498 case LFUN_CHANGE_REJECT: {
1499 acceptOrRejectChanges(cur, REJECT);
1500 break;
1503 case LFUN_THESAURUS_ENTRY: {
1504 docstring arg = cmd.argument();
1505 if (arg.empty()) {
1506 arg = cur.selectionAsString(false);
1507 // FIXME
1508 if (arg.size() > 100 || arg.empty()) {
1509 // Get word or selection
1510 selectWordWhenUnderCursor(cur, WHOLE_WORD);
1511 arg = cur.selectionAsString(false);
1514 bv->showDialogWithData("thesaurus", to_utf8(arg));
1515 break;
1518 case LFUN_PARAGRAPH_PARAMS_APPLY: {
1519 // Given data, an encoding of the ParagraphParameters
1520 // generated in the Paragraph dialog, this function sets
1521 // the current paragraph, or currently selected paragraphs,
1522 // appropriately.
1523 // NOTE: This function overrides all existing settings.
1524 setParagraphs(cur, cmd.argument());
1525 cur.message(_("Paragraph layout set"));
1526 break;
1529 case LFUN_PARAGRAPH_PARAMS: {
1530 // Given data, an encoding of the ParagraphParameters as we'd
1531 // find them in a LyX file, this function modifies the current paragraph,
1532 // or currently selected paragraphs.
1533 // NOTE: This function only modifies, and does not override, existing
1534 // settings.
1535 setParagraphs(cur, cmd.argument(), true);
1536 cur.message(_("Paragraph layout set"));
1537 break;
1540 case LFUN_ESCAPE:
1541 if (cur.selection()) {
1542 cur.selection() = false;
1543 } else {
1544 cur.undispatched();
1545 // This used to be LFUN_FINISHED_RIGHT, I think FORWARD is more
1546 // correct, but I'm not 100% sure -- dov, 071019
1547 cmd = FuncRequest(LFUN_FINISHED_FORWARD);
1549 break;
1551 default:
1552 LYXERR(Debug::ACTION)
1553 << BOOST_CURRENT_FUNCTION
1554 << ": Command " << cmd
1555 << " not DISPATCHED by Text" << endl;
1556 cur.undispatched();
1557 break;
1560 needsUpdate |= (cur.pos() != cur.lastpos()) && cur.selection();
1562 // FIXME: The cursor flag is reset two lines below
1563 // so we need to check here if some of the LFUN did touch that.
1564 // for now only Text::erase() and Text::backspace() do that.
1565 // The plan is to verify all the LFUNs and then to remove this
1566 // singleParUpdate boolean altogether.
1567 if (cur.result().update() & Update::Force) {
1568 singleParUpdate = false;
1569 needsUpdate = true;
1572 // FIXME: the following code should go in favor of fine grained
1573 // update flag treatment.
1574 if (singleParUpdate) {
1575 // Inserting characters does not change par height
1576 ParagraphMetrics const & pms
1577 = cur.bv().parMetrics(cur.bottom().text(), cur.bottom().pit());
1578 if (pms.dim().height()
1579 == olddim.height()) {
1580 // if so, update _only_ this paragraph
1581 cur.updateFlags(Update::SinglePar |
1582 Update::FitCursor |
1583 Update::MultiParSel);
1584 return;
1585 } else
1586 needsUpdate = true;
1589 if (!needsUpdate
1590 && &oldTopSlice.inset() == &cur.inset()
1591 && oldTopSlice.idx() == cur.idx()
1592 && !sel // sel is a backup of cur.selection() at the biginning of the function.
1593 && !cur.selection())
1594 // FIXME: it would be better if we could just do this
1596 //if (cur.result().update() != Update::FitCursor)
1597 // cur.noUpdate();
1599 // But some LFUNs do not set Update::FitCursor when needed, so we
1600 // do it for all. This is not very harmfull as FitCursor will provoke
1601 // a full redraw only if needed but still, a proper review of all LFUN
1602 // should be done and this needsUpdate boolean can then be removed.
1603 cur.updateFlags(Update::FitCursor);
1604 else
1605 cur.updateFlags(Update::Force | Update::FitCursor);
1609 bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
1610 FuncStatus & flag) const
1612 BOOST_ASSERT(cur.text() == this);
1614 Font const & font = cur.real_current_font;
1615 bool enable = true;
1616 InsetCode code = NO_CODE;
1618 switch (cmd.action) {
1620 case LFUN_DEPTH_DECREMENT:
1621 enable = changeDepthAllowed(cur, DEC_DEPTH);
1622 break;
1624 case LFUN_DEPTH_INCREMENT:
1625 enable = changeDepthAllowed(cur, INC_DEPTH);
1626 break;
1628 case LFUN_APPENDIX:
1629 flag.setOnOff(cur.paragraph().params().startOfAppendix());
1630 return true;
1632 case LFUN_BIBITEM_INSERT:
1633 enable = (cur.paragraph().layout()->labeltype == LABEL_BIBLIO
1634 && cur.pos() == 0);
1635 break;
1637 case LFUN_DIALOG_SHOW_NEW_INSET:
1638 if (cmd.argument() == "bibitem")
1639 code = BIBITEM_CODE;
1640 else if (cmd.argument() == "bibtex")
1641 code = BIBTEX_CODE;
1642 else if (cmd.argument() == "box")
1643 code = BOX_CODE;
1644 else if (cmd.argument() == "branch")
1645 code = BRANCH_CODE;
1646 else if (cmd.argument() == "citation")
1647 code = CITE_CODE;
1648 else if (cmd.argument() == "ert")
1649 code = ERT_CODE;
1650 else if (cmd.argument() == "external")
1651 code = EXTERNAL_CODE;
1652 else if (cmd.argument() == "float")
1653 code = FLOAT_CODE;
1654 else if (cmd.argument() == "graphics")
1655 code = GRAPHICS_CODE;
1656 else if (cmd.argument() == "href")
1657 code = HYPERLINK_CODE;
1658 else if (cmd.argument() == "include")
1659 code = INCLUDE_CODE;
1660 else if (cmd.argument() == "index")
1661 code = INDEX_CODE;
1662 else if (cmd.argument() == "nomenclature")
1663 code = NOMENCL_CODE;
1664 else if (cmd.argument() == "label")
1665 code = LABEL_CODE;
1666 else if (cmd.argument() == "note")
1667 code = NOTE_CODE;
1668 else if (cmd.argument() == "ref")
1669 code = REF_CODE;
1670 else if (cmd.argument() == "toc")
1671 code = TOC_CODE;
1672 else if (cmd.argument() == "vspace")
1673 code = VSPACE_CODE;
1674 else if (cmd.argument() == "wrap")
1675 code = WRAP_CODE;
1676 else if (cmd.argument() == "listings")
1677 code = LISTINGS_CODE;
1678 break;
1680 case LFUN_ERT_INSERT:
1681 code = ERT_CODE;
1682 break;
1683 case LFUN_LISTING_INSERT:
1684 code = LISTINGS_CODE;
1685 break;
1686 case LFUN_FOOTNOTE_INSERT:
1687 code = FOOT_CODE;
1688 break;
1689 case LFUN_TABULAR_INSERT:
1690 code = TABULAR_CODE;
1691 break;
1692 case LFUN_MARGINALNOTE_INSERT:
1693 code = MARGIN_CODE;
1694 break;
1695 case LFUN_FLOAT_INSERT:
1696 case LFUN_FLOAT_WIDE_INSERT:
1697 code = FLOAT_CODE;
1698 break;
1699 case LFUN_WRAP_INSERT:
1700 code = WRAP_CODE;
1701 break;
1702 case LFUN_FLOAT_LIST:
1703 code = FLOAT_LIST_CODE;
1704 break;
1705 #if 0
1706 case LFUN_LIST_INSERT:
1707 code = LIST_CODE;
1708 break;
1709 case LFUN_THEOREM_INSERT:
1710 code = THEOREM_CODE;
1711 break;
1712 #endif
1713 case LFUN_CAPTION_INSERT:
1714 code = CAPTION_CODE;
1715 break;
1716 case LFUN_NOTE_INSERT:
1717 code = NOTE_CODE;
1718 break;
1719 case LFUN_FLEX_INSERT: {
1720 code = FLEX_CODE;
1721 string s = cmd.getArg(0);
1722 InsetLayout il = cur.buffer().params().getTextClass().insetlayout(from_utf8(s));
1723 if (il.lyxtype != "charstyle" &&
1724 il.lyxtype != "custom" &&
1725 il.lyxtype != "element")
1726 enable = false;
1727 break;
1729 case LFUN_BOX_INSERT:
1730 code = BOX_CODE;
1731 break;
1732 case LFUN_BRANCH_INSERT:
1733 code = BRANCH_CODE;
1734 if (cur.buffer().masterBuffer()->params().branchlist().empty())
1735 enable = false;
1736 break;
1737 case LFUN_LABEL_INSERT:
1738 code = LABEL_CODE;
1739 break;
1740 case LFUN_INFO_INSERT:
1741 code = INFO_CODE;
1742 break;
1743 case LFUN_OPTIONAL_INSERT:
1744 code = OPTARG_CODE;
1745 enable = cur.paragraph().numberOfOptArgs()
1746 < cur.paragraph().layout()->optionalargs;
1747 break;
1748 case LFUN_ENVIRONMENT_INSERT:
1749 code = BOX_CODE;
1750 break;
1751 case LFUN_INDEX_INSERT:
1752 code = INDEX_CODE;
1753 break;
1754 case LFUN_INDEX_PRINT:
1755 code = INDEX_PRINT_CODE;
1756 break;
1757 case LFUN_NOMENCL_INSERT:
1758 code = NOMENCL_CODE;
1759 break;
1760 case LFUN_NOMENCL_PRINT:
1761 code = NOMENCL_PRINT_CODE;
1762 break;
1763 case LFUN_TOC_INSERT:
1764 code = TOC_CODE;
1765 break;
1766 case LFUN_HYPERLINK_INSERT:
1767 code = HYPERLINK_CODE;
1768 break;
1769 case LFUN_QUOTE_INSERT:
1770 // always allow this, since we will inset a raw quote
1771 // if an inset is not allowed.
1772 break;
1773 case LFUN_HYPHENATION_POINT_INSERT:
1774 case LFUN_LIGATURE_BREAK_INSERT:
1775 case LFUN_HFILL_INSERT:
1776 case LFUN_MENU_SEPARATOR_INSERT:
1777 case LFUN_DOTS_INSERT:
1778 case LFUN_END_OF_SENTENCE_PERIOD_INSERT:
1779 code = SPECIALCHAR_CODE;
1780 break;
1781 case LFUN_SPACE_INSERT:
1782 // slight hack: we know this is allowed in math mode
1783 if (cur.inTexted())
1784 code = SPACE_CODE;
1785 break;
1787 case LFUN_INSET_MODIFY:
1788 // We need to disable this, because we may get called for a
1789 // tabular cell via
1790 // InsetTabular::getStatus() -> InsetText::getStatus()
1791 // and we don't handle LFUN_INSET_MODIFY.
1792 enable = false;
1793 break;
1795 case LFUN_FONT_EMPH:
1796 flag.setOnOff(font.emph() == Font::ON);
1797 return true;
1799 case LFUN_FONT_NOUN:
1800 flag.setOnOff(font.noun() == Font::ON);
1801 return true;
1803 case LFUN_FONT_BOLD:
1804 flag.setOnOff(font.series() == Font::BOLD_SERIES);
1805 return true;
1807 case LFUN_FONT_SANS:
1808 flag.setOnOff(font.family() == Font::SANS_FAMILY);
1809 return true;
1811 case LFUN_FONT_ROMAN:
1812 flag.setOnOff(font.family() == Font::ROMAN_FAMILY);
1813 return true;
1815 case LFUN_FONT_TYPEWRITER:
1816 flag.setOnOff(font.family() == Font::TYPEWRITER_FAMILY);
1817 return true;
1819 case LFUN_CUT:
1820 case LFUN_COPY:
1821 enable = cur.selection();
1822 break;
1824 case LFUN_PASTE:
1825 if (cmd.argument().empty()) {
1826 if (theClipboard().isInternal())
1827 enable = cap::numberOfSelections() > 0;
1828 else
1829 enable = !theClipboard().empty();
1830 } else {
1831 string const arg = to_utf8(cmd.argument());
1832 if (isStrUnsignedInt(arg)) {
1833 unsigned int n = convert<unsigned int>(arg);
1834 enable = cap::numberOfSelections() > n;
1835 } else
1836 // unknown argument
1837 enable = false;
1839 break;
1841 case LFUN_CLIPBOARD_PASTE:
1842 enable = !theClipboard().empty();
1843 break;
1845 case LFUN_PRIMARY_SELECTION_PASTE:
1846 enable = cur.selection() || !theSelection().empty();
1847 break;
1849 case LFUN_PARAGRAPH_MOVE_UP:
1850 enable = cur.pit() > 0 && !cur.selection();
1851 break;
1853 case LFUN_PARAGRAPH_MOVE_DOWN:
1854 enable = cur.pit() < cur.lastpit() && !cur.selection();
1855 break;
1857 case LFUN_INSET_DISSOLVE:
1858 if (!cmd.argument().empty()) {
1859 InsetLayout il = cur.inset().getLayout(cur.buffer().params());
1860 enable = cur.inset().lyxCode() == FLEX_CODE
1861 && il.lyxtype == to_utf8(cmd.argument());
1862 } else {
1863 enable = !isMainText(cur.bv().buffer())
1864 && cur.inset().nargs() == 1;
1866 break;
1868 case LFUN_CHANGE_ACCEPT:
1869 case LFUN_CHANGE_REJECT:
1870 // TODO: context-sensitive enabling of LFUN_CHANGE_ACCEPT/REJECT
1871 // In principle, these LFUNs should only be enabled if there
1872 // is a change at the current position/in the current selection.
1873 // However, without proper optimizations, this will inevitably
1874 // result in unacceptable performance - just imagine a user who
1875 // wants to select the complete content of a long document.
1876 enable = true;
1877 break;
1879 case LFUN_WORD_DELETE_FORWARD:
1880 case LFUN_WORD_DELETE_BACKWARD:
1881 case LFUN_LINE_DELETE:
1882 case LFUN_WORD_FORWARD:
1883 case LFUN_WORD_BACKWARD:
1884 case LFUN_CHAR_FORWARD:
1885 case LFUN_CHAR_FORWARD_SELECT:
1886 case LFUN_CHAR_BACKWARD:
1887 case LFUN_CHAR_BACKWARD_SELECT:
1888 case LFUN_CHAR_LEFT:
1889 case LFUN_CHAR_LEFT_SELECT:
1890 case LFUN_CHAR_RIGHT:
1891 case LFUN_CHAR_RIGHT_SELECT:
1892 case LFUN_UP:
1893 case LFUN_UP_SELECT:
1894 case LFUN_DOWN:
1895 case LFUN_DOWN_SELECT:
1896 case LFUN_PARAGRAPH_UP_SELECT:
1897 case LFUN_PARAGRAPH_DOWN_SELECT:
1898 case LFUN_SCREEN_UP_SELECT:
1899 case LFUN_SCREEN_DOWN_SELECT:
1900 case LFUN_LINE_BEGIN_SELECT:
1901 case LFUN_LINE_END_SELECT:
1902 case LFUN_WORD_FORWARD_SELECT:
1903 case LFUN_WORD_BACKWARD_SELECT:
1904 case LFUN_WORD_SELECT:
1905 case LFUN_PARAGRAPH_UP:
1906 case LFUN_PARAGRAPH_DOWN:
1907 case LFUN_LINE_BEGIN:
1908 case LFUN_LINE_END:
1909 case LFUN_BREAK_LINE:
1910 case LFUN_CHAR_DELETE_FORWARD:
1911 case LFUN_DELETE_FORWARD_SKIP:
1912 case LFUN_CHAR_DELETE_BACKWARD:
1913 case LFUN_DELETE_BACKWARD_SKIP:
1914 case LFUN_BREAK_PARAGRAPH:
1915 case LFUN_BREAK_PARAGRAPH_SKIP:
1916 case LFUN_PARAGRAPH_SPACING:
1917 case LFUN_INSET_INSERT:
1918 case LFUN_WORD_UPCASE:
1919 case LFUN_WORD_LOWCASE:
1920 case LFUN_WORD_CAPITALIZE:
1921 case LFUN_CHARS_TRANSPOSE:
1922 case LFUN_SERVER_GET_XY:
1923 case LFUN_SERVER_SET_XY:
1924 case LFUN_SERVER_GET_FONT:
1925 case LFUN_SERVER_GET_LAYOUT:
1926 case LFUN_LAYOUT:
1927 case LFUN_DATE_INSERT:
1928 case LFUN_SELF_INSERT:
1929 case LFUN_LINE_INSERT:
1930 case LFUN_PAGEBREAK_INSERT:
1931 case LFUN_CLEARPAGE_INSERT:
1932 case LFUN_CLEARDOUBLEPAGE_INSERT:
1933 case LFUN_MATH_DISPLAY:
1934 case LFUN_MATH_IMPORT_SELECTION:
1935 case LFUN_MATH_MODE:
1936 case LFUN_MATH_MACRO:
1937 case LFUN_MATH_MATRIX:
1938 case LFUN_MATH_DELIM:
1939 case LFUN_MATH_BIGDELIM:
1940 case LFUN_MATH_INSERT:
1941 case LFUN_MATH_SUBSCRIPT:
1942 case LFUN_MATH_SUPERSCRIPT:
1943 case LFUN_FONT_DEFAULT:
1944 case LFUN_FONT_UNDERLINE:
1945 case LFUN_FONT_SIZE:
1946 case LFUN_LANGUAGE:
1947 case LFUN_FONT_FREE_APPLY:
1948 case LFUN_FONT_FREE_UPDATE:
1949 case LFUN_LAYOUT_PARAGRAPH:
1950 case LFUN_PARAGRAPH_UPDATE:
1951 case LFUN_ACCENT_UMLAUT:
1952 case LFUN_ACCENT_CIRCUMFLEX:
1953 case LFUN_ACCENT_GRAVE:
1954 case LFUN_ACCENT_ACUTE:
1955 case LFUN_ACCENT_TILDE:
1956 case LFUN_ACCENT_CEDILLA:
1957 case LFUN_ACCENT_MACRON:
1958 case LFUN_ACCENT_DOT:
1959 case LFUN_ACCENT_UNDERDOT:
1960 case LFUN_ACCENT_UNDERBAR:
1961 case LFUN_ACCENT_CARON:
1962 case LFUN_ACCENT_SPECIAL_CARON:
1963 case LFUN_ACCENT_BREVE:
1964 case LFUN_ACCENT_TIE:
1965 case LFUN_ACCENT_HUNGARIAN_UMLAUT:
1966 case LFUN_ACCENT_CIRCLE:
1967 case LFUN_ACCENT_OGONEK:
1968 case LFUN_THESAURUS_ENTRY:
1969 case LFUN_PARAGRAPH_PARAMS_APPLY:
1970 case LFUN_PARAGRAPH_PARAMS:
1971 case LFUN_ESCAPE:
1972 case LFUN_BUFFER_END:
1973 case LFUN_BUFFER_BEGIN:
1974 case LFUN_BUFFER_BEGIN_SELECT:
1975 case LFUN_BUFFER_END_SELECT:
1976 case LFUN_UNICODE_INSERT:
1977 // these are handled in our dispatch()
1978 enable = true;
1979 break;
1981 default:
1982 return false;
1985 if (code != NO_CODE
1986 && (cur.empty() || !cur.inset().insetAllowed(code)))
1987 enable = false;
1989 flag.enabled(enable);
1990 return true;
1994 void Text::pasteString(Cursor & cur, docstring const & clip,
1995 bool asParagraphs)
1997 cur.clearSelection();
1998 if (!clip.empty()) {
1999 cur.recordUndo();
2000 if (asParagraphs)
2001 insertStringAsParagraphs(cur, clip);
2002 else
2003 insertStringAsLines(cur, clip);
2007 } // namespace lyx