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: (c) 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;
36 inline bool IsAlpha(char c)
38 return ('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z');
41 // This was very smaller, I'll change it later
42 inline bool IsMacro(short tok, int id)
44 return (tok != LM_TK_STACK && tok != LM_TK_FRAC && tok != LM_TK_SQRT
46 && tok != LM_TK_SPACE && tok != LM_TK_DOTS
47 && tok != LM_TK_FUNCLIM
48 && tok != LM_TK_BIGSYM && tok != LM_TK_ACCENT &&
49 !(tok == LM_TK_SYM && id < 255));
53 // Yes, mathed isn't using string yet.
54 inline char * strnew(char const * s)
56 char * s1 = new char[strlen(s)+1];
63 #define MAX_STACK_ITEMS 32
65 struct MathStackXIter {
70 MathStackXIter(int n = MAX_STACK_ITEMS): imax(n) {
71 item = new MathedXIter[imax];
75 MathStackXIter(MathStackXIter & stk);
81 void push(MathedXIter ** a) {
90 MathedXIter * Item(int idx) {
91 return (idx + 1 <= i) ? &item[i - idx - 1] : 0;
99 return i >= MAX_STACK_ITEMS;
106 int Level() { return i; }
108 } mathstk, *selstk = 0;
111 MathStackXIter::MathStackXIter(MathStackXIter & stk) {
113 item = new MathedXIter[imax];
115 for (int k = 0; k < i; ++k) {
116 item[k].SetData(stk.item[k].getPar());
118 item[k].goPosAbs(stk.item[k].getPos());
123 /***---------------- Mathed Cursor ---------------------------***/
125 MathedCursor::MathedCursor(MathParInset * p) // : par(p)
129 lastcode = LM_TC_MIN;
132 if (!MathMacroTable::built)
133 MathMacroTable::mathMTable.builtinMacros();
137 void MathedCursor::SetPar(MathParInset * p)
142 selection = false; // not SelClear() ?
144 mathstk.push(&cursor);
146 cursor->SetData(par);
150 void MathedCursor::draw(Painter & pain, int x, int y)
152 // lyxerr << "Cursor[" << x << " " << y << "] ";
153 //win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
155 int w = par->Width() + 2;
156 int a = par->Ascent() + 1;
157 int h = par->Height() + 1;
158 if (par->GetType() > LM_OT_PAR) { a += 4; h += 8; }
160 pain.rectangle(x - 1, y - a, w, h, LColor::mathframe);
162 par->draw(pain, x, y);
167 void MathedCursor::Redraw(Painter & pain)
169 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
171 int w = par->Width(), h = par->Height();
174 //mathed_set_font(LM_TC_VAR, 1);
175 pain.fillRectangle(x, y - par->Ascent(),
176 x + w, y - par->Ascent() + h,
178 par->draw(pain, x, y);
182 bool MathedCursor::Left(bool sel)
189 if (sel && !selection) SelStart();
190 if (!sel && selection) SelClear();
191 bool result = cursor->Prev();
192 if (!result && !mathstk.Empty()) {
193 cursor = mathstk.pop();
196 if (selection) SelClear();
198 if (result && cursor->IsActive()) {
199 if (cursor->IsScript()) {
201 if (!cursor->IsScript())
207 MathParInset * p = cursor->GetActiveInset();
211 p->setArgumentIdx(p->getMaxArgumentIdx());
212 mathstk.push(&cursor);
222 bool MathedCursor::Pop()
224 if (!mathstk.Empty()) {
225 cursor = mathstk.pop();
234 bool MathedCursor::Push()
236 if (cursor->IsActive()) {
237 MathParInset * p = cursor->GetActiveInset();
238 if (!p) return false;
239 mathstk.push(&cursor);
247 bool MathedCursor::Right(bool sel)
254 if (sel && !selection) SelStart();
255 if (!sel && selection) SelClear();
258 if (cursor->IsActive()) {
259 if (cursor->IsScript()) {
261 // A script may be followed by another script
262 if (cursor->IsScript())
267 MathParInset *p = cursor->GetActiveInset();
269 lyxerr << "Math error: Inset expected." << endl;
270 return cursor->Next();
272 p->setArgumentIdx(0);
273 mathstk.push(&cursor);
277 result = cursor->Next();
279 if (cursor->GetChar()!= LM_TC_CR)
280 result = cursor->Next();
281 if (!result && !mathstk.Empty()) {
282 cursor = mathstk.pop();
286 if (selection) SelClear();
293 void MathedCursor::SetPos(int x, int y)
297 if (macro_mode) MacroModeClose();
298 lastcode = LM_TC_MIN;
300 mathstk.push(&cursor);
301 cursor->SetData(par);
302 cursor->fitCoord(x, y);
303 while (cursor->GetX()<x && cursor->OK()) {
304 if (cursor->IsActive()) {
305 MathParInset * p = cursor->GetActiveInset();
306 if (p->Inside(x, y)) {
308 mathstk.push(&cursor);
310 cursor->fitCoord(x, y);
316 if (!cursor->Next() && !Pop())
319 if (x-xp < cursor->GetX()-x) cursor->ipop();
324 void MathedCursor::Home()
326 if (macro_mode) MacroModeClose();
329 mathstk.push(&cursor);
334 void MathedCursor::End()
336 if (macro_mode) MacroModeClose();
339 mathstk.push(&cursor);
344 void MathedCursor::Insert(byte c, MathedTextCodes t)
346 if (selection) SelDel();
351 if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
355 MathParInset * p = cursor->p;
356 if (p == par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
357 MathMatrixInset * mt = new MathMatrixInset(3, 0);
358 mt->SetAlign(' ', "rcl");
359 mt->SetStyle(LM_ST_DISPLAY);
360 mt->SetType((p->GetType() == LM_OT_PARN) ? LM_OT_MPARN: LM_OT_MPAR);
361 mt->SetData(p->GetData());
362 p->SetData(0);//BUG duda
367 int pos = cursor->getPos();
368 cursor->SetData(par);
369 cursor->goPosAbs(pos);
371 if (p && p->Permit(LMPF_ALLOW_CR)) {
375 if (t == LM_TC_TAB) {
376 MathParInset * p = cursor->p;
377 if (p && p->Permit(LMPF_ALLOW_TAB)) {
379 cursor->Insert(c, t);
382 cursor->goNextColumn();
383 } else // Navigate between arguments
384 if (p && p->GetType() == LM_OT_MACRO) {
385 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
386 p->setArgumentIdx(p->getArgumentIdx()+1);
393 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
401 cursor->Insert(c, t);
410 void MathedCursor::Insert(MathedInset * p, int t)
412 if (macro_mode) MacroModeClose();
414 if (MathIsActive(t)) {
416 static_cast<MathParInset*>(p)->SetData(selarray);
421 if (mathstk.i < MAX_STACK_ITEMS - 1) {
423 if (accent && !MathIsActive(t)) {
426 cursor->Insert(p, t);
428 if (MathIsActive(t)) {
435 lyxerr << "Math error: Full stack." << endl;
439 void MathedCursor::Delete()
441 if (macro_mode) return;
446 if (cursor->Empty() && !mathstk.Empty()) {
447 cursor = mathstk.pop();
449 // if (cursor->GetChar()!= LM_TC_TAB)
455 void MathedCursor::DelLine()
457 if (macro_mode) MacroModeClose();
462 MathParInset *p= cursor->p;
463 if (p && (p->GetType()<= LM_OT_MATRIX && p->GetType()>= LM_OT_MPAR)) {
469 bool MathedCursor::Up(bool sel)
473 if (macro_mode) MacroModeClose();
475 if (sel && !selection) SelStart();
476 if (!sel && selection) SelClear();
479 if (cursor->IsScript()) {
480 char cd = cursor->GetChar();
485 // A subscript may be followed by a superscript
488 if (MathIsUp(cursor->GetChar())) {
491 } else // return to the previous state
496 result = cursor->Up();
497 if (!result && cursor->p) {
498 MathParInset * p = cursor->p;
500 if (p->GetType() == LM_OT_SCRIPT) {
501 MathedXIter * cx = mathstk.Item(1);
502 bool is_down = (cx->GetChar() == LM_TC_DOWN);
503 cursor = mathstk.pop();
505 result = (is_down) ? true: Up();
507 result = (p->getArgumentIdx() > 0);
509 p->setArgumentIdx(p->getArgumentIdx()-1);
513 if (!result && !mathstk.Empty()) {
514 cursor = mathstk.pop();
522 bool MathedCursor::Down(bool sel)
526 if (macro_mode) MacroModeClose();
528 if (sel && !selection) SelStart();
529 if (!sel && selection) SelClear();
530 // if (selection) SelClear();
532 if (cursor->IsScript()) {
533 char cd = cursor->GetChar();
534 if (MathIsDown(cd)) {
538 // A superscript may be followed by a subscript
541 if (MathIsDown(cursor->GetChar())) {
549 result = cursor->Down();
550 if (!result && cursor->p) {
551 MathParInset * p= cursor->p;
552 if (p->GetType() == LM_OT_SCRIPT) {
553 MathedXIter * cx = mathstk.Item(1);
554 bool is_up = (cx->GetChar() == LM_TC_UP);
555 cursor = mathstk.pop();
557 result = (is_up) ? true: Down();
559 result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
561 p->setArgumentIdx(p->getArgumentIdx()+1);
565 if (!result && !mathstk.Empty()) {
566 cursor = mathstk.pop();
574 bool MathedCursor::Limits()
576 if (cursor->IsInset()) {
577 MathedInset * p = cursor->GetInset();
578 bool ol = p->GetLimits();
580 return (ol!= p->GetLimits());
586 void MathedCursor::SetSize(short size)
588 MathParInset * p = cursor->p;
589 p->UserSetSize(size);
594 void MathedCursor::setLabel(char const * label)
595 { // ugly hack and possible bug
596 if (!cursor->setLabel(strnew(label)))
597 lyxerr << "MathErr: Bad place to set labels." << endl;
601 void MathedCursor::setNumbered()
602 { // another ugly hack
603 MathedRowSt * crow = cursor->crow;
605 crow->setNumbered(!crow->isNumbered());
609 void MathedCursor::Interpret(char const * s)
613 MathedTextCodes tcode = LM_TC_INSET;
615 if (s[0] == '^' || s[0] == '_') {
616 char c = cursor->GetChar();
617 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
620 } else // A script may be followed by a script
621 if (MathIsUp(c) || MathIsDown(c)) {
624 c = cursor->GetChar();
625 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
631 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
632 Insert (p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
635 if (s[0] == '!' || s[0] == ',' || s[0] == ':' || s[0] == ';') {
636 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
637 p = new MathSpaceInset(sp);
641 l = in_word_set (s, strlen(s));
644 p = MathMacroTable::mathMTable.getMacro(s);
646 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
647 if (strcmp("root", s) == 0) {
648 p = new MathRootInset();
649 tcode = LM_TC_ACTIVE_INSET;
651 p = new MathFuncInset(s, LM_OT_UNDEF);
653 tcode = static_cast<MathMacro*>(p)->getTCode();
654 lyxerr << "Macro2 " << s << ' ' << tcode << " " ;
657 MathedInsetTypes fractype = LM_OT_FRAC;
661 p = new MathBigopInset(l->name, l->id);
667 Insert(static_cast<byte>(l->id), MathIsBOPS(l->id) ?
668 LM_TC_BOPS: LM_TC_SYMB);
670 p = new MathFuncInset(l->name);
675 fractype = LM_OT_STACKREL;
676 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
679 p = new MathFracInset(fractype);
680 tcode = LM_TC_ACTIVE_INSET;
685 p = new MathSqrtInset;
686 tcode = LM_TC_ACTIVE_INSET;
691 p = new MathDecorationInset(l->id);
692 tcode = LM_TC_ACTIVE_INSET;
697 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
702 p = new MathSpaceInset(l->id);
707 p = new MathDotsInset(l->name, l->id);
714 p = MathMacroTable::mathMTable.getMacro(s);
715 tcode = static_cast<MathMacro*>(p)->getTCode();
716 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
720 p = new MathFuncInset(l->name);
732 bool MathedCursor::pullArg()
734 if (cursor->IsActive()) {
735 MathParInset * p = cursor->GetActiveInset();
739 LyxArrayBase * a = p->GetData();
753 void MathedCursor::MacroModeOpen()
758 imacro = new MathFuncInset(¯obf[0]);
762 lyxerr << "Mathed Warning: Already in macro mode" << endl;
766 void MathedCursor::MacroModeClose()
770 latexkeys * l = in_word_set(macrobf, macroln);
771 if (macroln > 0 && (!l || (l && IsMacro(l->token, l->id))) &&
772 !MathMacroTable::mathMTable.getMacro(macrobf)) {
774 imacro->SetName(strnew(macrobf));
775 // This guarantees that the string will be removed by destructor
776 imacro->SetType(LM_OT_UNDEF);
778 imacro->SetName(l->name);
782 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
783 setAccent(static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
786 if (l || MathMacroTable::mathMTable.getMacro(macrobf)) {
795 void MathedCursor::MacroModeBack()
799 macrobf[--macroln] = '\0';
804 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
808 void MathedCursor::MacroModeInsert(char c)
811 macrobf[macroln + 1] = macrobf[macroln];
812 macrobf[macroln++] = c;
815 lyxerr << "Mathed Warning: we are not in macro mode" << endl;
819 void MathedCursor::SelCopy()
822 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
823 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
824 selarray = cursor->Copy(p1, p2);
831 void MathedCursor::SelCut()
834 if (cursor->pos == selpos) return;
836 int p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
837 int p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
838 selarray = cursor->Copy(p1, p2);
839 cursor->Clean(selpos);
846 void MathedCursor::SelDel()
848 // lyxerr << "Deleting sel "
850 if (cursor->pos == selpos) return;
851 cursor->Clean(selpos);
858 void MathedCursor::SelPaste()
860 // lyxerr << "paste " << selarray << " " << curor->pos;
861 if (selection) SelDel();
863 cursor->Merge(selarray);
869 void MathedCursor::SelStart()
871 lyxerr[Debug::MATHED] << "Starting sel " << endl;
873 selpos = cursor->pos;
874 selstk = new MathStackXIter(mathstk);
875 anchor = selstk->Item(-1);
876 anchor->SetData(cursor->p);
878 anchor->goPosAbs(selpos);
885 void MathedCursor::SelClear()
887 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
896 // Anchor position must be at the same level that stack.
897 void MathedCursor::SelBalance()
899 int d = mathstk.Level() - selstk->Level();
901 // If unbalanced, balance them
904 // lyxerr << "b[" << mathstk.Level() << " " << selstk->Level << " " << anchor->GetX() << " " << cursor->GetX() << "]";
905 anchor = selstk->pop();
906 if (anchor->GetX() >= cursor->GetX())
909 // lyxerr <<"a[" << mathstk.Level() << " " << selstk->Level() <<"]";
912 d = mathstk.Level() - selstk->Level();
915 // Once balanced the levels, check that they are at the same paragraph
916 selpos = anchor->pos;
920 void MathedCursor::SelGetArea(int ** xp, int ** yp, int & np)
922 static int xpoint[10];
923 static int ypoint[10];
934 // single row selection
935 int i = 0, x, y, a, d, xo, yo, x1, y1, a1, d1;
937 // Balance anchor and cursor
940 cursor->p->GetXY(xo, yo);
941 int w = cursor->p->Width();
942 cursor->GetPos(x1, y1);
943 cursor->getAD(a1, d1);
944 anchor->GetPos(x, y);
958 ypoint[i++] = y1 - a;
963 ypoint[i++] = y1 - a;
965 ypoint[i++] = y1 + d;
969 ypoint[i++] = y1 + d;
975 xpoint[i] = xpoint[0];
976 ypoint[i++] = ypoint[0];
981 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
982 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
983 // for (i = 0; i < np; ++i)
984 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
989 void MathedCursor::setAccent(int ac)
991 if (ac > 0 && accent < 8) {
992 nestaccent[accent++] = ac;
994 accent = 0; // consumed!
998 int MathedCursor::getAccent() const
1000 return (accent > 0) ? nestaccent[accent - 1]: 0;
1004 void MathedCursor::doAccent(byte c, MathedTextCodes t)
1006 MathedInset * ac = 0;
1008 for (int i = accent - 1; i >= 0; --i) {
1009 if (i == accent - 1)
1010 ac = new MathAccentInset(c, t, nestaccent[i]);
1012 ac = new MathAccentInset(ac, nestaccent[i]);
1017 accent = 0; // consumed!
1021 void MathedCursor::doAccent(MathedInset * p)
1023 MathedInset * ac = 0;
1025 for (int i = accent - 1; i >= 0; --i) {
1026 if (i == accent - 1)
1027 ac = new MathAccentInset(p, nestaccent[i]);
1029 ac = new MathAccentInset(ac, nestaccent[i]);
1034 accent = 0; // consumed!