2 * \file InsetMathNest.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 "InsetMathNest.h"
15 #include "InsetMathArray.h"
16 #include "InsetMathBig.h"
17 #include "InsetMathBox.h"
18 #include "InsetMathBrace.h"
19 #include "InsetMathColor.h"
20 #include "InsetMathComment.h"
22 #include "InsetMathDelim.h"
23 #include "MathFactory.h"
24 #include "InsetMathHull.h"
25 #include "MathMLStream.h"
26 #include "MathMacroArgument.h"
27 //#include "InsetMathMBox.h"
28 #include "MathParser.h"
29 #include "InsetMathScript.h"
30 #include "InsetMathSpace.h"
31 #include "InsetMathSymbol.h"
32 #include "MathSupport.h"
33 #include "InsetMathUnknown.h"
34 #include "InsetMathRef.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/Application.h"
53 #include "frontends/Painter.h"
54 #include "frontends/Selection.h"
55 #include "frontends/nullpainter.h"
59 using lyx::cap::copySelection;
60 using lyx::cap::grabAndEraseSelection;
61 using lyx::cap::cutSelection;
62 using lyx::cap::replaceSelection;
63 using lyx::cap::selClearOrDel;
65 using lyx::frontend::Clipboard;
69 using std::istringstream;
72 InsetMathNest::InsetMathNest(idx_type nargs)
73 : cells_(nargs), lock_(false)
77 InsetMath::idx_type InsetMathNest::nargs() const
83 MathArray & InsetMathNest::cell(idx_type i)
89 MathArray const & InsetMathNest::cell(idx_type i) const
95 void InsetMathNest::cursorPos(CursorSlice const & sl, bool /*boundary*/,
96 int & x, int & y) const
98 // FIXME: This is a hack. Ideally, the coord cache should not store
99 // absolute positions, but relative ones. This would mean to call
100 // setXY() not in MathArray::draw(), but in the parent insets' draw()
101 // with the correctly adjusted x,y values. But this means that we'd have
102 // to touch all (math)inset's draw() methods. Right now, we'll store
103 // absolute value, and make them here relative, only to make them
104 // absolute again when actually drawing the cursor. What a mess.
105 BOOST_ASSERT(ptr_cmp(&sl.inset(), this));
106 MathArray const & ar = sl.cell();
107 if (!theCoords.getArrays().has(&ar)) {
108 // this can (semi-)legally happen if we just created this cell
109 // and it never has been drawn before. So don't ASSERT.
110 //lyxerr << "no cached data for array " << &ar << endl;
115 Point const pt = theCoords.getArrays().xy(&ar);
116 if (!theCoords.getInsets().has(this)) {
118 //lyxerr << "no cached data for inset " << this << endl;
123 Point const pt2 = theCoords.getInsets().xy(this);
124 //lyxerr << "retrieving position cache for MathArray "
125 // << pt.x_ << ' ' << pt.y_ << std::endl;
126 x = pt.x_ - pt2.x_ + ar.pos2x(sl.pos());
128 // lyxerr << "pt.y_ : " << pt.y_ << " pt2_.y_ : " << pt2.y_
129 // << " asc: " << ascent() << " des: " << descent()
130 // << " ar.asc: " << ar.ascent() << " ar.des: " << ar.descent() << endl;
131 // move cursor visually into empty cells ("blue rectangles");
137 void InsetMathNest::metrics(MetricsInfo const & mi) const
140 for (idx_type i = 0, n = nargs(); i != n; ++i)
145 bool InsetMathNest::idxNext(LCursor & cur) const
147 BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
148 if (cur.idx() == cur.lastidx())
156 bool InsetMathNest::idxRight(LCursor & cur) const
162 bool InsetMathNest::idxPrev(LCursor & cur) const
164 BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
168 cur.pos() = cur.lastpos();
173 bool InsetMathNest::idxLeft(LCursor & cur) const
179 bool InsetMathNest::idxFirst(LCursor & cur) const
181 BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
190 bool InsetMathNest::idxLast(LCursor & cur) const
192 BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
195 cur.idx() = cur.lastidx();
196 cur.pos() = cur.lastpos();
201 void InsetMathNest::dump() const
203 WriteStream os(lyxerr);
204 os << "---------------------------------------------\n";
207 for (idx_type i = 0, n = nargs(); i != n; ++i)
208 os << cell(i) << "\n";
209 os << "---------------------------------------------\n";
213 void InsetMathNest::draw(PainterInfo & pi, int x, int y) const
217 pi.pain.fillRectangle(x, y - ascent(), width(), height(),
220 setPosCache(pi, x, y);
224 void InsetMathNest::drawSelection(PainterInfo & pi, int x, int y) const
226 // this should use the x/y values given, not the cached values
227 LCursor & cur = pi.base.bv->cursor();
228 if (!cur.selection())
230 if (!ptr_cmp(&cur.inset(), this))
233 // FIXME: hack to get position cache warm
234 static lyx::frontend::NullPainter nop;
235 PainterInfo pinop(pi);
239 CursorSlice s1 = cur.selBegin();
240 CursorSlice s2 = cur.selEnd();
241 //lyxerr << "InsetMathNest::drawing selection: "
242 // << " s1: " << s1 << " s2: " << s2 << endl;
243 if (s1.idx() == s2.idx()) {
244 MathArray const & c = cell(s1.idx());
245 int x1 = c.xo() + c.pos2x(s1.pos());
246 int y1 = c.yo() - c.ascent();
247 int x2 = c.xo() + c.pos2x(s2.pos());
248 int y2 = c.yo() + c.descent();
249 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
250 //lyxerr << "InsetMathNest::drawing selection 3: "
251 // << " x1: " << x1 << " x2: " << x2
252 // << " y1: " << y1 << " y2: " << y2 << endl;
254 for (idx_type i = 0; i < nargs(); ++i) {
255 if (idxBetween(i, s1.idx(), s2.idx())) {
256 MathArray const & c = cell(i);
258 int y1 = c.yo() - c.ascent();
259 int x2 = c.xo() + c.width();
260 int y2 = c.yo() + c.descent();
261 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
268 void InsetMathNest::validate(LaTeXFeatures & features) const
270 for (idx_type i = 0; i < nargs(); ++i)
271 cell(i).validate(features);
275 void InsetMathNest::replace(ReplaceData & rep)
277 for (idx_type i = 0; i < nargs(); ++i)
278 cell(i).replace(rep);
282 bool InsetMathNest::contains(MathArray const & ar) const
284 for (idx_type i = 0; i < nargs(); ++i)
285 if (cell(i).contains(ar))
291 bool InsetMathNest::lock() const
297 void InsetMathNest::lock(bool l)
303 bool InsetMathNest::isActive() const
309 MathArray InsetMathNest::glue() const
312 for (size_t i = 0; i < nargs(); ++i)
318 void InsetMathNest::write(WriteStream & os) const
320 os << '\\' << name().c_str();
321 for (size_t i = 0; i < nargs(); ++i)
322 os << '{' << cell(i) << '}';
324 os.pendingSpace(true);
325 if (lock_ && !os.latex()) {
327 os.pendingSpace(true);
332 void InsetMathNest::normalize(NormalStream & os) const
334 os << '[' << name().c_str();
335 for (size_t i = 0; i < nargs(); ++i)
336 os << ' ' << cell(i);
341 int InsetMathNest::latex(Buffer const &, std::ostream & os,
342 OutputParams const & runparams) const
344 WriteStream wi(os, runparams.moving_arg, true);
350 bool InsetMathNest::notifyCursorLeaves(LCursor & /*cur*/)
356 MathArray & ar = cur.cell();
357 // remove base-only "scripts"
358 for (pos_type i = 0; i + 1 < ar.size(); ++i) {
359 InsetMathScript * p = operator[](i).nucleus()->asScriptInset();
360 if (p && p->nargs() == 1) {
361 MathArray ar = p->nuc();
364 cur.adjust(i, ar.size() - 1);
368 // glue adjacent font insets of the same kind
369 for (pos_type i = 0; i + 1 < size(); ++i) {
370 InsetMathFont * p = operator[](i).nucleus()->asFontInset();
371 InsetMathFont const * q = operator[](i + 1)->asFontInset();
372 if (p && q && p->name() == q->name()) {
373 p->cell(0).append(q->cell(0));
383 void InsetMathNest::handleFont
384 (LCursor & cur, string const & arg, string const & font)
386 // this whole function is a hack and won't work for incremental font
388 recordUndo(cur, Undo::ATOMIC);
390 if (cur.inset().asInsetMath()->name() == font)
391 cur.handleFont(font);
393 cur.handleNest(createInsetMath(font));
399 void InsetMathNest::handleFont2(LCursor & cur, string const & arg)
401 recordUndo(cur, Undo::ATOMIC);
404 bv_funcs::string2font(arg, font, b);
405 if (font.color() != LColor::inherit) {
406 MathAtom at = MathAtom(new InsetMathColor(true, font.color()));
407 cur.handleNest(at, 0);
412 void InsetMathNest::doDispatch(LCursor & cur, FuncRequest & cmd)
414 //lyxerr << "InsetMathNest: request: " << cmd << std::endl;
415 //CursorSlice sl = cur.current();
417 switch (cmd.action) {
421 cur.message(_("Paste"));
422 replaceSelection(cur);
424 istringstream is(lyx::to_utf8(cmd.argument()));
426 string const selection = lyx::cap::getSelection(cur.buffer(), n);
427 cur.niceInsert(selection);
428 cur.clearSelection(); // bug 393
429 cur.bv().switchKeyMap();
436 cutSelection(cur, true, true);
437 cur.message(_("Cut"));
438 // Prevent stale position >= size crash
439 // Probably not necessary anymore, see eraseSelection (gb 2005-10-09)
446 cur.message(_("Copy"));
449 case LFUN_MOUSE_PRESS:
450 lfunMousePress(cur, cmd);
453 case LFUN_MOUSE_MOTION:
454 lfunMouseMotion(cur, cmd);
457 case LFUN_MOUSE_RELEASE:
458 lfunMouseRelease(cur, cmd);
461 case LFUN_FINISHED_LEFT:
462 cur.bv().cursor() = cur;
465 case LFUN_FINISHED_RIGHT:
467 cur.bv().cursor() = cur;
470 case LFUN_FINISHED_UP:
471 cur.bv().cursor() = cur;
474 case LFUN_FINISHED_DOWN:
476 cur.bv().cursor() = cur;
479 case LFUN_CHAR_FORWARD_SELECT:
480 case LFUN_CHAR_FORWARD:
481 cur.selHandle(cmd.action == LFUN_CHAR_FORWARD_SELECT);
482 cur.autocorrect() = false;
484 cur.macroModeClose();
485 if (cur.pos() != cur.lastpos() && cur.openable(cur.nextAtom())) {
486 cur.pushLeft(*cur.nextAtom().nucleus());
487 cur.inset().idxFirst(cur);
488 } else if (cur.posRight() || idxRight(cur)
489 || cur.popRight() || cur.selection())
492 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
497 case LFUN_CHAR_BACKWARD_SELECT:
498 case LFUN_CHAR_BACKWARD:
499 cur.selHandle(cmd.action == LFUN_CHAR_BACKWARD_SELECT);
500 cur.autocorrect() = false;
502 cur.macroModeClose();
503 if (cur.pos() != 0 && cur.openable(cur.prevAtom())) {
505 cur.push(*cur.nextAtom().nucleus());
506 cur.inset().idxLast(cur);
507 } else if (cur.posLeft() || idxLeft(cur)
508 || cur.popLeft() || cur.selection())
511 cmd = FuncRequest(LFUN_FINISHED_LEFT);
518 // FIXME Tried to use clearTargetX and macroModeClose, crashed on cur.up()
519 if (cur.inMacroMode()) {
521 cur.macroModeClose();
524 cur.selHandle(cmd.action == LFUN_UP_SELECT);
526 cmd = FuncRequest(LFUN_FINISHED_UP);
529 // fixes bug 1598. Please check!
533 case LFUN_DOWN_SELECT:
535 if (cur.inMacroMode()) {
536 cur.macroModeClose();
539 cur.selHandle(cmd.action == LFUN_DOWN_SELECT);
541 cmd = FuncRequest(LFUN_FINISHED_DOWN);
544 // fixes bug 1598. Please check!
548 case LFUN_MOUSE_DOUBLE:
549 case LFUN_MOUSE_TRIPLE:
550 case LFUN_WORD_SELECT:
554 cur.selection() = true;
555 cur.pos() = cur.lastpos();
556 cur.idx() = cur.lastidx();
559 case LFUN_PARAGRAPH_UP_SELECT:
560 case LFUN_PARAGRAPH_UP:
561 case LFUN_PARAGRAPH_DOWN_SELECT:
562 case LFUN_PARAGRAPH_DOWN:
565 case LFUN_LINE_BEGIN_SELECT:
566 case LFUN_LINE_BEGIN:
567 case LFUN_WORD_BACKWARD_SELECT:
568 case LFUN_WORD_BACKWARD:
569 cur.selHandle(cmd.action == LFUN_WORD_BACKWARD_SELECT ||
570 cmd.action == LFUN_LINE_BEGIN_SELECT);
571 cur.macroModeClose();
572 if (cur.pos() != 0) {
574 } else if (cur.col() != 0) {
575 cur.idx() -= cur.col();
577 } else if (cur.idx() != 0) {
581 cmd = FuncRequest(LFUN_FINISHED_LEFT);
586 case LFUN_WORD_FORWARD_SELECT:
587 case LFUN_WORD_FORWARD:
588 case LFUN_LINE_END_SELECT:
590 cur.selHandle(cmd.action == LFUN_WORD_FORWARD_SELECT ||
591 cmd.action == LFUN_LINE_END_SELECT);
592 cur.macroModeClose();
594 if (cur.pos() != cur.lastpos()) {
595 cur.pos() = cur.lastpos();
596 } else if (ncols() && (cur.col() != cur.lastcol())) {
597 cur.idx() = cur.idx() - cur.col() + cur.lastcol();
598 cur.pos() = cur.lastpos();
599 } else if (cur.idx() != cur.lastidx()) {
600 cur.idx() = cur.lastidx();
601 cur.pos() = cur.lastpos();
603 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
608 case LFUN_SCREEN_UP_SELECT:
610 cmd = FuncRequest(LFUN_FINISHED_LEFT);
614 case LFUN_SCREEN_DOWN_SELECT:
615 case LFUN_SCREEN_DOWN:
616 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
620 case LFUN_CELL_FORWARD:
621 cur.inset().idxNext(cur);
624 case LFUN_CELL_BACKWARD:
625 cur.inset().idxPrev(cur);
628 case LFUN_WORD_DELETE_BACKWARD:
629 case LFUN_CHAR_DELETE_BACKWARD:
631 // May affect external cell:
632 recordUndoInset(cur, Undo::ATOMIC);
634 recordUndo(cur, Undo::ATOMIC);
638 case LFUN_WORD_DELETE_FORWARD:
639 case LFUN_CHAR_DELETE_FORWARD:
640 if (cur.pos() == cur.lastpos())
641 // May affect external cell:
642 recordUndoInset(cur, Undo::ATOMIC);
644 recordUndo(cur, Undo::ATOMIC);
650 cur.clearSelection();
652 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
657 case LFUN_INSET_TOGGLE:
663 case LFUN_SELF_INSERT:
664 if (cmd.argument().size() != 1) {
666 string const arg = lyx::to_utf8(cmd.argument());
667 if (!interpret(cur, arg))
671 // Don't record undo steps if we are in macro mode and
672 // cmd.argument is the next character of the macro name.
673 // Otherwise we'll get an invalid cursor if we undo after
674 // the macro was finished and the macro is a known command,
675 // e.g. sqrt. LCursor::macroModeClose replaces in this case
676 // the InsetMathUnknown with name "frac" by an empty
677 // InsetMathFrac -> a pos value > 0 is invalid.
678 // A side effect is that an undo before the macro is finished
679 // undoes the complete macro, not only the last character.
680 if (!cur.inMacroMode())
683 // spacial handling of space. If we insert an inset
684 // via macro mode, we want to put the cursor inside it
685 // if relevant. Think typing "\frac<space>".
686 if (cmd.argument()[0] == ' '
687 && cur.inMacroMode() && cur.macroName() != "\\"
688 && cur.macroModeClose()) {
689 MathAtom const atom = cur.prevAtom();
690 if (atom->asNestInset() && atom->nargs() > 0) {
692 cur.pushLeft(*cur.nextInset());
695 // } else if (!interpret(cur, cmd.argument()[0])) {
696 // when interpret accepts UCS4 characters
697 } else if (!interpret(cur, lyx::to_utf8(cmd.argument()))) {
698 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
703 //case LFUN_SERVER_GET_XY:
704 // sprintf(dispatch_buffer, "%d %d",);
707 case LFUN_SERVER_SET_XY: {
708 lyxerr << "LFUN_SERVER_SET_XY broken!" << endl;
711 istringstream is(lyx::to_utf8(cmd.argument()));
713 cur.setScreenPos(x, y);
717 // Special casing for superscript in case of LyX handling
719 case LFUN_ACCENT_CIRCUMFLEX:
720 if (cmd.argument().empty()) {
721 // do superscript if LyX handles
723 recordUndo(cur, Undo::ATOMIC);
724 script(cur, true, grabAndEraseSelection(cur));
728 case LFUN_ACCENT_UMLAUT:
729 case LFUN_ACCENT_ACUTE:
730 case LFUN_ACCENT_GRAVE:
731 case LFUN_ACCENT_BREVE:
732 case LFUN_ACCENT_DOT:
733 case LFUN_ACCENT_MACRON:
734 case LFUN_ACCENT_CARON:
735 case LFUN_ACCENT_TILDE:
736 case LFUN_ACCENT_CEDILLA:
737 case LFUN_ACCENT_CIRCLE:
738 case LFUN_ACCENT_UNDERDOT:
739 case LFUN_ACCENT_TIE:
740 case LFUN_ACCENT_OGONEK:
741 case LFUN_ACCENT_HUNGARIAN_UMLAUT:
745 case LFUN_FONT_FREE_APPLY:
746 case LFUN_FONT_FREE_UPDATE:
747 handleFont2(cur, lyx::to_utf8(cmd.argument()));
751 if (currentMode() == TEXT_MODE)
752 handleFont(cur, lyx::to_utf8(cmd.argument()), "textbf");
754 handleFont(cur, lyx::to_utf8(cmd.argument()), "mathbf");
757 if (currentMode() == TEXT_MODE)
758 handleFont(cur, lyx::to_utf8(cmd.argument()), "textsf");
760 handleFont(cur, lyx::to_utf8(cmd.argument()), "mathsf");
763 if (currentMode() == TEXT_MODE)
764 handleFont(cur, lyx::to_utf8(cmd.argument()), "emph");
766 handleFont(cur, lyx::to_utf8(cmd.argument()), "mathcal");
768 case LFUN_FONT_ROMAN:
769 if (currentMode() == TEXT_MODE)
770 handleFont(cur, lyx::to_utf8(cmd.argument()), "textrm");
772 handleFont(cur, lyx::to_utf8(cmd.argument()), "mathrm");
775 if (currentMode() == TEXT_MODE)
776 handleFont(cur, lyx::to_utf8(cmd.argument()), "texttt");
778 handleFont(cur, lyx::to_utf8(cmd.argument()), "mathtt");
781 handleFont(cur, lyx::to_utf8(cmd.argument()), "mathfrak");
784 if (currentMode() == TEXT_MODE)
785 handleFont(cur, lyx::to_utf8(cmd.argument()), "textit");
787 handleFont(cur, lyx::to_utf8(cmd.argument()), "mathit");
790 if (currentMode() == TEXT_MODE)
791 // FIXME: should be "noun"
792 handleFont(cur, lyx::to_utf8(cmd.argument()), "textsc");
794 handleFont(cur, lyx::to_utf8(cmd.argument()), "mathbb");
796 //case LFUN_FONT_FREE_APPLY:
797 handleFont(cur, lyx::to_utf8(cmd.argument()), "textrm");
799 case LFUN_FONT_DEFAULT:
800 handleFont(cur, lyx::to_utf8(cmd.argument()), "textnormal");
803 case LFUN_MATH_MODE: {
805 // ignore math-mode on when already in math mode
806 if (currentMode() == InsetBase::MATH_MODE && cmd.argument() == "on")
808 cur.macroModeClose();
809 string const save_selection = grabAndEraseSelection(cur);
811 //cur.plainInsert(MathAtom(new InsetMathMBox(cur.bv())));
812 cur.plainInsert(MathAtom(new InsetMathBox("mbox")));
814 cur.pushLeft(*cur.nextInset());
815 cur.niceInsert(save_selection);
817 if (currentMode() == InsetBase::TEXT_MODE) {
818 cur.niceInsert(MathAtom(new InsetMathHull("simple")));
819 cur.message(_("create new math text environment ($...$)"));
821 handleFont(cur, lyx::to_utf8(cmd.argument()), "textrm");
822 cur.message(_("entered math text mode (textrm)"));
835 case LFUN_MATH_MATRIX: {
836 recordUndo(cur, Undo::ATOMIC);
841 istringstream is(lyx::to_utf8(cmd.argument()));
842 is >> m >> n >> v_align >> h_align;
849 MathAtom(new InsetMathArray("array", m, n, v_align[0], h_align)));
853 case LFUN_MATH_DELIM: {
855 string rs = lyx::support::split(lyx::to_utf8(cmd.argument()), ls, ' ');
856 // Reasonable default values
861 recordUndo(cur, Undo::ATOMIC);
862 cur.handleNest(MathAtom(new InsetMathDelim(ls, rs)));
866 case LFUN_MATH_BIGDELIM: {
867 string const lname = cmd.getArg(0);
868 string const ldelim = cmd.getArg(1);
869 string const rname = cmd.getArg(2);
870 string const rdelim = cmd.getArg(3);
871 latexkeys const * l = in_word_set(lname);
872 bool const have_l = l && l->inset == "big" &&
873 InsetMathBig::isBigInsetDelim(ldelim);
874 l = in_word_set(rname);
875 bool const have_r = l && l->inset == "big" &&
876 InsetMathBig::isBigInsetDelim(rdelim);
877 // We mimic LFUN_MATH_DELIM in case we have an empty left
878 // or right delimiter.
879 if (have_l || have_r) {
880 recordUndo(cur, Undo::ATOMIC);
881 string const selection = grabAndEraseSelection(cur);
884 cur.insert(MathAtom(new InsetMathBig(lname,
886 cur.niceInsert(selection);
888 cur.insert(MathAtom(new InsetMathBig(rname,
891 // Don't call cur.undispatched() if we did nothing, this would
892 // lead to infinite recursion via LyXText::dispatch().
896 case LFUN_SPACE_INSERT:
897 case LFUN_MATH_SPACE:
898 recordUndo(cur, Undo::ATOMIC);
899 cur.insert(MathAtom(new InsetMathSpace(",")));
902 case LFUN_ERT_INSERT:
903 // interpret this as if a backslash was typed
904 recordUndo(cur, Undo::ATOMIC);
905 interpret(cur, '\\');
908 case LFUN_MATH_SUBSCRIPT:
909 // interpret this as if a _ was typed
910 recordUndo(cur, Undo::ATOMIC);
914 case LFUN_MATH_SUPERSCRIPT:
915 // interpret this as if a ^ was typed
916 recordUndo(cur, Undo::ATOMIC);
920 // FIXME: We probably should swap parts of "math-insert" and "self-insert"
921 // handling such that "self-insert" works on "arbitrary stuff" too, and
922 // math-insert only handles special math things like "matrix".
923 case LFUN_MATH_INSERT: {
924 recordUndo(cur, Undo::ATOMIC);
925 if (cmd.argument() == "^" || cmd.argument() == "_")
926 interpret(cur, cmd.argument()[0]);
928 cur.niceInsert(lyx::to_utf8(cmd.argument()));
932 case LFUN_DIALOG_SHOW_NEW_INSET: {
933 string const & name = lyx::to_utf8(cmd.argument());
937 data = tmp.createDialogStr(name);
939 cur.bv().showInsetDialog(name, data, 0);
944 InsetMathDim::doDispatch(cur, cmd);
950 bool InsetMathNest::getStatus(LCursor & cur, FuncRequest const & cmd,
951 FuncStatus & flag) const
953 // the font related toggles
954 //string tc = "mathnormal";
956 string const arg = lyx::to_utf8(cmd.argument());
957 switch (cmd.action) {
958 case LFUN_TABULAR_FEATURE:
962 case LFUN_TABULAR_FEATURE:
963 // FIXME: check temporarily disabled
965 char align = mathcursor::valign();
970 if (cmd.argument().empty()) {
974 if (!contains("tcb", cmd.argument()[0])) {
978 flag.setOnOff(cmd.argument()[0] == align);
981 /// We have to handle them since 1.4 blocks all unhandled actions
988 case LFUN_FONT_ROMAN:
989 case LFUN_FONT_DEFAULT:
992 case LFUN_MATH_MUTATE:
993 //flag.setOnOff(mathcursor::formula()->hullType() == lyx::to_utf8(cmd.argument()));
994 flag.setOnOff(false);
997 // we just need to be in math mode to enable that
999 case LFUN_MATH_SPACE:
1000 case LFUN_MATH_LIMITS:
1001 case LFUN_MATH_NONUMBER:
1002 case LFUN_MATH_NUMBER:
1003 case LFUN_MATH_EXTERN:
1007 case LFUN_FONT_FRAK:
1008 flag.enabled(currentMode() != TEXT_MODE);
1011 case LFUN_MATH_INSERT: {
1012 bool const textarg =
1013 arg == "\\textbf" || arg == "\\textsf" ||
1014 arg == "\\textrm" || arg == "\\textmd" ||
1015 arg == "\\textit" || arg == "\\textsc" ||
1016 arg == "\\textsl" || arg == "\\textup" ||
1017 arg == "\\texttt" || arg == "\\textbb" ||
1018 arg == "\\textnormal";
1019 flag.enabled(currentMode() != TEXT_MODE || textarg);
1023 case LFUN_MATH_MATRIX:
1024 flag.enabled(currentMode() == MATH_MODE);
1027 case LFUN_MATH_DELIM:
1028 case LFUN_MATH_BIGDELIM:
1029 // Don't do this with multi-cell selections
1030 flag.enabled(cur.selBegin().idx() == cur.selEnd().idx());
1041 void InsetMathNest::edit(LCursor & cur, bool left)
1044 cur.idx() = left ? 0 : cur.lastidx();
1045 cur.pos() = left ? 0 : cur.lastpos();
1047 //lyxerr << "InsetMathNest::edit, cur:\n" << cur << endl;
1051 InsetBase * InsetMathNest::editXY(LCursor & cur, int x, int y)
1054 int dist_min = 1000000;
1055 for (idx_type i = 0, n = nargs(); i != n; ++i) {
1056 int const d = cell(i).dist(x, y);
1062 MathArray & ar = cell(idx_min);
1064 cur.idx() = idx_min;
1065 cur.pos() = ar.x2pos(x - ar.xo());
1066 //lyxerr << "found cell : " << idx_min << " pos: " << cur.pos() << endl;
1067 if (dist_min == 0) {
1069 for (pos_type i = 0, n = ar.size(); i < n; ++i)
1070 if (ar[i]->covers(x, y))
1071 return ar[i].nucleus()->editXY(cur, x, y);
1077 void InsetMathNest::lfunMousePress(LCursor & cur, FuncRequest & cmd)
1079 //lyxerr << "## lfunMousePress: buttons: " << cmd.button() << endl;
1080 BufferView & bv = cur.bv();
1081 if (cmd.button() == mouse_button::button1) {
1082 //lyxerr << "## lfunMousePress: setting cursor to: " << cur << endl;
1083 bv.mouseSetCursor(cur);
1084 } else if (cmd.button() == mouse_button::button2) {
1086 if (cur.selection())
1087 asArray(lyx::to_utf8(bv.cursor().selectionAsString(false)), ar);
1089 asArray(lyx::to_utf8(theApp->selection().get()), ar);
1092 bv.mouseSetCursor(cur);
1097 void InsetMathNest::lfunMouseMotion(LCursor & cur, FuncRequest & cmd)
1099 // only select with button 1
1100 if (cmd.button() == mouse_button::button1) {
1101 LCursor & bvcur = cur.bv().cursor();
1102 if (bvcur.anchor_.hasPart(cur)) {
1103 //lyxerr << "## lfunMouseMotion: cursor: " << cur << endl;
1104 bvcur.setCursor(cur);
1105 bvcur.selection() = true;
1106 //lyxerr << "MOTION " << bvcur << endl;
1115 void InsetMathNest::lfunMouseRelease(LCursor & cur, FuncRequest & cmd)
1117 //lyxerr << "## lfunMouseRelease: buttons: " << cmd.button() << endl;
1119 if (cmd.button() == mouse_button::button1) {
1120 //theApp->selection().put(cur.grabSelection());
1124 if (cmd.button() == mouse_button::button3) {
1125 // try to dispatch to enclosed insets first
1126 cur.bv().showDialog("mathpanel");
1134 bool InsetMathNest::interpret(LCursor & cur, char c)
1136 //lyxerr << "interpret 2: '" << c << "'" << endl;
1137 string save_selection;
1138 if (c == '^' || c == '_')
1139 save_selection = grabAndEraseSelection(cur);
1144 if (cur.inMacroMode()) {
1145 string name = cur.macroName();
1147 /// are we currently typing '#1' or '#2' or...?
1148 if (name == "\\#") {
1151 if (n >= 1 && n <= 9)
1152 cur.insert(new MathMacroArgument(n));
1157 cur.activeMacro()->setName(name + c);
1161 // handle 'special char' macros
1166 if (currentMode() == InsetMath::TEXT_MODE)
1167 cur.niceInsert(createInsetMath("textbackslash"));
1169 cur.niceInsert(createInsetMath("backslash"));
1170 } else if (c == '{') {
1172 cur.niceInsert(MathAtom(new InsetMathBrace));
1173 } else if (c == '%') {
1175 cur.niceInsert(MathAtom(new InsetMathComment));
1176 } else if (c == '#') {
1177 BOOST_ASSERT(cur.activeMacro());
1178 cur.activeMacro()->setName(name + c);
1181 cur.niceInsert(createInsetMath(string(1, c)));
1186 // One character big delimiters. The others are handled in
1187 // the other interpret() method.
1188 latexkeys const * l = in_word_set(name.substr(1));
1189 if (name[0] == '\\' && l && l->inset == "big") {
1199 delim = string(1, c);
1202 if (InsetMathBig::isBigInsetDelim(delim)) {
1203 // name + delim ared a valid InsetMathBig.
1204 // We can't use cur.macroModeClose() because
1205 // it does not handle delim.
1206 InsetMathUnknown * p = cur.activeMacro();
1209 cur.cell().erase(cur.pos());
1210 cur.plainInsert(MathAtom(
1211 new InsetMathBig(name.substr(1), delim)));
1216 // leave macro mode and try again if necessary
1217 cur.macroModeClose();
1219 cur.niceInsert(MathAtom(new InsetMathBrace));
1225 // This is annoying as one has to press <space> far too often.
1229 // leave autocorrect mode if necessary
1230 if (autocorrect() && c == ' ') {
1231 autocorrect() = false;
1236 // just clear selection on pressing the space bar
1237 if (cur.selection() && c == ' ') {
1238 cur.selection() = false;
1245 //lyxerr << "starting with macro" << endl;
1246 cur.insert(MathAtom(new InsetMathUnknown("\\", false)));
1251 if (currentMode() == InsetMath::TEXT_MODE)
1257 if (currentMode() == InsetMath::TEXT_MODE) {
1258 // insert spaces in text mode,
1259 // but suppress direct insertion of two spaces in a row
1260 // the still allows typing '<space>a<space>' and deleting the 'a', but
1261 // it is better than nothing...
1262 if (!cur.pos() != 0 || cur.prevAtom()->getChar() != ' ')
1266 if (cur.pos() != 0 && cur.prevAtom()->asSpaceInset()) {
1267 cur.prevAtom().nucleus()->asSpaceInset()->incSpace();
1272 // if are at the very end, leave the formula
1273 return cur.pos() != cur.lastpos();
1276 // These shouldn't work in text mode:
1277 if (currentMode() != InsetMath::TEXT_MODE) {
1279 script(cur, false, save_selection);
1283 script(cur, true, save_selection);
1287 cur.niceInsert(createInsetMath("sim"));
1292 if (c == '{' || c == '}' || c == '&' || c == '$' || c == '#' ||
1293 c == '%' || c == '_' || c == '^') {
1294 cur.niceInsert(createInsetMath(string(1, c)));
1299 // try auto-correction
1300 //if (autocorrect() && hasPrevAtom() && math_autocorrect(prevAtom(), c))
1303 // no special circumstances, so insert the character without any fuss
1305 cur.autocorrect() = true;
1310 bool InsetMathNest::interpret(LCursor & cur, string const & str)
1312 // Create a InsetMathBig from cur.cell()[cur.pos() - 1] and t if
1314 if (!cur.empty() && cur.pos() > 0 &&
1315 cur.cell()[cur.pos() - 1]->asUnknownInset()) {
1316 if (InsetMathBig::isBigInsetDelim(str)) {
1317 string prev = asString(cur.cell()[cur.pos() - 1]);
1318 if (prev[0] == '\\') {
1319 prev = prev.substr(1);
1320 latexkeys const * l = in_word_set(prev);
1321 if (l && l->inset == "big") {
1322 cur.cell()[cur.pos() - 1] =
1323 MathAtom(new InsetMathBig(prev, str));
1333 bool InsetMathNest::script(LCursor & cur, bool up, string const &
1336 // Hack to get \^ and \_ working
1337 //lyxerr << "handling script: up: " << up << endl;
1338 if (cur.inMacroMode() && cur.macroName() == "\\") {
1340 cur.niceInsert(createInsetMath("mathcircumflex"));
1342 interpret(cur, '_');
1346 cur.macroModeClose();
1347 if (asScriptInset() && cur.idx() == 0) {
1348 // we are in a nucleus of a script inset, move to _our_ script
1349 InsetMathScript * inset = asScriptInset();
1350 //lyxerr << " going to cell " << inset->idxOfScript(up) << endl;
1352 cur.idx() = inset->idxOfScript(up);
1354 } else if (cur.pos() != 0 && cur.prevAtom()->asScriptInset()) {
1356 InsetMathScript * inset = cur.nextAtom().nucleus()->asScriptInset();
1359 cur.idx() = inset->idxOfScript(up);
1360 cur.pos() = cur.lastpos();
1362 // convert the thing to our left to a scriptinset or create a new
1363 // one if in the very first position of the array
1364 if (cur.pos() == 0) {
1365 //lyxerr << "new scriptinset" << endl;
1366 cur.insert(new InsetMathScript(up));
1368 //lyxerr << "converting prev atom " << endl;
1369 cur.prevAtom() = MathAtom(new InsetMathScript(cur.prevAtom(), up));
1372 InsetMathScript * inset = cur.nextAtom().nucleus()->asScriptInset();
1373 // special handling of {}-bases
1374 // is this always correct?
1375 if (inset->nuc().size() == 1
1376 && inset->nuc().back()->asBraceInset())
1377 inset->nuc() = inset->nuc().back()->asNestInset()->cell(0);
1383 //lyxerr << "inserting selection 1:\n" << save_selection << endl;
1384 cur.niceInsert(save_selection);
1386 //lyxerr << "inserting selection 2:\n" << save_selection << endl;