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 token, int id)
46 return (token!=LM_TK_STACK && token!=LM_TK_FRAC && token!=LM_TK_SQRT && token!=LM_TK_WIDE &&
47 token!=LM_TK_SPACE && token!=LM_TK_DOTS && token!=LM_TK_FUNCLIM &&
48 token!=LM_TK_BIGSYM && token!=LM_TK_ACCENT &&
49 !(token==LM_TK_SYM && id<255));
52 // Yes, mathed isn't using string yet.
53 inline char *strnew(char const* s)
55 char *s1 = new char[strlen(s)+1];
62 #define MAX_STACK_ITEMS 32
64 struct MathStackXIter {
69 MathStackXIter(int n=MAX_STACK_ITEMS): imax(n) {
70 item = new MathedXIter[imax];
74 MathStackXIter(MathStackXIter &stk);
80 void push(MathedXIter** a) {
89 MathedXIter* Item(int idx) {
90 return (idx+1 <= i) ? &item[i-idx-1]: (MathedXIter*)0;
98 return (i>=MAX_STACK_ITEMS);
105 int Level() { return i; }
107 } mathstk, *selstk=0;
110 MathStackXIter::MathStackXIter(MathStackXIter &stk) {
112 item = new MathedXIter[imax];
114 for (int k=0; k<i; k++) {
115 item[k].SetData(stk.item[k].getPar());
117 item[k].goPosAbs(stk.item[k].getPos());
122 /***---------------- Mathed Cursor ---------------------------***/
124 MathedCursor::MathedCursor(MathParInset *p) // : par(p)
128 lastcode = LM_TC_MIN;
131 if (!MathMacroTable::built)
132 MathMacroTable::mathMTable.builtinMacros();
136 void MathedCursor::SetPar(MathParInset *p)
141 selection = false; // not SelClear() ?
143 mathstk.push(&cursor);
145 cursor->SetData(par);
148 void MathedCursor::Draw(long unsigned pm, int x, int y)
150 // lyxerr << "Cursor[" << x << " " << y << "] ";
151 win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
153 int w = par->Width()+2, a = par->Ascent()+1, h = par->Height()+1;
154 if (par->GetType()>LM_OT_PAR) { a += 4; h += 8; }
156 if (!canvasGC) mathed_set_font(LM_TC_VAR, 1);
157 // XFillRectangle(fl_display,pm, canvasGC, x, y-a, w, h);
158 XDrawRectangle(fl_display,pm, mathFrameGC, x-1, y-a, w, h);
160 MathParInset::pm = pm;
166 void MathedCursor::Redraw()
168 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
170 int w = par->Width(), h = par->Height();
173 mathed_set_font(LM_TC_VAR, 1);
174 XFillRectangle(fl_display, win,canvasGC,x, y-par->Ascent(), w, h);
176 MathParInset::pm = win;
180 bool MathedCursor::Left(bool sel)
187 if (sel && !selection) SelStart();
188 if (!sel && selection) SelClear();
189 bool result = cursor->Prev();
190 if (!result && !mathstk.Empty()) {
191 cursor = mathstk.pop();
194 if (selection) SelClear();
196 if (result && cursor->IsActive()) {
197 if (cursor->IsScript()) {
199 if (!cursor->IsScript())
205 MathParInset *p = cursor->GetActiveInset();
209 p->setArgumentIdx(p->getMaxArgumentIdx());
210 mathstk.push(&cursor);
219 bool MathedCursor::Pop()
221 if (!mathstk.Empty()) {
222 cursor = mathstk.pop();
230 bool MathedCursor::Push()
232 if (cursor->IsActive()) {
233 MathParInset *p = cursor->GetActiveInset();
234 if (!p) return false;
235 mathstk.push(&cursor);
242 bool MathedCursor::Right(bool sel)
249 if (sel && !selection) SelStart();
250 if (!sel && selection) SelClear();
253 if (cursor->IsActive()) {
254 if (cursor->IsScript()) {
256 // A script may be followed by another script
257 if (cursor->IsScript())
262 MathParInset *p = cursor->GetActiveInset();
264 lyxerr << "Math error: Inset expected." << endl;
265 return cursor->Next();
267 p->setArgumentIdx(0);
268 mathstk.push(&cursor);
272 result = cursor->Next();
274 if (cursor->GetChar()!=LM_TC_CR)
275 result = cursor->Next();
276 if (!result && !mathstk.Empty()) {
277 cursor = mathstk.pop();
281 if (selection) SelClear();
288 void MathedCursor::SetPos(int x, int y)
292 if (macro_mode) MacroModeClose();
293 lastcode = LM_TC_MIN;
295 mathstk.push(&cursor);
296 cursor->SetData(par);
297 cursor->fitCoord(x, y);
298 while (cursor->GetX()<x && cursor->OK()) {
299 if (cursor->IsActive()) {
300 MathParInset *p = cursor->GetActiveInset();
301 if (p->Inside(x, y)) {
303 mathstk.push(&cursor);
305 cursor->fitCoord(x, y);
311 if (!cursor->Next() && !Pop())
314 if (x-xp < cursor->GetX()-x) cursor->ipop();
319 void MathedCursor::Home()
321 if (macro_mode) MacroModeClose();
324 mathstk.push(&cursor);
328 void MathedCursor::End()
330 if (macro_mode) MacroModeClose();
333 mathstk.push(&cursor);
337 void MathedCursor::Insert(byte c, MathedTextCodes t)
339 if (selection) SelDel();
344 if (macro_mode && !(MathIsAlphaFont(t) || t==LM_TC_MIN))
348 MathParInset *p= cursor->p;
349 if (p==par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
350 MathMatrixInset* mt = new MathMatrixInset(3, 0);
351 mt->SetAlign(' ', "rcl");
352 mt->SetStyle(LM_ST_DISPLAY);
353 mt->SetType((p->GetType()==LM_OT_PARN) ? LM_OT_MPARN: LM_OT_MPAR);
354 mt->SetData(p->GetData());
355 p->SetData(0);//BUG duda
360 int pos = cursor->getPos();
361 cursor->SetData(par);
362 cursor->goPosAbs(pos);
364 if (p && p->Permit(LMPF_ALLOW_CR)) {
369 MathParInset *p = cursor->p;
370 if (p && p->Permit(LMPF_ALLOW_TAB)) {
372 cursor->Insert(c, t);
375 cursor->goNextColumn();
376 } else // Navigate between arguments
377 if (p && p->GetType()==LM_OT_MACRO) {
378 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
379 p->setArgumentIdx(p->getArgumentIdx()+1);
386 if (MathIsAlphaFont(t) || t==LM_TC_MIN) {
394 cursor->Insert(c, t);
402 void MathedCursor::Insert(MathedInset* p, int t)
404 if (macro_mode) MacroModeClose();
406 if (MathIsActive(t)) {
408 ((MathParInset*)p)->SetData(selarray);
413 if (mathstk.i<MAX_STACK_ITEMS-1) {
415 if (accent && !MathIsActive(t)) {
418 cursor->Insert(p, t);
420 if (MathIsActive(t)) {
427 lyxerr << "Math error: Full stack." << endl;
430 void MathedCursor::Delete()
432 if (macro_mode) return;
437 if (cursor->Empty() && !mathstk.Empty()) {
438 cursor = mathstk.pop();
440 // if (cursor->GetChar()!=LM_TC_TAB)
445 void MathedCursor::DelLine()
447 if (macro_mode) MacroModeClose();
452 MathParInset *p= cursor->p;
453 if (p && (p->GetType()<=LM_OT_MATRIX && p->GetType()>=LM_OT_MPAR)) {
459 bool MathedCursor::Up(bool sel)
463 if (macro_mode) MacroModeClose();
465 if (sel && !selection) SelStart();
466 if (!sel && selection) SelClear();
470 if (cursor->IsScript()) {
471 char cd = cursor->GetChar();
476 // A subscript may be followed by a superscript
479 if (MathIsUp(cursor->GetChar())) {
482 } else // return to the previous state
487 result = cursor->Up();
488 if (!result && cursor->p) {
491 if (p->GetType()==LM_OT_SCRIPT) {
492 MathedXIter *cx = mathstk.Item(1);
493 bool is_down = (cx->GetChar()==LM_TC_DOWN);
494 cursor = mathstk.pop();
496 result = (is_down) ? true: Up();
498 result = (p->getArgumentIdx() > 0);
500 p->setArgumentIdx(p->getArgumentIdx()-1);
504 if (!result && !mathstk.Empty()) {
505 cursor = mathstk.pop();
513 bool MathedCursor::Down(bool sel)
517 if (macro_mode) MacroModeClose();
519 if (sel && !selection) SelStart();
520 if (!sel && selection) SelClear();
521 // if (selection) SelClear();
525 if (cursor->IsScript()) {
526 char cd = cursor->GetChar();
527 if (MathIsDown(cd)) {
531 // A superscript may be followed by a subscript
534 if (MathIsDown(cursor->GetChar())) {
542 result = cursor->Down();
543 if (!result && cursor->p) {
545 if (p->GetType()==LM_OT_SCRIPT) {
546 MathedXIter *cx = mathstk.Item(1);
547 bool is_up = (cx->GetChar()==LM_TC_UP);
548 cursor = mathstk.pop();
550 result = (is_up) ? true: Down();
552 result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
554 p->setArgumentIdx(p->getArgumentIdx()+1);
558 if (!result && !mathstk.Empty()) {
559 cursor = mathstk.pop();
566 bool MathedCursor::Limits()
568 if (cursor->IsInset()) {
569 MathedInset *p = cursor->GetInset();
570 bool ol = p->GetLimits();
572 return (ol!=p->GetLimits());
577 void MathedCursor::SetSize(short size)
579 MathParInset *p = cursor->p;
580 p->UserSetSize(size);
585 void MathedCursor::setLabel(char const* label)
586 { // ugly hack and possible bug
587 if (!cursor->setLabel(strnew(label)))
588 lyxerr << "MathErr: Bad place to set labels." << endl;
592 void MathedCursor::setNumbered()
593 { // another ugly hack
594 MathedRowSt *crow = cursor->crow;
596 crow->setNumbered(!crow->isNumbered());
600 void MathedCursor::Interpret(char const *s)
604 MathedTextCodes tcode = LM_TC_INSET;
606 if (s[0]=='^' || s[0]=='_') {
607 char c = cursor->GetChar();
608 if (MathIsUp(c) && s[0]=='^' || MathIsDown(c) && s[0]=='_') {
611 } else // A script may be followed by a script
612 if (MathIsUp(c) || MathIsDown(c)) {
615 c = cursor->GetChar();
616 if (MathIsUp(c) && s[0]=='^' || MathIsDown(c) && s[0]=='_') {
622 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
623 Insert (p, (s[0]=='_') ? LM_TC_DOWN: LM_TC_UP);
626 if (s[0]=='!' || s[0]==',' || s[0]==':' || s[0]==';') {
627 int sp = ((s[0]==',') ? 1:((s[0]==':') ? 2:((s[0]==';') ? 3: 0)));
628 p = new MathSpaceInset(sp);
632 l = in_word_set (s, strlen(s));
635 p = MathMacroTable::mathMTable.getMacro(s);
637 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
638 if (strcmp("root", s)==0) {
639 p = new MathRootInset();
640 tcode = LM_TC_ACTIVE_INSET;
642 p = new MathFuncInset(s, LM_OT_UNDEF);
644 tcode = ((MathMacro*)p)->getTCode();
645 lyxerr << "Macro2 " << s << ' ' << tcode << " " ;
648 MathedInsetTypes fractype = LM_OT_FRAC;
652 p = new MathBigopInset(l->name, l->id);
658 Insert((byte)l->id, MathIsBOPS(l->id) ?
659 LM_TC_BOPS: LM_TC_SYMB);
661 p = new MathFuncInset(l->name);
666 fractype = LM_OT_STACKREL;
667 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
670 p = new MathFracInset(fractype);
671 tcode = LM_TC_ACTIVE_INSET;
676 p = new MathSqrtInset;
677 tcode = LM_TC_ACTIVE_INSET;
682 p = new MathDecorationInset(l->id);
683 tcode = LM_TC_ACTIVE_INSET;
688 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
693 p = new MathSpaceInset(l->id);
698 p = new MathDotsInset(l->name, l->id);
705 p = MathMacroTable::mathMTable.getMacro(s);
706 tcode = ((MathMacro*)p)->getTCode();
707 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
711 p = new MathFuncInset(l->name);
723 bool MathedCursor::pullArg()
725 if (cursor->IsActive()) {
726 MathParInset *p = cursor->GetActiveInset();
730 LyxArrayBase *a = p->GetData();
744 void MathedCursor::MacroModeOpen()
749 imacro = new MathFuncInset(¯obf[0]);
753 lyxerr << "Mathed Warning: Already in macro mode" << endl;
756 void MathedCursor::MacroModeClose()
760 latexkeys *l = in_word_set(macrobf, macroln);
761 if (macroln>0 && (!l || (l && IsMacro(l->token, l->id))) &&
762 !MathMacroTable::mathMTable.getMacro(macrobf)) {
764 imacro->SetName(strnew(macrobf));
765 // This guarantees that the string will be removed by destructor
766 imacro->SetType(LM_OT_UNDEF);
768 imacro->SetName(l->name);
772 if (cursor->GetInset()->GetType()==LM_OT_ACCENT) {
773 setAccent(((MathAccentInset*)cursor->GetInset())->getAccentCode());
776 if (l || MathMacroTable::mathMTable.getMacro(macrobf)) {
784 void MathedCursor::MacroModeBack()
788 macrobf[--macroln] = '\0';
793 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
796 void MathedCursor::MacroModeInsert(char c)
799 macrobf[macroln+1] = macrobf[macroln];
800 macrobf[macroln++] = c;
803 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
806 void MathedCursor::SelCopy()
810 p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
811 p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
812 selarray = cursor->Copy(p1, p2);
818 void MathedCursor::SelCut()
821 if (cursor->pos==selpos) return;
824 p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
825 p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
826 selarray = cursor->Copy(p1, p2);
827 cursor->Clean(selpos);
833 void MathedCursor::SelDel()
835 // lyxerr << "Deleting sel "
837 if (cursor->pos==selpos) return;
838 cursor->Clean(selpos);
844 void MathedCursor::SelPaste()
846 // lyxerr << "paste " << selarray << " " << curor->pos;
847 if (selection) SelDel();
849 cursor->Merge(selarray);
854 void MathedCursor::SelStart()
856 lyxerr[Debug::MATHED] << "Starting sel " << endl;
858 selpos = cursor->pos;
859 selstk = new MathStackXIter(mathstk);
860 anchor = selstk->Item(-1);
861 anchor->SetData(cursor->p);
863 anchor->goPosAbs(selpos);
869 void MathedCursor::SelClear()
871 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
880 // Anchor position must be at the same level that stack.
881 void MathedCursor::SelBalance()
883 int d = mathstk.Level() - selstk->Level();
885 // If unbalanced, balance them
888 // lyxerr << "b[" << mathstk.Level() << " " << selstk->Level << " " << anchor->GetX() << " " << cursor->GetX() << "]";
889 anchor = selstk->pop();
890 if (anchor->GetX() >= cursor->GetX())
893 // lyxerr <<"a[" << mathstk.Level() << " " << selstk->Level() <<"]";
896 d = mathstk.Level() - selstk->Level();
899 // Once balanced the levels, check that they are at the same paragraph
900 selpos = anchor->pos;
904 XPoint *MathedCursor::SelGetArea(int& np)
911 static XPoint point[10];
913 // single row selection
914 int i = 0, x, y, a, d, w, xo, yo, x1, y1, a1, d1; //, p1, p2;
916 // Balance anchor and cursor
919 cursor->p->GetXY(xo, yo);
920 w = cursor->p->Width();
921 cursor->GetPos(x1, y1);
922 cursor->getAD(a1, d1);
923 anchor->GetPos(x, y);
953 point[i].x = point[0].x;
954 point[i++].y = point[0].y;
956 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
957 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
958 // for (i=0; i<np; i++)
959 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
966 void MathedCursor::setAccent(int ac)
968 if (ac > 0 && accent < 8) {
969 nestaccent[accent++] = ac;
971 accent = 0; // consumed!
975 int MathedCursor::getAccent() const
977 return (accent>0) ? nestaccent[accent-1]: 0;
981 void MathedCursor::doAccent(byte c, MathedTextCodes t)
985 for (int i=accent-1; i>=0; i--) {
987 ac = new MathAccentInset(c, t, nestaccent[i]);
989 ac = new MathAccentInset(ac, nestaccent[i]);
994 accent = 0; // consumed!
998 void MathedCursor::doAccent(MathedInset *p)
1000 MathedInset *ac = 0;
1002 for (int i=accent-1; i>=0; i--) {
1004 ac = new MathAccentInset(p, nestaccent[i]);
1006 ac = new MathAccentInset(ac, nestaccent[i]);
1011 accent = 0; // consumed!