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));
56 static int const MAX_STACK_ITEMS = 32;
58 struct MathStackXIter {
62 MathStackXIter(int n = MAX_STACK_ITEMS): imax(n) {
63 item = new MathedXIter[imax];
67 MathStackXIter(MathStackXIter & stk);
73 void push(MathedXIter ** a) {
82 MathedXIter * Item(int idx) {
83 return (idx + 1 <= i) ? &item[i - idx - 1] : 0;
91 return i >= MAX_STACK_ITEMS;
98 int Level() { return i; }
100 } mathstk, *selstk = 0;
103 MathStackXIter::MathStackXIter(MathStackXIter & stk)
106 item = new MathedXIter[imax];
108 for (int k = 0; k < i; ++k) {
109 item[k].SetData(stk.item[k].getPar());
111 item[k].goPosAbs(stk.item[k].getPos());
116 /***---------------- Mathed Cursor ---------------------------***/
118 MathedCursor::MathedCursor(MathParInset * p) // : par(p)
122 lastcode = LM_TC_MIN;
125 if (!MathMacroTable::built)
126 MathMacroTable::mathMTable.builtinMacros();
130 void MathedCursor::SetPar(MathParInset * p)
135 selection = false; // not SelClear() ?
137 mathstk.push(&cursor);
139 cursor->SetData(par);
143 void MathedCursor::draw(Painter & pain, int x, int y)
145 // lyxerr << "Cursor[" << x << " " << y << "] ";
146 //win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
148 int w = par->Width() + 2;
149 int a = par->Ascent() + 1;
150 int h = par->Height() + 1;
151 if (par->GetType() > LM_OT_PAR) { a += 4; h += 8; }
153 pain.rectangle(x - 1, y - a, w, h, LColor::mathframe);
155 par->draw(pain, x, y);
160 void MathedCursor::Redraw(Painter & pain)
162 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
164 int w = par->Width(), h = par->Height();
167 //mathed_set_font(LM_TC_VAR, 1);
168 pain.fillRectangle(x, y - par->Ascent(),
169 x + w, y - par->Ascent() + h,
171 par->draw(pain, x, y);
175 bool MathedCursor::Left(bool sel)
182 if (sel && !selection) SelStart();
183 if (!sel && selection) SelClear();
184 bool result = cursor->Prev();
185 if (!result && !mathstk.Empty()) {
186 cursor = mathstk.pop();
189 if (selection) SelClear();
191 if (result && cursor->IsActive()) {
192 if (cursor->IsScript()) {
194 if (!cursor->IsScript())
200 MathParInset * p = cursor->GetActiveInset();
204 p->setArgumentIdx(p->getMaxArgumentIdx());
205 mathstk.push(&cursor);
215 bool MathedCursor::Pop()
217 if (!mathstk.Empty()) {
218 cursor = mathstk.pop();
227 bool MathedCursor::Push()
229 if (cursor->IsActive()) {
230 MathParInset * p = cursor->GetActiveInset();
231 if (!p) return false;
232 mathstk.push(&cursor);
240 bool MathedCursor::Right(bool sel)
247 if (sel && !selection) SelStart();
248 if (!sel && selection) SelClear();
251 if (cursor->IsActive()) {
252 if (cursor->IsScript()) {
254 // A script may be followed by another script
255 if (cursor->IsScript())
260 MathParInset *p = cursor->GetActiveInset();
262 lyxerr << "Math error: Inset expected." << endl;
263 return cursor->Next();
265 p->setArgumentIdx(0);
266 mathstk.push(&cursor);
270 result = cursor->Next();
272 if (cursor->GetChar()!= LM_TC_CR)
273 result = cursor->Next();
274 if (!result && !mathstk.Empty()) {
275 cursor = mathstk.pop();
279 if (selection) SelClear();
286 void MathedCursor::SetPos(int x, int y)
290 if (macro_mode) MacroModeClose();
291 lastcode = LM_TC_MIN;
293 mathstk.push(&cursor);
294 cursor->SetData(par);
295 cursor->fitCoord(x, y);
296 while (cursor->GetX()<x && cursor->OK()) {
297 if (cursor->IsActive()) {
298 MathParInset * p = cursor->GetActiveInset();
299 if (p->Inside(x, y)) {
301 mathstk.push(&cursor);
303 cursor->fitCoord(x, y);
309 if (!cursor->Next() && !Pop())
312 if (x-xp < cursor->GetX()-x) cursor->ipop();
317 void MathedCursor::Home()
319 if (macro_mode) MacroModeClose();
322 mathstk.push(&cursor);
327 void MathedCursor::End()
329 if (macro_mode) MacroModeClose();
332 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)) {
368 if (t == LM_TC_TAB) {
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);
403 void MathedCursor::Insert(MathedInset * p, int t)
405 if (macro_mode) MacroModeClose();
407 if (MathIsActive(t)) {
409 static_cast<MathParInset*>(p)->SetData(selarray);
414 if (mathstk.i < MAX_STACK_ITEMS - 1) {
416 if (accent && !MathIsActive(t)) {
419 cursor->Insert(p, t);
421 if (MathIsActive(t)) {
428 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)
448 void MathedCursor::DelLine()
450 if (macro_mode) MacroModeClose();
455 MathParInset *p= cursor->p;
456 if (p && (p->GetType()<= LM_OT_MATRIX && p->GetType()>= LM_OT_MPAR)) {
462 bool MathedCursor::Up(bool sel)
466 if (macro_mode) MacroModeClose();
468 if (sel && !selection) SelStart();
469 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) {
491 MathParInset * p = 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();
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) {
544 MathParInset * p= 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();
567 bool MathedCursor::Limits()
569 if (cursor->IsInset()) {
570 MathedInset * p = cursor->GetInset();
571 bool ol = p->GetLimits();
573 return (ol!= p->GetLimits());
579 void MathedCursor::SetSize(short size)
581 MathParInset * p = cursor->p;
582 p->UserSetSize(size);
587 void MathedCursor::setLabel(string const & label)
588 { // ugly hack and possible bug
589 if (!cursor->setLabel(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(string 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);
637 p = MathMacroTable::mathMTable.getMacro(s);
639 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
641 p = new MathRootInset();
642 tcode = LM_TC_ACTIVE_INSET;
644 p = new MathFuncInset(s, LM_OT_UNDEF);
646 tcode = static_cast<MathMacro*>(p)->getTCode();
647 lyxerr << "Macro2 " << s << ' ' << tcode << " " ;
650 MathedInsetTypes fractype = LM_OT_FRAC;
654 p = new MathBigopInset(l->name, l->id);
660 Insert(static_cast<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 = static_cast<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()
749 imacro = new MathFuncInset("");
753 lyxerr << "Mathed Warning: Already in macro mode" << endl;
757 void MathedCursor::MacroModeClose()
761 latexkeys * l = in_word_set(imacro->GetName());
762 if (!imacro->GetName().empty()
763 && (!l || (l && IsMacro(l->token, l->id))) &&
764 !MathMacroTable::mathMTable.getMacro(imacro->GetName())) {
766 //imacro->SetName(macrobf);
767 // This guarantees that the string will be removed by destructor
768 imacro->SetType(LM_OT_UNDEF);
770 imacro->SetName(l->name);
773 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
774 setAccent(static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
777 if (l || MathMacroTable::mathMTable.getMacro(imacro->GetName())) {
778 Interpret(imacro->GetName());
787 void MathedCursor::MacroModeBack()
790 if (!imacro->GetName().empty()) {
791 imacro->SetName(imacro->GetName().substr(0, imacro->GetName().length() - 1));
796 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
800 void MathedCursor::MacroModeInsert(char c)
803 imacro->SetName(imacro->GetName() + c);
806 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
810 void MathedCursor::SelCopy()
813 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
814 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
815 selarray = cursor->Copy(p1, p2);
822 void MathedCursor::SelCut()
825 if (cursor->pos == selpos) return;
827 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
828 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
829 selarray = cursor->Copy(p1, p2);
830 cursor->Clean(selpos);
837 void MathedCursor::SelDel()
839 // lyxerr << "Deleting sel "
841 if (cursor->pos == selpos) return;
842 cursor->Clean(selpos);
849 void MathedCursor::SelPaste()
851 // lyxerr << "paste " << selarray << " " << curor->pos;
852 if (selection) SelDel();
854 cursor->Merge(selarray);
860 void MathedCursor::SelStart()
862 lyxerr[Debug::MATHED] << "Starting sel " << endl;
864 selpos = cursor->pos;
865 selstk = new MathStackXIter(mathstk);
866 anchor = selstk->Item(-1);
867 anchor->SetData(cursor->p);
869 anchor->goPosAbs(selpos);
876 void MathedCursor::SelClear()
878 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
887 // Anchor position must be at the same level that stack.
888 void MathedCursor::SelBalance()
890 int d = mathstk.Level() - selstk->Level();
892 // If unbalanced, balance them
895 // lyxerr << "b[" << mathstk.Level() << " " << selstk->Level << " " << anchor->GetX() << " " << cursor->GetX() << "]";
896 anchor = selstk->pop();
897 if (anchor->GetX() >= cursor->GetX())
900 // lyxerr <<"a[" << mathstk.Level() << " " << selstk->Level() <<"]";
903 d = mathstk.Level() - selstk->Level();
906 // Once balanced the levels, check that they are at the same paragraph
907 selpos = anchor->pos;
911 void MathedCursor::SelGetArea(int ** xp, int ** yp, int & np)
913 static int xpoint[10];
914 static int ypoint[10];
925 // single row selection
926 int i = 0, x, y, a, d, xo, yo, x1, y1, a1, d1;
928 // Balance anchor and cursor
931 cursor->p->GetXY(xo, yo);
932 int w = cursor->p->Width();
933 cursor->GetPos(x1, y1);
934 cursor->getAD(a1, d1);
935 anchor->GetPos(x, y);
949 ypoint[i++] = y1 - a;
954 ypoint[i++] = y1 - a;
956 ypoint[i++] = y1 + d;
960 ypoint[i++] = y1 + d;
966 xpoint[i] = xpoint[0];
967 ypoint[i++] = ypoint[0];
972 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
973 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
974 // for (i = 0; i < np; ++i)
975 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
980 void MathedCursor::setAccent(int ac)
982 if (ac > 0 && accent < 8) {
983 nestaccent[accent++] = ac;
985 accent = 0; // consumed!
989 int MathedCursor::getAccent() const
991 return (accent > 0) ? nestaccent[accent - 1]: 0;
995 void MathedCursor::doAccent(byte c, MathedTextCodes t)
997 MathedInset * ac = 0;
999 for (int i = accent - 1; i >= 0; --i) {
1000 if (i == accent - 1)
1001 ac = new MathAccentInset(c, t, nestaccent[i]);
1003 ac = new MathAccentInset(ac, nestaccent[i]);
1008 accent = 0; // consumed!
1012 void MathedCursor::doAccent(MathedInset * p)
1014 MathedInset * ac = 0;
1016 for (int i = accent - 1; i >= 0; --i) {
1017 if (i == accent - 1)
1018 ac = new MathAccentInset(p, nestaccent[i]);
1020 ac = new MathAccentInset(ac, nestaccent[i]);
1025 accent = 0; // consumed!