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_root.h"
29 #include "support/lstrings.h"
34 static LyxArrayBase * selarray = 0;
38 // This was very smaller, I'll change it later
40 bool IsMacro(short tok, int id)
42 return (tok != LM_TK_STACK && tok != LM_TK_FRAC && tok != LM_TK_SQRT
44 && tok != LM_TK_SPACE && tok != LM_TK_DOTS
45 && tok != LM_TK_FUNCLIM
46 && tok != LM_TK_BIGSYM && tok != LM_TK_ACCENT &&
47 !(tok == LM_TK_SYM && id < 255));
50 static int const MAX_STACK_ITEMS = 32;
52 struct MathStackXIter {
56 MathStackXIter(int n = MAX_STACK_ITEMS): imax(n) {
57 item = new MathedXIter[imax];
61 MathStackXIter(MathStackXIter & stk);
67 void push(MathedXIter ** a) {
76 MathedXIter * Item(int idx) {
77 return (idx + 1 <= i) ? &item[i - idx - 1] : 0;
85 return i >= MAX_STACK_ITEMS;
92 int Level() { return i; }
94 } mathstk, *selstk = 0;
97 MathStackXIter::MathStackXIter(MathStackXIter & stk)
100 item = new MathedXIter[imax];
102 for (int k = 0; k < i; ++k) {
103 item[k].SetData(stk.item[k].getPar());
105 item[k].goPosAbs(stk.item[k].getPos());
110 /***---------------- Mathed Cursor ---------------------------***/
112 MathedCursor::MathedCursor(MathParInset * p) // : par(p)
116 lastcode = LM_TC_MIN;
119 if (!MathMacroTable::built)
120 MathMacroTable::mathMTable.builtinMacros();
124 void MathedCursor::SetPar(MathParInset * p)
129 selection = false; // not SelClear() ?
131 mathstk.push(&cursor);
133 cursor->SetData(par);
137 void MathedCursor::draw(Painter & pain, int x, int y)
139 // lyxerr << "Cursor[" << x << " " << y << "] ";
140 //win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
142 int w = par->Width() + 2;
143 int a = par->Ascent() + 1;
144 int h = par->Height() + 1;
145 if (par->GetType() > LM_OT_PAR) { a += 4; h += 8; }
147 pain.rectangle(x - 1, y - a, w, h, LColor::mathframe);
149 par->draw(pain, x, y);
154 void MathedCursor::Redraw(Painter & pain)
156 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
158 int w = par->Width(), h = par->Height();
162 //mathed_set_font(LM_TC_VAR, 1);
163 pain.fillRectangle(x, y - par->Ascent(),
164 x + w, y - par->Ascent() + h,
166 par->draw(pain, x, y);
170 bool MathedCursor::Left(bool sel)
177 if (sel && !selection) SelStart();
178 if (!sel && selection) SelClear();
179 bool result = cursor->Prev();
180 if (!result && !mathstk.Empty()) {
181 cursor = mathstk.pop();
184 if (selection) SelClear();
186 if (result && cursor->IsActive()) {
187 if (cursor->IsScript()) {
189 if (!cursor->IsScript())
195 MathParInset * p = cursor->GetActiveInset();
199 p->setArgumentIdx(p->getMaxArgumentIdx());
200 mathstk.push(&cursor);
210 bool MathedCursor::Pop()
212 if (!mathstk.Empty()) {
213 cursor = mathstk.pop();
222 bool MathedCursor::Push()
224 if (cursor->IsActive()) {
225 MathParInset * p = cursor->GetActiveInset();
226 if (!p) return false;
227 mathstk.push(&cursor);
235 bool MathedCursor::Right(bool sel)
242 if (sel && !selection) SelStart();
243 if (!sel && selection) SelClear();
246 if (cursor->IsActive()) {
247 if (cursor->IsScript()) {
249 // A script may be followed by another script
250 if (cursor->IsScript())
255 MathParInset *p = cursor->GetActiveInset();
257 lyxerr << "Math error: Inset expected." << endl;
258 return cursor->Next();
260 p->setArgumentIdx(0);
261 mathstk.push(&cursor);
265 result = cursor->Next();
267 if (cursor->GetChar()!= LM_TC_CR)
268 result = cursor->Next();
269 if (!result && !mathstk.Empty()) {
270 cursor = mathstk.pop();
274 if (selection) SelClear();
281 void MathedCursor::SetPos(int x, int y)
285 if (macro_mode) MacroModeClose();
286 lastcode = LM_TC_MIN;
288 mathstk.push(&cursor);
289 cursor->SetData(par);
290 cursor->fitCoord(x, y);
291 while (cursor->GetX()<x && cursor->OK()) {
292 if (cursor->IsActive()) {
293 MathParInset * p = cursor->GetActiveInset();
294 if (p->Inside(x, y)) {
296 mathstk.push(&cursor);
298 cursor->fitCoord(x, y);
304 if (!cursor->Next() && !Pop())
307 if (x-xp < cursor->GetX()-x) cursor->ipop();
312 void MathedCursor::Home()
314 if (macro_mode) MacroModeClose();
317 mathstk.push(&cursor);
322 void MathedCursor::End()
324 if (macro_mode) MacroModeClose();
327 mathstk.push(&cursor);
332 MathMatrixInset * create_multiline(short int type, int cols)
343 for (int i = 0; i < cols; ++i)
349 for (int i = 0; i < cols; ++i)
353 case LM_OT_MULTLINEN:
355 align = "c"; // This is incorrect!
365 MathMatrixInset * mt = new MathMatrixInset(columns, -1);
366 mt->SetAlign(' ', align);
370 void MathedCursor::Insert(byte c, MathedTextCodes t)
372 if (selection) SelDel();
377 if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
381 MathParInset * p = cursor->p;
382 if (p == par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
383 short int type = LM_OT_MPAR;
385 if (c >= '1' && c <= '9') {
388 } else if (c >= 'A' && c <= 'I') {
389 type = LM_OT_ALIGNAT;
392 type = LM_OT_MULTLINE;
396 if (p->GetType() == LM_OT_PARN)
398 MathMatrixInset * mt = create_multiline(type, cols);
399 mt->SetStyle(LM_ST_DISPLAY);
401 mt->SetData(p->GetData());
402 p->SetData(0);//BUG duda
407 int pos = cursor->getPos();
408 cursor->SetData(par);
409 cursor->goPosAbs(pos);
411 if (p && p->Permit(LMPF_ALLOW_CR)) {
415 if (t == LM_TC_TAB) {
416 MathParInset * p = cursor->p;
417 if (p && p->Permit(LMPF_ALLOW_TAB)) {
419 cursor->Insert(c, t);
422 cursor->goNextColumn();
423 } else // Navigate between arguments
424 if (p && p->GetType() == LM_OT_MACRO) {
425 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
426 p->setArgumentIdx(p->getArgumentIdx() + 1);
433 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
441 cursor->Insert(c, t);
450 void MathedCursor::Insert(MathedInset * p, int t)
452 if (macro_mode) MacroModeClose();
454 if (MathIsActive(t)) {
456 static_cast<MathParInset*>(p)->SetData(selarray);
461 if (mathstk.i < MAX_STACK_ITEMS - 1) {
463 if (accent && !MathIsActive(t)) {
466 cursor->Insert(p, t);
468 if (MathIsActive(t)) {
475 lyxerr << "Math error: Full stack." << endl;
479 void MathedCursor::Delete()
481 if (macro_mode) return;
486 if (cursor->Empty() && !mathstk.Empty()) {
487 cursor = mathstk.pop();
489 // if (cursor->GetChar()!= LM_TC_TAB)
495 void MathedCursor::DelLine()
497 if (macro_mode) MacroModeClose();
502 MathParInset *p= cursor->p;
503 if (p && p->GetType() <= LM_OT_MATRIX && p->GetType() >= LM_OT_MPAR) {
509 bool MathedCursor::Up(bool sel)
513 if (macro_mode) MacroModeClose();
515 if (sel && !selection) SelStart();
516 if (!sel && selection) SelClear();
519 if (cursor->IsScript()) {
520 char cd = cursor->GetChar();
525 // A subscript may be followed by a superscript
528 if (MathIsUp(cursor->GetChar())) {
531 } else // return to the previous state
536 result = cursor->Up();
537 if (!result && cursor->p) {
538 MathParInset * p = cursor->p;
540 if (p->GetType() == LM_OT_SCRIPT) {
541 MathedXIter * cx = mathstk.Item(1);
542 bool is_down = (cx->GetChar() == LM_TC_DOWN);
543 cursor = mathstk.pop();
545 result = (is_down) ? true: Up();
547 result = (p->getArgumentIdx() > 0);
549 p->setArgumentIdx(p->getArgumentIdx() - 1);
553 if (!result && !mathstk.Empty()) {
554 cursor = mathstk.pop();
562 bool MathedCursor::Down(bool sel)
566 if (macro_mode) MacroModeClose();
568 if (sel && !selection) SelStart();
569 if (!sel && selection) SelClear();
570 // if (selection) SelClear();
572 if (cursor->IsScript()) {
573 char cd = cursor->GetChar();
574 if (MathIsDown(cd)) {
578 // A superscript may be followed by a subscript
581 if (MathIsDown(cursor->GetChar())) {
589 result = cursor->Down();
590 if (!result && cursor->p) {
591 MathParInset * p= cursor->p;
592 if (p->GetType() == LM_OT_SCRIPT) {
593 MathedXIter * cx = mathstk.Item(1);
594 bool is_up = (cx->GetChar() == LM_TC_UP);
595 cursor = mathstk.pop();
597 result = (is_up) ? true: Down();
599 result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
601 p->setArgumentIdx(p->getArgumentIdx() + 1);
605 if (!result && !mathstk.Empty()) {
606 cursor = mathstk.pop();
614 bool MathedCursor::Limits()
616 if (cursor->IsInset()) {
617 MathedInset * p = cursor->GetInset();
618 bool ol = p->GetLimits();
620 return (ol!= p->GetLimits());
626 void MathedCursor::SetSize(short size)
628 MathParInset * p = cursor->p;
629 p->UserSetSize(size);
634 void MathedCursor::setLabel(string const & label)
635 { // ugly hack and possible bug
636 if (!cursor->setLabel(label))
637 lyxerr << "MathErr: Bad place to set labels." << endl;
641 void MathedCursor::setNumbered()
642 { // another ugly hack
643 MathedRowSt * crow = cursor->crow;
645 crow->setNumbered(!crow->isNumbered());
649 void MathedCursor::Interpret(string const & s)
653 MathedTextCodes tcode = LM_TC_INSET;
655 if (s[0] == '^' || s[0] == '_') {
656 char c = cursor->GetChar();
657 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
660 } else // A script may be followed by a script
661 if (MathIsUp(c) || MathIsDown(c)) {
664 c = cursor->GetChar();
665 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
671 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
672 Insert (p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
675 if (s[0] == '!' || s[0] == ',' || s[0] == ':' || s[0] == ';') {
676 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
677 p = new MathSpaceInset(sp);
684 p = MathMacroTable::mathMTable.getMacro(s);
686 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
688 p = new MathRootInset();
689 tcode = LM_TC_ACTIVE_INSET;
691 p = new MathFuncInset(s, LM_OT_UNDEF);
693 tcode = static_cast<MathMacro*>(p)->getTCode();
694 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
697 MathedInsetTypes fractype = LM_OT_FRAC;
701 p = new MathBigopInset(l->name, l->id);
707 Insert(static_cast<byte>(l->id), MathIsBOPS(l->id) ?
708 LM_TC_BOPS: LM_TC_SYMB);
710 p = new MathFuncInset(l->name);
715 fractype = LM_OT_STACKREL;
716 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
719 p = new MathFracInset(fractype);
720 tcode = LM_TC_ACTIVE_INSET;
725 p = new MathSqrtInset;
726 tcode = LM_TC_ACTIVE_INSET;
731 p = new MathDecorationInset(l->id);
732 tcode = LM_TC_ACTIVE_INSET;
737 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
742 p = new MathSpaceInset(l->id);
747 p = new MathDotsInset(l->name, l->id);
754 p = MathMacroTable::mathMTable.getMacro(s);
755 tcode = static_cast<MathMacro*>(p)->getTCode();
756 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
760 p = new MathFuncInset(l->name);
772 bool MathedCursor::pullArg()
774 if (cursor->IsActive()) {
775 MathParInset * p = cursor->GetActiveInset();
779 LyxArrayBase * a = p->GetData();
793 void MathedCursor::MacroModeOpen()
796 imacro = new MathFuncInset("");
800 lyxerr << "Mathed Warning: Already in macro mode" << endl;
804 void MathedCursor::MacroModeClose()
808 latexkeys * l = in_word_set(imacro->GetName());
809 if (!imacro->GetName().empty()
810 && (!l || (l && IsMacro(l->token, l->id))) &&
811 !MathMacroTable::mathMTable.getMacro(imacro->GetName())) {
813 //imacro->SetName(macrobf);
814 // This guarantees that the string will be removed by destructor
815 imacro->SetType(LM_OT_UNDEF);
817 imacro->SetName(l->name);
820 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
821 setAccent(static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
824 if (l || MathMacroTable::mathMTable.getMacro(imacro->GetName())) {
825 Interpret(imacro->GetName());
834 void MathedCursor::MacroModeBack()
837 if (!imacro->GetName().empty()) {
838 imacro->SetName(imacro->GetName().substr(0, imacro->GetName().length() - 1));
843 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
847 void MathedCursor::MacroModeInsert(char c)
850 imacro->SetName(imacro->GetName() + c);
853 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
857 void MathedCursor::SelCopy()
860 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
861 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
862 selarray = cursor->Copy(p1, p2);
869 void MathedCursor::SelCut()
872 if (cursor->pos == selpos) return;
874 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
875 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
876 selarray = cursor->Copy(p1, p2);
877 cursor->Clean(selpos);
884 void MathedCursor::SelDel()
886 // lyxerr << "Deleting sel "
888 if (cursor->pos == selpos) return;
889 cursor->Clean(selpos);
896 void MathedCursor::SelPaste()
898 // lyxerr << "paste " << selarray << " " << curor->pos;
899 if (selection) SelDel();
901 cursor->Merge(selarray);
907 void MathedCursor::SelStart()
909 lyxerr[Debug::MATHED] << "Starting sel " << endl;
911 selpos = cursor->pos;
912 selstk = new MathStackXIter(mathstk);
913 anchor = selstk->Item(-1);
914 anchor->SetData(cursor->p);
916 anchor->goPosAbs(selpos);
923 void MathedCursor::SelClear()
925 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
934 // Anchor position must be at the same level that stack.
935 void MathedCursor::SelBalance()
937 int d = mathstk.Level() - selstk->Level();
939 // If unbalanced, balance them
942 // lyxerr << "b[" << mathstk.Level() << " " << selstk->Level << " " << anchor->GetX() << " " << cursor->GetX() << "]";
943 anchor = selstk->pop();
944 if (anchor->GetX() >= cursor->GetX())
947 // lyxerr <<"a[" << mathstk.Level() << " " << selstk->Level() <<"]";
950 d = mathstk.Level() - selstk->Level();
953 // Once balanced the levels, check that they are at the same paragraph
954 selpos = anchor->pos;
958 void MathedCursor::SelGetArea(int ** xp, int ** yp, int & np)
960 static int xpoint[10];
961 static int ypoint[10];
972 // single row selection
979 // Balance anchor and cursor
984 cursor->p->GetXY(xo, yo);
985 int w = cursor->p->Width();
988 cursor->GetPos(x1, y1);
989 cursor->getAD(a1, d1);
992 anchor->GetPos(x, y);
1002 ypoint[i++] = y - a;
1006 ypoint[i++] = y1 - a;
1011 ypoint[i++] = y1 - a;
1013 ypoint[i++] = y1 + d;
1017 ypoint[i++] = y1 + d;
1020 ypoint[i++] = y + d;
1023 xpoint[i] = xpoint[0];
1024 ypoint[i++] = ypoint[0];
1029 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
1030 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
1031 // for (i = 0; i < np; ++i)
1032 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
1037 void MathedCursor::setAccent(int ac)
1039 if (ac > 0 && accent < 8) {
1040 nestaccent[accent++] = ac;
1042 accent = 0; // consumed!
1046 int MathedCursor::getAccent() const
1048 return (accent > 0) ? nestaccent[accent - 1]: 0;
1052 void MathedCursor::doAccent(byte c, MathedTextCodes t)
1054 MathedInset * ac = 0;
1056 for (int i = accent - 1; i >= 0; --i) {
1057 if (i == accent - 1)
1058 ac = new MathAccentInset(c, t, nestaccent[i]);
1060 ac = new MathAccentInset(ac, nestaccent[i]);
1065 accent = 0; // consumed!
1069 void MathedCursor::doAccent(MathedInset * p)
1071 MathedInset * ac = 0;
1073 for (int i = accent - 1; i >= 0; --i) {
1074 if (i == accent - 1)
1075 ac = new MathAccentInset(p, nestaccent[i]);
1077 ac = new MathAccentInset(ac, nestaccent[i]);
1082 accent = 0; // consumed!
1086 void MathedCursor::toggleLastCode(MathedTextCodes t)
1089 lastcode = LM_TC_VAR;