3 * Purpose: Interaction for mathed
4 * Author: Alejandro Aguilar Sierra <asierra@servidor.unam.mx>
5 * Created: January 1996
6 * Description: Math interaction for a WYSIWYG math editor.
8 * Dependencies: Xlib, XForms
10 * Copyright: 1996, Alejandro Aguilar Sierra
12 * Version: 0.8beta, Mathed & Lyx project.
14 * You are free to use and modify this code under the terms of
15 * the GNU General Public Licence version 2 or later.
19 #pragma implementation
23 #include FORMS_H_LOCATION
24 #include "math_inset.h"
25 #include "math_parser.h"
26 #include "math_cursor.h"
27 #include "math_macro.h"
28 #include "math_macrotable.h"
29 #include "math_root.h"
30 #include "support/lstrings.h"
34 #include "math_matrixinset.h"
35 #include "math_rowst.h"
36 #include "math_spaceinset.h"
37 #include "math_funcinset.h"
38 #include "math_bigopinset.h"
39 #include "math_fracinset.h"
40 #include "math_decorationinset.h"
41 #include "math_dotsinset.h"
42 #include "math_accentinset.h"
43 #include "mathed/support.h"
45 static MathedArray selarray;
49 // This was very smaller, I'll change it later
51 bool IsMacro(short tok, int id)
53 return tok != LM_TK_STACK &&
59 tok != LM_TK_FUNCLIM &&
60 tok != LM_TK_BIGSYM &&
61 tok != LM_TK_ACCENT &&
62 !(tok == LM_TK_SYM && id < 255);
65 static int const MAX_STACK_ITEMS = 32;
67 struct MathStackXIter {
71 MathStackXIter(int n = MAX_STACK_ITEMS): imax(n) {
72 item = new MathedXIter[imax];
76 MathStackXIter(MathStackXIter & stk);
82 void push(MathedXIter ** a) {
91 MathedXIter * Item(int idx) {
92 return (idx + 1 <= i) ? &item[i - idx - 1] : 0;
100 return i >= MAX_STACK_ITEMS;
107 int Level() { return i; }
109 } mathstk, *selstk = 0;
112 MathStackXIter::MathStackXIter(MathStackXIter & stk)
115 item = new MathedXIter[imax];
117 for (int k = 0; k < i; ++k) {
118 item[k].SetData(stk.item[k].getPar());
120 item[k].goPosAbs(stk.item[k].getPos());
125 /***---------------- Mathed Cursor ---------------------------***/
127 MathedCursor::MathedCursor(MathParInset * p) // : par(p)
131 lastcode = LM_TC_MIN;
133 if (!MathMacroTable::built)
134 MathMacroTable::mathMTable.builtinMacros();
138 void MathedCursor::SetPar(MathParInset * p)
141 selection = false; // not SelClear() ?
143 mathstk.push(&cursor);
145 cursor->SetData(par);
149 void MathedCursor::draw(Painter & pain, int x, int y)
151 // lyxerr << "Cursor[" << x << " " << y << "] ";
152 //win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
155 int w = par->Width() + 2;
156 int a = par->Ascent() + 1;
157 int h = par->Height() + 1;
159 if (par->GetType() > LM_OT_PAR) {
164 pain.rectangle(x - 1, y - a, w, h, LColor::mathframe);
166 par->draw(pain, x, y);
171 void MathedCursor::Redraw(Painter & pain)
173 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
175 int w = par->Width();
176 int h = par->Height();
180 //mathed_set_font(LM_TC_VAR, 1);
181 pain.fillRectangle(x, y - par->Ascent(),
182 x + w, y - par->Ascent() + h,
184 par->draw(pain, x, y);
188 bool MathedCursor::Left(bool sel)
191 // was MacroModeBack()
192 if (!imacro->GetName().empty()) {
193 imacro->SetName(imacro->GetName()
194 .substr(0, imacro->GetName()
204 if (sel && !selection)
207 if (!sel && selection)
210 bool result = cursor->Prev();
212 if (!result && !mathstk.Empty()) {
213 cursor = mathstk.pop();
218 } else if (result && cursor->IsActive()) {
219 if (cursor->IsScript()) {
221 if (!cursor->IsScript())
228 MathParInset * p = cursor->GetActiveInset();
232 p->setArgumentIdx(p->getMaxArgumentIdx());
233 mathstk.push(&cursor);
243 bool MathedCursor::Pop()
245 if (!mathstk.Empty()) {
246 cursor = mathstk.pop();
255 bool MathedCursor::Push()
257 if (cursor->IsActive()) {
258 MathParInset * p = cursor->GetActiveInset();
261 mathstk.push(&cursor);
269 bool MathedCursor::Right(bool sel)
278 if (sel && !selection)
281 if (!sel && selection)
286 if (cursor->IsActive()) {
287 if (cursor->IsScript()) {
289 // A script may be followed by another script
290 if (cursor->IsScript())
296 MathParInset *p = cursor->GetActiveInset();
298 lyxerr << "Math error: Inset expected." << endl;
299 return cursor->Next();
301 p->setArgumentIdx(0);
302 mathstk.push(&cursor);
306 result = cursor->Next();
309 if (cursor->GetChar()!= LM_TC_CR)
310 result = cursor->Next();
311 if (!result && !mathstk.Empty()) {
312 cursor = mathstk.pop();
324 void MathedCursor::SetPos(int x, int y)
331 lastcode = LM_TC_MIN;
333 mathstk.push(&cursor);
334 cursor->SetData(par);
335 cursor->fitCoord(x, y);
337 while (cursor->GetX()<x && cursor->OK()) {
338 if (cursor->IsActive()) {
339 MathParInset * p = cursor->GetActiveInset();
340 if (p->Inside(x, y)) {
342 mathstk.push(&cursor);
344 cursor->fitCoord(x, y);
350 if (!cursor->Next() && !Pop())
353 if (x-xp < cursor->GetX()-x) cursor->ipop();
358 void MathedCursor::Home()
364 mathstk.push(&cursor);
369 void MathedCursor::End()
375 mathstk.push(&cursor);
380 MathMatrixInset * create_multiline(short int type, int cols)
391 for (int i = 0; i < cols; ++i)
398 for (int i = 0; i < cols; ++i)
403 case LM_OT_MULTLINEN:
416 MathMatrixInset * mt = new MathMatrixInset(columns, -1);
417 mt->SetAlign(' ', align);
422 void MathedCursor::Insert(byte c, MathedTextCodes t)
430 if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
434 MathParInset * p = cursor->getPar();
435 if (p == par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
436 short int type = LM_OT_MPAR;
438 if (c >= '1' && c <= '9') {
441 } else if (c >= 'A' && c <= 'I') {
442 type = LM_OT_ALIGNAT;
445 type = LM_OT_MULTLINE;
449 if (p->GetType() == LM_OT_PARN)
451 MathMatrixInset * mt = create_multiline(type, cols);
452 mt->SetStyle(LM_ST_DISPLAY);
454 mt->setData(p->GetData());
459 int pos = cursor->getPos();
460 cursor->SetData(par);
461 cursor->goPosAbs(pos);
463 if (p && p->Permit(LMPF_ALLOW_CR)) {
466 } else if (t == LM_TC_TAB) {
467 MathParInset * p = cursor->getPar();
468 if (p && p->Permit(LMPF_ALLOW_TAB)) {
470 cursor->insert(c, t);
473 cursor->goNextColumn();
474 } else // Navigate between arguments
475 if (p && p->GetType() == LM_OT_MACRO) {
476 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
477 p->setArgumentIdx(p->getArgumentIdx() + 1);
484 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
485 // was MacroModeInsert(c);
486 imacro->SetName(imacro->GetName() + static_cast<char>(c));
494 cursor->insert(c, t);
503 void MathedCursor::insertInset(MathedInset * p, int t)
509 if (MathIsActive(t)) {
511 static_cast<MathParInset*>(p)->setData(selarray);
516 if (mathstk.i < MAX_STACK_ITEMS - 1) {
517 if (accent && !MathIsActive(t)) {
520 cursor->insertInset(p, t);
521 if (MathIsActive(t)) {
527 lyxerr << "Math error: Full stack." << endl;
531 void MathedCursor::Delete()
541 if (cursor->Empty() && !mathstk.Empty())
542 cursor = mathstk.pop();
544 // if (cursor->GetChar()!= LM_TC_TAB)
550 void MathedCursor::DelLine()
560 MathParInset * p = cursor->getPar();
562 if (p && p->GetType() <= LM_OT_MATRIX && p->GetType() >= LM_OT_MPAR) {
568 bool MathedCursor::Up(bool sel)
575 if (sel && !selection)
578 if (!sel && selection)
581 if (cursor->IsScript()) {
582 char cd = cursor->GetChar();
587 // A subscript may be followed by a superscript
590 if (MathIsUp(cursor->GetChar())) {
593 } else // return to the previous state
598 result = cursor->Up();
599 if (!result && cursor->getPar()) {
600 MathParInset * p = cursor->getPar();
602 if (p->GetType() == LM_OT_SCRIPT) {
603 MathedXIter * cx = mathstk.Item(1);
604 bool is_down = (cx->GetChar() == LM_TC_DOWN);
605 cursor = mathstk.pop();
607 result = (is_down) ? true: Up();
609 result = (p->getArgumentIdx() > 0);
611 p->setArgumentIdx(p->getArgumentIdx() - 1);
616 if (!result && !mathstk.Empty()) {
617 cursor = mathstk.pop();
625 bool MathedCursor::Down(bool sel)
632 if (sel && !selection)
635 if (!sel && selection)
638 if (cursor->IsScript()) {
639 char cd = cursor->GetChar();
640 if (MathIsDown(cd)) {
644 // A superscript may be followed by a subscript
647 if (MathIsDown(cursor->GetChar())) {
655 result = cursor->Down();
656 if (!result && cursor->getPar()) {
657 MathParInset * p= cursor->getPar();
658 if (p->GetType() == LM_OT_SCRIPT) {
659 MathedXIter * cx = mathstk.Item(1);
660 bool is_up = (cx->GetChar() == LM_TC_UP);
661 cursor = mathstk.pop();
663 result = (is_up) ? true: Down();
665 result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
667 p->setArgumentIdx(p->getArgumentIdx() + 1);
671 if (!result && !mathstk.Empty()) {
672 cursor = mathstk.pop();
680 bool MathedCursor::Limits()
682 if (cursor->IsInset()) {
683 MathedInset * p = cursor->GetInset();
684 bool ol = p->GetLimits();
686 return (ol!= p->GetLimits());
692 void MathedCursor::SetSize(short size)
694 MathParInset * p = cursor->getPar();
695 p->UserSetSize(size);
700 void MathedCursor::setLabel(string const & label)
702 // ugly hack and possible bug
703 if (!cursor->setLabel(label))
704 lyxerr << "MathErr: Bad place to set labels." << endl;
708 void MathedCursor::setNumbered()
711 MathedRowSt * crow = cursor->currentRow();
713 crow->setNumbered(!crow->isNumbered());
717 void MathedCursor::Interpret(string const & s)
720 latexkeys const * l = 0;
721 MathedTextCodes tcode = LM_TC_INSET;
723 if (s[0] == '^' || s[0] == '_') {
724 char c = cursor->GetChar();
725 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
730 // A script may be followed by a script
731 if (MathIsUp(c) || MathIsDown(c)) {
734 c = cursor->GetChar();
735 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
741 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
742 insertInset(p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
745 if (s[0] == '!' || s[0] == ',' || s[0] == ':' || s[0] == ';') {
746 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
747 p = new MathSpaceInset(sp);
748 insertInset(p, LM_TC_INSET);
754 p = MathMacroTable::mathMTable.getMacro(s);
756 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
758 p = new MathRootInset;
759 tcode = LM_TC_ACTIVE_INSET;
761 p = new MathFuncInset(s, LM_OT_UNDEF);
763 tcode = static_cast<MathMacro*>(p)->getTCode();
764 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
767 MathedInsetTypes fractype = LM_OT_FRAC;
770 p = new MathBigopInset(l->name, l->id);
775 Insert(static_cast<byte>(l->id), MathIsBOPS(l->id) ?
776 LM_TC_BOPS: LM_TC_SYMB);
778 p = new MathFuncInset(l->name);
783 fractype = LM_OT_STACKREL;
784 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
787 p = new MathFracInset(fractype);
788 tcode = LM_TC_ACTIVE_INSET;
792 p = new MathSqrtInset;
793 tcode = LM_TC_ACTIVE_INSET;
797 p = new MathDecorationInset(l->id);
798 tcode = LM_TC_ACTIVE_INSET;
802 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
806 p = new MathSpaceInset(l->id);
810 p = new MathDotsInset(l->name, l->id);
818 p = MathMacroTable::mathMTable.getMacro(s);
819 tcode = static_cast<MathMacro*>(p)->getTCode();
820 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
824 p = new MathFuncInset(l->name);
830 insertInset(p, tcode);
836 bool MathedCursor::pullArg()
838 if (cursor->IsActive()) {
839 MathParInset * p = cursor->GetActiveInset();
843 MathedArray a = p->GetData();
857 void MathedCursor::MacroModeOpen()
860 imacro = new MathFuncInset("");
861 insertInset(imacro, LM_TC_INSET);
864 lyxerr << "Mathed Warning: Already in macro mode" << endl;
868 void MathedCursor::MacroModeClose()
872 latexkeys const * l = in_word_set(imacro->GetName());
873 if (!imacro->GetName().empty()
874 && (!l || (l && IsMacro(l->token, l->id))) &&
875 !MathMacroTable::mathMTable.getMacro(imacro->GetName())) {
877 //imacro->SetName(macrobf);
878 // This guarantees that the string will be removed by destructor
879 imacro->SetType(LM_OT_UNDEF);
881 imacro->SetName(l->name);
884 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
886 static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
889 if (l || MathMacroTable::mathMTable.getMacro(imacro->GetName())) {
890 Interpret(imacro->GetName());
899 void MathedCursor::SelCopy()
902 int p1 = (cursor->getPos() < selpos) ? cursor->getPos() : selpos;
903 int p2 = (cursor->getPos() > selpos) ?
904 cursor->getPos() : selpos;
905 selarray = *(cursor->GetData());
906 selarray.shrink(p1, p2);
913 void MathedCursor::SelCut()
916 if (cursor->getPos() == selpos)
919 int p1 = (cursor->getPos() < selpos) ? cursor->getPos() : selpos;
920 int p2 = (cursor->getPos() > selpos) ? cursor->getPos() : selpos;
921 selarray = *(cursor->GetData());
922 selarray.shrink(p1, p2);
923 cursor->Clean(selpos);
930 void MathedCursor::SelDel()
932 // lyxerr << "Deleting sel "
934 if (cursor->getPos() == selpos)
936 cursor->Clean(selpos);
943 void MathedCursor::SelPaste()
945 // lyxerr << "paste " << selarray << " " << curor->pos;
949 if (!selarray.empty()) {
950 cursor->Merge(selarray);
956 void MathedCursor::SelStart()
958 lyxerr[Debug::MATHED] << "Starting sel " << endl;
960 selpos = cursor->getPos();
961 selstk = new MathStackXIter(mathstk);
962 anchor = selstk->Item(-1);
963 anchor->SetData(cursor->getPar());
965 anchor->goPosAbs(selpos);
971 void MathedCursor::SelClear()
973 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
982 // Anchor position must be at the same level that stack.
983 void MathedCursor::SelBalance()
985 int d = mathstk.Level() - selstk->Level();
987 // If unbalanced, balance them
990 // lyxerr << "b[" << mathstk.Level() << " " << selstk->Level
991 // << " " << anchor->GetX() << " " << cursor->GetX() << "]";
992 anchor = selstk->pop();
993 if (anchor->GetX() >= cursor->GetX())
996 // lyxerr <<"a[" << mathstk.Level() << " " << selstk->Level() <<"]";
999 d = mathstk.Level() - selstk->Level();
1002 // Once balanced the levels, check that they are at the same paragraph
1003 selpos = anchor->getPos();
1007 void MathedCursor::SelGetArea(int ** xp, int ** yp, int & np)
1009 static int xpoint[10];
1010 static int ypoint[10];
1021 // Balance anchor and cursor
1026 cursor->getPar()->GetXY(xo, yo);
1027 int w = cursor->getPar()->Width();
1030 cursor->GetPos(x1, y1);
1033 cursor->getAD(a1, d1);
1036 anchor->GetPos(x, y);
1039 anchor->getAD(a, d);
1041 // single row selection
1044 ypoint[i++] = y + d;
1046 ypoint[i++] = y - a;
1050 ypoint[i++] = y - a;
1054 ypoint[i++] = y1 - a;
1059 ypoint[i++] = y1 - a;
1061 ypoint[i++] = y1 + d;
1065 ypoint[i++] = y1 + d;
1068 ypoint[i++] = y + d;
1071 xpoint[i] = xpoint[0];
1072 ypoint[i++] = ypoint[0];
1077 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
1078 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
1079 // for (i = 0; i < np; ++i)
1080 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
1084 void MathedCursor::setAccent(int ac)
1086 if (ac > 0 && accent < 8) {
1087 nestaccent[accent++] = ac;
1089 accent = 0; // consumed!
1093 int MathedCursor::getAccent() const
1095 return (accent > 0) ? nestaccent[accent - 1]: 0;
1099 void MathedCursor::doAccent(byte c, MathedTextCodes t)
1101 MathedInset * ac = 0;
1103 for (int i = accent - 1; i >= 0; --i) {
1104 if (i == accent - 1)
1105 ac = new MathAccentInset(c, t, nestaccent[i]);
1107 ac = new MathAccentInset(ac, nestaccent[i]);
1111 cursor->insertInset(ac, LM_TC_INSET);
1113 accent = 0; // consumed!
1117 void MathedCursor::doAccent(MathedInset * p)
1119 MathedInset * ac = 0;
1121 for (int i = accent - 1; i >= 0; --i) {
1122 if (i == accent - 1)
1123 ac = new MathAccentInset(p, nestaccent[i]);
1125 ac = new MathAccentInset(ac, nestaccent[i]);
1129 cursor->insertInset(ac, LM_TC_INSET);
1131 accent = 0; // consumed!
1135 void MathedCursor::toggleLastCode(MathedTextCodes t)
1138 lastcode = LM_TC_VAR;
1144 void MathedCursor::GetPos(int & x, int & y)
1146 cursor->GetPos(x, y);
1150 short MathedCursor::GetFCode()
1152 return cursor->fcode();
1156 MathParInset * MathedCursor::GetPar()
1162 MathParInset * MathedCursor::getCurrentPar() const
1164 return cursor->getPar();
1168 string const & MathedCursor::getLabel() const
1170 return cursor->getLabel();
1174 bool MathedCursor::IsEnd() const
1176 return !cursor->OK();
1180 bool MathedCursor::InMacroMode()
1186 bool MathedCursor::Selection()
1192 void MathedCursor::clearLastCode()
1194 lastcode = LM_TC_MIN;
1198 void MathedCursor::setLastCode(MathedTextCodes t)
1204 MathedTextCodes MathedCursor::getLastCode() const