2 * \file math_nestinset.C
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * Full author contact details are available in file CREDITS.
13 #include "math_nestinset.h"
15 #include "math_arrayinset.h"
16 #include "math_biginset.h"
17 #include "math_boxinset.h"
18 #include "math_braceinset.h"
19 #include "math_colorinset.h"
20 #include "math_commentinset.h"
21 #include "math_data.h"
22 #include "math_deliminset.h"
23 #include "math_factory.h"
24 #include "math_hullinset.h"
25 #include "math_mathmlstream.h"
26 #include "math_macroarg.h"
27 //#include "math_mboxinset.h"
28 #include "math_parser.h"
29 #include "math_scriptinset.h"
30 #include "math_spaceinset.h"
31 #include "math_symbolinset.h"
32 #include "math_support.h"
33 #include "math_unknowninset.h"
34 #include "ref_inset.h"
36 #include "BufferView.h"
37 #include "CutAndPaste.h"
38 #include "FuncStatus.h"
40 #include "bufferview_funcs.h"
41 #include "coordcache.h"
44 #include "dispatchresult.h"
45 #include "funcrequest.h"
47 #include "outputparams.h"
50 #include "support/lstrings.h"
52 #include "frontends/Dialogs.h"
53 #include "frontends/Gui.h"
54 #include "frontends/LyXView.h"
55 #include "frontends/Painter.h"
56 #include "frontends/Selection.h"
57 #include "frontends/nullpainter.h"
61 using lyx::cap::copySelection;
62 using lyx::cap::grabAndEraseSelection;
63 using lyx::cap::cutSelection;
64 using lyx::cap::replaceSelection;
65 using lyx::cap::selClearOrDel;
67 using lyx::frontend::Gui;
68 using lyx::frontend::Clipboard;
72 using std::istringstream;
75 MathNestInset::MathNestInset(idx_type nargs)
76 : cells_(nargs), lock_(false)
80 MathInset::idx_type MathNestInset::nargs() const
86 MathArray & MathNestInset::cell(idx_type i)
92 MathArray const & MathNestInset::cell(idx_type i) const
98 void MathNestInset::cursorPos(CursorSlice const & sl, bool /*boundary*/,
99 int & x, int & y) const
101 // FIXME: This is a hack. Ideally, the coord cache should not store
102 // absolute positions, but relative ones. This would mean to call
103 // setXY() not in MathArray::draw(), but in the parent insets' draw()
104 // with the correctly adjusted x,y values. But this means that we'd have
105 // to touch all (math)inset's draw() methods. Right now, we'll store
106 // absolute value, and make them here relative, only to make them
107 // absolute again when actually drawing the cursor. What a mess.
108 BOOST_ASSERT(ptr_cmp(&sl.inset(), this));
109 MathArray const & ar = sl.cell();
110 if (!theCoords.getArrays().has(&ar)) {
111 // this can (semi-)legally happen if we just created this cell
112 // and it never has been drawn before. So don't ASSERT.
113 //lyxerr << "no cached data for array " << &ar << endl;
118 Point const pt = theCoords.getArrays().xy(&ar);
119 if (!theCoords.getInsets().has(this)) {
121 //lyxerr << "no cached data for inset " << this << endl;
126 Point const pt2 = theCoords.getInsets().xy(this);
127 //lyxerr << "retrieving position cache for MathArray "
128 // << pt.x_ << ' ' << pt.y_ << std::endl;
129 x = pt.x_ - pt2.x_ + ar.pos2x(sl.pos());
131 // lyxerr << "pt.y_ : " << pt.y_ << " pt2_.y_ : " << pt2.y_
132 // << " asc: " << ascent() << " des: " << descent()
133 // << " ar.asc: " << ar.ascent() << " ar.des: " << ar.descent() << endl;
134 // move cursor visually into empty cells ("blue rectangles");
140 void MathNestInset::metrics(MetricsInfo const & mi) const
143 for (idx_type i = 0, n = nargs(); i != n; ++i)
148 bool MathNestInset::idxNext(LCursor & cur) const
150 BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
151 if (cur.idx() == cur.lastidx())
159 bool MathNestInset::idxRight(LCursor & cur) const
165 bool MathNestInset::idxPrev(LCursor & cur) const
167 BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
171 cur.pos() = cur.lastpos();
176 bool MathNestInset::idxLeft(LCursor & cur) const
182 bool MathNestInset::idxFirst(LCursor & cur) const
184 BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
193 bool MathNestInset::idxLast(LCursor & cur) const
195 BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
198 cur.idx() = cur.lastidx();
199 cur.pos() = cur.lastpos();
204 void MathNestInset::dump() const
206 WriteStream os(lyxerr);
207 os << "---------------------------------------------\n";
210 for (idx_type i = 0, n = nargs(); i != n; ++i)
211 os << cell(i) << "\n";
212 os << "---------------------------------------------\n";
216 void MathNestInset::draw(PainterInfo & pi, int x, int y) const
220 pi.pain.fillRectangle(x, y - ascent(), width(), height(),
223 setPosCache(pi, x, y);
227 void MathNestInset::drawSelection(PainterInfo & pi, int x, int y) const
229 // this should use the x/y values given, not the cached values
230 LCursor & cur = pi.base.bv->cursor();
231 if (!cur.selection())
233 if (!ptr_cmp(&cur.inset(), this))
236 // FIXME: hack to get position cache warm
237 static lyx::frontend::NullPainter nop;
238 PainterInfo pinop(pi);
242 CursorSlice s1 = cur.selBegin();
243 CursorSlice s2 = cur.selEnd();
244 //lyxerr << "MathNestInset::drawing selection: "
245 // << " s1: " << s1 << " s2: " << s2 << endl;
246 if (s1.idx() == s2.idx()) {
247 MathArray const & c = cell(s1.idx());
248 int x1 = c.xo() + c.pos2x(s1.pos());
249 int y1 = c.yo() - c.ascent();
250 int x2 = c.xo() + c.pos2x(s2.pos());
251 int y2 = c.yo() + c.descent();
252 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
253 //lyxerr << "MathNestInset::drawing selection 3: "
254 // << " x1: " << x1 << " x2: " << x2
255 // << " y1: " << y1 << " y2: " << y2 << endl;
257 for (idx_type i = 0; i < nargs(); ++i) {
258 if (idxBetween(i, s1.idx(), s2.idx())) {
259 MathArray const & c = cell(i);
261 int y1 = c.yo() - c.ascent();
262 int x2 = c.xo() + c.width();
263 int y2 = c.yo() + c.descent();
264 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
271 void MathNestInset::validate(LaTeXFeatures & features) const
273 for (idx_type i = 0; i < nargs(); ++i)
274 cell(i).validate(features);
278 void MathNestInset::replace(ReplaceData & rep)
280 for (idx_type i = 0; i < nargs(); ++i)
281 cell(i).replace(rep);
285 bool MathNestInset::contains(MathArray const & ar) const
287 for (idx_type i = 0; i < nargs(); ++i)
288 if (cell(i).contains(ar))
294 bool MathNestInset::lock() const
300 void MathNestInset::lock(bool l)
306 bool MathNestInset::isActive() const
312 MathArray MathNestInset::glue() const
315 for (size_t i = 0; i < nargs(); ++i)
321 void MathNestInset::write(WriteStream & os) const
323 os << '\\' << name().c_str();
324 for (size_t i = 0; i < nargs(); ++i)
325 os << '{' << cell(i) << '}';
327 os.pendingSpace(true);
328 if (lock_ && !os.latex()) {
330 os.pendingSpace(true);
335 void MathNestInset::normalize(NormalStream & os) const
337 os << '[' << name().c_str();
338 for (size_t i = 0; i < nargs(); ++i)
339 os << ' ' << cell(i);
344 int MathNestInset::latex(Buffer const &, std::ostream & os,
345 OutputParams const & runparams) const
347 WriteStream wi(os, runparams.moving_arg, true);
353 bool MathNestInset::notifyCursorLeaves(LCursor & /*cur*/)
359 MathArray & ar = cur.cell();
360 // remove base-only "scripts"
361 for (pos_type i = 0; i + 1 < ar.size(); ++i) {
362 MathScriptInset * p = operator[](i).nucleus()->asScriptInset();
363 if (p && p->nargs() == 1) {
364 MathArray ar = p->nuc();
367 cur.adjust(i, ar.size() - 1);
371 // glue adjacent font insets of the same kind
372 for (pos_type i = 0; i + 1 < size(); ++i) {
373 MathFontInset * p = operator[](i).nucleus()->asFontInset();
374 MathFontInset const * q = operator[](i + 1)->asFontInset();
375 if (p && q && p->name() == q->name()) {
376 p->cell(0).append(q->cell(0));
386 void MathNestInset::handleFont
387 (LCursor & cur, string const & arg, string const & font)
389 // this whole function is a hack and won't work for incremental font
391 recordUndo(cur, Undo::ATOMIC);
393 if (cur.inset().asMathInset()->name() == font)
394 cur.handleFont(font);
396 cur.handleNest(createMathInset(font));
402 void MathNestInset::handleFont2(LCursor & cur, string const & arg)
404 recordUndo(cur, Undo::ATOMIC);
407 bv_funcs::string2font(arg, font, b);
408 if (font.color() != LColor::inherit) {
409 MathAtom at = MathAtom(new MathColorInset(true, font.color()));
410 cur.handleNest(at, 0);
415 void MathNestInset::doDispatch(LCursor & cur, FuncRequest & cmd)
417 //lyxerr << "MathNestInset: request: " << cmd << std::endl;
418 //CursorSlice sl = cur.current();
420 switch (cmd.action) {
424 cur.message(_("Paste"));
425 replaceSelection(cur);
427 istringstream is(lyx::to_utf8(cmd.argument()));
429 string const selection = lyx::cap::getSelection(cur.buffer(), n);
430 cur.niceInsert(selection);
431 cur.clearSelection(); // bug 393
432 cur.bv().switchKeyMap();
439 cutSelection(cur, true, true);
440 cur.message(_("Cut"));
441 // Prevent stale position >= size crash
442 // Probably not necessary anymore, see eraseSelection (gb 2005-10-09)
448 cur.message(_("Copy"));
451 case LFUN_MOUSE_PRESS:
452 lfunMousePress(cur, cmd);
455 case LFUN_MOUSE_MOTION:
456 lfunMouseMotion(cur, cmd);
459 case LFUN_MOUSE_RELEASE:
460 lfunMouseRelease(cur, cmd);
463 case LFUN_FINISHED_LEFT:
464 cur.bv().cursor() = cur;
467 case LFUN_FINISHED_RIGHT:
469 cur.bv().cursor() = cur;
472 case LFUN_FINISHED_UP:
473 cur.bv().cursor() = cur;
476 case LFUN_FINISHED_DOWN:
478 cur.bv().cursor() = cur;
481 case LFUN_CHAR_FORWARD_SELECT:
482 case LFUN_CHAR_FORWARD:
483 cur.selHandle(cmd.action == LFUN_CHAR_FORWARD_SELECT);
484 cur.autocorrect() = false;
486 cur.macroModeClose();
487 if (cur.pos() != cur.lastpos() && cur.openable(cur.nextAtom())) {
488 cur.pushLeft(*cur.nextAtom().nucleus());
489 cur.inset().idxFirst(cur);
490 } else if (cur.posRight() || idxRight(cur)
491 || cur.popRight() || cur.selection())
494 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
499 case LFUN_CHAR_BACKWARD_SELECT:
500 case LFUN_CHAR_BACKWARD:
501 cur.selHandle(cmd.action == LFUN_CHAR_BACKWARD_SELECT);
502 cur.autocorrect() = false;
504 cur.macroModeClose();
505 if (cur.pos() != 0 && cur.openable(cur.prevAtom())) {
507 cur.push(*cur.nextAtom().nucleus());
508 cur.inset().idxLast(cur);
509 } else if (cur.posLeft() || idxLeft(cur)
510 || cur.popLeft() || cur.selection())
513 cmd = FuncRequest(LFUN_FINISHED_LEFT);
520 // FIXME Tried to use clearTargetX and macroModeClose, crashed on cur.up()
521 if (cur.inMacroMode()) {
523 cur.macroModeClose();
526 cur.selHandle(cmd.action == LFUN_UP_SELECT);
528 cmd = FuncRequest(LFUN_FINISHED_UP);
531 // fixes bug 1598. Please check!
535 case LFUN_DOWN_SELECT:
537 if (cur.inMacroMode()) {
538 cur.macroModeClose();
541 cur.selHandle(cmd.action == LFUN_DOWN_SELECT);
543 cmd = FuncRequest(LFUN_FINISHED_DOWN);
546 // fixes bug 1598. Please check!
550 case LFUN_MOUSE_DOUBLE:
551 case LFUN_MOUSE_TRIPLE:
552 case LFUN_WORD_SELECT:
556 cur.selection() = true;
557 cur.pos() = cur.lastpos();
558 cur.idx() = cur.lastidx();
561 case LFUN_PARAGRAPH_UP_SELECT:
562 case LFUN_PARAGRAPH_UP:
563 case LFUN_PARAGRAPH_DOWN_SELECT:
564 case LFUN_PARAGRAPH_DOWN:
567 case LFUN_LINE_BEGIN_SELECT:
568 case LFUN_LINE_BEGIN:
569 case LFUN_WORD_BACKWARD_SELECT:
570 case LFUN_WORD_BACKWARD:
571 cur.selHandle(cmd.action == LFUN_WORD_BACKWARD_SELECT ||
572 cmd.action == LFUN_LINE_BEGIN_SELECT);
573 cur.macroModeClose();
574 if (cur.pos() != 0) {
576 } else if (cur.col() != 0) {
577 cur.idx() -= cur.col();
579 } else if (cur.idx() != 0) {
583 cmd = FuncRequest(LFUN_FINISHED_LEFT);
588 case LFUN_WORD_FORWARD_SELECT:
589 case LFUN_WORD_FORWARD:
590 case LFUN_LINE_END_SELECT:
592 cur.selHandle(cmd.action == LFUN_WORD_FORWARD_SELECT ||
593 cmd.action == LFUN_LINE_END_SELECT);
594 cur.macroModeClose();
596 if (cur.pos() != cur.lastpos()) {
597 cur.pos() = cur.lastpos();
598 } else if (ncols() && (cur.col() != cur.lastcol())) {
599 cur.idx() = cur.idx() - cur.col() + cur.lastcol();
600 cur.pos() = cur.lastpos();
601 } else if (cur.idx() != cur.lastidx()) {
602 cur.idx() = cur.lastidx();
603 cur.pos() = cur.lastpos();
605 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
610 case LFUN_SCREEN_UP_SELECT:
612 cmd = FuncRequest(LFUN_FINISHED_LEFT);
616 case LFUN_SCREEN_DOWN_SELECT:
617 case LFUN_SCREEN_DOWN:
618 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
622 case LFUN_CELL_FORWARD:
623 cur.inset().idxNext(cur);
626 case LFUN_CELL_BACKWARD:
627 cur.inset().idxPrev(cur);
630 case LFUN_WORD_DELETE_BACKWARD:
631 case LFUN_CHAR_DELETE_BACKWARD:
633 // May affect external cell:
634 recordUndoInset(cur, Undo::ATOMIC);
636 recordUndo(cur, Undo::ATOMIC);
640 case LFUN_WORD_DELETE_FORWARD:
641 case LFUN_CHAR_DELETE_FORWARD:
642 if (cur.pos() == cur.lastpos())
643 // May affect external cell:
644 recordUndoInset(cur, Undo::ATOMIC);
646 recordUndo(cur, Undo::ATOMIC);
652 cur.clearSelection();
654 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
659 case LFUN_INSET_TOGGLE:
665 case LFUN_SELF_INSERT:
666 if (cmd.argument().size() != 1) {
668 string const arg = lyx::to_utf8(cmd.argument());
669 if (!interpret(cur, arg))
673 // Don't record undo steps if we are in macro mode and
674 // cmd.argument is the next character of the macro name.
675 // Otherwise we'll get an invalid cursor if we undo after
676 // the macro was finished and the macro is a known command,
677 // e.g. sqrt. LCursor::macroModeClose replaces in this case
678 // the MathUnknownInset with name "frac" by an empty
679 // MathFracInset -> a pos value > 0 is invalid.
680 // A side effect is that an undo before the macro is finished
681 // undoes the complete macro, not only the last character.
682 if (!cur.inMacroMode())
685 // spacial handling of space. If we insert an inset
686 // via macro mode, we want to put the cursor inside it
687 // if relevant. Think typing "\frac<space>".
688 if (cmd.argument()[0] == ' '
689 && cur.inMacroMode() && cur.macroName() != "\\"
690 && cur.macroModeClose()) {
691 MathAtom const atom = cur.prevAtom();
692 if (atom->asNestInset() && atom->nargs() > 0) {
694 cur.pushLeft(*cur.nextInset());
697 // } else if (!interpret(cur, cmd.argument()[0])) {
698 // when interpret accepts UCS4 characters
699 } else if (!interpret(cur, lyx::to_utf8(cmd.argument()))) {
700 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
705 //case LFUN_SERVER_GET_XY:
706 // sprintf(dispatch_buffer, "%d %d",);
709 case LFUN_SERVER_SET_XY: {
710 lyxerr << "LFUN_SERVER_SET_XY broken!" << endl;
713 istringstream is(lyx::to_utf8(cmd.argument()));
715 cur.setScreenPos(x, y);
719 // Special casing for superscript in case of LyX handling
721 case LFUN_ACCENT_CIRCUMFLEX:
722 if (cmd.argument().empty()) {
723 // do superscript if LyX handles
725 recordUndo(cur, Undo::ATOMIC);
726 script(cur, true, grabAndEraseSelection(cur));
730 case LFUN_ACCENT_UMLAUT:
731 case LFUN_ACCENT_ACUTE:
732 case LFUN_ACCENT_GRAVE:
733 case LFUN_ACCENT_BREVE:
734 case LFUN_ACCENT_DOT:
735 case LFUN_ACCENT_MACRON:
736 case LFUN_ACCENT_CARON:
737 case LFUN_ACCENT_TILDE:
738 case LFUN_ACCENT_CEDILLA:
739 case LFUN_ACCENT_CIRCLE:
740 case LFUN_ACCENT_UNDERDOT:
741 case LFUN_ACCENT_TIE:
742 case LFUN_ACCENT_OGONEK:
743 case LFUN_ACCENT_HUNGARIAN_UMLAUT:
747 case LFUN_FONT_FREE_APPLY:
748 case LFUN_FONT_FREE_UPDATE:
749 handleFont2(cur, lyx::to_utf8(cmd.argument()));
753 if (currentMode() == TEXT_MODE)
754 handleFont(cur, lyx::to_utf8(cmd.argument()), "textbf");
756 handleFont(cur, lyx::to_utf8(cmd.argument()), "mathbf");
759 if (currentMode() == TEXT_MODE)
760 handleFont(cur, lyx::to_utf8(cmd.argument()), "textsf");
762 handleFont(cur, lyx::to_utf8(cmd.argument()), "mathsf");
765 if (currentMode() == TEXT_MODE)
766 handleFont(cur, lyx::to_utf8(cmd.argument()), "emph");
768 handleFont(cur, lyx::to_utf8(cmd.argument()), "mathcal");
770 case LFUN_FONT_ROMAN:
771 if (currentMode() == TEXT_MODE)
772 handleFont(cur, lyx::to_utf8(cmd.argument()), "textrm");
774 handleFont(cur, lyx::to_utf8(cmd.argument()), "mathrm");
777 if (currentMode() == TEXT_MODE)
778 handleFont(cur, lyx::to_utf8(cmd.argument()), "texttt");
780 handleFont(cur, lyx::to_utf8(cmd.argument()), "mathtt");
783 handleFont(cur, lyx::to_utf8(cmd.argument()), "mathfrak");
786 if (currentMode() == TEXT_MODE)
787 handleFont(cur, lyx::to_utf8(cmd.argument()), "textit");
789 handleFont(cur, lyx::to_utf8(cmd.argument()), "mathit");
792 if (currentMode() == TEXT_MODE)
793 // FIXME: should be "noun"
794 handleFont(cur, lyx::to_utf8(cmd.argument()), "textsc");
796 handleFont(cur, lyx::to_utf8(cmd.argument()), "mathbb");
798 //case LFUN_FONT_FREE_APPLY:
799 handleFont(cur, lyx::to_utf8(cmd.argument()), "textrm");
801 case LFUN_FONT_DEFAULT:
802 handleFont(cur, lyx::to_utf8(cmd.argument()), "textnormal");
805 case LFUN_MATH_MODE: {
807 // ignore math-mode on when already in math mode
808 if (currentMode() == InsetBase::MATH_MODE && cmd.argument() == "on")
810 cur.macroModeClose();
811 string const save_selection = grabAndEraseSelection(cur);
813 //cur.plainInsert(MathAtom(new MathMBoxInset(cur.bv())));
814 cur.plainInsert(MathAtom(new MathBoxInset("mbox")));
816 cur.pushLeft(*cur.nextInset());
817 cur.niceInsert(save_selection);
819 if (currentMode() == InsetBase::TEXT_MODE) {
820 cur.niceInsert(MathAtom(new MathHullInset("simple")));
821 cur.message(_("create new math text environment ($...$)"));
823 handleFont(cur, lyx::to_utf8(cmd.argument()), "textrm");
824 cur.message(_("entered math text mode (textrm)"));
837 case LFUN_MATH_MATRIX: {
838 recordUndo(cur, Undo::ATOMIC);
843 istringstream is(lyx::to_utf8(cmd.argument()));
844 is >> m >> n >> v_align >> h_align;
851 MathAtom(new MathArrayInset("array", m, n, v_align[0], h_align)));
855 case LFUN_MATH_DELIM: {
857 string rs = lyx::support::split(lyx::to_utf8(cmd.argument()), ls, ' ');
858 // Reasonable default values
863 recordUndo(cur, Undo::ATOMIC);
864 cur.handleNest(MathAtom(new MathDelimInset(ls, rs)));
868 case LFUN_MATH_BIGDELIM: {
869 string const lname = cmd.getArg(0);
870 string const ldelim = cmd.getArg(1);
871 string const rname = cmd.getArg(2);
872 string const rdelim = cmd.getArg(3);
873 latexkeys const * l = in_word_set(lname);
874 bool const have_l = l && l->inset == "big" &&
875 MathBigInset::isBigInsetDelim(ldelim);
876 l = in_word_set(rname);
877 bool const have_r = l && l->inset == "big" &&
878 MathBigInset::isBigInsetDelim(rdelim);
879 // We mimic LFUN_MATH_DELIM in case we have an empty left
880 // or right delimiter.
881 if (have_l || have_r) {
882 recordUndo(cur, Undo::ATOMIC);
883 string const selection = grabAndEraseSelection(cur);
886 cur.insert(MathAtom(new MathBigInset(lname,
888 cur.niceInsert(selection);
890 cur.insert(MathAtom(new MathBigInset(rname,
893 // Don't call cur.undispatched() if we did nothing, this would
894 // lead to infinite recursion via LyXText::dispatch().
898 case LFUN_SPACE_INSERT:
899 case LFUN_MATH_SPACE:
900 recordUndo(cur, Undo::ATOMIC);
901 cur.insert(MathAtom(new MathSpaceInset(",")));
904 case LFUN_ERT_INSERT:
905 // interpret this as if a backslash was typed
906 recordUndo(cur, Undo::ATOMIC);
907 interpret(cur, '\\');
910 case LFUN_MATH_SUBSCRIPT:
911 // interpret this as if a _ was typed
912 recordUndo(cur, Undo::ATOMIC);
916 case LFUN_MATH_SUPERSCRIPT:
917 // interpret this as if a ^ was typed
918 recordUndo(cur, Undo::ATOMIC);
922 // FIXME: We probably should swap parts of "math-insert" and "self-insert"
923 // handling such that "self-insert" works on "arbitrary stuff" too, and
924 // math-insert only handles special math things like "matrix".
925 case LFUN_MATH_INSERT: {
926 recordUndo(cur, Undo::ATOMIC);
927 if (cmd.argument() == "^" || cmd.argument() == "_")
928 interpret(cur, cmd.argument()[0]);
930 cur.niceInsert(lyx::to_utf8(cmd.argument()));
934 case LFUN_DIALOG_SHOW_NEW_INSET: {
935 string const & name = lyx::to_utf8(cmd.argument());
939 data = tmp.createDialogStr(name);
941 cur.bv().owner()->getDialogs().show(name, data, 0);
946 MathDimInset::doDispatch(cur, cmd);
952 bool MathNestInset::getStatus(LCursor & cur, FuncRequest const & cmd,
953 FuncStatus & flag) const
955 // the font related toggles
956 //string tc = "mathnormal";
958 string const arg = lyx::to_utf8(cmd.argument());
959 switch (cmd.action) {
960 case LFUN_TABULAR_FEATURE:
964 case LFUN_TABULAR_FEATURE:
965 // FIXME: check temporarily disabled
967 char align = mathcursor::valign();
972 if (cmd.argument().empty()) {
976 if (!contains("tcb", cmd.argument()[0])) {
980 flag.setOnOff(cmd.argument()[0] == align);
983 /// We have to handle them since 1.4 blocks all unhandled actions
990 case LFUN_FONT_ROMAN:
991 case LFUN_FONT_DEFAULT:
994 case LFUN_MATH_MUTATE:
995 //flag.setOnOff(mathcursor::formula()->hullType() == lyx::to_utf8(cmd.argument()));
996 flag.setOnOff(false);
999 // we just need to be in math mode to enable that
1000 case LFUN_MATH_SIZE:
1001 case LFUN_MATH_SPACE:
1002 case LFUN_MATH_LIMITS:
1003 case LFUN_MATH_NONUMBER:
1004 case LFUN_MATH_NUMBER:
1005 case LFUN_MATH_EXTERN:
1009 case LFUN_FONT_FRAK:
1010 flag.enabled(currentMode() != TEXT_MODE);
1013 case LFUN_MATH_INSERT: {
1014 bool const textarg =
1015 arg == "\\textbf" || arg == "\\textsf" ||
1016 arg == "\\textrm" || arg == "\\textmd" ||
1017 arg == "\\textit" || arg == "\\textsc" ||
1018 arg == "\\textsl" || arg == "\\textup" ||
1019 arg == "\\texttt" || arg == "\\textbb" ||
1020 arg == "\\textnormal";
1021 flag.enabled(currentMode() != TEXT_MODE || textarg);
1025 case LFUN_MATH_MATRIX:
1026 flag.enabled(currentMode() == MATH_MODE);
1029 case LFUN_MATH_DELIM:
1030 case LFUN_MATH_BIGDELIM:
1031 // Don't do this with multi-cell selections
1032 flag.enabled(cur.selBegin().idx() == cur.selEnd().idx());
1043 void MathNestInset::edit(LCursor & cur, bool left)
1046 cur.idx() = left ? 0 : cur.lastidx();
1047 cur.pos() = left ? 0 : cur.lastpos();
1049 //lyxerr << "MathNestInset::edit, cur:\n" << cur << endl;
1053 InsetBase * MathNestInset::editXY(LCursor & cur, int x, int y)
1056 int dist_min = 1000000;
1057 for (idx_type i = 0, n = nargs(); i != n; ++i) {
1058 int const d = cell(i).dist(x, y);
1064 MathArray & ar = cell(idx_min);
1066 cur.idx() = idx_min;
1067 cur.pos() = ar.x2pos(x - ar.xo());
1068 //lyxerr << "found cell : " << idx_min << " pos: " << cur.pos() << endl;
1069 if (dist_min == 0) {
1071 for (pos_type i = 0, n = ar.size(); i < n; ++i)
1072 if (ar[i]->covers(x, y))
1073 return ar[i].nucleus()->editXY(cur, x, y);
1079 void MathNestInset::lfunMousePress(LCursor & cur, FuncRequest & cmd)
1081 //lyxerr << "## lfunMousePress: buttons: " << cmd.button() << endl;
1082 BufferView & bv = cur.bv();
1083 if (cmd.button() == mouse_button::button1) {
1084 //lyxerr << "## lfunMousePress: setting cursor to: " << cur << endl;
1085 bv.mouseSetCursor(cur);
1086 } else if (cmd.button() == mouse_button::button2) {
1088 if (cur.selection())
1089 asArray(lyx::to_utf8(bv.cursor().selectionAsString(false)), ar);
1091 asArray(lyx::to_utf8(bv.owner()->gui().selection().get()), ar);
1094 bv.mouseSetCursor(cur);
1099 void MathNestInset::lfunMouseMotion(LCursor & cur, FuncRequest & cmd)
1101 // only select with button 1
1102 if (cmd.button() == mouse_button::button1) {
1103 LCursor & bvcur = cur.bv().cursor();
1104 if (bvcur.anchor_.hasPart(cur)) {
1105 //lyxerr << "## lfunMouseMotion: cursor: " << cur << endl;
1106 bvcur.setCursor(cur);
1107 bvcur.selection() = true;
1108 //lyxerr << "MOTION " << bvcur << endl;
1117 void MathNestInset::lfunMouseRelease(LCursor & cur, FuncRequest & cmd)
1119 //lyxerr << "## lfunMouseRelease: buttons: " << cmd.button() << endl;
1121 if (cmd.button() == mouse_button::button1) {
1122 //cur.bv().owner()->gui().selection().put(cur.grabSelection());
1126 if (cmd.button() == mouse_button::button3) {
1127 // try to dispatch to enclosed insets first
1128 cur.bv().owner()->getDialogs().show("mathpanel");
1136 bool MathNestInset::interpret(LCursor & cur, char c)
1138 //lyxerr << "interpret 2: '" << c << "'" << endl;
1139 string save_selection;
1140 if (c == '^' || c == '_')
1141 save_selection = grabAndEraseSelection(cur);
1146 if (cur.inMacroMode()) {
1147 string name = cur.macroName();
1149 /// are we currently typing '#1' or '#2' or...?
1150 if (name == "\\#") {
1153 if (n >= 1 && n <= 9)
1154 cur.insert(new MathMacroArgument(n));
1159 cur.activeMacro()->setName(name + c);
1163 // handle 'special char' macros
1168 if (currentMode() == MathInset::TEXT_MODE)
1169 cur.niceInsert(createMathInset("textbackslash"));
1171 cur.niceInsert(createMathInset("backslash"));
1172 } else if (c == '{') {
1174 cur.niceInsert(MathAtom(new MathBraceInset));
1175 } else if (c == '%') {
1177 cur.niceInsert(MathAtom(new MathCommentInset));
1178 } else if (c == '#') {
1179 BOOST_ASSERT(cur.activeMacro());
1180 cur.activeMacro()->setName(name + c);
1183 cur.niceInsert(createMathInset(string(1, c)));
1188 // One character big delimiters. The others are handled in
1189 // the other interpret() method.
1190 latexkeys const * l = in_word_set(name.substr(1));
1191 if (name[0] == '\\' && l && l->inset == "big") {
1201 delim = string(1, c);
1204 if (MathBigInset::isBigInsetDelim(delim)) {
1205 // name + delim ared a valid MathBigInset.
1206 // We can't use cur.macroModeClose() because
1207 // it does not handle delim.
1208 MathUnknownInset * p = cur.activeMacro();
1211 cur.cell().erase(cur.pos());
1212 cur.plainInsert(MathAtom(
1213 new MathBigInset(name.substr(1), delim)));
1218 // leave macro mode and try again if necessary
1219 cur.macroModeClose();
1221 cur.niceInsert(MathAtom(new MathBraceInset));
1227 // This is annoying as one has to press <space> far too often.
1231 // leave autocorrect mode if necessary
1232 if (autocorrect() && c == ' ') {
1233 autocorrect() = false;
1238 // just clear selection on pressing the space bar
1239 if (cur.selection() && c == ' ') {
1240 cur.selection() = false;
1247 //lyxerr << "starting with macro" << endl;
1248 cur.insert(MathAtom(new MathUnknownInset("\\", false)));
1253 if (currentMode() == MathInset::TEXT_MODE)
1259 if (currentMode() == MathInset::TEXT_MODE) {
1260 // insert spaces in text mode,
1261 // but suppress direct insertion of two spaces in a row
1262 // the still allows typing '<space>a<space>' and deleting the 'a', but
1263 // it is better than nothing...
1264 if (!cur.pos() != 0 || cur.prevAtom()->getChar() != ' ')
1268 if (cur.pos() != 0 && cur.prevAtom()->asSpaceInset()) {
1269 cur.prevAtom().nucleus()->asSpaceInset()->incSpace();
1274 // if are at the very end, leave the formula
1275 return cur.pos() != cur.lastpos();
1278 // These shouldn't work in text mode:
1279 if (currentMode() != MathInset::TEXT_MODE) {
1281 script(cur, false, save_selection);
1285 script(cur, true, save_selection);
1289 cur.niceInsert(createMathInset("sim"));
1294 if (c == '{' || c == '}' || c == '&' || c == '$' || c == '#' ||
1295 c == '%' || c == '_' || c == '^') {
1296 cur.niceInsert(createMathInset(string(1, c)));
1301 // try auto-correction
1302 //if (autocorrect() && hasPrevAtom() && math_autocorrect(prevAtom(), c))
1305 // no special circumstances, so insert the character without any fuss
1307 cur.autocorrect() = true;
1312 bool MathNestInset::interpret(LCursor & cur, string const & str)
1314 // Create a MathBigInset from cur.cell()[cur.pos() - 1] and t if
1316 if (!cur.empty() && cur.pos() > 0 &&
1317 cur.cell()[cur.pos() - 1]->asUnknownInset()) {
1318 if (MathBigInset::isBigInsetDelim(str)) {
1319 string prev = asString(cur.cell()[cur.pos() - 1]);
1320 if (prev[0] == '\\') {
1321 prev = prev.substr(1);
1322 latexkeys const * l = in_word_set(prev);
1323 if (l && l->inset == "big") {
1324 cur.cell()[cur.pos() - 1] =
1325 MathAtom(new MathBigInset(prev, str));
1335 bool MathNestInset::script(LCursor & cur, bool up, string const &
1338 // Hack to get \^ and \_ working
1339 //lyxerr << "handling script: up: " << up << endl;
1340 if (cur.inMacroMode() && cur.macroName() == "\\") {
1342 cur.niceInsert(createMathInset("mathcircumflex"));
1344 interpret(cur, '_');
1348 cur.macroModeClose();
1349 if (asScriptInset() && cur.idx() == 0) {
1350 // we are in a nucleus of a script inset, move to _our_ script
1351 MathScriptInset * inset = asScriptInset();
1352 //lyxerr << " going to cell " << inset->idxOfScript(up) << endl;
1354 cur.idx() = inset->idxOfScript(up);
1356 } else if (cur.pos() != 0 && cur.prevAtom()->asScriptInset()) {
1358 MathScriptInset * inset = cur.nextAtom().nucleus()->asScriptInset();
1361 cur.idx() = inset->idxOfScript(up);
1362 cur.pos() = cur.lastpos();
1364 // convert the thing to our left to a scriptinset or create a new
1365 // one if in the very first position of the array
1366 if (cur.pos() == 0) {
1367 //lyxerr << "new scriptinset" << endl;
1368 cur.insert(new MathScriptInset(up));
1370 //lyxerr << "converting prev atom " << endl;
1371 cur.prevAtom() = MathAtom(new MathScriptInset(cur.prevAtom(), up));
1374 MathScriptInset * inset = cur.nextAtom().nucleus()->asScriptInset();
1375 // special handling of {}-bases
1376 // is this always correct?
1377 if (inset->nuc().size() == 1
1378 && inset->nuc().back()->asBraceInset())
1379 inset->nuc() = inset->nuc().back()->asNestInset()->cell(0);
1385 //lyxerr << "inserting selection 1:\n" << save_selection << endl;
1386 cur.niceInsert(save_selection);
1388 //lyxerr << "inserting selection 2:\n" << save_selection << endl;