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 "math_macrotemplate.h"
44 #include "mathed/support.h"
46 static MathedArray selarray;
50 // This was very smaller, I'll change it later
52 bool IsMacro(short tok, int id)
54 return tok != LM_TK_STACK &&
60 tok != LM_TK_FUNCLIM &&
61 tok != LM_TK_BIGSYM &&
62 tok != LM_TK_ACCENT &&
63 !(tok == LM_TK_SYM && id < 255);
66 static int const MAX_STACK_ITEMS = 32;
68 struct MathStackXIter {
72 MathStackXIter(int n = MAX_STACK_ITEMS): imax(n) {
73 item = new MathedXIter[imax];
77 MathStackXIter(MathStackXIter & stk);
83 void push(MathedXIter ** a) {
92 MathedXIter * Item(int idx) {
93 return (idx + 1 <= i) ? &item[i - idx - 1] : 0;
101 return i >= MAX_STACK_ITEMS;
108 int Level() { return i; }
110 } mathstk, *selstk = 0;
113 MathStackXIter::MathStackXIter(MathStackXIter & stk)
116 item = new MathedXIter[imax];
118 for (int k = 0; k < i; ++k) {
119 item[k].SetData(stk.item[k].getPar());
121 item[k].goPosAbs(stk.item[k].getPos());
126 /***---------------- Mathed Cursor ---------------------------***/
128 MathedCursor::MathedCursor(MathParInset * p) // : par(p)
132 lastcode = LM_TC_MIN;
134 if (!MathMacroTable::built)
135 MathMacroTable::mathMTable.builtinMacros();
139 void MathedCursor::SetPar(MathParInset * p)
142 selection = false; // not SelClear() ?
144 mathstk.push(&cursor);
146 cursor->SetData(par);
150 void MathedCursor::draw(Painter & pain, int x, int y)
152 // lyxerr << "Cursor[" << x << " " << y << "] ";
153 //win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
156 int w = par->Width() + 2;
157 int a = par->Ascent() + 1;
158 int h = par->Height() + 1;
160 if (par->GetType() > LM_OT_PAR) {
165 pain.rectangle(x - 1, y - a, w, h, LColor::mathframe);
167 par->draw(pain, x, y);
172 void MathedCursor::Redraw(Painter & pain)
174 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
176 int w = par->Width();
177 int h = par->Height();
181 //mathed_set_font(LM_TC_VAR, 1);
182 pain.fillRectangle(x, y - par->Ascent(),
183 x + w, y - par->Ascent() + h,
185 par->draw(pain, x, y);
189 bool MathedCursor::Left(bool sel)
192 // was MacroModeBack()
193 if (!imacro->GetName().empty()) {
194 imacro->SetName(imacro->GetName()
195 .substr(0, imacro->GetName()
205 if (sel && !selection)
208 if (!sel && selection)
211 bool result = cursor->Prev();
213 if (!result && !mathstk.Empty()) {
214 cursor = mathstk.pop();
219 } else if (result && cursor->IsActive()) {
220 if (cursor->IsScript()) {
222 if (!cursor->IsScript())
229 MathParInset * p = cursor->GetActiveInset();
233 p->setArgumentIdx(p->getMaxArgumentIdx());
234 mathstk.push(&cursor);
244 bool MathedCursor::Pop()
246 if (!mathstk.Empty()) {
247 cursor = mathstk.pop();
256 bool MathedCursor::Push()
258 if (cursor->IsActive()) {
259 MathParInset * p = cursor->GetActiveInset();
262 mathstk.push(&cursor);
270 bool MathedCursor::Right(bool sel)
279 if (sel && !selection)
282 if (!sel && selection)
287 if (cursor->IsActive()) {
288 if (cursor->IsScript()) {
290 // A script may be followed by another script
291 if (cursor->IsScript())
297 MathParInset *p = cursor->GetActiveInset();
299 lyxerr << "Math error: Inset expected." << endl;
300 return cursor->Next();
302 p->setArgumentIdx(0);
303 mathstk.push(&cursor);
307 result = cursor->Next();
310 if (cursor->GetChar()!= LM_TC_CR)
311 result = cursor->Next();
312 if (!result && !mathstk.Empty()) {
313 cursor = mathstk.pop();
325 void MathedCursor::SetPos(int x, int y)
332 lastcode = LM_TC_MIN;
334 mathstk.push(&cursor);
335 cursor->SetData(par);
336 cursor->fitCoord(x, y);
338 while (cursor->GetX()<x && cursor->OK()) {
339 if (cursor->IsActive()) {
340 MathParInset * p = cursor->GetActiveInset();
341 if (p->Inside(x, y)) {
343 mathstk.push(&cursor);
345 cursor->fitCoord(x, y);
351 if (!cursor->Next() && !Pop())
354 if (x-xp < cursor->GetX()-x) cursor->ipop();
359 void MathedCursor::Home()
365 mathstk.push(&cursor);
370 void MathedCursor::End()
376 mathstk.push(&cursor);
381 MathMatrixInset * create_multiline(short int type, int cols)
392 for (int i = 0; i < cols; ++i)
399 for (int i = 0; i < cols; ++i)
404 case LM_OT_MULTLINEN:
417 MathMatrixInset * mt = new MathMatrixInset(columns, -1);
418 mt->SetAlign(' ', align);
423 void MathedCursor::Insert(byte c, MathedTextCodes t)
431 if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
435 MathParInset * p = cursor->getPar();
436 if (p == par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
437 short int type = LM_OT_MPAR;
439 if (c >= '1' && c <= '9') {
442 } else if (c >= 'A' && c <= 'I') {
443 type = LM_OT_ALIGNAT;
446 type = LM_OT_MULTLINE;
450 if (p->GetType() == LM_OT_PARN)
452 MathMatrixInset * mt = create_multiline(type, cols);
453 mt->SetStyle(LM_ST_DISPLAY);
455 mt->setData(p->GetData());
460 int pos = cursor->getPos();
461 cursor->SetData(par);
462 cursor->goPosAbs(pos);
464 if (p && p->Permit(LMPF_ALLOW_CR)) {
467 } else if (t == LM_TC_TAB) {
468 MathParInset * p = cursor->getPar();
469 if (p && p->Permit(LMPF_ALLOW_TAB)) {
471 cursor->insert(c, t);
474 cursor->goNextColumn();
475 } else // Navigate between arguments
476 if (p && p->GetType() == LM_OT_MACRO) {
477 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
478 p->setArgumentIdx(p->getArgumentIdx() + 1);
485 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
486 // was MacroModeInsert(c);
487 imacro->SetName(imacro->GetName() + static_cast<char>(c));
495 cursor->insert(c, t);
504 void MathedCursor::insertInset(MathedInset * p, int t)
510 if (MathIsActive(t)) {
512 static_cast<MathParInset*>(p)->setData(selarray);
517 if (mathstk.i < MAX_STACK_ITEMS - 1) {
518 if (accent && !MathIsActive(t)) {
521 cursor->insertInset(p, t);
522 if (MathIsActive(t)) {
528 lyxerr << "Math error: Full stack." << endl;
532 void MathedCursor::Delete()
542 if (cursor->Empty() && !mathstk.Empty())
543 cursor = mathstk.pop();
545 // if (cursor->GetChar()!= LM_TC_TAB)
551 void MathedCursor::DelLine()
561 MathParInset * p = cursor->getPar();
563 if (p && p->GetType() <= LM_OT_MATRIX && p->GetType() >= LM_OT_MPAR) {
569 bool MathedCursor::Up(bool sel)
576 if (sel && !selection)
579 if (!sel && selection)
582 if (cursor->IsScript()) {
583 char cd = cursor->GetChar();
588 // A subscript may be followed by a superscript
591 if (MathIsUp(cursor->GetChar())) {
594 } else // return to the previous state
599 result = cursor->Up();
600 if (!result && cursor->getPar()) {
601 MathParInset * p = cursor->getPar();
603 if (p->GetType() == LM_OT_SCRIPT) {
604 MathedXIter * cx = mathstk.Item(1);
605 bool is_down = (cx->GetChar() == LM_TC_DOWN);
606 cursor = mathstk.pop();
608 result = (is_down) ? true: Up();
610 result = (p->getArgumentIdx() > 0);
612 p->setArgumentIdx(p->getArgumentIdx() - 1);
617 if (!result && !mathstk.Empty()) {
618 cursor = mathstk.pop();
626 bool MathedCursor::Down(bool sel)
633 if (sel && !selection)
636 if (!sel && selection)
639 if (cursor->IsScript()) {
640 char cd = cursor->GetChar();
641 if (MathIsDown(cd)) {
645 // A superscript may be followed by a subscript
648 if (MathIsDown(cursor->GetChar())) {
656 result = cursor->Down();
657 if (!result && cursor->getPar()) {
658 MathParInset * p= cursor->getPar();
659 if (p->GetType() == LM_OT_SCRIPT) {
660 MathedXIter * cx = mathstk.Item(1);
661 bool is_up = (cx->GetChar() == LM_TC_UP);
662 cursor = mathstk.pop();
664 result = (is_up) ? true: Down();
666 result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
668 p->setArgumentIdx(p->getArgumentIdx() + 1);
672 if (!result && !mathstk.Empty()) {
673 cursor = mathstk.pop();
681 bool MathedCursor::Limits()
683 if (cursor->IsInset()) {
684 MathedInset * p = cursor->GetInset();
685 bool ol = p->GetLimits();
687 return (ol!= p->GetLimits());
693 void MathedCursor::SetSize(short size)
695 MathParInset * p = cursor->getPar();
696 p->UserSetSize(size);
701 void MathedCursor::setLabel(string const & label)
703 // ugly hack and possible bug
704 if (!cursor->setLabel(label))
705 lyxerr << "MathErr: Bad place to set labels." << endl;
709 void MathedCursor::setNumbered()
712 MathedRowSt * crow = cursor->currentRow();
714 crow->setNumbered(!crow->isNumbered());
718 void MathedCursor::Interpret(string const & s)
721 latexkeys const * l = 0;
722 MathedTextCodes tcode = LM_TC_INSET;
724 if (s[0] == '^' || s[0] == '_') {
725 char c = cursor->GetChar();
726 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
731 // A script may be followed by a script
732 if (MathIsUp(c) || MathIsDown(c)) {
735 c = cursor->GetChar();
736 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
742 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
743 insertInset(p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
746 if (s[0] == '!' || s[0] == ',' || s[0] == ':' || s[0] == ';') {
747 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
748 p = new MathSpaceInset(sp);
749 insertInset(p, LM_TC_INSET);
755 p = MathMacroTable::mathMTable.createMacro(s);
757 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
759 p = new MathRootInset;
760 tcode = LM_TC_ACTIVE_INSET;
762 p = new MathFuncInset(s, LM_OT_UNDEF);
764 tcode = static_cast<MathMacro*>(p)->getTCode();
765 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
768 MathedInsetTypes fractype = LM_OT_FRAC;
771 p = new MathBigopInset(l->name, l->id);
776 Insert(static_cast<byte>(l->id), MathIsBOPS(l->id) ?
777 LM_TC_BOPS: LM_TC_SYMB);
779 p = new MathFuncInset(l->name);
784 fractype = LM_OT_STACKREL;
785 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
788 p = new MathFracInset(fractype);
789 tcode = LM_TC_ACTIVE_INSET;
793 p = new MathSqrtInset;
794 tcode = LM_TC_ACTIVE_INSET;
798 p = new MathDecorationInset(l->id);
799 tcode = LM_TC_ACTIVE_INSET;
803 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
807 p = new MathSpaceInset(l->id);
811 p = new MathDotsInset(l->name, l->id);
819 p = MathMacroTable::mathMTable.createMacro(s);
820 tcode = static_cast<MathMacro*>(p)->getTCode();
821 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
825 p = new MathFuncInset(l->name);
831 insertInset(p, tcode);
837 bool MathedCursor::pullArg()
839 if (cursor->IsActive()) {
840 MathParInset * p = cursor->GetActiveInset();
844 MathedArray a = p->GetData();
858 void MathedCursor::MacroModeOpen()
861 imacro = new MathFuncInset("");
862 insertInset(imacro, LM_TC_INSET);
865 lyxerr << "Mathed Warning: Already in macro mode" << endl;
869 void MathedCursor::MacroModeClose()
873 latexkeys const * l = in_word_set(imacro->GetName());
874 if (!imacro->GetName().empty()
875 && (!l || (l && IsMacro(l->token, l->id))) &&
876 !MathMacroTable::mathMTable.createMacro(imacro->GetName())) {
878 //imacro->SetName(macrobf);
879 // This guarantees that the string will be removed by destructor
880 imacro->SetType(LM_OT_UNDEF);
882 imacro->SetName(l->name);
885 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
887 static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
890 if (l || MathMacroTable::mathMTable.createMacro(imacro->GetName())) {
891 Interpret(imacro->GetName());
900 void MathedCursor::SelCopy()
903 int const p1 = (cursor->getPos() < selpos) ?
904 cursor->getPos() : selpos;
905 int const p2 = (cursor->getPos() > selpos) ?
906 cursor->getPos() : selpos;
907 selarray = *(cursor->GetData());
908 selarray.shrink(p1, p2);
915 void MathedCursor::SelCut()
918 if (cursor->getPos() == selpos)
921 int const p1 = (cursor->getPos() < selpos) ?
922 cursor->getPos() : selpos;
923 int const p2 = (cursor->getPos() > selpos) ?
924 cursor->getPos() : selpos;
925 selarray = *(cursor->GetData());
926 selarray.shrink(p1, p2);
927 cursor->Clean(selpos);
934 void MathedCursor::SelDel()
936 // lyxerr << "Deleting sel "
938 if (cursor->getPos() == selpos)
940 cursor->Clean(selpos);
947 void MathedCursor::SelPaste()
949 // lyxerr << "paste " << selarray << " " << curor->pos;
953 if (!selarray.empty()) {
954 cursor->Merge(selarray);
960 void MathedCursor::SelStart()
962 lyxerr[Debug::MATHED] << "Starting sel " << endl;
964 selpos = cursor->getPos();
965 selstk = new MathStackXIter(mathstk);
966 anchor = selstk->Item(-1);
967 anchor->SetData(cursor->getPar());
969 anchor->goPosAbs(selpos);
975 void MathedCursor::SelClear()
977 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
986 // Anchor position must be at the same level that stack.
987 void MathedCursor::SelBalance()
989 int d = mathstk.Level() - selstk->Level();
991 // If unbalanced, balance them
994 // lyxerr << "b[" << mathstk.Level() << " " << selstk->Level
995 // << " " << anchor->GetX() << " " << cursor->GetX() << "]";
996 anchor = selstk->pop();
997 if (anchor->GetX() >= cursor->GetX())
1000 // lyxerr <<"a[" << mathstk.Level() << " " << selstk->Level() <<"]";
1003 d = mathstk.Level() - selstk->Level();
1006 // Once balanced the levels, check that they are at the same paragraph
1007 selpos = anchor->getPos();
1011 void MathedCursor::SelGetArea(int ** xp, int ** yp, int & np)
1013 static int xpoint[10];
1014 static int ypoint[10];
1025 // Balance anchor and cursor
1030 cursor->getPar()->GetXY(xo, yo);
1031 int w = cursor->getPar()->Width();
1034 cursor->GetPos(x1, y1);
1037 cursor->getAD(a1, d1);
1040 anchor->GetPos(x, y);
1043 anchor->getAD(a, d);
1045 // single row selection
1048 ypoint[i++] = y + d;
1050 ypoint[i++] = y - a;
1054 ypoint[i++] = y - a;
1058 ypoint[i++] = y1 - a;
1063 ypoint[i++] = y1 - a;
1065 ypoint[i++] = y1 + d;
1069 ypoint[i++] = y1 + d;
1072 ypoint[i++] = y + d;
1075 xpoint[i] = xpoint[0];
1076 ypoint[i++] = ypoint[0];
1081 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
1082 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
1083 // for (i = 0; i < np; ++i)
1084 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
1088 void MathedCursor::setAccent(int ac)
1090 if (ac > 0 && accent < 8) {
1091 nestaccent[accent++] = ac;
1093 accent = 0; // consumed!
1097 int MathedCursor::getAccent() const
1099 return (accent > 0) ? nestaccent[accent - 1]: 0;
1103 void MathedCursor::doAccent(byte c, MathedTextCodes t)
1105 MathedInset * ac = 0;
1107 for (int i = accent - 1; i >= 0; --i) {
1108 if (i == accent - 1)
1109 ac = new MathAccentInset(c, t, nestaccent[i]);
1111 ac = new MathAccentInset(ac, nestaccent[i]);
1115 cursor->insertInset(ac, LM_TC_INSET);
1117 accent = 0; // consumed!
1121 void MathedCursor::doAccent(MathedInset * p)
1123 MathedInset * ac = 0;
1125 for (int i = accent - 1; i >= 0; --i) {
1126 if (i == accent - 1)
1127 ac = new MathAccentInset(p, nestaccent[i]);
1129 ac = new MathAccentInset(ac, nestaccent[i]);
1133 cursor->insertInset(ac, LM_TC_INSET);
1135 accent = 0; // consumed!
1139 void MathedCursor::toggleLastCode(MathedTextCodes t)
1142 lastcode = LM_TC_VAR;
1148 void MathedCursor::GetPos(int & x, int & y)
1150 cursor->GetPos(x, y);
1154 short MathedCursor::GetFCode()
1156 return cursor->fcode();
1160 MathParInset * MathedCursor::GetPar()
1166 MathParInset * MathedCursor::getCurrentPar() const
1168 return cursor->getPar();
1172 string const & MathedCursor::getLabel() const
1174 return cursor->getLabel();
1178 bool MathedCursor::IsEnd() const
1180 return !cursor->OK();
1184 bool MathedCursor::InMacroMode()
1190 bool MathedCursor::Selection()
1196 void MathedCursor::clearLastCode()
1198 lastcode = LM_TC_MIN;
1202 void MathedCursor::setLastCode(MathedTextCodes t)
1208 MathedTextCodes MathedCursor::getLastCode() const