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 static int const MAX_STACK_ITEMS = 32;
71 struct MathStackXIter {
76 MathStackXIter(int n = MAX_STACK_ITEMS): imax(n) {
77 item = new MathedXIter[imax];
81 MathStackXIter(MathStackXIter & stk);
87 void push(MathedXIter ** a) {
96 MathedXIter * Item(int idx) {
97 return (idx + 1 <= i) ? &item[i - idx - 1] : 0;
105 return i >= MAX_STACK_ITEMS;
112 int Level() { return i; }
114 } mathstk, *selstk = 0;
117 MathStackXIter::MathStackXIter(MathStackXIter & stk) {
119 item = new MathedXIter[imax];
121 for (int k = 0; k < i; ++k) {
122 item[k].SetData(stk.item[k].getPar());
124 item[k].goPosAbs(stk.item[k].getPos());
129 /***---------------- Mathed Cursor ---------------------------***/
131 MathedCursor::MathedCursor(MathParInset * p) // : par(p)
135 lastcode = LM_TC_MIN;
138 if (!MathMacroTable::built)
139 MathMacroTable::mathMTable.builtinMacros();
143 void MathedCursor::SetPar(MathParInset * p)
148 selection = false; // not SelClear() ?
150 mathstk.push(&cursor);
152 cursor->SetData(par);
156 void MathedCursor::draw(Painter & pain, int x, int y)
158 // lyxerr << "Cursor[" << x << " " << y << "] ";
159 //win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
161 int w = par->Width() + 2;
162 int a = par->Ascent() + 1;
163 int h = par->Height() + 1;
164 if (par->GetType() > LM_OT_PAR) { a += 4; h += 8; }
166 pain.rectangle(x - 1, y - a, w, h, LColor::mathframe);
168 par->draw(pain, x, y);
173 void MathedCursor::Redraw(Painter & pain)
175 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
177 int w = par->Width(), h = par->Height();
180 //mathed_set_font(LM_TC_VAR, 1);
181 pain.fillRectangle(x, y - par->Ascent(),
182 x + w, y - par->Ascent() + h,
184 par->draw(pain, x, y);
188 bool MathedCursor::Left(bool sel)
195 if (sel && !selection) SelStart();
196 if (!sel && selection) SelClear();
197 bool result = cursor->Prev();
198 if (!result && !mathstk.Empty()) {
199 cursor = mathstk.pop();
202 if (selection) SelClear();
204 if (result && cursor->IsActive()) {
205 if (cursor->IsScript()) {
207 if (!cursor->IsScript())
213 MathParInset * p = cursor->GetActiveInset();
217 p->setArgumentIdx(p->getMaxArgumentIdx());
218 mathstk.push(&cursor);
228 bool MathedCursor::Pop()
230 if (!mathstk.Empty()) {
231 cursor = mathstk.pop();
240 bool MathedCursor::Push()
242 if (cursor->IsActive()) {
243 MathParInset * p = cursor->GetActiveInset();
244 if (!p) return false;
245 mathstk.push(&cursor);
253 bool MathedCursor::Right(bool sel)
260 if (sel && !selection) SelStart();
261 if (!sel && selection) SelClear();
264 if (cursor->IsActive()) {
265 if (cursor->IsScript()) {
267 // A script may be followed by another script
268 if (cursor->IsScript())
273 MathParInset *p = cursor->GetActiveInset();
275 lyxerr << "Math error: Inset expected." << endl;
276 return cursor->Next();
278 p->setArgumentIdx(0);
279 mathstk.push(&cursor);
283 result = cursor->Next();
285 if (cursor->GetChar()!= LM_TC_CR)
286 result = cursor->Next();
287 if (!result && !mathstk.Empty()) {
288 cursor = mathstk.pop();
292 if (selection) SelClear();
299 void MathedCursor::SetPos(int x, int y)
303 if (macro_mode) MacroModeClose();
304 lastcode = LM_TC_MIN;
306 mathstk.push(&cursor);
307 cursor->SetData(par);
308 cursor->fitCoord(x, y);
309 while (cursor->GetX()<x && cursor->OK()) {
310 if (cursor->IsActive()) {
311 MathParInset * p = cursor->GetActiveInset();
312 if (p->Inside(x, y)) {
314 mathstk.push(&cursor);
316 cursor->fitCoord(x, y);
322 if (!cursor->Next() && !Pop())
325 if (x-xp < cursor->GetX()-x) cursor->ipop();
330 void MathedCursor::Home()
332 if (macro_mode) MacroModeClose();
335 mathstk.push(&cursor);
340 void MathedCursor::End()
342 if (macro_mode) MacroModeClose();
345 mathstk.push(&cursor);
350 void MathedCursor::Insert(byte c, MathedTextCodes t)
352 if (selection) SelDel();
357 if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
361 MathParInset * p = cursor->p;
362 if (p == par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
363 MathMatrixInset * mt = new MathMatrixInset(3, 0);
364 mt->SetAlign(' ', "rcl");
365 mt->SetStyle(LM_ST_DISPLAY);
366 mt->SetType((p->GetType() == LM_OT_PARN) ? LM_OT_MPARN: LM_OT_MPAR);
367 mt->SetData(p->GetData());
368 p->SetData(0);//BUG duda
373 int pos = cursor->getPos();
374 cursor->SetData(par);
375 cursor->goPosAbs(pos);
377 if (p && p->Permit(LMPF_ALLOW_CR)) {
381 if (t == LM_TC_TAB) {
382 MathParInset * p = cursor->p;
383 if (p && p->Permit(LMPF_ALLOW_TAB)) {
385 cursor->Insert(c, t);
388 cursor->goNextColumn();
389 } else // Navigate between arguments
390 if (p && p->GetType() == LM_OT_MACRO) {
391 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
392 p->setArgumentIdx(p->getArgumentIdx()+1);
399 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
407 cursor->Insert(c, t);
416 void MathedCursor::Insert(MathedInset * p, int t)
418 if (macro_mode) MacroModeClose();
420 if (MathIsActive(t)) {
422 static_cast<MathParInset*>(p)->SetData(selarray);
427 if (mathstk.i < MAX_STACK_ITEMS - 1) {
429 if (accent && !MathIsActive(t)) {
432 cursor->Insert(p, t);
434 if (MathIsActive(t)) {
441 lyxerr << "Math error: Full stack." << endl;
445 void MathedCursor::Delete()
447 if (macro_mode) return;
452 if (cursor->Empty() && !mathstk.Empty()) {
453 cursor = mathstk.pop();
455 // if (cursor->GetChar()!= LM_TC_TAB)
461 void MathedCursor::DelLine()
463 if (macro_mode) MacroModeClose();
468 MathParInset *p= cursor->p;
469 if (p && (p->GetType()<= LM_OT_MATRIX && p->GetType()>= LM_OT_MPAR)) {
475 bool MathedCursor::Up(bool sel)
479 if (macro_mode) MacroModeClose();
481 if (sel && !selection) SelStart();
482 if (!sel && selection) SelClear();
485 if (cursor->IsScript()) {
486 char cd = cursor->GetChar();
491 // A subscript may be followed by a superscript
494 if (MathIsUp(cursor->GetChar())) {
497 } else // return to the previous state
502 result = cursor->Up();
503 if (!result && cursor->p) {
504 MathParInset * p = cursor->p;
506 if (p->GetType() == LM_OT_SCRIPT) {
507 MathedXIter * cx = mathstk.Item(1);
508 bool is_down = (cx->GetChar() == LM_TC_DOWN);
509 cursor = mathstk.pop();
511 result = (is_down) ? true: Up();
513 result = (p->getArgumentIdx() > 0);
515 p->setArgumentIdx(p->getArgumentIdx()-1);
519 if (!result && !mathstk.Empty()) {
520 cursor = mathstk.pop();
528 bool MathedCursor::Down(bool sel)
532 if (macro_mode) MacroModeClose();
534 if (sel && !selection) SelStart();
535 if (!sel && selection) SelClear();
536 // if (selection) SelClear();
538 if (cursor->IsScript()) {
539 char cd = cursor->GetChar();
540 if (MathIsDown(cd)) {
544 // A superscript may be followed by a subscript
547 if (MathIsDown(cursor->GetChar())) {
555 result = cursor->Down();
556 if (!result && cursor->p) {
557 MathParInset * p= cursor->p;
558 if (p->GetType() == LM_OT_SCRIPT) {
559 MathedXIter * cx = mathstk.Item(1);
560 bool is_up = (cx->GetChar() == LM_TC_UP);
561 cursor = mathstk.pop();
563 result = (is_up) ? true: Down();
565 result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
567 p->setArgumentIdx(p->getArgumentIdx()+1);
571 if (!result && !mathstk.Empty()) {
572 cursor = mathstk.pop();
580 bool MathedCursor::Limits()
582 if (cursor->IsInset()) {
583 MathedInset * p = cursor->GetInset();
584 bool ol = p->GetLimits();
586 return (ol!= p->GetLimits());
592 void MathedCursor::SetSize(short size)
594 MathParInset * p = cursor->p;
595 p->UserSetSize(size);
600 void MathedCursor::setLabel(string const & label)
601 { // ugly hack and possible bug
602 if (!cursor->setLabel(label))
603 lyxerr << "MathErr: Bad place to set labels." << endl;
607 void MathedCursor::setNumbered()
608 { // another ugly hack
609 MathedRowSt * crow = cursor->crow;
611 crow->setNumbered(!crow->isNumbered());
615 void MathedCursor::Interpret(string const & s)
619 MathedTextCodes tcode = LM_TC_INSET;
621 if (s[0] == '^' || s[0] == '_') {
622 char c = cursor->GetChar();
623 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
626 } else // A script may be followed by a script
627 if (MathIsUp(c) || MathIsDown(c)) {
630 c = cursor->GetChar();
631 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
637 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
638 Insert (p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
641 if (s[0] == '!' || s[0] == ',' || s[0] == ':' || s[0] == ';') {
642 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
643 p = new MathSpaceInset(sp);
650 p = MathMacroTable::mathMTable.getMacro(s);
652 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
654 p = new MathRootInset();
655 tcode = LM_TC_ACTIVE_INSET;
657 p = new MathFuncInset(s, LM_OT_UNDEF);
659 tcode = static_cast<MathMacro*>(p)->getTCode();
660 lyxerr << "Macro2 " << s << ' ' << tcode << " " ;
663 MathedInsetTypes fractype = LM_OT_FRAC;
667 p = new MathBigopInset(l->name, l->id);
673 Insert(static_cast<byte>(l->id), MathIsBOPS(l->id) ?
674 LM_TC_BOPS: LM_TC_SYMB);
676 p = new MathFuncInset(l->name);
681 fractype = LM_OT_STACKREL;
682 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
685 p = new MathFracInset(fractype);
686 tcode = LM_TC_ACTIVE_INSET;
691 p = new MathSqrtInset;
692 tcode = LM_TC_ACTIVE_INSET;
697 p = new MathDecorationInset(l->id);
698 tcode = LM_TC_ACTIVE_INSET;
703 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
708 p = new MathSpaceInset(l->id);
713 p = new MathDotsInset(l->name, l->id);
720 p = MathMacroTable::mathMTable.getMacro(s);
721 tcode = static_cast<MathMacro*>(p)->getTCode();
722 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
726 p = new MathFuncInset(l->name);
738 bool MathedCursor::pullArg()
740 if (cursor->IsActive()) {
741 MathParInset * p = cursor->GetActiveInset();
745 LyxArrayBase * a = p->GetData();
759 void MathedCursor::MacroModeOpen()
764 imacro = new MathFuncInset(¯obf[0]);
768 lyxerr << "Mathed Warning: Already in macro mode" << endl;
772 void MathedCursor::MacroModeClose()
776 latexkeys * l = in_word_set(macrobf, macroln);
777 if (macroln > 0 && (!l || (l && IsMacro(l->token, l->id))) &&
778 !MathMacroTable::mathMTable.getMacro(macrobf)) {
780 imacro->SetName(strnew(macrobf));
781 // This guarantees that the string will be removed by destructor
782 imacro->SetType(LM_OT_UNDEF);
784 imacro->SetName(l->name);
788 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
789 setAccent(static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
792 if (l || MathMacroTable::mathMTable.getMacro(macrobf)) {
801 void MathedCursor::MacroModeBack()
805 macrobf[--macroln] = '\0';
810 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
814 void MathedCursor::MacroModeInsert(char c)
817 macrobf[macroln + 1] = macrobf[macroln];
818 macrobf[macroln++] = c;
821 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
825 void MathedCursor::SelCopy()
828 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
829 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
830 selarray = cursor->Copy(p1, p2);
837 void MathedCursor::SelCut()
840 if (cursor->pos == selpos) return;
842 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
843 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
844 selarray = cursor->Copy(p1, p2);
845 cursor->Clean(selpos);
852 void MathedCursor::SelDel()
854 // lyxerr << "Deleting sel "
856 if (cursor->pos == selpos) return;
857 cursor->Clean(selpos);
864 void MathedCursor::SelPaste()
866 // lyxerr << "paste " << selarray << " " << curor->pos;
867 if (selection) SelDel();
869 cursor->Merge(selarray);
875 void MathedCursor::SelStart()
877 lyxerr[Debug::MATHED] << "Starting sel " << endl;
879 selpos = cursor->pos;
880 selstk = new MathStackXIter(mathstk);
881 anchor = selstk->Item(-1);
882 anchor->SetData(cursor->p);
884 anchor->goPosAbs(selpos);
891 void MathedCursor::SelClear()
893 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
902 // Anchor position must be at the same level that stack.
903 void MathedCursor::SelBalance()
905 int d = mathstk.Level() - selstk->Level();
907 // If unbalanced, balance them
910 // lyxerr << "b[" << mathstk.Level() << " " << selstk->Level << " " << anchor->GetX() << " " << cursor->GetX() << "]";
911 anchor = selstk->pop();
912 if (anchor->GetX() >= cursor->GetX())
915 // lyxerr <<"a[" << mathstk.Level() << " " << selstk->Level() <<"]";
918 d = mathstk.Level() - selstk->Level();
921 // Once balanced the levels, check that they are at the same paragraph
922 selpos = anchor->pos;
926 void MathedCursor::SelGetArea(int ** xp, int ** yp, int & np)
928 static int xpoint[10];
929 static int ypoint[10];
940 // single row selection
941 int i = 0, x, y, a, d, xo, yo, x1, y1, a1, d1;
943 // Balance anchor and cursor
946 cursor->p->GetXY(xo, yo);
947 int w = cursor->p->Width();
948 cursor->GetPos(x1, y1);
949 cursor->getAD(a1, d1);
950 anchor->GetPos(x, y);
964 ypoint[i++] = y1 - a;
969 ypoint[i++] = y1 - a;
971 ypoint[i++] = y1 + d;
975 ypoint[i++] = y1 + d;
981 xpoint[i] = xpoint[0];
982 ypoint[i++] = ypoint[0];
987 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
988 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
989 // for (i = 0; i < np; ++i)
990 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
995 void MathedCursor::setAccent(int ac)
997 if (ac > 0 && accent < 8) {
998 nestaccent[accent++] = ac;
1000 accent = 0; // consumed!
1004 int MathedCursor::getAccent() const
1006 return (accent > 0) ? nestaccent[accent - 1]: 0;
1010 void MathedCursor::doAccent(byte c, MathedTextCodes t)
1012 MathedInset * ac = 0;
1014 for (int i = accent - 1; i >= 0; --i) {
1015 if (i == accent - 1)
1016 ac = new MathAccentInset(c, t, nestaccent[i]);
1018 ac = new MathAccentInset(ac, nestaccent[i]);
1023 accent = 0; // consumed!
1027 void MathedCursor::doAccent(MathedInset * p)
1029 MathedInset * ac = 0;
1031 for (int i = accent - 1; i >= 0; --i) {
1032 if (i == accent - 1)
1033 ac = new MathAccentInset(p, nestaccent[i]);
1035 ac = new MathAccentInset(ac, nestaccent[i]);
1040 accent = 0; // consumed!