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_macrotable.h"
29 #include "math_root.h"
30 #include "support/lstrings.h"
34 #include "math_matrixinset.h"
35 #include "math_rowst.h"
36 #include "math_spaceinset.h"
37 #include "math_funcinset.h"
38 #include "math_bigopinset.h"
39 #include "math_fracinset.h"
40 #include "math_decorationinset.h"
41 #include "math_dotsinset.h"
42 #include "math_accentinset.h"
44 static MathedArray * selarray = 0;
48 // This was very smaller, I'll change it later
50 bool IsMacro(short tok, int id)
52 return (tok != LM_TK_STACK && tok != LM_TK_FRAC && tok != LM_TK_SQRT
54 && tok != LM_TK_SPACE && tok != LM_TK_DOTS
55 && tok != LM_TK_FUNCLIM
56 && tok != LM_TK_BIGSYM && tok != LM_TK_ACCENT &&
57 !(tok == LM_TK_SYM && id < 255));
60 static int const MAX_STACK_ITEMS = 32;
62 struct MathStackXIter {
66 MathStackXIter(int n = MAX_STACK_ITEMS): imax(n) {
67 item = new MathedXIter[imax];
71 MathStackXIter(MathStackXIter & stk);
77 void push(MathedXIter ** a) {
86 MathedXIter * Item(int idx) {
87 return (idx + 1 <= i) ? &item[i - idx - 1] : 0;
95 return i >= MAX_STACK_ITEMS;
102 int Level() { return i; }
104 } mathstk, *selstk = 0;
107 MathStackXIter::MathStackXIter(MathStackXIter & stk)
110 item = new MathedXIter[imax];
112 for (int k = 0; k < i; ++k) {
113 item[k].SetData(stk.item[k].getPar());
115 item[k].goPosAbs(stk.item[k].getPos());
120 /***---------------- Mathed Cursor ---------------------------***/
122 MathedCursor::MathedCursor(MathParInset * p) // : par(p)
126 lastcode = LM_TC_MIN;
129 if (!MathMacroTable::built)
130 MathMacroTable::mathMTable.builtinMacros();
134 void MathedCursor::SetPar(MathParInset * p)
139 selection = false; // not SelClear() ?
141 mathstk.push(&cursor);
143 cursor->SetData(par);
147 void MathedCursor::draw(Painter & pain, int x, int y)
149 // lyxerr << "Cursor[" << x << " " << y << "] ";
150 //win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
152 int w = par->Width() + 2;
153 int a = par->Ascent() + 1;
154 int h = par->Height() + 1;
155 if (par->GetType() > LM_OT_PAR) { a += 4; h += 8; }
157 pain.rectangle(x - 1, y - a, w, h, LColor::mathframe);
159 par->draw(pain, x, y);
164 void MathedCursor::Redraw(Painter & pain)
166 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
168 int w = par->Width(), h = par->Height();
172 //mathed_set_font(LM_TC_VAR, 1);
173 pain.fillRectangle(x, y - par->Ascent(),
174 x + w, y - par->Ascent() + h,
176 par->draw(pain, x, y);
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);
220 bool MathedCursor::Pop()
222 if (!mathstk.Empty()) {
223 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);
245 bool MathedCursor::Right(bool sel)
252 if (sel && !selection) SelStart();
253 if (!sel && selection) SelClear();
256 if (cursor->IsActive()) {
257 if (cursor->IsScript()) {
259 // A script may be followed by another script
260 if (cursor->IsScript())
265 MathParInset *p = cursor->GetActiveInset();
267 lyxerr << "Math error: Inset expected." << endl;
268 return cursor->Next();
270 p->setArgumentIdx(0);
271 mathstk.push(&cursor);
275 result = cursor->Next();
277 if (cursor->GetChar()!= LM_TC_CR)
278 result = cursor->Next();
279 if (!result && !mathstk.Empty()) {
280 cursor = mathstk.pop();
284 if (selection) SelClear();
291 void MathedCursor::SetPos(int x, int y)
295 if (macro_mode) MacroModeClose();
296 lastcode = LM_TC_MIN;
298 mathstk.push(&cursor);
299 cursor->SetData(par);
300 cursor->fitCoord(x, y);
301 while (cursor->GetX()<x && cursor->OK()) {
302 if (cursor->IsActive()) {
303 MathParInset * p = cursor->GetActiveInset();
304 if (p->Inside(x, y)) {
306 mathstk.push(&cursor);
308 cursor->fitCoord(x, y);
314 if (!cursor->Next() && !Pop())
317 if (x-xp < cursor->GetX()-x) cursor->ipop();
322 void MathedCursor::Home()
324 if (macro_mode) MacroModeClose();
327 mathstk.push(&cursor);
332 void MathedCursor::End()
334 if (macro_mode) MacroModeClose();
337 mathstk.push(&cursor);
342 MathMatrixInset * create_multiline(short int type, int cols)
353 for (int i = 0; i < cols; ++i)
359 for (int i = 0; i < cols; ++i)
363 case LM_OT_MULTLINEN:
375 MathMatrixInset * mt = new MathMatrixInset(columns, -1);
376 mt->SetAlign(' ', align);
380 void MathedCursor::Insert(byte c, MathedTextCodes t)
382 if (selection) SelDel();
387 if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
391 MathParInset * p = cursor->p;
392 if (p == par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
393 short int type = LM_OT_MPAR;
395 if (c >= '1' && c <= '9') {
398 } else if (c >= 'A' && c <= 'I') {
399 type = LM_OT_ALIGNAT;
402 type = LM_OT_MULTLINE;
406 if (p->GetType() == LM_OT_PARN)
408 MathMatrixInset * mt = create_multiline(type, cols);
409 mt->SetStyle(LM_ST_DISPLAY);
411 mt->SetData(p->GetData());
412 p->SetData(0);//BUG duda
417 int pos = cursor->getPos();
418 cursor->SetData(par);
419 cursor->goPosAbs(pos);
421 if (p && p->Permit(LMPF_ALLOW_CR)) {
425 if (t == LM_TC_TAB) {
426 MathParInset * p = cursor->p;
427 if (p && p->Permit(LMPF_ALLOW_TAB)) {
429 cursor->Insert(c, t);
432 cursor->goNextColumn();
433 } else // Navigate between arguments
434 if (p && p->GetType() == LM_OT_MACRO) {
435 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
436 p->setArgumentIdx(p->getArgumentIdx() + 1);
443 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
451 cursor->Insert(c, t);
460 void MathedCursor::Insert(MathedInset * p, int t)
462 if (macro_mode) MacroModeClose();
464 if (MathIsActive(t)) {
466 static_cast<MathParInset*>(p)->SetData(selarray);
471 if (mathstk.i < MAX_STACK_ITEMS - 1) {
473 if (accent && !MathIsActive(t)) {
476 cursor->Insert(p, t);
478 if (MathIsActive(t)) {
485 lyxerr << "Math error: Full stack." << endl;
489 void MathedCursor::Delete()
491 if (macro_mode) return;
496 if (cursor->Empty() && !mathstk.Empty()) {
497 cursor = mathstk.pop();
499 // if (cursor->GetChar()!= LM_TC_TAB)
505 void MathedCursor::DelLine()
507 if (macro_mode) MacroModeClose();
512 MathParInset *p= cursor->p;
513 if (p && p->GetType() <= LM_OT_MATRIX && p->GetType() >= LM_OT_MPAR) {
519 bool MathedCursor::Up(bool sel)
523 if (macro_mode) MacroModeClose();
525 if (sel && !selection) SelStart();
526 if (!sel && selection) SelClear();
529 if (cursor->IsScript()) {
530 char cd = cursor->GetChar();
535 // A subscript may be followed by a superscript
538 if (MathIsUp(cursor->GetChar())) {
541 } else // return to the previous state
546 result = cursor->Up();
547 if (!result && cursor->p) {
548 MathParInset * p = cursor->p;
550 if (p->GetType() == LM_OT_SCRIPT) {
551 MathedXIter * cx = mathstk.Item(1);
552 bool is_down = (cx->GetChar() == LM_TC_DOWN);
553 cursor = mathstk.pop();
555 result = (is_down) ? true: Up();
557 result = (p->getArgumentIdx() > 0);
559 p->setArgumentIdx(p->getArgumentIdx() - 1);
563 if (!result && !mathstk.Empty()) {
564 cursor = mathstk.pop();
572 bool MathedCursor::Down(bool sel)
576 if (macro_mode) MacroModeClose();
578 if (sel && !selection) SelStart();
579 if (!sel && selection) SelClear();
580 // if (selection) SelClear();
582 if (cursor->IsScript()) {
583 char cd = cursor->GetChar();
584 if (MathIsDown(cd)) {
588 // A superscript may be followed by a subscript
591 if (MathIsDown(cursor->GetChar())) {
599 result = cursor->Down();
600 if (!result && cursor->p) {
601 MathParInset * p= cursor->p;
602 if (p->GetType() == LM_OT_SCRIPT) {
603 MathedXIter * cx = mathstk.Item(1);
604 bool is_up = (cx->GetChar() == LM_TC_UP);
605 cursor = mathstk.pop();
607 result = (is_up) ? true: Down();
609 result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
611 p->setArgumentIdx(p->getArgumentIdx() + 1);
615 if (!result && !mathstk.Empty()) {
616 cursor = mathstk.pop();
624 bool MathedCursor::Limits()
626 if (cursor->IsInset()) {
627 MathedInset * p = cursor->GetInset();
628 bool ol = p->GetLimits();
630 return (ol!= p->GetLimits());
636 void MathedCursor::SetSize(short size)
638 MathParInset * p = cursor->p;
639 p->UserSetSize(size);
644 void MathedCursor::setLabel(string const & label)
645 { // ugly hack and possible bug
646 if (!cursor->setLabel(label))
647 lyxerr << "MathErr: Bad place to set labels." << endl;
651 void MathedCursor::setNumbered()
652 { // another ugly hack
653 MathedRowSt * crow = cursor->crow;
655 crow->setNumbered(!crow->isNumbered());
659 void MathedCursor::Interpret(string const & s)
663 MathedTextCodes tcode = LM_TC_INSET;
665 if (s[0] == '^' || s[0] == '_') {
666 char c = cursor->GetChar();
667 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
670 } else // A script may be followed by a script
671 if (MathIsUp(c) || MathIsDown(c)) {
674 c = cursor->GetChar();
675 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
681 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
682 Insert (p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
685 if (s[0] == '!' || s[0] == ',' || s[0] == ':' || s[0] == ';') {
686 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
687 p = new MathSpaceInset(sp);
694 p = MathMacroTable::mathMTable.getMacro(s);
696 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
698 p = new MathRootInset();
699 tcode = LM_TC_ACTIVE_INSET;
701 p = new MathFuncInset(s, LM_OT_UNDEF);
703 tcode = static_cast<MathMacro*>(p)->getTCode();
704 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
707 MathedInsetTypes fractype = LM_OT_FRAC;
711 p = new MathBigopInset(l->name, l->id);
717 Insert(static_cast<byte>(l->id), MathIsBOPS(l->id) ?
718 LM_TC_BOPS: LM_TC_SYMB);
720 p = new MathFuncInset(l->name);
725 fractype = LM_OT_STACKREL;
726 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
729 p = new MathFracInset(fractype);
730 tcode = LM_TC_ACTIVE_INSET;
735 p = new MathSqrtInset;
736 tcode = LM_TC_ACTIVE_INSET;
741 p = new MathDecorationInset(l->id);
742 tcode = LM_TC_ACTIVE_INSET;
747 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
752 p = new MathSpaceInset(l->id);
757 p = new MathDotsInset(l->name, l->id);
764 p = MathMacroTable::mathMTable.getMacro(s);
765 tcode = static_cast<MathMacro*>(p)->getTCode();
766 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
770 p = new MathFuncInset(l->name);
782 bool MathedCursor::pullArg()
784 if (cursor->IsActive()) {
785 MathParInset * p = cursor->GetActiveInset();
789 MathedArray * a = p->GetData();
803 void MathedCursor::MacroModeOpen()
806 imacro = new MathFuncInset("");
810 lyxerr << "Mathed Warning: Already in macro mode" << endl;
814 void MathedCursor::MacroModeClose()
818 latexkeys * l = in_word_set(imacro->GetName());
819 if (!imacro->GetName().empty()
820 && (!l || (l && IsMacro(l->token, l->id))) &&
821 !MathMacroTable::mathMTable.getMacro(imacro->GetName())) {
823 //imacro->SetName(macrobf);
824 // This guarantees that the string will be removed by destructor
825 imacro->SetType(LM_OT_UNDEF);
827 imacro->SetName(l->name);
830 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
831 setAccent(static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
834 if (l || MathMacroTable::mathMTable.getMacro(imacro->GetName())) {
835 Interpret(imacro->GetName());
844 void MathedCursor::MacroModeBack()
847 if (!imacro->GetName().empty()) {
848 imacro->SetName(imacro->GetName().substr(0, imacro->GetName().length() - 1));
853 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
857 void MathedCursor::MacroModeInsert(char c)
860 imacro->SetName(imacro->GetName() + c);
863 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
867 void MathedCursor::SelCopy()
870 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
871 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
872 selarray = cursor->Copy(p1, p2);
879 void MathedCursor::SelCut()
882 if (cursor->pos == selpos) return;
884 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
885 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
886 selarray = cursor->Copy(p1, p2);
887 cursor->Clean(selpos);
894 void MathedCursor::SelDel()
896 // lyxerr << "Deleting sel "
898 if (cursor->pos == selpos) return;
899 cursor->Clean(selpos);
906 void MathedCursor::SelPaste()
908 // lyxerr << "paste " << selarray << " " << curor->pos;
909 if (selection) SelDel();
911 cursor->Merge(selarray);
917 void MathedCursor::SelStart()
919 lyxerr[Debug::MATHED] << "Starting sel " << endl;
921 selpos = cursor->pos;
922 selstk = new MathStackXIter(mathstk);
923 anchor = selstk->Item(-1);
924 anchor->SetData(cursor->p);
926 anchor->goPosAbs(selpos);
933 void MathedCursor::SelClear()
935 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
944 // Anchor position must be at the same level that stack.
945 void MathedCursor::SelBalance()
947 int d = mathstk.Level() - selstk->Level();
949 // If unbalanced, balance them
952 // lyxerr << "b[" << mathstk.Level() << " " << selstk->Level << " " << anchor->GetX() << " " << cursor->GetX() << "]";
953 anchor = selstk->pop();
954 if (anchor->GetX() >= cursor->GetX())
957 // lyxerr <<"a[" << mathstk.Level() << " " << selstk->Level() <<"]";
960 d = mathstk.Level() - selstk->Level();
963 // Once balanced the levels, check that they are at the same paragraph
964 selpos = anchor->pos;
968 void MathedCursor::SelGetArea(int ** xp, int ** yp, int & np)
970 static int xpoint[10];
971 static int ypoint[10];
982 // single row selection
989 // Balance anchor and cursor
994 cursor->p->GetXY(xo, yo);
995 int w = cursor->p->Width();
998 cursor->GetPos(x1, y1);
999 cursor->getAD(a1, d1);
1002 anchor->GetPos(x, y);
1003 anchor->getAD(a, d);
1006 ypoint[i++] = y + d;
1008 ypoint[i++] = y - a;
1012 ypoint[i++] = y - a;
1016 ypoint[i++] = y1 - a;
1021 ypoint[i++] = y1 - a;
1023 ypoint[i++] = y1 + d;
1027 ypoint[i++] = y1 + d;
1030 ypoint[i++] = y + d;
1033 xpoint[i] = xpoint[0];
1034 ypoint[i++] = ypoint[0];
1039 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
1040 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
1041 // for (i = 0; i < np; ++i)
1042 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
1047 void MathedCursor::setAccent(int ac)
1049 if (ac > 0 && accent < 8) {
1050 nestaccent[accent++] = ac;
1052 accent = 0; // consumed!
1056 int MathedCursor::getAccent() const
1058 return (accent > 0) ? nestaccent[accent - 1]: 0;
1062 void MathedCursor::doAccent(byte c, MathedTextCodes t)
1064 MathedInset * ac = 0;
1066 for (int i = accent - 1; i >= 0; --i) {
1067 if (i == accent - 1)
1068 ac = new MathAccentInset(c, t, nestaccent[i]);
1070 ac = new MathAccentInset(ac, nestaccent[i]);
1075 accent = 0; // consumed!
1079 void MathedCursor::doAccent(MathedInset * p)
1081 MathedInset * ac = 0;
1083 for (int i = accent - 1; i >= 0; --i) {
1084 if (i == accent - 1)
1085 ac = new MathAccentInset(p, nestaccent[i]);
1087 ac = new MathAccentInset(ac, nestaccent[i]);
1092 accent = 0; // consumed!
1096 void MathedCursor::toggleLastCode(MathedTextCodes t)
1099 lastcode = LM_TC_VAR;