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"
32 extern void mathed_set_font(short type, int style);
34 extern GC canvasGC, mathGC, latexGC, cursorGC, mathFrameGC;
36 static LyxArrayBase *selarray= 0;
38 inline bool IsAlpha(char c)
40 return ('A' <= c && c<= 'Z' || 'a' <= c && c<= 'z');
43 // This was very smaller, I'll change it later
44 inline bool IsMacro(short tok, int id)
46 return (tok != LM_TK_STACK && tok != LM_TK_FRAC && tok != LM_TK_SQRT
48 && tok != LM_TK_SPACE && tok != LM_TK_DOTS
49 && tok != LM_TK_FUNCLIM
50 && tok != LM_TK_BIGSYM && tok != LM_TK_ACCENT &&
51 !(tok == LM_TK_SYM && id < 255));
54 // Yes, mathed isn't using string yet.
55 inline char *strnew(char const* s)
57 char *s1 = new char[strlen(s)+1];
64 #define MAX_STACK_ITEMS 32
66 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]: (MathedXIter*)0;
100 return (i>= MAX_STACK_ITEMS);
107 int Level() { return i; }
109 } mathstk, *selstk= 0;
112 MathStackXIter::MathStackXIter(MathStackXIter &stk) {
114 item = new MathedXIter[imax];
116 for (int k= 0; k<i; k++) {
117 item[k].SetData(stk.item[k].getPar());
119 item[k].goPosAbs(stk.item[k].getPos());
124 /***---------------- Mathed Cursor ---------------------------***/
126 MathedCursor::MathedCursor(MathParInset *p) // : par(p)
130 lastcode = LM_TC_MIN;
133 if (!MathMacroTable::built)
134 MathMacroTable::mathMTable.builtinMacros();
138 void MathedCursor::SetPar(MathParInset *p)
143 selection = false; // not SelClear() ?
145 mathstk.push(&cursor);
147 cursor->SetData(par);
150 void MathedCursor::Draw(long unsigned pm, int x, int y)
152 // lyxerr << "Cursor[" << x << " " << y << "] ";
153 win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
155 int w = par->Width()+2, a = par->Ascent()+1, h = par->Height()+1;
156 if (par->GetType()>LM_OT_PAR) { a += 4; h += 8; }
158 if (!canvasGC) mathed_set_font(LM_TC_VAR, 1);
159 // XFillRectangle(fl_display, pm, canvasGC, x, y-a, w, h);
160 XDrawRectangle(fl_display, pm, mathFrameGC, x-1, y-a, w, h);
162 MathParInset::pm = pm;
168 void MathedCursor::Redraw()
170 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
172 int w = par->Width(), h = par->Height();
175 mathed_set_font(LM_TC_VAR, 1);
176 XFillRectangle(fl_display, win, canvasGC, x, y-par->Ascent(), w, h);
178 MathParInset::pm = win;
182 bool MathedCursor::Left(bool sel)
189 if (sel && !selection) SelStart();
190 if (!sel && selection) SelClear();
191 bool result = cursor->Prev();
192 if (!result && !mathstk.Empty()) {
193 cursor = mathstk.pop();
196 if (selection) SelClear();
198 if (result && cursor->IsActive()) {
199 if (cursor->IsScript()) {
201 if (!cursor->IsScript())
207 MathParInset *p = cursor->GetActiveInset();
211 p->setArgumentIdx(p->getMaxArgumentIdx());
212 mathstk.push(&cursor);
221 bool MathedCursor::Pop()
223 if (!mathstk.Empty()) {
224 cursor = mathstk.pop();
232 bool MathedCursor::Push()
234 if (cursor->IsActive()) {
235 MathParInset *p = cursor->GetActiveInset();
236 if (!p) return false;
237 mathstk.push(&cursor);
244 bool MathedCursor::Right(bool sel)
251 if (sel && !selection) SelStart();
252 if (!sel && selection) SelClear();
255 if (cursor->IsActive()) {
256 if (cursor->IsScript()) {
258 // A script may be followed by another script
259 if (cursor->IsScript())
264 MathParInset *p = cursor->GetActiveInset();
266 lyxerr << "Math error: Inset expected." << endl;
267 return cursor->Next();
269 p->setArgumentIdx(0);
270 mathstk.push(&cursor);
274 result = cursor->Next();
276 if (cursor->GetChar()!= LM_TC_CR)
277 result = cursor->Next();
278 if (!result && !mathstk.Empty()) {
279 cursor = mathstk.pop();
283 if (selection) SelClear();
290 void MathedCursor::SetPos(int x, int y)
294 if (macro_mode) MacroModeClose();
295 lastcode = LM_TC_MIN;
297 mathstk.push(&cursor);
298 cursor->SetData(par);
299 cursor->fitCoord(x, y);
300 while (cursor->GetX()<x && cursor->OK()) {
301 if (cursor->IsActive()) {
302 MathParInset *p = cursor->GetActiveInset();
303 if (p->Inside(x, y)) {
305 mathstk.push(&cursor);
307 cursor->fitCoord(x, y);
313 if (!cursor->Next() && !Pop())
316 if (x-xp < cursor->GetX()-x) cursor->ipop();
321 void MathedCursor::Home()
323 if (macro_mode) MacroModeClose();
326 mathstk.push(&cursor);
330 void MathedCursor::End()
332 if (macro_mode) MacroModeClose();
335 mathstk.push(&cursor);
339 void MathedCursor::Insert(byte c, MathedTextCodes t)
341 if (selection) SelDel();
346 if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
350 MathParInset *p= cursor->p;
351 if (p == par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
352 MathMatrixInset* mt = new MathMatrixInset(3, 0);
353 mt->SetAlign(' ', "rcl");
354 mt->SetStyle(LM_ST_DISPLAY);
355 mt->SetType((p->GetType() == LM_OT_PARN) ? LM_OT_MPARN: LM_OT_MPAR);
356 mt->SetData(p->GetData());
357 p->SetData(0);//BUG duda
362 int pos = cursor->getPos();
363 cursor->SetData(par);
364 cursor->goPosAbs(pos);
366 if (p && p->Permit(LMPF_ALLOW_CR)) {
370 if (t == LM_TC_TAB) {
371 MathParInset *p = cursor->p;
372 if (p && p->Permit(LMPF_ALLOW_TAB)) {
374 cursor->Insert(c, t);
377 cursor->goNextColumn();
378 } else // Navigate between arguments
379 if (p && p->GetType() == LM_OT_MACRO) {
380 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
381 p->setArgumentIdx(p->getArgumentIdx()+1);
388 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
396 cursor->Insert(c, t);
404 void MathedCursor::Insert(MathedInset* p, int t)
406 if (macro_mode) MacroModeClose();
408 if (MathIsActive(t)) {
410 ((MathParInset*)p)->SetData(selarray);
415 if (mathstk.i<MAX_STACK_ITEMS-1) {
417 if (accent && !MathIsActive(t)) {
420 cursor->Insert(p, t);
422 if (MathIsActive(t)) {
429 lyxerr << "Math error: Full stack." << endl;
432 void MathedCursor::Delete()
434 if (macro_mode) return;
439 if (cursor->Empty() && !mathstk.Empty()) {
440 cursor = mathstk.pop();
442 // if (cursor->GetChar()!= LM_TC_TAB)
447 void MathedCursor::DelLine()
449 if (macro_mode) MacroModeClose();
454 MathParInset *p= cursor->p;
455 if (p && (p->GetType()<= LM_OT_MATRIX && p->GetType()>= LM_OT_MPAR)) {
461 bool MathedCursor::Up(bool sel)
465 if (macro_mode) MacroModeClose();
467 if (sel && !selection) SelStart();
468 if (!sel && selection) SelClear();
472 if (cursor->IsScript()) {
473 char cd = cursor->GetChar();
478 // A subscript may be followed by a superscript
481 if (MathIsUp(cursor->GetChar())) {
484 } else // return to the previous state
489 result = cursor->Up();
490 if (!result && cursor->p) {
493 if (p->GetType() == LM_OT_SCRIPT) {
494 MathedXIter *cx = mathstk.Item(1);
495 bool is_down = (cx->GetChar() == LM_TC_DOWN);
496 cursor = mathstk.pop();
498 result = (is_down) ? true: Up();
500 result = (p->getArgumentIdx() > 0);
502 p->setArgumentIdx(p->getArgumentIdx()-1);
506 if (!result && !mathstk.Empty()) {
507 cursor = mathstk.pop();
515 bool MathedCursor::Down(bool sel)
519 if (macro_mode) MacroModeClose();
521 if (sel && !selection) SelStart();
522 if (!sel && selection) SelClear();
523 // if (selection) SelClear();
527 if (cursor->IsScript()) {
528 char cd = cursor->GetChar();
529 if (MathIsDown(cd)) {
533 // A superscript may be followed by a subscript
536 if (MathIsDown(cursor->GetChar())) {
544 result = cursor->Down();
545 if (!result && cursor->p) {
547 if (p->GetType() == LM_OT_SCRIPT) {
548 MathedXIter *cx = mathstk.Item(1);
549 bool is_up = (cx->GetChar() == LM_TC_UP);
550 cursor = mathstk.pop();
552 result = (is_up) ? true: Down();
554 result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
556 p->setArgumentIdx(p->getArgumentIdx()+1);
560 if (!result && !mathstk.Empty()) {
561 cursor = mathstk.pop();
568 bool MathedCursor::Limits()
570 if (cursor->IsInset()) {
571 MathedInset *p = cursor->GetInset();
572 bool ol = p->GetLimits();
574 return (ol!= p->GetLimits());
579 void MathedCursor::SetSize(short size)
581 MathParInset *p = cursor->p;
582 p->UserSetSize(size);
587 void MathedCursor::setLabel(char const* label)
588 { // ugly hack and possible bug
589 if (!cursor->setLabel(strnew(label)))
590 lyxerr << "MathErr: Bad place to set labels." << endl;
594 void MathedCursor::setNumbered()
595 { // another ugly hack
596 MathedRowSt *crow = cursor->crow;
598 crow->setNumbered(!crow->isNumbered());
602 void MathedCursor::Interpret(char const *s)
606 MathedTextCodes tcode = LM_TC_INSET;
608 if (s[0] == '^' || s[0] == '_') {
609 char c = cursor->GetChar();
610 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
613 } else // A script may be followed by a script
614 if (MathIsUp(c) || MathIsDown(c)) {
617 c = cursor->GetChar();
618 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
624 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
625 Insert (p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
628 if (s[0] == '!' || s[0] == ',' || s[0] == ':' || s[0] == ';') {
629 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
630 p = new MathSpaceInset(sp);
634 l = in_word_set (s, strlen(s));
637 p = MathMacroTable::mathMTable.getMacro(s);
639 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
640 if (strcmp("root", s) == 0) {
641 p = new MathRootInset();
642 tcode = LM_TC_ACTIVE_INSET;
644 p = new MathFuncInset(s, LM_OT_UNDEF);
646 tcode = ((MathMacro*)p)->getTCode();
647 lyxerr << "Macro2 " << s << ' ' << tcode << " " ;
650 MathedInsetTypes fractype = LM_OT_FRAC;
654 p = new MathBigopInset(l->name, l->id);
660 Insert((byte)l->id, MathIsBOPS(l->id) ?
661 LM_TC_BOPS: LM_TC_SYMB);
663 p = new MathFuncInset(l->name);
668 fractype = LM_OT_STACKREL;
669 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
672 p = new MathFracInset(fractype);
673 tcode = LM_TC_ACTIVE_INSET;
678 p = new MathSqrtInset;
679 tcode = LM_TC_ACTIVE_INSET;
684 p = new MathDecorationInset(l->id);
685 tcode = LM_TC_ACTIVE_INSET;
690 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
695 p = new MathSpaceInset(l->id);
700 p = new MathDotsInset(l->name, l->id);
707 p = MathMacroTable::mathMTable.getMacro(s);
708 tcode = ((MathMacro*)p)->getTCode();
709 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
713 p = new MathFuncInset(l->name);
725 bool MathedCursor::pullArg()
727 if (cursor->IsActive()) {
728 MathParInset *p = cursor->GetActiveInset();
732 LyxArrayBase *a = p->GetData();
746 void MathedCursor::MacroModeOpen()
751 imacro = new MathFuncInset(¯obf[0]);
755 lyxerr << "Mathed Warning: Already in macro mode" << endl;
758 void MathedCursor::MacroModeClose()
762 latexkeys *l = in_word_set(macrobf, macroln);
763 if (macroln>0 && (!l || (l && IsMacro(l->token, l->id))) &&
764 !MathMacroTable::mathMTable.getMacro(macrobf)) {
766 imacro->SetName(strnew(macrobf));
767 // This guarantees that the string will be removed by destructor
768 imacro->SetType(LM_OT_UNDEF);
770 imacro->SetName(l->name);
774 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
775 setAccent(((MathAccentInset*)cursor->GetInset())->getAccentCode());
778 if (l || MathMacroTable::mathMTable.getMacro(macrobf)) {
786 void MathedCursor::MacroModeBack()
790 macrobf[--macroln] = '\0';
795 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
798 void MathedCursor::MacroModeInsert(char c)
801 macrobf[macroln+1] = macrobf[macroln];
802 macrobf[macroln++] = c;
805 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
808 void MathedCursor::SelCopy()
812 p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
813 p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
814 selarray = cursor->Copy(p1, p2);
820 void MathedCursor::SelCut()
823 if (cursor->pos == selpos) return;
826 p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
827 p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
828 selarray = cursor->Copy(p1, p2);
829 cursor->Clean(selpos);
835 void MathedCursor::SelDel()
837 // lyxerr << "Deleting sel "
839 if (cursor->pos == selpos) return;
840 cursor->Clean(selpos);
846 void MathedCursor::SelPaste()
848 // lyxerr << "paste " << selarray << " " << curor->pos;
849 if (selection) SelDel();
851 cursor->Merge(selarray);
856 void MathedCursor::SelStart()
858 lyxerr[Debug::MATHED] << "Starting sel " << endl;
860 selpos = cursor->pos;
861 selstk = new MathStackXIter(mathstk);
862 anchor = selstk->Item(-1);
863 anchor->SetData(cursor->p);
865 anchor->goPosAbs(selpos);
871 void MathedCursor::SelClear()
873 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
882 // Anchor position must be at the same level that stack.
883 void MathedCursor::SelBalance()
885 int d = mathstk.Level() - selstk->Level();
887 // If unbalanced, balance them
890 // lyxerr << "b[" << mathstk.Level() << " " << selstk->Level << " " << anchor->GetX() << " " << cursor->GetX() << "]";
891 anchor = selstk->pop();
892 if (anchor->GetX() >= cursor->GetX())
895 // lyxerr <<"a[" << mathstk.Level() << " " << selstk->Level() <<"]";
898 d = mathstk.Level() - selstk->Level();
901 // Once balanced the levels, check that they are at the same paragraph
902 selpos = anchor->pos;
906 XPoint *MathedCursor::SelGetArea(int& np)
913 static XPoint point[10];
915 // single row selection
916 int i = 0, x, y, a, d, w, xo, yo, x1, y1, a1, d1; //, p1, p2;
918 // Balance anchor and cursor
921 cursor->p->GetXY(xo, yo);
922 w = cursor->p->Width();
923 cursor->GetPos(x1, y1);
924 cursor->getAD(a1, d1);
925 anchor->GetPos(x, y);
955 point[i].x = point[0].x;
956 point[i++].y = point[0].y;
958 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
959 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
960 // for (i= 0; i<np; i++)
961 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
968 void MathedCursor::setAccent(int ac)
970 if (ac > 0 && accent < 8) {
971 nestaccent[accent++] = ac;
973 accent = 0; // consumed!
977 int MathedCursor::getAccent() const
979 return (accent>0) ? nestaccent[accent-1]: 0;
983 void MathedCursor::doAccent(byte c, MathedTextCodes t)
987 for (int i= accent-1; i>= 0; i--) {
989 ac = new MathAccentInset(c, t, nestaccent[i]);
991 ac = new MathAccentInset(ac, nestaccent[i]);
996 accent = 0; // consumed!
1000 void MathedCursor::doAccent(MathedInset *p)
1002 MathedInset *ac = 0;
1004 for (int i= accent-1; i>= 0; i--) {
1006 ac = new MathAccentInset(p, nestaccent[i]);
1008 ac = new MathAccentInset(ac, nestaccent[i]);
1013 accent = 0; // consumed!