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"
43 #include "math_macrotemplate.h"
44 #include "mathed/support.h"
46 static MathedArray selarray;
50 // This was very smaller, I'll change it later
52 bool IsMacro(short tok, int id)
54 return tok != LM_TK_STACK &&
60 tok != LM_TK_FUNCLIM &&
61 tok != LM_TK_BIGSYM &&
62 tok != LM_TK_ACCENT &&
63 !(tok == LM_TK_SYM && id < 255);
66 static int const MAX_STACK_ITEMS = 32;
68 struct MathStackXIter {
69 std::vector<MathedXIter> item;
72 MathStackXIter(int n = MAX_STACK_ITEMS)
76 MathedXIter * push() {
84 MathedXIter * Item(int idx) {
85 return (idx + 1 <= i) ? &item[i - idx - 1] : 0;
93 return i >= MAX_STACK_ITEMS;
100 int Level() { return i; }
106 /***---------------- Mathed Cursor ---------------------------***/
108 MathedCursor::MathedCursor(MathParInset * p) // : par(p)
112 lastcode = LM_TC_MIN;
114 if (!MathMacroTable::built)
115 MathMacroTable::mathMTable.builtinMacros();
119 void MathedCursor::SetPar(MathParInset * p)
122 selection = false; // not SelClear() ?
124 cursor = mathstk.push();
126 cursor->SetData(par);
130 void MathedCursor::draw(Painter & pain, int x, int y)
132 // lyxerr << "Cursor[" << x << " " << y << "] ";
133 //win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
136 int w = par->Width() + 2;
137 int a = par->Ascent() + 1;
138 int h = par->Height() + 1;
140 if (par->GetType() > LM_OT_PAR) {
145 pain.rectangle(x - 1, y - a, w, h, LColor::mathframe);
147 par->draw(pain, x, y);
152 void MathedCursor::Redraw(Painter & pain)
154 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
156 int w = par->Width();
157 int h = par->Height();
161 //mathed_set_font(LM_TC_VAR, 1);
162 pain.fillRectangle(x, y - par->Ascent(),
163 x + w, y - par->Ascent() + h,
165 par->draw(pain, x, y);
169 bool MathedCursor::Left(bool sel)
172 // was MacroModeBack()
173 if (!imacro->GetName().empty()) {
174 imacro->SetName(imacro->GetName()
175 .substr(0, imacro->GetName()
185 if (sel && !selection)
188 if (!sel && selection)
191 bool result = cursor->Prev();
193 if (!result && !mathstk.Empty()) {
194 cursor = mathstk.pop();
199 } else if (result && cursor->IsActive()) {
200 if (cursor->IsScript()) {
202 if (!cursor->IsScript())
209 MathParInset * p = cursor->GetActiveInset();
213 p->setArgumentIdx(p->getMaxArgumentIdx());
214 cursor = mathstk.push();
224 bool MathedCursor::Pop()
226 if (!mathstk.Empty()) {
227 cursor = mathstk.pop();
236 bool MathedCursor::Push()
238 if (cursor->IsActive()) {
239 MathParInset * p = cursor->GetActiveInset();
242 cursor = mathstk.push();
250 bool MathedCursor::Right(bool sel)
259 if (sel && !selection)
262 if (!sel && selection)
267 if (cursor->IsActive()) {
268 if (cursor->IsScript()) {
270 // A script may be followed by another script
271 if (cursor->IsScript())
277 MathParInset *p = cursor->GetActiveInset();
279 lyxerr << "Math error: Inset expected." << endl;
280 return cursor->Next();
282 p->setArgumentIdx(0);
283 cursor = mathstk.push();
287 result = cursor->Next();
290 if (cursor->GetChar()!= LM_TC_CR)
291 result = cursor->Next();
292 if (!result && !mathstk.Empty()) {
293 cursor = mathstk.pop();
305 void MathedCursor::SetPos(int x, int y)
312 lastcode = LM_TC_MIN;
314 cursor = mathstk.push();
315 cursor->SetData(par);
316 cursor->fitCoord(x, y);
318 while (cursor->GetX()<x && cursor->OK()) {
319 if (cursor->IsActive()) {
320 MathParInset * p = cursor->GetActiveInset();
321 if (p->Inside(x, y)) {
323 cursor = mathstk.push();
325 cursor->fitCoord(x, y);
331 if (!cursor->Next() && !Pop())
334 if (x - xp < cursor->GetX() - x)
340 void MathedCursor::Home()
346 cursor = mathstk.push();
351 void MathedCursor::End()
357 cursor = mathstk.push();
362 MathMatrixInset * create_multiline(short int type, int cols)
373 for (int i = 0; i < cols; ++i)
380 for (int i = 0; i < cols; ++i)
385 case LM_OT_MULTLINEN:
398 MathMatrixInset * mt = new MathMatrixInset(columns, -1);
399 mt->SetAlign(' ', align);
404 void MathedCursor::Insert(byte c, MathedTextCodes t)
412 if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
416 MathParInset * p = cursor->getPar();
417 if (p == par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
418 short int type = LM_OT_MPAR;
420 if (c >= '1' && c <= '9') {
423 } else if (c >= 'A' && c <= 'I') {
424 type = LM_OT_ALIGNAT;
427 type = LM_OT_MULTLINE;
431 if (p->GetType() == LM_OT_PARN)
433 MathMatrixInset * mt = create_multiline(type, cols);
434 mt->SetStyle(LM_ST_DISPLAY);
436 mt->setData(p->GetData());
441 int pos = cursor->getPos();
442 cursor->SetData(par);
443 cursor->goPosAbs(pos);
445 if (p && p->Permit(LMPF_ALLOW_CR)) {
448 } else if (t == LM_TC_TAB) {
449 MathParInset * p = cursor->getPar();
450 if (p && p->Permit(LMPF_ALLOW_TAB)) {
452 cursor->insert(c, t);
455 cursor->goNextColumn();
456 } else // Navigate between arguments
457 if (p && p->GetType() == LM_OT_MACRO) {
458 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
459 p->setArgumentIdx(p->getArgumentIdx() + 1);
466 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
467 // was MacroModeInsert(c);
468 imacro->SetName(imacro->GetName() + static_cast<char>(c));
476 cursor->insert(c, t);
485 void MathedCursor::insertInset(MathedInset * p, int t)
491 if (MathIsActive(t)) {
493 static_cast<MathParInset*>(p)->setData(selarray);
498 if (mathstk.i < MAX_STACK_ITEMS - 1) {
499 if (accent && !MathIsActive(t)) {
502 cursor->insertInset(p, t);
503 if (MathIsActive(t)) {
509 lyxerr << "Math error: Full stack." << endl;
513 void MathedCursor::Delete()
523 if (cursor->Empty() && !mathstk.Empty())
524 cursor = mathstk.pop();
526 // if (cursor->GetChar()!= LM_TC_TAB)
532 void MathedCursor::DelLine()
542 MathParInset * p = cursor->getPar();
544 if (p && p->GetType() <= LM_OT_MATRIX && p->GetType() >= LM_OT_MPAR) {
550 bool MathedCursor::Up(bool sel)
557 if (sel && !selection)
560 if (!sel && selection)
563 if (cursor->IsScript()) {
564 char cd = cursor->GetChar();
569 // A subscript may be followed by a superscript
572 if (MathIsUp(cursor->GetChar())) {
575 } else // return to the previous state
580 result = cursor->Up();
581 if (!result && cursor->getPar()) {
582 MathParInset * p = cursor->getPar();
584 if (p->GetType() == LM_OT_SCRIPT) {
585 MathedXIter * cx = mathstk.Item(1);
586 bool is_down = (cx->GetChar() == LM_TC_DOWN);
587 cursor = mathstk.pop();
589 result = (is_down) ? true: Up();
591 result = (p->getArgumentIdx() > 0);
593 p->setArgumentIdx(p->getArgumentIdx() - 1);
598 if (!result && !mathstk.Empty()) {
599 cursor = mathstk.pop();
607 bool MathedCursor::Down(bool sel)
614 if (sel && !selection)
617 if (!sel && selection)
620 if (cursor->IsScript()) {
621 char cd = cursor->GetChar();
622 if (MathIsDown(cd)) {
626 // A superscript may be followed by a subscript
629 if (MathIsDown(cursor->GetChar())) {
637 result = cursor->Down();
638 if (!result && cursor->getPar()) {
639 MathParInset * p= cursor->getPar();
640 if (p->GetType() == LM_OT_SCRIPT) {
641 MathedXIter * cx = mathstk.Item(1);
642 bool is_up = (cx->GetChar() == LM_TC_UP);
643 cursor = mathstk.pop();
645 result = (is_up) ? true : Down();
647 result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
649 p->setArgumentIdx(p->getArgumentIdx() + 1);
653 if (!result && !mathstk.Empty()) {
654 cursor = mathstk.pop();
662 bool MathedCursor::Limits()
664 if (cursor->IsInset()) {
665 MathedInset * p = cursor->GetInset();
666 bool ol = p->GetLimits();
668 return (ol!= p->GetLimits());
674 void MathedCursor::SetSize(short size)
676 MathParInset * p = cursor->getPar();
677 p->UserSetSize(size);
682 void MathedCursor::setLabel(string const & label)
684 // ugly hack and possible bug
685 if (!cursor->setLabel(label))
686 lyxerr << "MathErr: Bad place to set labels." << endl;
690 void MathedCursor::setNumbered()
693 MathedRowContainer::iterator crow = cursor->currentRow();
695 crow->setNumbered(!crow->isNumbered());
699 void MathedCursor::Interpret(string const & s)
702 latexkeys const * l = 0;
703 MathedTextCodes tcode = LM_TC_INSET;
705 if (s[0] == '^' || s[0] == '_') {
706 char c = cursor->GetChar();
707 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
712 // A script may be followed by a script
713 if (MathIsUp(c) || MathIsDown(c)) {
716 c = cursor->GetChar();
717 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
723 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
724 insertInset(p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
727 if (s[0] == '!' || s[0] == ',' || s[0] == ':' || s[0] == ';') {
728 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
729 p = new MathSpaceInset(sp);
730 insertInset(p, LM_TC_INSET);
736 p = MathMacroTable::mathMTable.createMacro(s);
738 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
740 p = new MathRootInset;
741 tcode = LM_TC_ACTIVE_INSET;
743 p = new MathFuncInset(s, LM_OT_UNDEF);
745 tcode = static_cast<MathMacro*>(p)->getTCode();
746 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
749 MathedInsetTypes fractype = LM_OT_FRAC;
752 p = new MathBigopInset(l->name, l->id);
757 Insert(static_cast<byte>(l->id), MathIsBOPS(l->id) ?
758 LM_TC_BOPS: LM_TC_SYMB);
760 p = new MathFuncInset(l->name);
765 fractype = LM_OT_STACKREL;
766 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
769 p = new MathFracInset(fractype);
770 tcode = LM_TC_ACTIVE_INSET;
774 p = new MathSqrtInset;
775 tcode = LM_TC_ACTIVE_INSET;
779 p = new MathDecorationInset(l->id);
780 tcode = LM_TC_ACTIVE_INSET;
784 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
788 p = new MathSpaceInset(l->id);
792 p = new MathDotsInset(l->name, l->id);
800 p = MathMacroTable::mathMTable.createMacro(s);
801 tcode = static_cast<MathMacro*>(p)->getTCode();
802 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
806 p = new MathFuncInset(l->name);
812 insertInset(p, tcode);
818 bool MathedCursor::pullArg()
820 if (cursor->IsActive()) {
821 MathParInset * p = cursor->GetActiveInset();
825 MathedArray a = p->GetData();
839 void MathedCursor::MacroModeOpen()
842 imacro = new MathFuncInset("");
843 insertInset(imacro, LM_TC_INSET);
846 lyxerr << "Mathed Warning: Already in macro mode" << endl;
850 void MathedCursor::MacroModeClose()
854 latexkeys const * l = in_word_set(imacro->GetName());
855 if (!imacro->GetName().empty()
856 && (!l || (l && IsMacro(l->token, l->id))) &&
857 !MathMacroTable::mathMTable.createMacro(imacro->GetName())) {
859 //imacro->SetName(macrobf);
860 // This guarantees that the string will be removed by destructor
861 imacro->SetType(LM_OT_UNDEF);
863 imacro->SetName(l->name);
866 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
868 static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
871 if (l || MathMacroTable::mathMTable.createMacro(imacro->GetName())) {
872 Interpret(imacro->GetName());
881 void MathedCursor::SelCopy()
884 int const p1 = (cursor->getPos() < selpos) ?
885 cursor->getPos() : selpos;
886 int const p2 = (cursor->getPos() > selpos) ?
887 cursor->getPos() : selpos;
888 selarray = *(cursor->GetData());
889 selarray.shrink(p1, p2);
896 void MathedCursor::SelCut()
899 if (cursor->getPos() == selpos)
902 int const p1 = (cursor->getPos() < selpos) ?
903 cursor->getPos() : selpos;
904 int const p2 = (cursor->getPos() > selpos) ?
905 cursor->getPos() : selpos;
906 selarray = *(cursor->GetData());
907 selarray.shrink(p1, p2);
908 cursor->Clean(selpos);
915 void MathedCursor::SelDel()
917 // lyxerr << "Deleting sel "
919 if (cursor->getPos() == selpos)
921 cursor->Clean(selpos);
928 void MathedCursor::SelPaste()
930 // lyxerr << "paste " << selarray << " " << curor->pos;
934 if (!selarray.empty()) {
935 cursor->Merge(selarray);
941 void MathedCursor::SelStart()
943 lyxerr[Debug::MATHED] << "Starting sel " << endl;
945 selpos = cursor->getPos();
947 anchor = selstk.Item(-1);
948 anchor->SetData(cursor->getPar());
950 anchor->goPosAbs(selpos);
956 void MathedCursor::SelClear()
958 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
965 // Anchor position must be at the same level that stack.
966 void MathedCursor::SelBalance()
968 int d = mathstk.Level() - selstk.Level();
970 // If unbalanced, balance them
973 // lyxerr << "b[" << mathstk.Level() << " " << selstk.Level
974 // << " " << anchor->GetX() << " " << cursor->GetX() << "]";
975 anchor = selstk.pop();
976 if (anchor->GetX() >= cursor->GetX())
979 // lyxerr <<"a[" << mathstk.Level() << " " << selstk.Level() <<"]";
982 d = mathstk.Level() - selstk.Level();
985 // Once balanced the levels, check that they are at the same paragraph
986 selpos = anchor->getPos();
990 void MathedCursor::SelGetArea(int ** xp, int ** yp, int & np)
992 static int xpoint[10];
993 static int ypoint[10];
1004 // Balance anchor and cursor
1009 cursor->getPar()->GetXY(xo, yo);
1010 int w = cursor->getPar()->Width();
1013 cursor->GetPos(x1, y1);
1016 cursor->getAD(a1, d1);
1019 anchor->GetPos(x, y);
1022 anchor->getAD(a, d);
1024 // single row selection
1027 ypoint[i++] = y + d;
1029 ypoint[i++] = y - a;
1033 ypoint[i++] = y - a;
1037 ypoint[i++] = y1 - a;
1042 ypoint[i++] = y1 - a;
1044 ypoint[i++] = y1 + d;
1048 ypoint[i++] = y1 + d;
1051 ypoint[i++] = y + d;
1054 xpoint[i] = xpoint[0];
1055 ypoint[i++] = ypoint[0];
1060 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
1061 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
1062 // for (i = 0; i < np; ++i)
1063 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
1067 void MathedCursor::setAccent(int ac)
1069 if (ac > 0 && accent < 8) {
1070 nestaccent[accent++] = ac;
1072 accent = 0; // consumed!
1076 int MathedCursor::getAccent() const
1078 return (accent > 0) ? nestaccent[accent - 1]: 0;
1082 void MathedCursor::doAccent(byte c, MathedTextCodes t)
1084 MathedInset * ac = 0;
1086 for (int i = accent - 1; i >= 0; --i) {
1087 if (i == accent - 1)
1088 ac = new MathAccentInset(c, t, nestaccent[i]);
1090 ac = new MathAccentInset(ac, nestaccent[i]);
1094 cursor->insertInset(ac, LM_TC_INSET);
1096 accent = 0; // consumed!
1100 void MathedCursor::doAccent(MathedInset * p)
1102 MathedInset * ac = 0;
1104 for (int i = accent - 1; i >= 0; --i) {
1105 if (i == accent - 1)
1106 ac = new MathAccentInset(p, nestaccent[i]);
1108 ac = new MathAccentInset(ac, nestaccent[i]);
1112 cursor->insertInset(ac, LM_TC_INSET);
1114 accent = 0; // consumed!
1118 void MathedCursor::toggleLastCode(MathedTextCodes t)
1121 lastcode = LM_TC_VAR;
1127 void MathedCursor::GetPos(int & x, int & y)
1129 cursor->GetPos(x, y);
1133 short MathedCursor::GetFCode()
1135 return cursor->fcode();
1139 MathParInset * MathedCursor::GetPar()
1145 MathParInset * MathedCursor::getCurrentPar() const
1147 return cursor->getPar();
1151 string const & MathedCursor::getLabel() const
1153 return cursor->getLabel();
1157 bool MathedCursor::IsEnd() const
1159 return !cursor->OK();
1163 bool MathedCursor::InMacroMode()
1169 bool MathedCursor::Selection()
1175 void MathedCursor::clearLastCode()
1177 lastcode = LM_TC_MIN;
1181 void MathedCursor::setLastCode(MathedTextCodes t)
1187 MathedTextCodes MathedCursor::getLastCode() const