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: (c) 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"
35 extern void mathed_set_font(short type, int style);
39 extern GC canvasGC, mathGC, latexGC, cursorGC, mathFrameGC;
42 static LyxArrayBase * selarray = 0;
44 inline bool IsAlpha(char c)
46 return ('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z');
49 // This was very smaller, I'll change it later
50 inline bool IsMacro(short tok, int id)
52 return (tok != LM_TK_STACK && tok != LM_TK_FRAC && tok != LM_TK_SQRT
54 && tok != LM_TK_SPACE && tok != LM_TK_DOTS
55 && tok != LM_TK_FUNCLIM
56 && tok != LM_TK_BIGSYM && tok != LM_TK_ACCENT &&
57 !(tok == LM_TK_SYM && id < 255));
61 // Yes, mathed isn't using string yet.
62 inline char * strnew(char const * s)
64 char * s1 = new char[strlen(s)+1];
71 #define MAX_STACK_ITEMS 32
73 struct MathStackXIter {
78 MathStackXIter(int n = MAX_STACK_ITEMS): imax(n) {
79 item = new MathedXIter[imax];
83 MathStackXIter(MathStackXIter & stk);
89 void push(MathedXIter ** a) {
98 MathedXIter * Item(int idx) {
99 return (idx + 1 <= i) ? &item[i - idx - 1] : 0;
107 return i >= MAX_STACK_ITEMS;
114 int Level() { return i; }
116 } mathstk, *selstk = 0;
119 MathStackXIter::MathStackXIter(MathStackXIter & stk) {
121 item = new MathedXIter[imax];
123 for (int k = 0; k < i; ++k) {
124 item[k].SetData(stk.item[k].getPar());
126 item[k].goPosAbs(stk.item[k].getPos());
131 /***---------------- Mathed Cursor ---------------------------***/
133 MathedCursor::MathedCursor(MathParInset * p) // : par(p)
137 lastcode = LM_TC_MIN;
140 if (!MathMacroTable::built)
141 MathMacroTable::mathMTable.builtinMacros();
145 void MathedCursor::SetPar(MathParInset * p)
150 selection = false; // not SelClear() ?
152 mathstk.push(&cursor);
154 cursor->SetData(par);
159 void MathedCursor::draw(Painter & pain, int x, int y)
161 // lyxerr << "Cursor[" << x << " " << y << "] ";
162 //win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
164 int w = par->Width() + 2;
165 int a = par->Ascent() + 1;
166 int h = par->Height() + 1;
167 if (par->GetType() > LM_OT_PAR) { a += 4; h += 8; }
169 pain.rectangle(x - 1, y - a, w, h, LColor::mathframe);
171 par->draw(pain, x, y);
175 void MathedCursor::Draw(long unsigned pm, int x, int y)
177 // lyxerr << "Cursor[" << x << " " << y << "] ";
178 win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
180 int w = par->Width()+2, a = par->Ascent()+1, h = par->Height()+1;
181 if (par->GetType() > LM_OT_PAR) { a += 4; h += 8; }
183 if (!canvasGC) mathed_set_font(LM_TC_VAR, 1);
184 // XFillRectangle(fl_display, pm, canvasGC, x, y-a, w, h);
185 XDrawRectangle(fl_display, pm, mathFrameGC, x - 1, y - a, w, h);
187 MathParInset::pm = pm;
195 void MathedCursor::Redraw(Painter & pain)
197 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
199 int w = par->Width(), h = par->Height();
202 //mathed_set_font(LM_TC_VAR, 1);
203 pain.fillRectangle(x, y - par->Ascent(),
204 x + w, y - par->Ascent() + h,
206 par->draw(pain, x, y);
209 void MathedCursor::Redraw()
211 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
213 int w = par->Width(), h = par->Height();
216 mathed_set_font(LM_TC_VAR, 1);
217 XFillRectangle(fl_display, win, canvasGC, x, y-par->Ascent(), w, h);
219 MathParInset::pm = win;
225 bool MathedCursor::Left(bool sel)
232 if (sel && !selection) SelStart();
233 if (!sel && selection) SelClear();
234 bool result = cursor->Prev();
235 if (!result && !mathstk.Empty()) {
236 cursor = mathstk.pop();
239 if (selection) SelClear();
241 if (result && cursor->IsActive()) {
242 if (cursor->IsScript()) {
244 if (!cursor->IsScript())
250 MathParInset * p = cursor->GetActiveInset();
254 p->setArgumentIdx(p->getMaxArgumentIdx());
255 mathstk.push(&cursor);
265 bool MathedCursor::Pop()
267 if (!mathstk.Empty()) {
268 cursor = mathstk.pop();
277 bool MathedCursor::Push()
279 if (cursor->IsActive()) {
280 MathParInset * p = cursor->GetActiveInset();
281 if (!p) return false;
282 mathstk.push(&cursor);
290 bool MathedCursor::Right(bool sel)
297 if (sel && !selection) SelStart();
298 if (!sel && selection) SelClear();
301 if (cursor->IsActive()) {
302 if (cursor->IsScript()) {
304 // A script may be followed by another script
305 if (cursor->IsScript())
310 MathParInset *p = cursor->GetActiveInset();
312 lyxerr << "Math error: Inset expected." << endl;
313 return cursor->Next();
315 p->setArgumentIdx(0);
316 mathstk.push(&cursor);
320 result = cursor->Next();
322 if (cursor->GetChar()!= LM_TC_CR)
323 result = cursor->Next();
324 if (!result && !mathstk.Empty()) {
325 cursor = mathstk.pop();
329 if (selection) SelClear();
336 void MathedCursor::SetPos(int x, int y)
340 if (macro_mode) MacroModeClose();
341 lastcode = LM_TC_MIN;
343 mathstk.push(&cursor);
344 cursor->SetData(par);
345 cursor->fitCoord(x, y);
346 while (cursor->GetX()<x && cursor->OK()) {
347 if (cursor->IsActive()) {
348 MathParInset * p = cursor->GetActiveInset();
349 if (p->Inside(x, y)) {
351 mathstk.push(&cursor);
353 cursor->fitCoord(x, y);
359 if (!cursor->Next() && !Pop())
362 if (x-xp < cursor->GetX()-x) cursor->ipop();
367 void MathedCursor::Home()
369 if (macro_mode) MacroModeClose();
372 mathstk.push(&cursor);
377 void MathedCursor::End()
379 if (macro_mode) MacroModeClose();
382 mathstk.push(&cursor);
387 void MathedCursor::Insert(byte c, MathedTextCodes t)
389 if (selection) SelDel();
394 if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
398 MathParInset * p = cursor->p;
399 if (p == par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
400 MathMatrixInset * mt = new MathMatrixInset(3, 0);
401 mt->SetAlign(' ', "rcl");
402 mt->SetStyle(LM_ST_DISPLAY);
403 mt->SetType((p->GetType() == LM_OT_PARN) ? LM_OT_MPARN: LM_OT_MPAR);
404 mt->SetData(p->GetData());
405 p->SetData(0);//BUG duda
410 int pos = cursor->getPos();
411 cursor->SetData(par);
412 cursor->goPosAbs(pos);
414 if (p && p->Permit(LMPF_ALLOW_CR)) {
418 if (t == LM_TC_TAB) {
419 MathParInset * p = cursor->p;
420 if (p && p->Permit(LMPF_ALLOW_TAB)) {
422 cursor->Insert(c, t);
425 cursor->goNextColumn();
426 } else // Navigate between arguments
427 if (p && p->GetType() == LM_OT_MACRO) {
428 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
429 p->setArgumentIdx(p->getArgumentIdx()+1);
436 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
444 cursor->Insert(c, t);
453 void MathedCursor::Insert(MathedInset * p, int t)
455 if (macro_mode) MacroModeClose();
457 if (MathIsActive(t)) {
459 static_cast<MathParInset*>(p)->SetData(selarray);
464 if (mathstk.i < MAX_STACK_ITEMS - 1) {
466 if (accent && !MathIsActive(t)) {
469 cursor->Insert(p, t);
471 if (MathIsActive(t)) {
478 lyxerr << "Math error: Full stack." << endl;
482 void MathedCursor::Delete()
484 if (macro_mode) return;
489 if (cursor->Empty() && !mathstk.Empty()) {
490 cursor = mathstk.pop();
492 // if (cursor->GetChar()!= LM_TC_TAB)
498 void MathedCursor::DelLine()
500 if (macro_mode) MacroModeClose();
505 MathParInset *p= cursor->p;
506 if (p && (p->GetType()<= LM_OT_MATRIX && p->GetType()>= LM_OT_MPAR)) {
512 bool MathedCursor::Up(bool sel)
516 if (macro_mode) MacroModeClose();
518 if (sel && !selection) SelStart();
519 if (!sel && selection) SelClear();
522 if (cursor->IsScript()) {
523 char cd = cursor->GetChar();
528 // A subscript may be followed by a superscript
531 if (MathIsUp(cursor->GetChar())) {
534 } else // return to the previous state
539 result = cursor->Up();
540 if (!result && cursor->p) {
541 MathParInset * p = cursor->p;
543 if (p->GetType() == LM_OT_SCRIPT) {
544 MathedXIter * cx = mathstk.Item(1);
545 bool is_down = (cx->GetChar() == LM_TC_DOWN);
546 cursor = mathstk.pop();
548 result = (is_down) ? true: Up();
550 result = (p->getArgumentIdx() > 0);
552 p->setArgumentIdx(p->getArgumentIdx()-1);
556 if (!result && !mathstk.Empty()) {
557 cursor = mathstk.pop();
565 bool MathedCursor::Down(bool sel)
569 if (macro_mode) MacroModeClose();
571 if (sel && !selection) SelStart();
572 if (!sel && selection) SelClear();
573 // if (selection) SelClear();
575 if (cursor->IsScript()) {
576 char cd = cursor->GetChar();
577 if (MathIsDown(cd)) {
581 // A superscript may be followed by a subscript
584 if (MathIsDown(cursor->GetChar())) {
592 result = cursor->Down();
593 if (!result && cursor->p) {
594 MathParInset * p= cursor->p;
595 if (p->GetType() == LM_OT_SCRIPT) {
596 MathedXIter * cx = mathstk.Item(1);
597 bool is_up = (cx->GetChar() == LM_TC_UP);
598 cursor = mathstk.pop();
600 result = (is_up) ? true: Down();
602 result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
604 p->setArgumentIdx(p->getArgumentIdx()+1);
608 if (!result && !mathstk.Empty()) {
609 cursor = mathstk.pop();
617 bool MathedCursor::Limits()
619 if (cursor->IsInset()) {
620 MathedInset * p = cursor->GetInset();
621 bool ol = p->GetLimits();
623 return (ol!= p->GetLimits());
629 void MathedCursor::SetSize(short size)
631 MathParInset * p = cursor->p;
632 p->UserSetSize(size);
637 void MathedCursor::setLabel(char const * label)
638 { // ugly hack and possible bug
639 if (!cursor->setLabel(strnew(label)))
640 lyxerr << "MathErr: Bad place to set labels." << endl;
644 void MathedCursor::setNumbered()
645 { // another ugly hack
646 MathedRowSt * crow = cursor->crow;
648 crow->setNumbered(!crow->isNumbered());
652 void MathedCursor::Interpret(char const * s)
656 MathedTextCodes tcode = LM_TC_INSET;
658 if (s[0] == '^' || s[0] == '_') {
659 char c = cursor->GetChar();
660 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
663 } else // A script may be followed by a script
664 if (MathIsUp(c) || MathIsDown(c)) {
667 c = cursor->GetChar();
668 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
674 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
675 Insert (p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
678 if (s[0] == '!' || s[0] == ',' || s[0] == ':' || s[0] == ';') {
679 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
680 p = new MathSpaceInset(sp);
684 l = in_word_set (s, strlen(s));
687 p = MathMacroTable::mathMTable.getMacro(s);
689 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
690 if (strcmp("root", s) == 0) {
691 p = new MathRootInset();
692 tcode = LM_TC_ACTIVE_INSET;
694 p = new MathFuncInset(s, LM_OT_UNDEF);
696 tcode = static_cast<MathMacro*>(p)->getTCode();
697 lyxerr << "Macro2 " << s << ' ' << tcode << " " ;
700 MathedInsetTypes fractype = LM_OT_FRAC;
704 p = new MathBigopInset(l->name, l->id);
710 Insert(static_cast<byte>(l->id), MathIsBOPS(l->id) ?
711 LM_TC_BOPS: LM_TC_SYMB);
713 p = new MathFuncInset(l->name);
718 fractype = LM_OT_STACKREL;
719 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
722 p = new MathFracInset(fractype);
723 tcode = LM_TC_ACTIVE_INSET;
728 p = new MathSqrtInset;
729 tcode = LM_TC_ACTIVE_INSET;
734 p = new MathDecorationInset(l->id);
735 tcode = LM_TC_ACTIVE_INSET;
740 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
745 p = new MathSpaceInset(l->id);
750 p = new MathDotsInset(l->name, l->id);
757 p = MathMacroTable::mathMTable.getMacro(s);
758 tcode = static_cast<MathMacro*>(p)->getTCode();
759 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
763 p = new MathFuncInset(l->name);
775 bool MathedCursor::pullArg()
777 if (cursor->IsActive()) {
778 MathParInset * p = cursor->GetActiveInset();
782 LyxArrayBase * a = p->GetData();
796 void MathedCursor::MacroModeOpen()
801 imacro = new MathFuncInset(¯obf[0]);
805 lyxerr << "Mathed Warning: Already in macro mode" << endl;
809 void MathedCursor::MacroModeClose()
813 latexkeys * l = in_word_set(macrobf, macroln);
814 if (macroln > 0 && (!l || (l && IsMacro(l->token, l->id))) &&
815 !MathMacroTable::mathMTable.getMacro(macrobf)) {
817 imacro->SetName(strnew(macrobf));
818 // This guarantees that the string will be removed by destructor
819 imacro->SetType(LM_OT_UNDEF);
821 imacro->SetName(l->name);
825 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
826 setAccent(static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
829 if (l || MathMacroTable::mathMTable.getMacro(macrobf)) {
838 void MathedCursor::MacroModeBack()
842 macrobf[--macroln] = '\0';
847 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
851 void MathedCursor::MacroModeInsert(char c)
854 macrobf[macroln + 1] = macrobf[macroln];
855 macrobf[macroln++] = c;
858 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
862 void MathedCursor::SelCopy()
865 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
866 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
867 selarray = cursor->Copy(p1, p2);
874 void MathedCursor::SelCut()
877 if (cursor->pos == selpos) return;
879 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
880 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
881 selarray = cursor->Copy(p1, p2);
882 cursor->Clean(selpos);
889 void MathedCursor::SelDel()
891 // lyxerr << "Deleting sel "
893 if (cursor->pos == selpos) return;
894 cursor->Clean(selpos);
901 void MathedCursor::SelPaste()
903 // lyxerr << "paste " << selarray << " " << curor->pos;
904 if (selection) SelDel();
906 cursor->Merge(selarray);
912 void MathedCursor::SelStart()
914 lyxerr[Debug::MATHED] << "Starting sel " << endl;
916 selpos = cursor->pos;
917 selstk = new MathStackXIter(mathstk);
918 anchor = selstk->Item(-1);
919 anchor->SetData(cursor->p);
921 anchor->goPosAbs(selpos);
928 void MathedCursor::SelClear()
930 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
939 // Anchor position must be at the same level that stack.
940 void MathedCursor::SelBalance()
942 int d = mathstk.Level() - selstk->Level();
944 // If unbalanced, balance them
947 // lyxerr << "b[" << mathstk.Level() << " " << selstk->Level << " " << anchor->GetX() << " " << cursor->GetX() << "]";
948 anchor = selstk->pop();
949 if (anchor->GetX() >= cursor->GetX())
952 // lyxerr <<"a[" << mathstk.Level() << " " << selstk->Level() <<"]";
955 d = mathstk.Level() - selstk->Level();
958 // Once balanced the levels, check that they are at the same paragraph
959 selpos = anchor->pos;
964 void MathedCursor::SelGetArea(int * xp, int * yp, int & np)
971 static int xpoint[10];
972 static int ypoint[10];
974 // single row selection
975 int i = 0, x, y, a, d, xo, yo, x1, y1, a1, d1;
977 // Balance anchor and cursor
980 cursor->p->GetXY(xo, yo);
981 int w = cursor->p->Width();
982 cursor->GetPos(x1, y1);
983 cursor->getAD(a1, d1);
984 anchor->GetPos(x, y);
998 ypoint[i++] = y1 - a;
1003 ypoint[i++] = y1 - a;
1005 ypoint[i++] = y1 + d;
1009 ypoint[i++] = y1 + d;
1012 ypoint[i++] = y + d;
1015 xpoint[i] = xpoint[0];
1016 ypoint[i++] = ypoint[0];
1021 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
1022 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
1023 // for (i = 0; i < np; ++i)
1024 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
1028 XPoint * MathedCursor::SelGetArea(int & np)
1035 static XPoint point[10];
1037 // single row selection
1038 int i = 0, x, y, a, d, xo, yo, x1, y1, a1, d1; //, p1, p2;
1040 // Balance anchor and cursor
1043 cursor->p->GetXY(xo, yo);
1044 int w = cursor->p->Width();
1045 cursor->GetPos(x1, y1);
1046 cursor->getAD(a1, d1);
1047 anchor->GetPos(x, y);
1048 anchor->getAD(a, d);
1056 point[i].x = xo + w;
1057 point[i++].y = y - a;
1059 point[i].x = xo + w;
1060 point[i++].y = y1 - a;
1065 point[i++].y = y1 - a;
1067 point[i++].y = y1 + d;
1071 point[i++].y = y1 + d;
1074 point[i++].y = y + d;
1077 point[i].x = point[0].x;
1078 point[i++].y = point[0].y;
1080 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
1081 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
1082 // for (i = 0; i < np; ++i)
1083 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
1090 void MathedCursor::setAccent(int ac)
1092 if (ac > 0 && accent < 8) {
1093 nestaccent[accent++] = ac;
1095 accent = 0; // consumed!
1099 int MathedCursor::getAccent() const
1101 return (accent > 0) ? nestaccent[accent - 1]: 0;
1105 void MathedCursor::doAccent(byte c, MathedTextCodes t)
1107 MathedInset * ac = 0;
1109 for (int i = accent - 1; i >= 0; --i) {
1110 if (i == accent - 1)
1111 ac = new MathAccentInset(c, t, nestaccent[i]);
1113 ac = new MathAccentInset(ac, nestaccent[i]);
1118 accent = 0; // consumed!
1122 void MathedCursor::doAccent(MathedInset * p)
1124 MathedInset * ac = 0;
1126 for (int i = accent - 1; i >= 0; --i) {
1127 if (i == accent - 1)
1128 ac = new MathAccentInset(p, nestaccent[i]);
1130 ac = new MathAccentInset(ac, nestaccent[i]);
1135 accent = 0; // consumed!