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 = 0;
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;
134 if (!MathMacroTable::built)
135 MathMacroTable::mathMTable.builtinMacros();
139 void MathedCursor::SetPar(MathParInset * p)
144 selection = false; // not SelClear() ?
146 mathstk.push(&cursor);
148 cursor->SetData(par);
152 void MathedCursor::draw(Painter & pain, int x, int y)
154 // lyxerr << "Cursor[" << x << " " << y << "] ";
155 //win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
158 int w = par->Width() + 2;
159 int a = par->Ascent() + 1;
160 int h = par->Height() + 1;
162 if (par->GetType() > LM_OT_PAR) {
167 pain.rectangle(x - 1, y - a, w, h, LColor::mathframe);
169 par->draw(pain, x, y);
174 void MathedCursor::Redraw(Painter & pain)
176 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
178 int w = par->Width();
179 int h = par->Height();
183 //mathed_set_font(LM_TC_VAR, 1);
184 pain.fillRectangle(x, y - par->Ascent(),
185 x + w, y - par->Ascent() + h,
187 par->draw(pain, x, y);
191 bool MathedCursor::Left(bool sel)
200 if (sel && !selection)
203 if (!sel && selection)
206 bool result = cursor->Prev();
208 if (!result && !mathstk.Empty()) {
209 cursor = mathstk.pop();
215 if (result && cursor->IsActive()) {
216 if (cursor->IsScript()) {
218 if (!cursor->IsScript())
225 MathParInset * p = cursor->GetActiveInset();
229 p->setArgumentIdx(p->getMaxArgumentIdx());
230 mathstk.push(&cursor);
240 bool MathedCursor::Pop()
242 if (!mathstk.Empty()) {
243 cursor = mathstk.pop();
252 bool MathedCursor::Push()
254 if (cursor->IsActive()) {
255 MathParInset * p = cursor->GetActiveInset();
258 mathstk.push(&cursor);
266 bool MathedCursor::Right(bool sel)
275 if (sel && !selection)
278 if (!sel && selection)
283 if (cursor->IsActive()) {
284 if (cursor->IsScript()) {
286 // A script may be followed by another script
287 if (cursor->IsScript())
293 MathParInset *p = cursor->GetActiveInset();
295 lyxerr << "Math error: Inset expected." << endl;
296 return cursor->Next();
298 p->setArgumentIdx(0);
299 mathstk.push(&cursor);
303 result = cursor->Next();
306 if (cursor->GetChar()!= LM_TC_CR)
307 result = cursor->Next();
308 if (!result && !mathstk.Empty()) {
309 cursor = mathstk.pop();
321 void MathedCursor::SetPos(int x, int y)
328 lastcode = LM_TC_MIN;
330 mathstk.push(&cursor);
331 cursor->SetData(par);
332 cursor->fitCoord(x, y);
334 while (cursor->GetX()<x && cursor->OK()) {
335 if (cursor->IsActive()) {
336 MathParInset * p = cursor->GetActiveInset();
337 if (p->Inside(x, y)) {
339 mathstk.push(&cursor);
341 cursor->fitCoord(x, y);
347 if (!cursor->Next() && !Pop())
350 if (x-xp < cursor->GetX()-x) cursor->ipop();
355 void MathedCursor::Home()
361 mathstk.push(&cursor);
366 void MathedCursor::End()
372 mathstk.push(&cursor);
377 MathMatrixInset * create_multiline(short int type, int cols)
388 for (int i = 0; i < cols; ++i)
395 for (int i = 0; i < cols; ++i)
400 case LM_OT_MULTLINEN:
413 MathMatrixInset * mt = new MathMatrixInset(columns, -1);
414 mt->SetAlign(' ', align);
419 void MathedCursor::Insert(byte c, MathedTextCodes t)
427 if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
431 MathParInset * p = cursor->p;
432 if (p == par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
433 short int type = LM_OT_MPAR;
435 if (c >= '1' && c <= '9') {
438 } else if (c >= 'A' && c <= 'I') {
439 type = LM_OT_ALIGNAT;
442 type = LM_OT_MULTLINE;
446 if (p->GetType() == LM_OT_PARN)
448 MathMatrixInset * mt = create_multiline(type, cols);
449 mt->SetStyle(LM_ST_DISPLAY);
451 mt->SetData(p->GetData());
452 p->SetData(0); // BUG duda
457 int pos = cursor->getPos();
458 cursor->SetData(par);
459 cursor->goPosAbs(pos);
461 if (p && p->Permit(LMPF_ALLOW_CR)) {
464 } else if (t == LM_TC_TAB) {
465 MathParInset * p = cursor->p;
466 if (p && p->Permit(LMPF_ALLOW_TAB)) {
468 cursor->Insert(c, t);
471 cursor->goNextColumn();
472 } else // Navigate between arguments
473 if (p && p->GetType() == LM_OT_MACRO) {
474 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
475 p->setArgumentIdx(p->getArgumentIdx() + 1);
482 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
491 cursor->Insert(c, t);
500 void MathedCursor::Insert(MathedInset * p, int t)
506 if (MathIsActive(t)) {
508 static_cast<MathParInset*>(p)->SetData(selarray);
513 if (mathstk.i < MAX_STACK_ITEMS - 1) {
514 if (accent && !MathIsActive(t)) {
517 cursor->Insert(p, t);
518 if (MathIsActive(t)) {
524 lyxerr << "Math error: Full stack." << endl;
528 void MathedCursor::Delete()
538 if (cursor->Empty() && !mathstk.Empty())
539 cursor = mathstk.pop();
541 // if (cursor->GetChar()!= LM_TC_TAB)
547 void MathedCursor::DelLine()
557 MathParInset * p = cursor->p;
559 if (p && p->GetType() <= LM_OT_MATRIX && p->GetType() >= LM_OT_MPAR) {
565 bool MathedCursor::Up(bool sel)
572 if (sel && !selection)
575 if (!sel && selection)
578 if (cursor->IsScript()) {
579 char cd = cursor->GetChar();
584 // A subscript may be followed by a superscript
587 if (MathIsUp(cursor->GetChar())) {
590 } else // return to the previous state
595 result = cursor->Up();
596 if (!result && cursor->p) {
597 MathParInset * p = cursor->p;
599 if (p->GetType() == LM_OT_SCRIPT) {
600 MathedXIter * cx = mathstk.Item(1);
601 bool is_down = (cx->GetChar() == LM_TC_DOWN);
602 cursor = mathstk.pop();
604 result = (is_down) ? true: Up();
606 result = (p->getArgumentIdx() > 0);
608 p->setArgumentIdx(p->getArgumentIdx() - 1);
613 if (!result && !mathstk.Empty()) {
614 cursor = mathstk.pop();
622 bool MathedCursor::Down(bool sel)
629 if (sel && !selection)
632 if (!sel && selection)
635 if (cursor->IsScript()) {
636 char cd = cursor->GetChar();
637 if (MathIsDown(cd)) {
641 // A superscript may be followed by a subscript
644 if (MathIsDown(cursor->GetChar())) {
652 result = cursor->Down();
653 if (!result && cursor->p) {
654 MathParInset * p= cursor->p;
655 if (p->GetType() == LM_OT_SCRIPT) {
656 MathedXIter * cx = mathstk.Item(1);
657 bool is_up = (cx->GetChar() == LM_TC_UP);
658 cursor = mathstk.pop();
660 result = (is_up) ? true: Down();
662 result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
664 p->setArgumentIdx(p->getArgumentIdx() + 1);
668 if (!result && !mathstk.Empty()) {
669 cursor = mathstk.pop();
677 bool MathedCursor::Limits()
679 if (cursor->IsInset()) {
680 MathedInset * p = cursor->GetInset();
681 bool ol = p->GetLimits();
683 return (ol!= p->GetLimits());
689 void MathedCursor::SetSize(short size)
691 MathParInset * p = cursor->p;
692 p->UserSetSize(size);
697 void MathedCursor::setLabel(string const & label)
699 // ugly hack and possible bug
700 if (!cursor->setLabel(label))
701 lyxerr << "MathErr: Bad place to set labels." << endl;
705 void MathedCursor::setNumbered()
708 MathedRowSt * crow = cursor->crow;
711 crow->setNumbered(!crow->isNumbered());
715 void MathedCursor::Interpret(string const & s)
719 MathedTextCodes tcode = LM_TC_INSET;
721 if (s[0] == '^' || s[0] == '_') {
722 char c = cursor->GetChar();
723 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
728 // A script may be followed by a script
729 if (MathIsUp(c) || MathIsDown(c)) {
732 c = cursor->GetChar();
733 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
739 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
740 Insert (p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
743 if (s[0] == '!' || s[0] == ',' || s[0] == ':' || s[0] == ';') {
744 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
745 p = new MathSpaceInset(sp);
752 p = MathMacroTable::mathMTable.getMacro(s);
754 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
756 p = new MathRootInset();
757 tcode = LM_TC_ACTIVE_INSET;
759 p = new MathFuncInset(s, LM_OT_UNDEF);
761 tcode = static_cast<MathMacro*>(p)->getTCode();
762 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
765 MathedInsetTypes fractype = LM_OT_FRAC;
768 p = new MathBigopInset(l->name, l->id);
773 Insert(static_cast<byte>(l->id), MathIsBOPS(l->id) ?
774 LM_TC_BOPS: LM_TC_SYMB);
776 p = new MathFuncInset(l->name);
781 fractype = LM_OT_STACKREL;
782 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
785 p = new MathFracInset(fractype);
786 tcode = LM_TC_ACTIVE_INSET;
790 p = new MathSqrtInset;
791 tcode = LM_TC_ACTIVE_INSET;
795 p = new MathDecorationInset(l->id);
796 tcode = LM_TC_ACTIVE_INSET;
800 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
804 p = new MathSpaceInset(l->id);
808 p = new MathDotsInset(l->name, l->id);
816 p = MathMacroTable::mathMTable.getMacro(s);
817 tcode = static_cast<MathMacro*>(p)->getTCode();
818 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
822 p = new MathFuncInset(l->name);
834 bool MathedCursor::pullArg()
836 if (cursor->IsActive()) {
837 MathParInset * p = cursor->GetActiveInset();
841 MathedArray * a = p->GetData();
855 void MathedCursor::MacroModeOpen()
858 imacro = new MathFuncInset("");
862 lyxerr << "Mathed Warning: Already in macro mode" << endl;
866 void MathedCursor::MacroModeClose()
870 latexkeys * l = in_word_set(imacro->GetName());
871 if (!imacro->GetName().empty()
872 && (!l || (l && IsMacro(l->token, l->id))) &&
873 !MathMacroTable::mathMTable.getMacro(imacro->GetName())) {
875 //imacro->SetName(macrobf);
876 // This guarantees that the string will be removed by destructor
877 imacro->SetType(LM_OT_UNDEF);
879 imacro->SetName(l->name);
882 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
884 static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
887 if (l || MathMacroTable::mathMTable.getMacro(imacro->GetName())) {
888 Interpret(imacro->GetName());
897 void MathedCursor::MacroModeBack()
900 if (!imacro->GetName().empty()) {
902 imacro->GetName().substr(0, imacro->GetName().length() - 1));
907 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
911 void MathedCursor::MacroModeInsert(char c)
914 imacro->SetName(imacro->GetName() + c);
917 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
921 void MathedCursor::SelCopy()
924 int p1 = (cursor->pos < selpos) ? cursor->pos : selpos;
925 int p2 = (cursor->pos > selpos) ? cursor->pos : selpos;
926 selarray = cursor->Copy(p1, p2);
933 void MathedCursor::SelCut()
936 if (cursor->pos == selpos)
939 int p1 = (cursor->pos < selpos) ? cursor->pos : selpos;
940 int p2 = (cursor->pos > selpos) ? cursor->pos : selpos;
941 selarray = cursor->Copy(p1, p2);
942 cursor->Clean(selpos);
949 void MathedCursor::SelDel()
951 // lyxerr << "Deleting sel "
953 if (cursor->pos == selpos)
955 cursor->Clean(selpos);
962 void MathedCursor::SelPaste()
964 // lyxerr << "paste " << selarray << " " << curor->pos;
969 cursor->Merge(selarray);
975 void MathedCursor::SelStart()
977 lyxerr[Debug::MATHED] << "Starting sel " << endl;
979 selpos = cursor->pos;
980 selstk = new MathStackXIter(mathstk);
981 anchor = selstk->Item(-1);
982 anchor->SetData(cursor->p);
984 anchor->goPosAbs(selpos);
990 void MathedCursor::SelClear()
992 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
1001 // Anchor position must be at the same level that stack.
1002 void MathedCursor::SelBalance()
1004 int d = mathstk.Level() - selstk->Level();
1006 // If unbalanced, balance them
1009 // lyxerr << "b[" << mathstk.Level() << " " << selstk->Level
1010 // << " " << anchor->GetX() << " " << cursor->GetX() << "]";
1011 anchor = selstk->pop();
1012 if (anchor->GetX() >= cursor->GetX())
1015 // lyxerr <<"a[" << mathstk.Level() << " " << selstk->Level() <<"]";
1018 d = mathstk.Level() - selstk->Level();
1021 // Once balanced the levels, check that they are at the same paragraph
1022 selpos = anchor->pos;
1026 void MathedCursor::SelGetArea(int ** xp, int ** yp, int & np)
1028 static int xpoint[10];
1029 static int ypoint[10];
1040 // single row selection
1047 // Balance anchor and cursor
1052 cursor->p->GetXY(xo, yo);
1053 int w = cursor->p->Width();
1056 cursor->GetPos(x1, y1);
1057 cursor->getAD(a1, d1);
1060 anchor->GetPos(x, y);
1061 anchor->getAD(a, d);
1064 ypoint[i++] = y + d;
1066 ypoint[i++] = y - a;
1070 ypoint[i++] = y - a;
1074 ypoint[i++] = y1 - a;
1079 ypoint[i++] = y1 - a;
1081 ypoint[i++] = y1 + d;
1085 ypoint[i++] = y1 + d;
1088 ypoint[i++] = y + d;
1091 xpoint[i] = xpoint[0];
1092 ypoint[i++] = ypoint[0];
1097 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
1098 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
1099 // for (i = 0; i < np; ++i)
1100 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
1104 void MathedCursor::setAccent(int ac)
1106 if (ac > 0 && accent < 8) {
1107 nestaccent[accent++] = ac;
1109 accent = 0; // consumed!
1113 int MathedCursor::getAccent() const
1115 return (accent > 0) ? nestaccent[accent - 1]: 0;
1119 void MathedCursor::doAccent(byte c, MathedTextCodes t)
1121 MathedInset * ac = 0;
1123 for (int i = accent - 1; i >= 0; --i) {
1124 if (i == accent - 1)
1125 ac = new MathAccentInset(c, t, nestaccent[i]);
1127 ac = new MathAccentInset(ac, nestaccent[i]);
1133 accent = 0; // consumed!
1137 void MathedCursor::doAccent(MathedInset * p)
1139 MathedInset * ac = 0;
1141 for (int i = accent - 1; i >= 0; --i) {
1142 if (i == accent - 1)
1143 ac = new MathAccentInset(p, nestaccent[i]);
1145 ac = new MathAccentInset(ac, nestaccent[i]);
1151 accent = 0; // consumed!
1155 void MathedCursor::toggleLastCode(MathedTextCodes t)
1158 lastcode = LM_TC_VAR;
1164 void MathedCursor::GetPos(int & x, int & y)
1166 cursor->GetPos(x, y);
1170 short MathedCursor::GetFCode()
1172 return cursor->FCode();
1176 MathParInset * MathedCursor::GetPar()
1182 MathParInset * MathedCursor::getCurrentPar() const
1188 string const & MathedCursor::getLabel() const
1190 return cursor->getLabel();
1194 bool MathedCursor::IsEnd() const
1196 return !cursor->OK();
1200 bool MathedCursor::InMacroMode()
1206 bool MathedCursor::Selection()
1212 void MathedCursor::clearLastCode()
1214 lastcode = LM_TC_MIN;
1218 void MathedCursor::setLastCode(MathedTextCodes t)
1224 MathedTextCodes MathedCursor::getLastCode() const