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;
39 return ('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z');
42 // This was very smaller, I'll change it later
44 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));
55 // Yes, mathed isn't using string yet.
57 char * strnew(char const * s)
59 char * s1 = new char[strlen(s)+1];
66 #define MAX_STACK_ITEMS 32
68 struct MathStackXIter {
73 MathStackXIter(int n = MAX_STACK_ITEMS): imax(n) {
74 item = new MathedXIter[imax];
78 MathStackXIter(MathStackXIter & stk);
84 void push(MathedXIter ** a) {
93 MathedXIter * Item(int idx) {
94 return (idx + 1 <= i) ? &item[i - idx - 1] : 0;
102 return i >= MAX_STACK_ITEMS;
109 int Level() { return i; }
111 } mathstk, *selstk = 0;
114 MathStackXIter::MathStackXIter(MathStackXIter & stk) {
116 item = new MathedXIter[imax];
118 for (int k = 0; k < i; ++k) {
119 item[k].SetData(stk.item[k].getPar());
121 item[k].goPosAbs(stk.item[k].getPos());
126 /***---------------- Mathed Cursor ---------------------------***/
128 MathedCursor::MathedCursor(MathParInset * p) // : par(p)
132 lastcode = LM_TC_MIN;
135 if (!MathMacroTable::built)
136 MathMacroTable::mathMTable.builtinMacros();
140 void MathedCursor::SetPar(MathParInset * p)
145 selection = false; // not SelClear() ?
147 mathstk.push(&cursor);
149 cursor->SetData(par);
153 void MathedCursor::draw(Painter & pain, int x, int y)
155 // lyxerr << "Cursor[" << x << " " << y << "] ";
156 //win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
158 int w = par->Width() + 2;
159 int a = par->Ascent() + 1;
160 int h = par->Height() + 1;
161 if (par->GetType() > LM_OT_PAR) { a += 4; h += 8; }
163 pain.rectangle(x - 1, y - a, w, h, LColor::mathframe);
165 par->draw(pain, x, y);
170 void MathedCursor::Redraw(Painter & pain)
172 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
174 int w = par->Width(), h = par->Height();
177 //mathed_set_font(LM_TC_VAR, 1);
178 pain.fillRectangle(x, y - par->Ascent(),
179 x + w, y - par->Ascent() + h,
181 par->draw(pain, x, y);
185 bool MathedCursor::Left(bool sel)
192 if (sel && !selection) SelStart();
193 if (!sel && selection) SelClear();
194 bool result = cursor->Prev();
195 if (!result && !mathstk.Empty()) {
196 cursor = mathstk.pop();
199 if (selection) SelClear();
201 if (result && cursor->IsActive()) {
202 if (cursor->IsScript()) {
204 if (!cursor->IsScript())
210 MathParInset * p = cursor->GetActiveInset();
214 p->setArgumentIdx(p->getMaxArgumentIdx());
215 mathstk.push(&cursor);
225 bool MathedCursor::Pop()
227 if (!mathstk.Empty()) {
228 cursor = mathstk.pop();
237 bool MathedCursor::Push()
239 if (cursor->IsActive()) {
240 MathParInset * p = cursor->GetActiveInset();
241 if (!p) return false;
242 mathstk.push(&cursor);
250 bool MathedCursor::Right(bool sel)
257 if (sel && !selection) SelStart();
258 if (!sel && selection) SelClear();
261 if (cursor->IsActive()) {
262 if (cursor->IsScript()) {
264 // A script may be followed by another script
265 if (cursor->IsScript())
270 MathParInset *p = cursor->GetActiveInset();
272 lyxerr << "Math error: Inset expected." << endl;
273 return cursor->Next();
275 p->setArgumentIdx(0);
276 mathstk.push(&cursor);
280 result = cursor->Next();
282 if (cursor->GetChar()!= LM_TC_CR)
283 result = cursor->Next();
284 if (!result && !mathstk.Empty()) {
285 cursor = mathstk.pop();
289 if (selection) SelClear();
296 void MathedCursor::SetPos(int x, int y)
300 if (macro_mode) MacroModeClose();
301 lastcode = LM_TC_MIN;
303 mathstk.push(&cursor);
304 cursor->SetData(par);
305 cursor->fitCoord(x, y);
306 while (cursor->GetX()<x && cursor->OK()) {
307 if (cursor->IsActive()) {
308 MathParInset * p = cursor->GetActiveInset();
309 if (p->Inside(x, y)) {
311 mathstk.push(&cursor);
313 cursor->fitCoord(x, y);
319 if (!cursor->Next() && !Pop())
322 if (x-xp < cursor->GetX()-x) cursor->ipop();
327 void MathedCursor::Home()
329 if (macro_mode) MacroModeClose();
332 mathstk.push(&cursor);
337 void MathedCursor::End()
339 if (macro_mode) MacroModeClose();
342 mathstk.push(&cursor);
347 void MathedCursor::Insert(byte c, MathedTextCodes t)
349 if (selection) SelDel();
354 if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
358 MathParInset * p = cursor->p;
359 if (p == par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
360 MathMatrixInset * mt = new MathMatrixInset(3, 0);
361 mt->SetAlign(' ', "rcl");
362 mt->SetStyle(LM_ST_DISPLAY);
363 mt->SetType((p->GetType() == LM_OT_PARN) ? LM_OT_MPARN: LM_OT_MPAR);
364 mt->SetData(p->GetData());
365 p->SetData(0);//BUG duda
370 int pos = cursor->getPos();
371 cursor->SetData(par);
372 cursor->goPosAbs(pos);
374 if (p && p->Permit(LMPF_ALLOW_CR)) {
378 if (t == LM_TC_TAB) {
379 MathParInset * p = cursor->p;
380 if (p && p->Permit(LMPF_ALLOW_TAB)) {
382 cursor->Insert(c, t);
385 cursor->goNextColumn();
386 } else // Navigate between arguments
387 if (p && p->GetType() == LM_OT_MACRO) {
388 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
389 p->setArgumentIdx(p->getArgumentIdx()+1);
396 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
404 cursor->Insert(c, t);
413 void MathedCursor::Insert(MathedInset * p, int t)
415 if (macro_mode) MacroModeClose();
417 if (MathIsActive(t)) {
419 static_cast<MathParInset*>(p)->SetData(selarray);
424 if (mathstk.i < MAX_STACK_ITEMS - 1) {
426 if (accent && !MathIsActive(t)) {
429 cursor->Insert(p, t);
431 if (MathIsActive(t)) {
438 lyxerr << "Math error: Full stack." << endl;
442 void MathedCursor::Delete()
444 if (macro_mode) return;
449 if (cursor->Empty() && !mathstk.Empty()) {
450 cursor = mathstk.pop();
452 // if (cursor->GetChar()!= LM_TC_TAB)
458 void MathedCursor::DelLine()
460 if (macro_mode) MacroModeClose();
465 MathParInset *p= cursor->p;
466 if (p && (p->GetType()<= LM_OT_MATRIX && p->GetType()>= LM_OT_MPAR)) {
472 bool MathedCursor::Up(bool sel)
476 if (macro_mode) MacroModeClose();
478 if (sel && !selection) SelStart();
479 if (!sel && selection) SelClear();
482 if (cursor->IsScript()) {
483 char cd = cursor->GetChar();
488 // A subscript may be followed by a superscript
491 if (MathIsUp(cursor->GetChar())) {
494 } else // return to the previous state
499 result = cursor->Up();
500 if (!result && cursor->p) {
501 MathParInset * p = cursor->p;
503 if (p->GetType() == LM_OT_SCRIPT) {
504 MathedXIter * cx = mathstk.Item(1);
505 bool is_down = (cx->GetChar() == LM_TC_DOWN);
506 cursor = mathstk.pop();
508 result = (is_down) ? true: Up();
510 result = (p->getArgumentIdx() > 0);
512 p->setArgumentIdx(p->getArgumentIdx()-1);
516 if (!result && !mathstk.Empty()) {
517 cursor = mathstk.pop();
525 bool MathedCursor::Down(bool sel)
529 if (macro_mode) MacroModeClose();
531 if (sel && !selection) SelStart();
532 if (!sel && selection) SelClear();
533 // if (selection) SelClear();
535 if (cursor->IsScript()) {
536 char cd = cursor->GetChar();
537 if (MathIsDown(cd)) {
541 // A superscript may be followed by a subscript
544 if (MathIsDown(cursor->GetChar())) {
552 result = cursor->Down();
553 if (!result && cursor->p) {
554 MathParInset * p= cursor->p;
555 if (p->GetType() == LM_OT_SCRIPT) {
556 MathedXIter * cx = mathstk.Item(1);
557 bool is_up = (cx->GetChar() == LM_TC_UP);
558 cursor = mathstk.pop();
560 result = (is_up) ? true: Down();
562 result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
564 p->setArgumentIdx(p->getArgumentIdx()+1);
568 if (!result && !mathstk.Empty()) {
569 cursor = mathstk.pop();
577 bool MathedCursor::Limits()
579 if (cursor->IsInset()) {
580 MathedInset * p = cursor->GetInset();
581 bool ol = p->GetLimits();
583 return (ol!= p->GetLimits());
589 void MathedCursor::SetSize(short size)
591 MathParInset * p = cursor->p;
592 p->UserSetSize(size);
597 void MathedCursor::setLabel(char const * label)
598 { // ugly hack and possible bug
599 if (!cursor->setLabel(strnew(label)))
600 lyxerr << "MathErr: Bad place to set labels." << endl;
604 void MathedCursor::setNumbered()
605 { // another ugly hack
606 MathedRowSt * crow = cursor->crow;
608 crow->setNumbered(!crow->isNumbered());
612 void MathedCursor::Interpret(char const * s)
616 MathedTextCodes tcode = LM_TC_INSET;
618 if (s[0] == '^' || s[0] == '_') {
619 char c = cursor->GetChar();
620 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
623 } else // A script may be followed by a script
624 if (MathIsUp(c) || MathIsDown(c)) {
627 c = cursor->GetChar();
628 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
634 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
635 Insert (p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
638 if (s[0] == '!' || s[0] == ',' || s[0] == ':' || s[0] == ';') {
639 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
640 p = new MathSpaceInset(sp);
644 l = in_word_set (s, strlen(s));
647 p = MathMacroTable::mathMTable.getMacro(s);
649 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
650 if (strcmp("root", s) == 0) {
651 p = new MathRootInset();
652 tcode = LM_TC_ACTIVE_INSET;
654 p = new MathFuncInset(s, LM_OT_UNDEF);
656 tcode = static_cast<MathMacro*>(p)->getTCode();
657 lyxerr << "Macro2 " << s << ' ' << tcode << " " ;
660 MathedInsetTypes fractype = LM_OT_FRAC;
664 p = new MathBigopInset(l->name, l->id);
670 Insert(static_cast<byte>(l->id), MathIsBOPS(l->id) ?
671 LM_TC_BOPS: LM_TC_SYMB);
673 p = new MathFuncInset(l->name);
678 fractype = LM_OT_STACKREL;
679 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
682 p = new MathFracInset(fractype);
683 tcode = LM_TC_ACTIVE_INSET;
688 p = new MathSqrtInset;
689 tcode = LM_TC_ACTIVE_INSET;
694 p = new MathDecorationInset(l->id);
695 tcode = LM_TC_ACTIVE_INSET;
700 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
705 p = new MathSpaceInset(l->id);
710 p = new MathDotsInset(l->name, l->id);
717 p = MathMacroTable::mathMTable.getMacro(s);
718 tcode = static_cast<MathMacro*>(p)->getTCode();
719 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
723 p = new MathFuncInset(l->name);
735 bool MathedCursor::pullArg()
737 if (cursor->IsActive()) {
738 MathParInset * p = cursor->GetActiveInset();
742 LyxArrayBase * a = p->GetData();
756 void MathedCursor::MacroModeOpen()
761 imacro = new MathFuncInset(¯obf[0]);
765 lyxerr << "Mathed Warning: Already in macro mode" << endl;
769 void MathedCursor::MacroModeClose()
773 latexkeys * l = in_word_set(macrobf, macroln);
774 if (macroln > 0 && (!l || (l && IsMacro(l->token, l->id))) &&
775 !MathMacroTable::mathMTable.getMacro(macrobf)) {
777 imacro->SetName(strnew(macrobf));
778 // This guarantees that the string will be removed by destructor
779 imacro->SetType(LM_OT_UNDEF);
781 imacro->SetName(l->name);
785 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
786 setAccent(static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
789 if (l || MathMacroTable::mathMTable.getMacro(macrobf)) {
798 void MathedCursor::MacroModeBack()
802 macrobf[--macroln] = '\0';
807 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
811 void MathedCursor::MacroModeInsert(char c)
814 macrobf[macroln + 1] = macrobf[macroln];
815 macrobf[macroln++] = c;
818 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
822 void MathedCursor::SelCopy()
825 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
826 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
827 selarray = cursor->Copy(p1, p2);
834 void MathedCursor::SelCut()
837 if (cursor->pos == selpos) return;
839 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
840 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
841 selarray = cursor->Copy(p1, p2);
842 cursor->Clean(selpos);
849 void MathedCursor::SelDel()
851 // lyxerr << "Deleting sel "
853 if (cursor->pos == selpos) return;
854 cursor->Clean(selpos);
861 void MathedCursor::SelPaste()
863 // lyxerr << "paste " << selarray << " " << curor->pos;
864 if (selection) SelDel();
866 cursor->Merge(selarray);
872 void MathedCursor::SelStart()
874 lyxerr[Debug::MATHED] << "Starting sel " << endl;
876 selpos = cursor->pos;
877 selstk = new MathStackXIter(mathstk);
878 anchor = selstk->Item(-1);
879 anchor->SetData(cursor->p);
881 anchor->goPosAbs(selpos);
888 void MathedCursor::SelClear()
890 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
899 // Anchor position must be at the same level that stack.
900 void MathedCursor::SelBalance()
902 int d = mathstk.Level() - selstk->Level();
904 // If unbalanced, balance them
907 // lyxerr << "b[" << mathstk.Level() << " " << selstk->Level << " " << anchor->GetX() << " " << cursor->GetX() << "]";
908 anchor = selstk->pop();
909 if (anchor->GetX() >= cursor->GetX())
912 // lyxerr <<"a[" << mathstk.Level() << " " << selstk->Level() <<"]";
915 d = mathstk.Level() - selstk->Level();
918 // Once balanced the levels, check that they are at the same paragraph
919 selpos = anchor->pos;
923 void MathedCursor::SelGetArea(int ** xp, int ** yp, int & np)
925 static int xpoint[10];
926 static int ypoint[10];
937 // single row selection
938 int i = 0, x, y, a, d, xo, yo, x1, y1, a1, d1;
940 // Balance anchor and cursor
943 cursor->p->GetXY(xo, yo);
944 int w = cursor->p->Width();
945 cursor->GetPos(x1, y1);
946 cursor->getAD(a1, d1);
947 anchor->GetPos(x, y);
961 ypoint[i++] = y1 - a;
966 ypoint[i++] = y1 - a;
968 ypoint[i++] = y1 + d;
972 ypoint[i++] = y1 + d;
978 xpoint[i] = xpoint[0];
979 ypoint[i++] = ypoint[0];
984 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
985 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
986 // for (i = 0; i < np; ++i)
987 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
992 void MathedCursor::setAccent(int ac)
994 if (ac > 0 && accent < 8) {
995 nestaccent[accent++] = ac;
997 accent = 0; // consumed!
1001 int MathedCursor::getAccent() const
1003 return (accent > 0) ? nestaccent[accent - 1]: 0;
1007 void MathedCursor::doAccent(byte c, MathedTextCodes t)
1009 MathedInset * ac = 0;
1011 for (int i = accent - 1; i >= 0; --i) {
1012 if (i == accent - 1)
1013 ac = new MathAccentInset(c, t, nestaccent[i]);
1015 ac = new MathAccentInset(ac, nestaccent[i]);
1020 accent = 0; // consumed!
1024 void MathedCursor::doAccent(MathedInset * p)
1026 MathedInset * ac = 0;
1028 for (int i = accent - 1; i >= 0; --i) {
1029 if (i == accent - 1)
1030 ac = new MathAccentInset(p, nestaccent[i]);
1032 ac = new MathAccentInset(ac, nestaccent[i]);
1037 accent = 0; // consumed!