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_macroarg.h"
29 #include "math_macrotable.h"
30 #include "math_root.h"
31 #include "support/lstrings.h"
35 #include "math_matrixinset.h"
36 #include "math_rowst.h"
37 #include "math_spaceinset.h"
38 #include "math_funcinset.h"
39 #include "math_bigopinset.h"
40 #include "math_fracinset.h"
41 #include "math_decorationinset.h"
42 #include "math_dotsinset.h"
43 #include "math_accentinset.h"
44 #include "math_macrotemplate.h"
45 #include "mathed/support.h"
56 // This was very smaller, I'll change it later
58 bool IsMacro(short tok, int id)
60 return tok != LM_TK_STACK &&
66 tok != LM_TK_FUNCLIM &&
67 tok != LM_TK_BIGSYM &&
68 tok != LM_TK_ACCENT &&
69 !(tok == LM_TK_SYM && id < 255);
72 int const MAX_STACK_ITEMS = 32;
74 struct MathStackXIter {
76 enum type {MATHSTK, SELSTK};
79 std::vector<MathedXIter> item;
86 MathStackXIter(type id)
87 : item(MAX_STACK_ITEMS), pos_(-1), id_(id) {
90 MathedXIter * push() {
98 item[pos_] = MathedXIter();
103 MathedXIter * Item(int idx) {
105 lyxerr << "Wrong index: " << idx << " pos_: " << pos_ << endl;
106 return &item[pos_ - idx];
114 return pos_ >= MAX_STACK_ITEMS - 2;
121 MathParInset * outer() {
122 return empty() ? 0 : item[0].getPar();
129 MathParInset * parInset(int i) {
130 return pos_ < 0 ? 0 : item[i].getPar();
135 lyxerr << "\n------------- MathStack ------------\n";
136 for (int i = 0; i < pos_ + 3; ++i) {
137 lyxerr << "pos: " << i << " par: "
138 << item[i].getPar() << " data: '"
139 << *item[i].GetData() << "'" << endl;
141 lyxerr << "------------- MathStack ------------\n";
146 MathStackXIter mathstk = MathStackXIter(MathStackXIter::MATHSTK);
147 MathStackXIter selstk = MathStackXIter(MathStackXIter::SELSTK);
152 extern MathedCursor * mathcursor;
154 bool is_mathcursor_inside(MathParInset * p)
156 //lyxerr << "called is_mathcursor_inside: " << p << endl;
159 //lyxerr << " mathcursor not set" << endl;
163 for (int i = 0; i < mathstk.Level(); ++i) {
164 //lyxerr << " level: " << i << " " << mathstk.parInset(i) << endl;
165 if (mathstk.parInset(i) == p) {
166 //lyxerr << " found!" << endl;
171 //lyxerr << " cursor: " << mathcursor->cursor->getPar() << endl;
172 if (mathcursor->cursor->getPar() == p) {
173 //lyxerr << " found!" << endl;
176 //lyxerr << " not found" << endl;
182 /***---------------- Mathed Cursor ---------------------------***/
185 MathedCursor::MathedCursor(MathParInset * p) // : par(p)
189 lastcode = LM_TC_MIN;
193 void MathedCursor::SetPar(MathParInset * p)
196 selection = false; // not SelClear() ?
198 cursor = mathstk.push();
204 void MathedCursor::SetCursorData(MathParInset * p)
206 //lyxerr << "SetCursorData: " << p << endl;
211 void MathedCursor::draw(Painter & pain, int x, int y)
213 // lyxerr << "Cursor[" << x << " " << y << "] ";
214 //lyxerr << "MathedCursor::draw: par: " << par << endl;
217 int w = par->Width() + 2;
218 int a = par->Ascent() + 1;
219 int h = par->Height() + 1;
221 if (par->GetType() > LM_OT_PAR) {
226 pain.rectangle(x - 1, y - a, w, h, LColor::green);
228 par->draw(pain, x, y);
233 void MathedCursor::Redraw(Painter & pain)
235 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
237 int w = par->Width();
238 int h = par->Height();
242 //mathed_set_font(LM_TC_VAR, 1);
243 pain.fillRectangle(x, y - par->Ascent(),
244 x + w, y - par->Ascent() + h, LColor::mathbg);
246 lyxerr << "MathedCursor::Redraw: par: " << par << endl;
247 par->draw(pain, x, y);
251 bool MathedCursor::Left(bool sel)
253 //par->GetData().dump(cerr);
255 // was MacroModeBack()
256 if (!imacro->GetName().empty()) {
257 imacro->SetName(imacro->GetName()
258 .substr(0, imacro->GetName()
268 if (sel && !selection)
271 if (!sel && selection)
274 bool result = cursor->Prev();
276 if (!result && !mathstk.empty()) {
277 cursor = mathstk.pop();
282 } else if (result && cursor->IsActive()) {
283 if (cursor->IsScript()) {
285 if (!cursor->IsScript())
292 MathParInset * p = cursor->GetActiveInset();
296 //lyxerr << "\nInset, max arg # " << p->getMaxArgumentIdx() << endl;
297 p->setArgumentIdx(p->getMaxArgumentIdx());
298 cursor = mathstk.push();
308 bool MathedCursor::Pop()
310 if (!mathstk.empty()) {
311 cursor = mathstk.pop();
320 bool MathedCursor::Push()
322 if (cursor->IsActive()) {
323 MathParInset * p = cursor->GetActiveInset();
326 cursor = mathstk.push();
334 bool MathedCursor::Right(bool sel)
343 if (sel && !selection)
346 if (!sel && selection)
351 if (cursor->IsActive()) {
352 if (cursor->IsScript()) {
354 // A script may be followed by another script
355 if (cursor->IsScript())
361 MathParInset * p = cursor->GetActiveInset();
363 lyxerr << "Math error: Inset expected." << endl;
364 return cursor->Next();
366 p->setArgumentIdx(0);
367 cursor = mathstk.push();
371 result = cursor->Next();
374 if (cursor->GetChar() != LM_TC_CR)
375 result = cursor->Next();
376 if (!result && !mathstk.empty()) {
377 cursor = mathstk.pop();
389 void MathedCursor::SetPos(int x, int y)
396 lastcode = LM_TC_MIN;
398 cursor = mathstk.push();
400 cursor->fitCoord(x, y);
402 while (cursor->GetX() < x && cursor->OK()) {
403 if (cursor->IsActive()) {
404 MathParInset * p = cursor->GetActiveInset();
405 if (p->Inside(x, y)) {
407 cursor = mathstk.push();
409 cursor->fitCoord(x, y);
415 if (!cursor->Next() && !Pop())
418 if (x - xp < cursor->GetX() - x)
424 void MathedCursor::Home()
430 cursor = mathstk.push();
435 void MathedCursor::End()
441 cursor = mathstk.push();
446 MathMatrixInset create_multiline(short int type, int cols)
457 for (int i = 0; i < cols; ++i)
464 for (int i = 0; i < cols; ++i)
469 case LM_OT_MULTLINEN:
482 MathMatrixInset mt(columns, -1);
483 mt.SetAlign(' ', align);
488 void MathedCursor::Insert(byte c, MathedTextCodes t)
496 if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
500 MathParInset * p = cursor->getPar();
501 if (p == par && p->GetType() < LM_OT_MPAR && p->GetType() > LM_OT_MIN) {
502 short int type = LM_OT_MPAR;
504 if (c >= '1' && c <= '9') {
507 } else if (c >= 'A' && c <= 'I') {
508 type = LM_OT_ALIGNAT;
511 type = LM_OT_MULTLINE;
515 if (p->GetType() == LM_OT_PARN)
517 MathMatrixInset * mt =
518 new MathMatrixInset(create_multiline(type, cols));
519 mt->SetStyle(LM_ST_DISPLAY);
521 mt->setData(p->GetData());
526 int pos = cursor->getPos();
528 cursor->goPosAbs(pos);
530 if (p && p->Permit(LMPF_ALLOW_CR)) {
533 } else if (t == LM_TC_TAB) {
534 MathParInset * p = cursor->getPar();
535 if (p && p->Permit(LMPF_ALLOW_TAB)) {
537 cursor->insert(c, t);
540 cursor->goNextColumn();
542 // Navigate between arguments
543 if (p && p->GetType() == LM_OT_MACRO) {
544 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
545 p->setArgumentIdx(p->getArgumentIdx() + 1);
553 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
554 // was MacroModeInsert(c);
555 imacro->SetName(imacro->GetName() + static_cast<char>(c));
563 cursor->insert(c, t);
572 void MathedCursor::insertInset(MathedInset * p, int t)
578 if (MathIsActive(t)) {
580 static_cast<MathParInset*>(p)->setData(selarray);
585 if (!mathstk.Full()) {
586 if (accent && !MathIsActive(t)) {
589 cursor->insertInset(p, t);
590 if (MathIsActive(t)) {
596 lyxerr << "Math error: Full stack." << endl;
600 void MathedCursor::Delete()
610 if (cursor->Empty() && !mathstk.empty())
611 cursor = mathstk.pop();
613 // if (cursor->GetChar() != LM_TC_TAB)
619 void MathedCursor::DelLine()
629 MathParInset * p = cursor->getPar();
631 if (p && p->GetType() <= LM_OT_MATRIX && p->GetType() >= LM_OT_MPAR)
636 bool MathedCursor::Up(bool sel)
643 if (sel && !selection)
646 if (!sel && selection)
649 if (cursor->IsScript()) {
650 char cd = cursor->GetChar();
656 // A subscript may be followed by a superscript
659 if (MathIsUp(cursor->GetChar())) {
664 // return to the previous state
668 result = cursor->Up();
669 if (!result && cursor->getPar()) {
670 MathParInset * p = cursor->getPar();
672 if (p->GetType() == LM_OT_SCRIPT) {
673 MathedXIter * cx = mathstk.Item(1);
674 bool is_down = (cx->GetChar() == LM_TC_DOWN);
675 cursor = mathstk.pop();
677 result = is_down ? true : Up();
679 result = p->getArgumentIdx() > 0;
681 p->setArgumentIdx(p->getArgumentIdx() - 1);
686 if (!result && !mathstk.empty()) {
687 cursor = mathstk.pop();
695 bool MathedCursor::Down(bool sel)
702 if (sel && !selection)
705 if (!sel && selection)
708 if (cursor->IsScript()) {
709 char cd = cursor->GetChar();
710 if (MathIsDown(cd)) {
714 // A superscript may be followed by a subscript
717 if (MathIsDown(cursor->GetChar())) {
722 // return to the previous state
726 result = cursor->Down();
727 if (!result && cursor->getPar()) {
728 MathParInset * p= cursor->getPar();
729 if (p->GetType() == LM_OT_SCRIPT) {
730 MathedXIter * cx = mathstk.Item(1);
731 bool is_up = (cx->GetChar() == LM_TC_UP);
732 cursor = mathstk.pop();
734 result = is_up ? true : Down();
736 result = p->getArgumentIdx() < p->getMaxArgumentIdx();
738 p->setArgumentIdx(p->getArgumentIdx() + 1);
742 if (!result && !mathstk.empty()) {
743 cursor = mathstk.pop();
751 bool MathedCursor::Limits()
753 if (cursor->IsInset()) {
754 MathedInset * p = cursor->GetInset();
755 bool ol = p->GetLimits();
757 return (ol!= p->GetLimits());
763 void MathedCursor::SetSize(short size)
765 MathParInset * p = cursor->getPar();
766 p->UserSetSize(size);
771 void MathedCursor::setLabel(string const & label)
773 // ugly hack and possible bug
774 if (!cursor->setLabel(label))
775 lyxerr << "MathErr: Bad place to set labels." << endl;
779 void MathedCursor::setNumbered()
782 MathedRowContainer::iterator crow = cursor->currentRow();
784 crow->setNumbered(!crow->isNumbered());
788 void MathedCursor::Interpret(string const & s)
791 latexkeys const * l = 0;
792 MathedTextCodes tcode = LM_TC_INSET;
794 if (s[0] == '^' || s[0] == '_') {
795 char c = cursor->GetChar();
796 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
801 // A script may be followed by a script
802 if (MathIsUp(c) || MathIsDown(c)) {
805 c = cursor->GetChar();
806 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
812 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
813 insertInset(p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
816 if (s[0] == '!' || s[0] == ',' || s[0] == ':' || s[0] == ';') {
817 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
818 p = new MathSpaceInset(sp);
819 insertInset(p, LM_TC_INSET);
825 p = new MathMacro(MathMacroTable::provideTemplate(s));
827 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
829 p = new MathRootInset;
830 tcode = LM_TC_ACTIVE_INSET;
832 p = new MathFuncInset(s, LM_OT_UNDEF);
834 tcode = static_cast<MathMacro*>(p)->getTCode();
835 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
838 MathedInsetTypes fractype = LM_OT_FRAC;
841 p = new MathBigopInset(l->name, l->id);
846 Insert(static_cast<byte>(l->id), MathIsBOPS(l->id) ?
847 LM_TC_BOPS: LM_TC_SYMB);
849 p = new MathFuncInset(l->name);
854 fractype = LM_OT_STACKREL;
855 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
858 p = new MathFracInset(fractype);
859 tcode = LM_TC_ACTIVE_INSET;
863 p = new MathSqrtInset;
864 tcode = LM_TC_ACTIVE_INSET;
868 p = new MathDecorationInset(l->id);
869 tcode = LM_TC_ACTIVE_INSET;
873 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
877 p = new MathSpaceInset(l->id);
881 p = new MathDotsInset(l->name, l->id);
889 p = new MathMacro(MathMacroTable::provideTemplate(s));
890 tcode = static_cast<MathMacro*>(p)->getTCode();
891 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
895 p = new MathFuncInset(l->name);
901 insertInset(p, tcode);
907 bool MathedCursor::pullArg()
909 if (cursor->IsActive()) {
910 MathParInset * p = cursor->GetActiveInset();
914 MathedArray a = p->GetData();
928 void MathedCursor::MacroModeOpen()
931 imacro = new MathFuncInset("");
932 insertInset(imacro, LM_TC_INSET);
935 lyxerr << "Mathed Warning: Already in macro mode" << endl;
939 void MathedCursor::MacroModeClose()
943 latexkeys const * l = in_word_set(imacro->GetName());
944 if (!imacro->GetName().empty()
945 && (!l || (l && IsMacro(l->token, l->id))) &&
946 !MathMacroTable::hasTemplate(imacro->GetName()))
949 //imacro->SetName(macrobf);
950 // This guarantees that the string will be removed by destructor
951 imacro->SetType(LM_OT_UNDEF);
953 imacro->SetName(l->name);
956 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
958 static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
961 if (l || MathMacroTable::hasTemplate(imacro->GetName())) {
962 Interpret(imacro->GetName());
971 void MathedCursor::SelCopy()
974 int const p1 = (cursor->getPos() < selpos) ?
975 cursor->getPos() : selpos;
976 int const p2 = (cursor->getPos() > selpos) ?
977 cursor->getPos() : selpos;
978 selarray = *(cursor->GetData());
979 selarray.shrink(p1, p2);
986 void MathedCursor::SelCut()
989 if (cursor->getPos() == selpos)
992 int const p1 = (cursor->getPos() < selpos) ?
993 cursor->getPos() : selpos;
994 int const p2 = (cursor->getPos() > selpos) ?
995 cursor->getPos() : selpos;
996 selarray = *(cursor->GetData());
997 selarray.shrink(p1, p2);
998 cursor->Clean(selpos);
1005 void MathedCursor::SelDel()
1007 // lyxerr << "Deleting sel "
1009 if (cursor->getPos() == selpos)
1011 cursor->Clean(selpos);
1018 void MathedCursor::SelPaste()
1020 // lyxerr << "paste " << selarray << " " << curor->pos;
1024 if (!selarray.empty()) {
1025 cursor->Merge(selarray);
1031 void MathedCursor::SelStart()
1033 lyxerr[Debug::MATHED] << "Starting sel " << endl;
1035 selpos = cursor->getPos();
1037 anchor = selstk.Item(-1);
1038 anchor->SetData(cursor->getPar());
1040 anchor->goPosAbs(selpos);
1046 void MathedCursor::SelClear()
1048 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
1055 // Anchor position must be at the same level that stack.
1056 void MathedCursor::SelBalance()
1058 int d = mathstk.Level() - selstk.Level();
1060 // If unbalanced, balance them
1063 // lyxerr << "b[" << mathstk.Level() << " " << selstk.Level
1064 // << " " << anchor->GetX() << " " << cursor->GetX() << "]";
1065 anchor = selstk.pop();
1066 if (anchor->GetX() >= cursor->GetX())
1069 // lyxerr <<"a[" << mathstk.Level() << " " << selstk.Level() <<"]";
1072 d = mathstk.Level() - selstk.Level();
1075 // Once balanced the levels, check that they are at the same paragraph
1076 selpos = anchor->getPos();
1080 void MathedCursor::SelGetArea(int ** xp, int ** yp, int & np)
1082 static int xpoint[10];
1083 static int ypoint[10];
1094 // Balance anchor and cursor
1099 cursor->getPar()->GetXY(xo, yo);
1100 int w = cursor->getPar()->Width();
1103 cursor->GetPos(x1, y1);
1106 cursor->getAD(a1, d1);
1109 anchor->GetPos(x, y);
1112 anchor->getAD(a, d);
1114 // single row selection
1117 ypoint[i++] = y + d;
1119 ypoint[i++] = y - a;
1123 ypoint[i++] = y - a;
1127 ypoint[i++] = y1 - a;
1132 ypoint[i++] = y1 - a;
1134 ypoint[i++] = y1 + d;
1138 ypoint[i++] = y1 + d;
1141 ypoint[i++] = y + d;
1144 xpoint[i] = xpoint[0];
1145 ypoint[i++] = ypoint[0];
1150 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
1151 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
1152 // for (i = 0; i < np; ++i)
1153 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
1157 void MathedCursor::setAccent(int ac)
1159 if (ac > 0 && accent < 8)
1160 nestaccent[accent++] = ac;
1162 accent = 0; // consumed!
1166 int MathedCursor::getAccent() const
1168 return (accent > 0) ? nestaccent[accent - 1] : 0;
1172 void MathedCursor::doAccent(byte c, MathedTextCodes t)
1174 MathedInset * ac = 0;
1176 for (int i = accent - 1; i >= 0; --i) {
1177 if (i == accent - 1)
1178 ac = new MathAccentInset(c, t, nestaccent[i]);
1180 ac = new MathAccentInset(ac, nestaccent[i]);
1184 cursor->insertInset(ac, LM_TC_INSET);
1186 accent = 0; // consumed!
1190 void MathedCursor::doAccent(MathedInset * p)
1192 MathedInset * ac = 0;
1194 for (int i = accent - 1; i >= 0; --i) {
1195 if (i == accent - 1)
1196 ac = new MathAccentInset(p, nestaccent[i]);
1198 ac = new MathAccentInset(ac, nestaccent[i]);
1202 cursor->insertInset(ac, LM_TC_INSET);
1204 accent = 0; // consumed!
1208 void MathedCursor::toggleLastCode(MathedTextCodes t)
1211 lastcode = LM_TC_VAR;
1217 void MathedCursor::GetPos(int & x, int & y)
1219 cursor->GetPos(x, y);
1223 short MathedCursor::GetFCode()
1225 return cursor->fcode();
1229 MathParInset * MathedCursor::GetPar()
1235 MathParInset * MathedCursor::getCurrentPar() const
1237 return cursor->getPar();
1241 string const & MathedCursor::getLabel() const
1243 return cursor->getLabel();
1247 bool MathedCursor::IsEnd() const
1249 return !cursor->OK();
1253 bool MathedCursor::InMacroMode()
1259 bool MathedCursor::Selection()
1265 void MathedCursor::clearLastCode()
1267 lastcode = LM_TC_MIN;
1271 void MathedCursor::setLastCode(MathedTextCodes t)
1277 MathedTextCodes MathedCursor::getLastCode() const
1283 bool MathedCursor::hasData(MathedArray const & ar)
1285 lyxerr << "hasData: ar: " << &ar << " cursor: " << cursor->GetData() <<
1287 return &ar == cursor->GetData();