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;
41 return ('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z');
44 // This was very smaller, I'll change it later
46 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.
59 char * strnew(char const * s)
61 char * s1 = new char[strlen(s)+1];
68 #define MAX_STACK_ITEMS 32
70 struct MathStackXIter {
75 MathStackXIter(int n = MAX_STACK_ITEMS): imax(n) {
76 item = new MathedXIter[imax];
80 MathStackXIter(MathStackXIter & stk);
86 void push(MathedXIter ** a) {
95 MathedXIter * Item(int idx) {
96 return (idx + 1 <= i) ? &item[i - idx - 1] : 0;
104 return i >= MAX_STACK_ITEMS;
111 int Level() { return i; }
113 } mathstk, *selstk = 0;
116 MathStackXIter::MathStackXIter(MathStackXIter & stk) {
118 item = new MathedXIter[imax];
120 for (int k = 0; k < i; ++k) {
121 item[k].SetData(stk.item[k].getPar());
123 item[k].goPosAbs(stk.item[k].getPos());
128 /***---------------- Mathed Cursor ---------------------------***/
130 MathedCursor::MathedCursor(MathParInset * p) // : par(p)
134 lastcode = LM_TC_MIN;
137 if (!MathMacroTable::built)
138 MathMacroTable::mathMTable.builtinMacros();
142 void MathedCursor::SetPar(MathParInset * p)
147 selection = false; // not SelClear() ?
149 mathstk.push(&cursor);
151 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, w, h, LColor::mathframe);
167 par->draw(pain, x, y);
172 void MathedCursor::Redraw(Painter & pain)
174 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
176 int w = par->Width(), h = par->Height();
179 //mathed_set_font(LM_TC_VAR, 1);
180 pain.fillRectangle(x, y - par->Ascent(),
181 x + w, y - par->Ascent() + h,
183 par->draw(pain, x, y);
187 bool MathedCursor::Left(bool sel)
194 if (sel && !selection) SelStart();
195 if (!sel && selection) SelClear();
196 bool result = cursor->Prev();
197 if (!result && !mathstk.Empty()) {
198 cursor = mathstk.pop();
201 if (selection) SelClear();
203 if (result && cursor->IsActive()) {
204 if (cursor->IsScript()) {
206 if (!cursor->IsScript())
212 MathParInset * p = cursor->GetActiveInset();
216 p->setArgumentIdx(p->getMaxArgumentIdx());
217 mathstk.push(&cursor);
227 bool MathedCursor::Pop()
229 if (!mathstk.Empty()) {
230 cursor = mathstk.pop();
239 bool MathedCursor::Push()
241 if (cursor->IsActive()) {
242 MathParInset * p = cursor->GetActiveInset();
243 if (!p) return false;
244 mathstk.push(&cursor);
252 bool MathedCursor::Right(bool sel)
259 if (sel && !selection) SelStart();
260 if (!sel && selection) SelClear();
263 if (cursor->IsActive()) {
264 if (cursor->IsScript()) {
266 // A script may be followed by another script
267 if (cursor->IsScript())
272 MathParInset *p = cursor->GetActiveInset();
274 lyxerr << "Math error: Inset expected." << endl;
275 return cursor->Next();
277 p->setArgumentIdx(0);
278 mathstk.push(&cursor);
282 result = cursor->Next();
284 if (cursor->GetChar()!= LM_TC_CR)
285 result = cursor->Next();
286 if (!result && !mathstk.Empty()) {
287 cursor = mathstk.pop();
291 if (selection) SelClear();
298 void MathedCursor::SetPos(int x, int y)
302 if (macro_mode) MacroModeClose();
303 lastcode = LM_TC_MIN;
305 mathstk.push(&cursor);
306 cursor->SetData(par);
307 cursor->fitCoord(x, y);
308 while (cursor->GetX()<x && cursor->OK()) {
309 if (cursor->IsActive()) {
310 MathParInset * p = cursor->GetActiveInset();
311 if (p->Inside(x, y)) {
313 mathstk.push(&cursor);
315 cursor->fitCoord(x, y);
321 if (!cursor->Next() && !Pop())
324 if (x-xp < cursor->GetX()-x) cursor->ipop();
329 void MathedCursor::Home()
331 if (macro_mode) MacroModeClose();
334 mathstk.push(&cursor);
339 void MathedCursor::End()
341 if (macro_mode) MacroModeClose();
344 mathstk.push(&cursor);
349 void MathedCursor::Insert(byte c, MathedTextCodes t)
351 if (selection) SelDel();
356 if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
360 MathParInset * p = cursor->p;
361 if (p == par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
362 MathMatrixInset * mt = new MathMatrixInset(3, 0);
363 mt->SetAlign(' ', "rcl");
364 mt->SetStyle(LM_ST_DISPLAY);
365 mt->SetType((p->GetType() == LM_OT_PARN) ? LM_OT_MPARN: LM_OT_MPAR);
366 mt->SetData(p->GetData());
367 p->SetData(0);//BUG duda
372 int pos = cursor->getPos();
373 cursor->SetData(par);
374 cursor->goPosAbs(pos);
376 if (p && p->Permit(LMPF_ALLOW_CR)) {
380 if (t == LM_TC_TAB) {
381 MathParInset * p = cursor->p;
382 if (p && p->Permit(LMPF_ALLOW_TAB)) {
384 cursor->Insert(c, t);
387 cursor->goNextColumn();
388 } else // Navigate between arguments
389 if (p && p->GetType() == LM_OT_MACRO) {
390 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
391 p->setArgumentIdx(p->getArgumentIdx()+1);
398 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
406 cursor->Insert(c, t);
415 void MathedCursor::Insert(MathedInset * p, int t)
417 if (macro_mode) MacroModeClose();
419 if (MathIsActive(t)) {
421 static_cast<MathParInset*>(p)->SetData(selarray);
426 if (mathstk.i < MAX_STACK_ITEMS - 1) {
428 if (accent && !MathIsActive(t)) {
431 cursor->Insert(p, t);
433 if (MathIsActive(t)) {
440 lyxerr << "Math error: Full stack." << endl;
444 void MathedCursor::Delete()
446 if (macro_mode) return;
451 if (cursor->Empty() && !mathstk.Empty()) {
452 cursor = mathstk.pop();
454 // if (cursor->GetChar()!= LM_TC_TAB)
460 void MathedCursor::DelLine()
462 if (macro_mode) MacroModeClose();
467 MathParInset *p= cursor->p;
468 if (p && (p->GetType()<= LM_OT_MATRIX && p->GetType()>= LM_OT_MPAR)) {
474 bool MathedCursor::Up(bool sel)
478 if (macro_mode) MacroModeClose();
480 if (sel && !selection) SelStart();
481 if (!sel && selection) SelClear();
484 if (cursor->IsScript()) {
485 char cd = cursor->GetChar();
490 // A subscript may be followed by a superscript
493 if (MathIsUp(cursor->GetChar())) {
496 } else // return to the previous state
501 result = cursor->Up();
502 if (!result && cursor->p) {
503 MathParInset * p = cursor->p;
505 if (p->GetType() == LM_OT_SCRIPT) {
506 MathedXIter * cx = mathstk.Item(1);
507 bool is_down = (cx->GetChar() == LM_TC_DOWN);
508 cursor = mathstk.pop();
510 result = (is_down) ? true: Up();
512 result = (p->getArgumentIdx() > 0);
514 p->setArgumentIdx(p->getArgumentIdx()-1);
518 if (!result && !mathstk.Empty()) {
519 cursor = mathstk.pop();
527 bool MathedCursor::Down(bool sel)
531 if (macro_mode) MacroModeClose();
533 if (sel && !selection) SelStart();
534 if (!sel && selection) SelClear();
535 // if (selection) SelClear();
537 if (cursor->IsScript()) {
538 char cd = cursor->GetChar();
539 if (MathIsDown(cd)) {
543 // A superscript may be followed by a subscript
546 if (MathIsDown(cursor->GetChar())) {
554 result = cursor->Down();
555 if (!result && cursor->p) {
556 MathParInset * p= cursor->p;
557 if (p->GetType() == LM_OT_SCRIPT) {
558 MathedXIter * cx = mathstk.Item(1);
559 bool is_up = (cx->GetChar() == LM_TC_UP);
560 cursor = mathstk.pop();
562 result = (is_up) ? true: Down();
564 result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
566 p->setArgumentIdx(p->getArgumentIdx()+1);
570 if (!result && !mathstk.Empty()) {
571 cursor = mathstk.pop();
579 bool MathedCursor::Limits()
581 if (cursor->IsInset()) {
582 MathedInset * p = cursor->GetInset();
583 bool ol = p->GetLimits();
585 return (ol!= p->GetLimits());
591 void MathedCursor::SetSize(short size)
593 MathParInset * p = cursor->p;
594 p->UserSetSize(size);
599 void MathedCursor::setLabel(char const * label)
600 { // ugly hack and possible bug
601 if (!cursor->setLabel(strnew(label)))
602 lyxerr << "MathErr: Bad place to set labels." << endl;
606 void MathedCursor::setNumbered()
607 { // another ugly hack
608 MathedRowSt * crow = cursor->crow;
610 crow->setNumbered(!crow->isNumbered());
614 void MathedCursor::Interpret(char const * s)
618 MathedTextCodes tcode = LM_TC_INSET;
620 if (s[0] == '^' || s[0] == '_') {
621 char c = cursor->GetChar();
622 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
625 } else // A script may be followed by a script
626 if (MathIsUp(c) || MathIsDown(c)) {
629 c = cursor->GetChar();
630 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
636 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
637 Insert (p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
640 if (s[0] == '!' || s[0] == ',' || s[0] == ':' || s[0] == ';') {
641 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
642 p = new MathSpaceInset(sp);
646 l = in_word_set (s, strlen(s));
649 p = MathMacroTable::mathMTable.getMacro(s);
651 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
652 if (strcmp("root", s) == 0) {
653 p = new MathRootInset();
654 tcode = LM_TC_ACTIVE_INSET;
656 p = new MathFuncInset(s, LM_OT_UNDEF);
658 tcode = static_cast<MathMacro*>(p)->getTCode();
659 lyxerr << "Macro2 " << s << ' ' << tcode << " " ;
662 MathedInsetTypes fractype = LM_OT_FRAC;
666 p = new MathBigopInset(l->name, l->id);
672 Insert(static_cast<byte>(l->id), MathIsBOPS(l->id) ?
673 LM_TC_BOPS: LM_TC_SYMB);
675 p = new MathFuncInset(l->name);
680 fractype = LM_OT_STACKREL;
681 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
684 p = new MathFracInset(fractype);
685 tcode = LM_TC_ACTIVE_INSET;
690 p = new MathSqrtInset;
691 tcode = LM_TC_ACTIVE_INSET;
696 p = new MathDecorationInset(l->id);
697 tcode = LM_TC_ACTIVE_INSET;
702 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
707 p = new MathSpaceInset(l->id);
712 p = new MathDotsInset(l->name, l->id);
719 p = MathMacroTable::mathMTable.getMacro(s);
720 tcode = static_cast<MathMacro*>(p)->getTCode();
721 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
725 p = new MathFuncInset(l->name);
737 bool MathedCursor::pullArg()
739 if (cursor->IsActive()) {
740 MathParInset * p = cursor->GetActiveInset();
744 LyxArrayBase * a = p->GetData();
758 void MathedCursor::MacroModeOpen()
763 imacro = new MathFuncInset(¯obf[0]);
767 lyxerr << "Mathed Warning: Already in macro mode" << endl;
771 void MathedCursor::MacroModeClose()
775 latexkeys * l = in_word_set(macrobf, macroln);
776 if (macroln > 0 && (!l || (l && IsMacro(l->token, l->id))) &&
777 !MathMacroTable::mathMTable.getMacro(macrobf)) {
779 imacro->SetName(strnew(macrobf));
780 // This guarantees that the string will be removed by destructor
781 imacro->SetType(LM_OT_UNDEF);
783 imacro->SetName(l->name);
787 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
788 setAccent(static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
791 if (l || MathMacroTable::mathMTable.getMacro(macrobf)) {
800 void MathedCursor::MacroModeBack()
804 macrobf[--macroln] = '\0';
809 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
813 void MathedCursor::MacroModeInsert(char c)
816 macrobf[macroln + 1] = macrobf[macroln];
817 macrobf[macroln++] = c;
820 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
824 void MathedCursor::SelCopy()
827 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
828 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
829 selarray = cursor->Copy(p1, p2);
836 void MathedCursor::SelCut()
839 if (cursor->pos == selpos) return;
841 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
842 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
843 selarray = cursor->Copy(p1, p2);
844 cursor->Clean(selpos);
851 void MathedCursor::SelDel()
853 // lyxerr << "Deleting sel "
855 if (cursor->pos == selpos) return;
856 cursor->Clean(selpos);
863 void MathedCursor::SelPaste()
865 // lyxerr << "paste " << selarray << " " << curor->pos;
866 if (selection) SelDel();
868 cursor->Merge(selarray);
874 void MathedCursor::SelStart()
876 lyxerr[Debug::MATHED] << "Starting sel " << endl;
878 selpos = cursor->pos;
879 selstk = new MathStackXIter(mathstk);
880 anchor = selstk->Item(-1);
881 anchor->SetData(cursor->p);
883 anchor->goPosAbs(selpos);
890 void MathedCursor::SelClear()
892 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
901 // Anchor position must be at the same level that stack.
902 void MathedCursor::SelBalance()
904 int d = mathstk.Level() - selstk->Level();
906 // If unbalanced, balance them
909 // lyxerr << "b[" << mathstk.Level() << " " << selstk->Level << " " << anchor->GetX() << " " << cursor->GetX() << "]";
910 anchor = selstk->pop();
911 if (anchor->GetX() >= cursor->GetX())
914 // lyxerr <<"a[" << mathstk.Level() << " " << selstk->Level() <<"]";
917 d = mathstk.Level() - selstk->Level();
920 // Once balanced the levels, check that they are at the same paragraph
921 selpos = anchor->pos;
925 void MathedCursor::SelGetArea(int ** xp, int ** yp, int & np)
927 static int xpoint[10];
928 static int ypoint[10];
939 // single row selection
940 int i = 0, x, y, a, d, xo, yo, x1, y1, a1, d1;
942 // Balance anchor and cursor
945 cursor->p->GetXY(xo, yo);
946 int w = cursor->p->Width();
947 cursor->GetPos(x1, y1);
948 cursor->getAD(a1, d1);
949 anchor->GetPos(x, y);
963 ypoint[i++] = y1 - a;
968 ypoint[i++] = y1 - a;
970 ypoint[i++] = y1 + d;
974 ypoint[i++] = y1 + d;
980 xpoint[i] = xpoint[0];
981 ypoint[i++] = ypoint[0];
986 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
987 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
988 // for (i = 0; i < np; ++i)
989 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
994 void MathedCursor::setAccent(int ac)
996 if (ac > 0 && accent < 8) {
997 nestaccent[accent++] = ac;
999 accent = 0; // consumed!
1003 int MathedCursor::getAccent() const
1005 return (accent > 0) ? nestaccent[accent - 1]: 0;
1009 void MathedCursor::doAccent(byte c, MathedTextCodes t)
1011 MathedInset * ac = 0;
1013 for (int i = accent - 1; i >= 0; --i) {
1014 if (i == accent - 1)
1015 ac = new MathAccentInset(c, t, nestaccent[i]);
1017 ac = new MathAccentInset(ac, nestaccent[i]);
1022 accent = 0; // consumed!
1026 void MathedCursor::doAccent(MathedInset * p)
1028 MathedInset * ac = 0;
1030 for (int i = accent - 1; i >= 0; --i) {
1031 if (i == accent - 1)
1032 ac = new MathAccentInset(p, nestaccent[i]);
1034 ac = new MathAccentInset(ac, nestaccent[i]);
1039 accent = 0; // consumed!