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"
34 extern void mathed_set_font(short type, int style);
36 extern GC canvasGC, mathGC, latexGC, cursorGC, mathFrameGC;
38 static LyxArrayBase * selarray = 0;
40 inline bool IsAlpha(char c)
42 return ('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z');
45 // This was very smaller, I'll change it later
46 inline bool IsMacro(short tok, int id)
48 return (tok != LM_TK_STACK && tok != LM_TK_FRAC && tok != LM_TK_SQRT
50 && tok != LM_TK_SPACE && tok != LM_TK_DOTS
51 && tok != LM_TK_FUNCLIM
52 && tok != LM_TK_BIGSYM && tok != LM_TK_ACCENT &&
53 !(tok == LM_TK_SYM && id < 255));
57 // Yes, mathed isn't using string yet.
58 inline char * strnew(char const * s)
60 char * s1 = new char[strlen(s)+1];
67 #define MAX_STACK_ITEMS 32
69 struct MathStackXIter {
74 MathStackXIter(int n = MAX_STACK_ITEMS): imax(n) {
75 item = new MathedXIter[imax];
79 MathStackXIter(MathStackXIter & stk);
85 void push(MathedXIter ** a) {
94 MathedXIter * Item(int idx) {
95 return (idx + 1 <= i) ? &item[i - idx - 1] : 0;
103 return i >= MAX_STACK_ITEMS;
110 int Level() { return i; }
112 } mathstk, *selstk = 0;
115 MathStackXIter::MathStackXIter(MathStackXIter & stk) {
117 item = new MathedXIter[imax];
119 for (int k = 0; k < i; ++k) {
120 item[k].SetData(stk.item[k].getPar());
122 item[k].goPosAbs(stk.item[k].getPos());
127 /***---------------- Mathed Cursor ---------------------------***/
129 MathedCursor::MathedCursor(MathParInset * p) // : par(p)
133 lastcode = LM_TC_MIN;
136 if (!MathMacroTable::built)
137 MathMacroTable::mathMTable.builtinMacros();
141 void MathedCursor::SetPar(MathParInset * p)
146 selection = false; // not SelClear() ?
148 mathstk.push(&cursor);
150 cursor->SetData(par);
155 void MathedCursor::draw(Painter & pain, int x, int y)
157 // lyxerr << "Cursor[" << x << " " << y << "] ";
158 //win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
160 int w = par->Width() + 2;
161 int a = par->Ascent() + 1;
162 int h = par->Height() + 1;
163 if (par->GetType() > LM_OT_PAR) { a += 4; h += 8; }
165 pain.rectangle(x - 1, y - a,
166 x - 1 + w, y - a + h,
169 par->draw(pain, x, y);
173 void MathedCursor::Draw(long unsigned pm, int x, int y)
175 // lyxerr << "Cursor[" << x << " " << y << "] ";
176 win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
178 int w = par->Width()+2, a = par->Ascent()+1, h = par->Height()+1;
179 if (par->GetType() > LM_OT_PAR) { a += 4; h += 8; }
181 if (!canvasGC) mathed_set_font(LM_TC_VAR, 1);
182 // XFillRectangle(fl_display, pm, canvasGC, x, y-a, w, h);
183 XDrawRectangle(fl_display, pm, mathFrameGC, x - 1, y - a, w, h);
185 MathParInset::pm = pm;
193 void MathedCursor::Redraw(Painter & pain)
195 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
197 int w = par->Width(), h = par->Height();
200 //mathed_set_font(LM_TC_VAR, 1);
201 pain.fillRectangle(x, y - par->Ascent(),
202 x + w, y - par->Ascent() + h,
204 par->draw(pain, x, y);
207 void MathedCursor::Redraw()
209 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
211 int w = par->Width(), h = par->Height();
214 mathed_set_font(LM_TC_VAR, 1);
215 XFillRectangle(fl_display, win, canvasGC, x, y-par->Ascent(), w, h);
217 MathParInset::pm = win;
223 bool MathedCursor::Left(bool sel)
230 if (sel && !selection) SelStart();
231 if (!sel && selection) SelClear();
232 bool result = cursor->Prev();
233 if (!result && !mathstk.Empty()) {
234 cursor = mathstk.pop();
237 if (selection) SelClear();
239 if (result && cursor->IsActive()) {
240 if (cursor->IsScript()) {
242 if (!cursor->IsScript())
248 MathParInset * p = cursor->GetActiveInset();
252 p->setArgumentIdx(p->getMaxArgumentIdx());
253 mathstk.push(&cursor);
263 bool MathedCursor::Pop()
265 if (!mathstk.Empty()) {
266 cursor = mathstk.pop();
275 bool MathedCursor::Push()
277 if (cursor->IsActive()) {
278 MathParInset * p = cursor->GetActiveInset();
279 if (!p) return false;
280 mathstk.push(&cursor);
288 bool MathedCursor::Right(bool sel)
295 if (sel && !selection) SelStart();
296 if (!sel && selection) SelClear();
299 if (cursor->IsActive()) {
300 if (cursor->IsScript()) {
302 // A script may be followed by another script
303 if (cursor->IsScript())
308 MathParInset *p = cursor->GetActiveInset();
310 lyxerr << "Math error: Inset expected." << endl;
311 return cursor->Next();
313 p->setArgumentIdx(0);
314 mathstk.push(&cursor);
318 result = cursor->Next();
320 if (cursor->GetChar()!= LM_TC_CR)
321 result = cursor->Next();
322 if (!result && !mathstk.Empty()) {
323 cursor = mathstk.pop();
327 if (selection) SelClear();
334 void MathedCursor::SetPos(int x, int y)
338 if (macro_mode) MacroModeClose();
339 lastcode = LM_TC_MIN;
341 mathstk.push(&cursor);
342 cursor->SetData(par);
343 cursor->fitCoord(x, y);
344 while (cursor->GetX()<x && cursor->OK()) {
345 if (cursor->IsActive()) {
346 MathParInset * p = cursor->GetActiveInset();
347 if (p->Inside(x, y)) {
349 mathstk.push(&cursor);
351 cursor->fitCoord(x, y);
357 if (!cursor->Next() && !Pop())
360 if (x-xp < cursor->GetX()-x) cursor->ipop();
365 void MathedCursor::Home()
367 if (macro_mode) MacroModeClose();
370 mathstk.push(&cursor);
375 void MathedCursor::End()
377 if (macro_mode) MacroModeClose();
380 mathstk.push(&cursor);
385 void MathedCursor::Insert(byte c, MathedTextCodes t)
387 if (selection) SelDel();
392 if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
396 MathParInset * p = cursor->p;
397 if (p == par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
398 MathMatrixInset * mt = new MathMatrixInset(3, 0);
399 mt->SetAlign(' ', "rcl");
400 mt->SetStyle(LM_ST_DISPLAY);
401 mt->SetType((p->GetType() == LM_OT_PARN) ? LM_OT_MPARN: LM_OT_MPAR);
402 mt->SetData(p->GetData());
403 p->SetData(0);//BUG duda
408 int pos = cursor->getPos();
409 cursor->SetData(par);
410 cursor->goPosAbs(pos);
412 if (p && p->Permit(LMPF_ALLOW_CR)) {
416 if (t == LM_TC_TAB) {
417 MathParInset * p = cursor->p;
418 if (p && p->Permit(LMPF_ALLOW_TAB)) {
420 cursor->Insert(c, t);
423 cursor->goNextColumn();
424 } else // Navigate between arguments
425 if (p && p->GetType() == LM_OT_MACRO) {
426 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
427 p->setArgumentIdx(p->getArgumentIdx()+1);
434 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
442 cursor->Insert(c, t);
451 void MathedCursor::Insert(MathedInset * p, int t)
453 if (macro_mode) MacroModeClose();
455 if (MathIsActive(t)) {
457 static_cast<MathParInset*>(p)->SetData(selarray);
462 if (mathstk.i < MAX_STACK_ITEMS - 1) {
464 if (accent && !MathIsActive(t)) {
467 cursor->Insert(p, t);
469 if (MathIsActive(t)) {
476 lyxerr << "Math error: Full stack." << endl;
480 void MathedCursor::Delete()
482 if (macro_mode) return;
487 if (cursor->Empty() && !mathstk.Empty()) {
488 cursor = mathstk.pop();
490 // if (cursor->GetChar()!= LM_TC_TAB)
496 void MathedCursor::DelLine()
498 if (macro_mode) MacroModeClose();
503 MathParInset *p= cursor->p;
504 if (p && (p->GetType()<= LM_OT_MATRIX && p->GetType()>= LM_OT_MPAR)) {
510 bool MathedCursor::Up(bool sel)
514 if (macro_mode) MacroModeClose();
516 if (sel && !selection) SelStart();
517 if (!sel && selection) SelClear();
520 if (cursor->IsScript()) {
521 char cd = cursor->GetChar();
526 // A subscript may be followed by a superscript
529 if (MathIsUp(cursor->GetChar())) {
532 } else // return to the previous state
537 result = cursor->Up();
538 if (!result && cursor->p) {
539 MathParInset * p = cursor->p;
541 if (p->GetType() == LM_OT_SCRIPT) {
542 MathedXIter * cx = mathstk.Item(1);
543 bool is_down = (cx->GetChar() == LM_TC_DOWN);
544 cursor = mathstk.pop();
546 result = (is_down) ? true: Up();
548 result = (p->getArgumentIdx() > 0);
550 p->setArgumentIdx(p->getArgumentIdx()-1);
554 if (!result && !mathstk.Empty()) {
555 cursor = mathstk.pop();
563 bool MathedCursor::Down(bool sel)
567 if (macro_mode) MacroModeClose();
569 if (sel && !selection) SelStart();
570 if (!sel && selection) SelClear();
571 // if (selection) SelClear();
573 if (cursor->IsScript()) {
574 char cd = cursor->GetChar();
575 if (MathIsDown(cd)) {
579 // A superscript may be followed by a subscript
582 if (MathIsDown(cursor->GetChar())) {
590 result = cursor->Down();
591 if (!result && cursor->p) {
592 MathParInset * p= cursor->p;
593 if (p->GetType() == LM_OT_SCRIPT) {
594 MathedXIter * cx = mathstk.Item(1);
595 bool is_up = (cx->GetChar() == LM_TC_UP);
596 cursor = mathstk.pop();
598 result = (is_up) ? true: Down();
600 result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
602 p->setArgumentIdx(p->getArgumentIdx()+1);
606 if (!result && !mathstk.Empty()) {
607 cursor = mathstk.pop();
615 bool MathedCursor::Limits()
617 if (cursor->IsInset()) {
618 MathedInset * p = cursor->GetInset();
619 bool ol = p->GetLimits();
621 return (ol!= p->GetLimits());
627 void MathedCursor::SetSize(short size)
629 MathParInset * p = cursor->p;
630 p->UserSetSize(size);
635 void MathedCursor::setLabel(char const * label)
636 { // ugly hack and possible bug
637 if (!cursor->setLabel(strnew(label)))
638 lyxerr << "MathErr: Bad place to set labels." << endl;
642 void MathedCursor::setNumbered()
643 { // another ugly hack
644 MathedRowSt * crow = cursor->crow;
646 crow->setNumbered(!crow->isNumbered());
650 void MathedCursor::Interpret(char const * s)
654 MathedTextCodes tcode = LM_TC_INSET;
656 if (s[0] == '^' || s[0] == '_') {
657 char c = cursor->GetChar();
658 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
661 } else // A script may be followed by a script
662 if (MathIsUp(c) || MathIsDown(c)) {
665 c = cursor->GetChar();
666 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
672 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
673 Insert (p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
676 if (s[0] == '!' || s[0] == ',' || s[0] == ':' || s[0] == ';') {
677 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
678 p = new MathSpaceInset(sp);
682 l = in_word_set (s, strlen(s));
685 p = MathMacroTable::mathMTable.getMacro(s);
687 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
688 if (strcmp("root", s) == 0) {
689 p = new MathRootInset();
690 tcode = LM_TC_ACTIVE_INSET;
692 p = new MathFuncInset(s, LM_OT_UNDEF);
694 tcode = static_cast<MathMacro*>(p)->getTCode();
695 lyxerr << "Macro2 " << s << ' ' << tcode << " " ;
698 MathedInsetTypes fractype = LM_OT_FRAC;
702 p = new MathBigopInset(l->name, l->id);
708 Insert(static_cast<byte>(l->id), MathIsBOPS(l->id) ?
709 LM_TC_BOPS: LM_TC_SYMB);
711 p = new MathFuncInset(l->name);
716 fractype = LM_OT_STACKREL;
717 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
720 p = new MathFracInset(fractype);
721 tcode = LM_TC_ACTIVE_INSET;
726 p = new MathSqrtInset;
727 tcode = LM_TC_ACTIVE_INSET;
732 p = new MathDecorationInset(l->id);
733 tcode = LM_TC_ACTIVE_INSET;
738 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
743 p = new MathSpaceInset(l->id);
748 p = new MathDotsInset(l->name, l->id);
755 p = MathMacroTable::mathMTable.getMacro(s);
756 tcode = static_cast<MathMacro*>(p)->getTCode();
757 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
761 p = new MathFuncInset(l->name);
773 bool MathedCursor::pullArg()
775 if (cursor->IsActive()) {
776 MathParInset * p = cursor->GetActiveInset();
780 LyxArrayBase * a = p->GetData();
794 void MathedCursor::MacroModeOpen()
799 imacro = new MathFuncInset(¯obf[0]);
803 lyxerr << "Mathed Warning: Already in macro mode" << endl;
807 void MathedCursor::MacroModeClose()
811 latexkeys * l = in_word_set(macrobf, macroln);
812 if (macroln > 0 && (!l || (l && IsMacro(l->token, l->id))) &&
813 !MathMacroTable::mathMTable.getMacro(macrobf)) {
815 imacro->SetName(strnew(macrobf));
816 // This guarantees that the string will be removed by destructor
817 imacro->SetType(LM_OT_UNDEF);
819 imacro->SetName(l->name);
823 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
824 setAccent(static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
827 if (l || MathMacroTable::mathMTable.getMacro(macrobf)) {
836 void MathedCursor::MacroModeBack()
840 macrobf[--macroln] = '\0';
845 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
849 void MathedCursor::MacroModeInsert(char c)
852 macrobf[macroln + 1] = macrobf[macroln];
853 macrobf[macroln++] = c;
856 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
860 void MathedCursor::SelCopy()
863 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
864 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
865 selarray = cursor->Copy(p1, p2);
872 void MathedCursor::SelCut()
875 if (cursor->pos == selpos) return;
877 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
878 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
879 selarray = cursor->Copy(p1, p2);
880 cursor->Clean(selpos);
887 void MathedCursor::SelDel()
889 // lyxerr << "Deleting sel "
891 if (cursor->pos == selpos) return;
892 cursor->Clean(selpos);
899 void MathedCursor::SelPaste()
901 // lyxerr << "paste " << selarray << " " << curor->pos;
902 if (selection) SelDel();
904 cursor->Merge(selarray);
910 void MathedCursor::SelStart()
912 lyxerr[Debug::MATHED] << "Starting sel " << endl;
914 selpos = cursor->pos;
915 selstk = new MathStackXIter(mathstk);
916 anchor = selstk->Item(-1);
917 anchor->SetData(cursor->p);
919 anchor->goPosAbs(selpos);
926 void MathedCursor::SelClear()
928 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
937 // Anchor position must be at the same level that stack.
938 void MathedCursor::SelBalance()
940 int d = mathstk.Level() - selstk->Level();
942 // If unbalanced, balance them
945 // lyxerr << "b[" << mathstk.Level() << " " << selstk->Level << " " << anchor->GetX() << " " << cursor->GetX() << "]";
946 anchor = selstk->pop();
947 if (anchor->GetX() >= cursor->GetX())
950 // lyxerr <<"a[" << mathstk.Level() << " " << selstk->Level() <<"]";
953 d = mathstk.Level() - selstk->Level();
956 // Once balanced the levels, check that they are at the same paragraph
957 selpos = anchor->pos;
962 void MathedCursor::SelGetArea(int * xp, int * yp, int & np)
969 static int xpoint[10];
970 static int ypoint[10];
972 // single row selection
973 int i = 0, x, y, a, d, xo, yo, x1, y1, a1, d1;
975 // Balance anchor and cursor
978 cursor->p->GetXY(xo, yo);
979 int w = cursor->p->Width();
980 cursor->GetPos(x1, y1);
981 cursor->getAD(a1, d1);
982 anchor->GetPos(x, y);
996 ypoint[i++] = y1 - a;
1001 ypoint[i++] = y1 - a;
1003 ypoint[i++] = y1 + d;
1007 ypoint[i++] = y1 + d;
1010 ypoint[i++] = y + d;
1013 xpoint[i] = xpoint[0];
1014 ypoint[i++] = ypoint[0];
1019 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
1020 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
1021 // for (i = 0; i < np; ++i)
1022 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
1026 XPoint * MathedCursor::SelGetArea(int & np)
1033 static XPoint point[10];
1035 // single row selection
1036 int i = 0, x, y, a, d, xo, yo, x1, y1, a1, d1; //, p1, p2;
1038 // Balance anchor and cursor
1041 cursor->p->GetXY(xo, yo);
1042 int w = cursor->p->Width();
1043 cursor->GetPos(x1, y1);
1044 cursor->getAD(a1, d1);
1045 anchor->GetPos(x, y);
1046 anchor->getAD(a, d);
1054 point[i].x = xo + w;
1055 point[i++].y = y - a;
1057 point[i].x = xo + w;
1058 point[i++].y = y1 - a;
1063 point[i++].y = y1 - a;
1065 point[i++].y = y1 + d;
1069 point[i++].y = y1 + d;
1072 point[i++].y = y + d;
1075 point[i].x = point[0].x;
1076 point[i++].y = point[0].y;
1078 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
1079 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
1080 // for (i = 0; i < np; ++i)
1081 // 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]);
1116 accent = 0; // consumed!
1120 void MathedCursor::doAccent(MathedInset * p)
1122 MathedInset * ac = 0;
1124 for (int i = accent - 1; i >= 0; --i) {
1125 if (i == accent - 1)
1126 ac = new MathAccentInset(p, nestaccent[i]);
1128 ac = new MathAccentInset(ac, nestaccent[i]);
1133 accent = 0; // consumed!