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 && tok != LM_TK_FRAC && tok != LM_TK_SQRT
55 && tok != LM_TK_SPACE && tok != LM_TK_DOTS
56 && tok != LM_TK_FUNCLIM
57 && tok != LM_TK_BIGSYM && tok != LM_TK_ACCENT &&
58 !(tok == LM_TK_SYM && id < 255));
61 static int const MAX_STACK_ITEMS = 32;
63 struct MathStackXIter {
67 MathStackXIter(int n = MAX_STACK_ITEMS): imax(n) {
68 item = new MathedXIter[imax];
72 MathStackXIter(MathStackXIter & stk);
78 void push(MathedXIter ** a) {
87 MathedXIter * Item(int idx) {
88 return (idx + 1 <= i) ? &item[i - idx - 1] : 0;
96 return i >= MAX_STACK_ITEMS;
103 int Level() { return i; }
105 } mathstk, *selstk = 0;
108 MathStackXIter::MathStackXIter(MathStackXIter & stk)
111 item = new MathedXIter[imax];
113 for (int k = 0; k < i; ++k) {
114 item[k].SetData(stk.item[k].getPar());
116 item[k].goPosAbs(stk.item[k].getPos());
121 /***---------------- Mathed Cursor ---------------------------***/
123 MathedCursor::MathedCursor(MathParInset * p) // : par(p)
127 lastcode = LM_TC_MIN;
130 if (!MathMacroTable::built)
131 MathMacroTable::mathMTable.builtinMacros();
135 void MathedCursor::SetPar(MathParInset * p)
140 selection = false; // not SelClear() ?
142 mathstk.push(&cursor);
144 cursor->SetData(par);
148 void MathedCursor::draw(Painter & pain, int x, int y)
150 // lyxerr << "Cursor[" << x << " " << y << "] ";
151 //win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
153 int w = par->Width() + 2;
154 int a = par->Ascent() + 1;
155 int h = par->Height() + 1;
156 if (par->GetType() > LM_OT_PAR) { a += 4; h += 8; }
158 pain.rectangle(x - 1, y - a, w, h, LColor::mathframe);
160 par->draw(pain, x, y);
165 void MathedCursor::Redraw(Painter & pain)
167 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
169 int w = par->Width(), h = par->Height();
173 //mathed_set_font(LM_TC_VAR, 1);
174 pain.fillRectangle(x, y - par->Ascent(),
175 x + w, y - par->Ascent() + h,
177 par->draw(pain, x, y);
181 bool MathedCursor::Left(bool sel)
188 if (sel && !selection) SelStart();
189 if (!sel && selection) SelClear();
190 bool result = cursor->Prev();
191 if (!result && !mathstk.Empty()) {
192 cursor = mathstk.pop();
195 if (selection) SelClear();
197 if (result && cursor->IsActive()) {
198 if (cursor->IsScript()) {
200 if (!cursor->IsScript())
206 MathParInset * p = cursor->GetActiveInset();
210 p->setArgumentIdx(p->getMaxArgumentIdx());
211 mathstk.push(&cursor);
221 bool MathedCursor::Pop()
223 if (!mathstk.Empty()) {
224 cursor = mathstk.pop();
233 bool MathedCursor::Push()
235 if (cursor->IsActive()) {
236 MathParInset * p = cursor->GetActiveInset();
237 if (!p) return false;
238 mathstk.push(&cursor);
246 bool MathedCursor::Right(bool sel)
253 if (sel && !selection) SelStart();
254 if (!sel && selection) SelClear();
257 if (cursor->IsActive()) {
258 if (cursor->IsScript()) {
260 // A script may be followed by another script
261 if (cursor->IsScript())
266 MathParInset *p = cursor->GetActiveInset();
268 lyxerr << "Math error: Inset expected." << endl;
269 return cursor->Next();
271 p->setArgumentIdx(0);
272 mathstk.push(&cursor);
276 result = cursor->Next();
278 if (cursor->GetChar()!= LM_TC_CR)
279 result = cursor->Next();
280 if (!result && !mathstk.Empty()) {
281 cursor = mathstk.pop();
285 if (selection) SelClear();
292 void MathedCursor::SetPos(int x, int y)
296 if (macro_mode) MacroModeClose();
297 lastcode = LM_TC_MIN;
299 mathstk.push(&cursor);
300 cursor->SetData(par);
301 cursor->fitCoord(x, y);
302 while (cursor->GetX()<x && cursor->OK()) {
303 if (cursor->IsActive()) {
304 MathParInset * p = cursor->GetActiveInset();
305 if (p->Inside(x, y)) {
307 mathstk.push(&cursor);
309 cursor->fitCoord(x, y);
315 if (!cursor->Next() && !Pop())
318 if (x-xp < cursor->GetX()-x) cursor->ipop();
323 void MathedCursor::Home()
325 if (macro_mode) MacroModeClose();
328 mathstk.push(&cursor);
333 void MathedCursor::End()
335 if (macro_mode) MacroModeClose();
338 mathstk.push(&cursor);
343 MathMatrixInset * create_multiline(short int type, int cols)
354 for (int i = 0; i < cols; ++i)
360 for (int i = 0; i < cols; ++i)
364 case LM_OT_MULTLINEN:
376 MathMatrixInset * mt = new MathMatrixInset(columns, -1);
377 mt->SetAlign(' ', align);
382 void MathedCursor::Insert(byte c, MathedTextCodes t)
384 if (selection) SelDel();
389 if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
393 MathParInset * p = cursor->p;
394 if (p == par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
395 short int type = LM_OT_MPAR;
397 if (c >= '1' && c <= '9') {
400 } else if (c >= 'A' && c <= 'I') {
401 type = LM_OT_ALIGNAT;
404 type = LM_OT_MULTLINE;
408 if (p->GetType() == LM_OT_PARN)
410 MathMatrixInset * mt = create_multiline(type, cols);
411 mt->SetStyle(LM_ST_DISPLAY);
413 mt->SetData(p->GetData());
414 p->SetData(0);//BUG duda
419 int pos = cursor->getPos();
420 cursor->SetData(par);
421 cursor->goPosAbs(pos);
423 if (p && p->Permit(LMPF_ALLOW_CR)) {
427 if (t == LM_TC_TAB) {
428 MathParInset * p = cursor->p;
429 if (p && p->Permit(LMPF_ALLOW_TAB)) {
431 cursor->Insert(c, t);
434 cursor->goNextColumn();
435 } else // Navigate between arguments
436 if (p && p->GetType() == LM_OT_MACRO) {
437 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
438 p->setArgumentIdx(p->getArgumentIdx() + 1);
445 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
453 cursor->Insert(c, t);
462 void MathedCursor::Insert(MathedInset * p, int t)
464 if (macro_mode) MacroModeClose();
466 if (MathIsActive(t)) {
468 static_cast<MathParInset*>(p)->SetData(selarray);
473 if (mathstk.i < MAX_STACK_ITEMS - 1) {
475 if (accent && !MathIsActive(t)) {
478 cursor->Insert(p, t);
480 if (MathIsActive(t)) {
487 lyxerr << "Math error: Full stack." << endl;
491 void MathedCursor::Delete()
493 if (macro_mode) return;
498 if (cursor->Empty() && !mathstk.Empty()) {
499 cursor = mathstk.pop();
501 // if (cursor->GetChar()!= LM_TC_TAB)
507 void MathedCursor::DelLine()
509 if (macro_mode) MacroModeClose();
514 MathParInset *p= cursor->p;
515 if (p && p->GetType() <= LM_OT_MATRIX && p->GetType() >= LM_OT_MPAR) {
521 bool MathedCursor::Up(bool sel)
525 if (macro_mode) MacroModeClose();
527 if (sel && !selection) SelStart();
528 if (!sel && selection) SelClear();
531 if (cursor->IsScript()) {
532 char cd = cursor->GetChar();
537 // A subscript may be followed by a superscript
540 if (MathIsUp(cursor->GetChar())) {
543 } else // return to the previous state
548 result = cursor->Up();
549 if (!result && cursor->p) {
550 MathParInset * p = cursor->p;
552 if (p->GetType() == LM_OT_SCRIPT) {
553 MathedXIter * cx = mathstk.Item(1);
554 bool is_down = (cx->GetChar() == LM_TC_DOWN);
555 cursor = mathstk.pop();
557 result = (is_down) ? true: Up();
559 result = (p->getArgumentIdx() > 0);
561 p->setArgumentIdx(p->getArgumentIdx() - 1);
565 if (!result && !mathstk.Empty()) {
566 cursor = mathstk.pop();
574 bool MathedCursor::Down(bool sel)
578 if (macro_mode) MacroModeClose();
580 if (sel && !selection) SelStart();
581 if (!sel && selection) SelClear();
582 // if (selection) SelClear();
584 if (cursor->IsScript()) {
585 char cd = cursor->GetChar();
586 if (MathIsDown(cd)) {
590 // A superscript may be followed by a subscript
593 if (MathIsDown(cursor->GetChar())) {
601 result = cursor->Down();
602 if (!result && cursor->p) {
603 MathParInset * p= cursor->p;
604 if (p->GetType() == LM_OT_SCRIPT) {
605 MathedXIter * cx = mathstk.Item(1);
606 bool is_up = (cx->GetChar() == LM_TC_UP);
607 cursor = mathstk.pop();
609 result = (is_up) ? true: Down();
611 result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
613 p->setArgumentIdx(p->getArgumentIdx() + 1);
617 if (!result && !mathstk.Empty()) {
618 cursor = mathstk.pop();
626 bool MathedCursor::Limits()
628 if (cursor->IsInset()) {
629 MathedInset * p = cursor->GetInset();
630 bool ol = p->GetLimits();
632 return (ol!= p->GetLimits());
638 void MathedCursor::SetSize(short size)
640 MathParInset * p = cursor->p;
641 p->UserSetSize(size);
646 void MathedCursor::setLabel(string const & label)
647 { // ugly hack and possible bug
648 if (!cursor->setLabel(label))
649 lyxerr << "MathErr: Bad place to set labels." << endl;
653 void MathedCursor::setNumbered()
654 { // another ugly hack
655 MathedRowSt * crow = cursor->crow;
657 crow->setNumbered(!crow->isNumbered());
661 void MathedCursor::Interpret(string const & s)
665 MathedTextCodes tcode = LM_TC_INSET;
667 if (s[0] == '^' || s[0] == '_') {
668 char c = cursor->GetChar();
669 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
672 } else // A script may be followed by a script
673 if (MathIsUp(c) || MathIsDown(c)) {
676 c = cursor->GetChar();
677 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
683 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
684 Insert (p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
687 if (s[0] == '!' || s[0] == ',' || s[0] == ':' || s[0] == ';') {
688 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
689 p = new MathSpaceInset(sp);
696 p = MathMacroTable::mathMTable.getMacro(s);
698 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
700 p = new MathRootInset();
701 tcode = LM_TC_ACTIVE_INSET;
703 p = new MathFuncInset(s, LM_OT_UNDEF);
705 tcode = static_cast<MathMacro*>(p)->getTCode();
706 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
709 MathedInsetTypes fractype = LM_OT_FRAC;
713 p = new MathBigopInset(l->name, l->id);
719 Insert(static_cast<byte>(l->id), MathIsBOPS(l->id) ?
720 LM_TC_BOPS: LM_TC_SYMB);
722 p = new MathFuncInset(l->name);
727 fractype = LM_OT_STACKREL;
728 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
731 p = new MathFracInset(fractype);
732 tcode = LM_TC_ACTIVE_INSET;
737 p = new MathSqrtInset;
738 tcode = LM_TC_ACTIVE_INSET;
743 p = new MathDecorationInset(l->id);
744 tcode = LM_TC_ACTIVE_INSET;
749 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
754 p = new MathSpaceInset(l->id);
759 p = new MathDotsInset(l->name, l->id);
766 p = MathMacroTable::mathMTable.getMacro(s);
767 tcode = static_cast<MathMacro*>(p)->getTCode();
768 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
772 p = new MathFuncInset(l->name);
784 bool MathedCursor::pullArg()
786 if (cursor->IsActive()) {
787 MathParInset * p = cursor->GetActiveInset();
791 MathedArray * a = p->GetData();
805 void MathedCursor::MacroModeOpen()
808 imacro = new MathFuncInset("");
812 lyxerr << "Mathed Warning: Already in macro mode" << endl;
816 void MathedCursor::MacroModeClose()
820 latexkeys * l = in_word_set(imacro->GetName());
821 if (!imacro->GetName().empty()
822 && (!l || (l && IsMacro(l->token, l->id))) &&
823 !MathMacroTable::mathMTable.getMacro(imacro->GetName())) {
825 //imacro->SetName(macrobf);
826 // This guarantees that the string will be removed by destructor
827 imacro->SetType(LM_OT_UNDEF);
829 imacro->SetName(l->name);
832 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
833 setAccent(static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
836 if (l || MathMacroTable::mathMTable.getMacro(imacro->GetName())) {
837 Interpret(imacro->GetName());
846 void MathedCursor::MacroModeBack()
849 if (!imacro->GetName().empty()) {
850 imacro->SetName(imacro->GetName().substr(0, imacro->GetName().length() - 1));
855 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
859 void MathedCursor::MacroModeInsert(char c)
862 imacro->SetName(imacro->GetName() + c);
865 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
869 void MathedCursor::SelCopy()
872 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
873 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
874 selarray = cursor->Copy(p1, p2);
881 void MathedCursor::SelCut()
884 if (cursor->pos == selpos) return;
886 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
887 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
888 selarray = cursor->Copy(p1, p2);
889 cursor->Clean(selpos);
896 void MathedCursor::SelDel()
898 // lyxerr << "Deleting sel "
900 if (cursor->pos == selpos) return;
901 cursor->Clean(selpos);
908 void MathedCursor::SelPaste()
910 // lyxerr << "paste " << selarray << " " << curor->pos;
911 if (selection) SelDel();
913 cursor->Merge(selarray);
919 void MathedCursor::SelStart()
921 lyxerr[Debug::MATHED] << "Starting sel " << endl;
923 selpos = cursor->pos;
924 selstk = new MathStackXIter(mathstk);
925 anchor = selstk->Item(-1);
926 anchor->SetData(cursor->p);
928 anchor->goPosAbs(selpos);
935 void MathedCursor::SelClear()
937 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
946 // Anchor position must be at the same level that stack.
947 void MathedCursor::SelBalance()
949 int d = mathstk.Level() - selstk->Level();
951 // If unbalanced, balance them
954 // lyxerr << "b[" << mathstk.Level() << " " << selstk->Level << " " << anchor->GetX() << " " << cursor->GetX() << "]";
955 anchor = selstk->pop();
956 if (anchor->GetX() >= cursor->GetX())
959 // lyxerr <<"a[" << mathstk.Level() << " " << selstk->Level() <<"]";
962 d = mathstk.Level() - selstk->Level();
965 // Once balanced the levels, check that they are at the same paragraph
966 selpos = anchor->pos;
970 void MathedCursor::SelGetArea(int ** xp, int ** yp, int & np)
972 static int xpoint[10];
973 static int ypoint[10];
984 // single row selection
991 // Balance anchor and cursor
996 cursor->p->GetXY(xo, yo);
997 int w = cursor->p->Width();
1000 cursor->GetPos(x1, y1);
1001 cursor->getAD(a1, d1);
1004 anchor->GetPos(x, y);
1005 anchor->getAD(a, d);
1008 ypoint[i++] = y + d;
1010 ypoint[i++] = y - a;
1014 ypoint[i++] = y - a;
1018 ypoint[i++] = y1 - a;
1023 ypoint[i++] = y1 - a;
1025 ypoint[i++] = y1 + d;
1029 ypoint[i++] = y1 + d;
1032 ypoint[i++] = y + d;
1035 xpoint[i] = xpoint[0];
1036 ypoint[i++] = ypoint[0];
1041 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
1042 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
1043 // for (i = 0; i < np; ++i)
1044 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
1049 void MathedCursor::setAccent(int ac)
1051 if (ac > 0 && accent < 8) {
1052 nestaccent[accent++] = ac;
1054 accent = 0; // consumed!
1058 int MathedCursor::getAccent() const
1060 return (accent > 0) ? nestaccent[accent - 1]: 0;
1064 void MathedCursor::doAccent(byte c, MathedTextCodes t)
1066 MathedInset * ac = 0;
1068 for (int i = accent - 1; i >= 0; --i) {
1069 if (i == accent - 1)
1070 ac = new MathAccentInset(c, t, nestaccent[i]);
1072 ac = new MathAccentInset(ac, nestaccent[i]);
1077 accent = 0; // consumed!
1081 void MathedCursor::doAccent(MathedInset * p)
1083 MathedInset * ac = 0;
1085 for (int i = accent - 1; i >= 0; --i) {
1086 if (i == accent - 1)
1087 ac = new MathAccentInset(p, nestaccent[i]);
1089 ac = new MathAccentInset(ac, nestaccent[i]);
1094 accent = 0; // consumed!
1098 void MathedCursor::toggleLastCode(MathedTextCodes t)
1101 lastcode = LM_TC_VAR;
1107 void MathedCursor::GetPos(int & x, int & y)
1109 cursor->GetPos(x, y);
1113 short MathedCursor::GetFCode()
1115 return cursor->FCode();
1119 MathParInset * MathedCursor::GetPar()
1125 MathParInset * MathedCursor::getCurrentPar() const
1131 string const & MathedCursor::getLabel() const
1133 return cursor->getLabel();
1137 bool MathedCursor::IsEnd() const
1139 return !cursor->OK();
1143 bool MathedCursor::InMacroMode()
1149 bool MathedCursor::Selection()
1155 void MathedCursor::clearLastCode()
1157 lastcode = LM_TC_MIN;
1161 void MathedCursor::setLastCode(MathedTextCodes t)
1167 MathedTextCodes MathedCursor::getLastCode() const