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"
52 // This was very smaller, I'll change it later
54 bool IsMacro(short tok, int id)
56 return tok != LM_TK_STACK &&
62 tok != LM_TK_FUNCLIM &&
63 tok != LM_TK_BIGSYM &&
64 tok != LM_TK_ACCENT &&
65 !(tok == LM_TK_SYM && id < 255);
68 int const MAX_STACK_ITEMS = 32;
70 struct MathStackXIter {
71 std::vector<MathedXIter> item;
74 MathStackXIter(int n = MAX_STACK_ITEMS)
78 MathedXIter * push() {
87 MathedXIter * Item(int idx) {
89 lyxerr << "Wrong index: " << idx << " i: " << i << endl;
90 return &item[i - idx - 1];
98 return i >= MAX_STACK_ITEMS;
105 int Level() { return i; }
112 /***---------------- Mathed Cursor ---------------------------***/
114 MathedCursor::MathedCursor(MathParInset * p) // : par(p)
118 lastcode = LM_TC_MIN;
120 if (!MathMacroTable::built)
121 MathMacroTable::mathMTable.builtinMacros();
125 void MathedCursor::SetPar(MathParInset * p)
128 selection = false; // not SelClear() ?
130 cursor = mathstk.push();
132 cursor->SetData(par);
136 void MathedCursor::draw(Painter & pain, int x, int y)
138 // lyxerr << "Cursor[" << x << " " << y << "] ";
139 //win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
142 int w = par->Width() + 2;
143 int a = par->Ascent() + 1;
144 int h = par->Height() + 1;
146 if (par->GetType() > LM_OT_PAR) {
151 pain.rectangle(x - 1, y - a, w, h, LColor::mathframe);
153 par->draw(pain, x, y);
158 void MathedCursor::Redraw(Painter & pain)
160 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
162 int w = par->Width();
163 int 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)
178 // was MacroModeBack()
179 if (!imacro->GetName().empty()) {
180 imacro->SetName(imacro->GetName()
181 .substr(0, imacro->GetName()
191 if (sel && !selection)
194 if (!sel && selection)
197 bool result = cursor->Prev();
199 if (!result && !mathstk.Empty()) {
200 cursor = mathstk.pop();
205 } else if (result && cursor->IsActive()) {
206 if (cursor->IsScript()) {
208 if (!cursor->IsScript())
215 MathParInset * p = cursor->GetActiveInset();
219 p->setArgumentIdx(p->getMaxArgumentIdx());
220 cursor = mathstk.push();
230 bool MathedCursor::Pop()
232 if (!mathstk.Empty()) {
233 cursor = mathstk.pop();
242 bool MathedCursor::Push()
244 if (cursor->IsActive()) {
245 MathParInset * p = cursor->GetActiveInset();
248 cursor = mathstk.push();
256 bool MathedCursor::Right(bool sel)
265 if (sel && !selection)
268 if (!sel && selection)
273 if (cursor->IsActive()) {
274 if (cursor->IsScript()) {
276 // A script may be followed by another script
277 if (cursor->IsScript())
283 MathParInset *p = cursor->GetActiveInset();
285 lyxerr << "Math error: Inset expected." << endl;
286 return cursor->Next();
288 p->setArgumentIdx(0);
289 cursor = mathstk.push();
293 result = cursor->Next();
296 if (cursor->GetChar()!= LM_TC_CR)
297 result = cursor->Next();
298 if (!result && !mathstk.Empty()) {
299 cursor = mathstk.pop();
311 void MathedCursor::SetPos(int x, int y)
318 lastcode = LM_TC_MIN;
320 cursor = mathstk.push();
321 cursor->SetData(par);
322 cursor->fitCoord(x, y);
324 while (cursor->GetX()<x && cursor->OK()) {
325 if (cursor->IsActive()) {
326 MathParInset * p = cursor->GetActiveInset();
327 if (p->Inside(x, y)) {
329 cursor = mathstk.push();
331 cursor->fitCoord(x, y);
337 if (!cursor->Next() && !Pop())
340 if (x - xp < cursor->GetX() - x)
346 void MathedCursor::Home()
352 cursor = mathstk.push();
357 void MathedCursor::End()
363 cursor = mathstk.push();
368 MathMatrixInset * create_multiline(short int type, int cols)
379 for (int i = 0; i < cols; ++i)
386 for (int i = 0; i < cols; ++i)
391 case LM_OT_MULTLINEN:
404 MathMatrixInset * mt = new MathMatrixInset(columns, -1);
405 mt->SetAlign(' ', align);
410 void MathedCursor::Insert(byte c, MathedTextCodes t)
418 if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
422 MathParInset * p = cursor->getPar();
423 if (p == par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
424 short int type = LM_OT_MPAR;
426 if (c >= '1' && c <= '9') {
429 } else if (c >= 'A' && c <= 'I') {
430 type = LM_OT_ALIGNAT;
433 type = LM_OT_MULTLINE;
437 if (p->GetType() == LM_OT_PARN)
439 MathMatrixInset * mt = create_multiline(type, cols);
440 mt->SetStyle(LM_ST_DISPLAY);
442 mt->setData(p->GetData());
447 int pos = cursor->getPos();
448 cursor->SetData(par);
449 cursor->goPosAbs(pos);
451 if (p && p->Permit(LMPF_ALLOW_CR)) {
454 } else if (t == LM_TC_TAB) {
455 MathParInset * p = cursor->getPar();
456 if (p && p->Permit(LMPF_ALLOW_TAB)) {
458 cursor->insert(c, t);
461 cursor->goNextColumn();
462 } else // Navigate between arguments
463 if (p && p->GetType() == LM_OT_MACRO) {
464 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
465 p->setArgumentIdx(p->getArgumentIdx() + 1);
472 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
473 // was MacroModeInsert(c);
474 imacro->SetName(imacro->GetName() + static_cast<char>(c));
482 cursor->insert(c, t);
491 void MathedCursor::insertInset(MathedInset * p, int t)
497 if (MathIsActive(t)) {
499 static_cast<MathParInset*>(p)->setData(selarray);
504 if (mathstk.i < MAX_STACK_ITEMS - 1) {
505 if (accent && !MathIsActive(t)) {
508 cursor->insertInset(p, t);
509 if (MathIsActive(t)) {
515 lyxerr << "Math error: Full stack." << endl;
519 void MathedCursor::Delete()
529 if (cursor->Empty() && !mathstk.Empty())
530 cursor = mathstk.pop();
532 // if (cursor->GetChar()!= LM_TC_TAB)
538 void MathedCursor::DelLine()
548 MathParInset * p = cursor->getPar();
550 if (p && p->GetType() <= LM_OT_MATRIX && p->GetType() >= LM_OT_MPAR) {
556 bool MathedCursor::Up(bool sel)
563 if (sel && !selection)
566 if (!sel && selection)
569 if (cursor->IsScript()) {
570 char cd = cursor->GetChar();
575 // A subscript may be followed by a superscript
578 if (MathIsUp(cursor->GetChar())) {
581 } else // return to the previous state
586 result = cursor->Up();
587 if (!result && cursor->getPar()) {
588 MathParInset * p = cursor->getPar();
590 if (p->GetType() == LM_OT_SCRIPT) {
591 MathedXIter * cx = mathstk.Item(1);
592 bool is_down = (cx->GetChar() == LM_TC_DOWN);
593 cursor = mathstk.pop();
595 result = (is_down) ? true: Up();
597 result = (p->getArgumentIdx() > 0);
599 p->setArgumentIdx(p->getArgumentIdx() - 1);
604 if (!result && !mathstk.Empty()) {
605 cursor = mathstk.pop();
613 bool MathedCursor::Down(bool sel)
620 if (sel && !selection)
623 if (!sel && selection)
626 if (cursor->IsScript()) {
627 char cd = cursor->GetChar();
628 if (MathIsDown(cd)) {
632 // A superscript may be followed by a subscript
635 if (MathIsDown(cursor->GetChar())) {
643 result = cursor->Down();
644 if (!result && cursor->getPar()) {
645 MathParInset * p= cursor->getPar();
646 if (p->GetType() == LM_OT_SCRIPT) {
647 MathedXIter * cx = mathstk.Item(1);
648 bool is_up = (cx->GetChar() == LM_TC_UP);
649 cursor = mathstk.pop();
651 result = (is_up) ? true : Down();
653 result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
655 p->setArgumentIdx(p->getArgumentIdx() + 1);
659 if (!result && !mathstk.Empty()) {
660 cursor = mathstk.pop();
668 bool MathedCursor::Limits()
670 if (cursor->IsInset()) {
671 MathedInset * p = cursor->GetInset();
672 bool ol = p->GetLimits();
674 return (ol!= p->GetLimits());
680 void MathedCursor::SetSize(short size)
682 MathParInset * p = cursor->getPar();
683 p->UserSetSize(size);
688 void MathedCursor::setLabel(string const & label)
690 // ugly hack and possible bug
691 if (!cursor->setLabel(label))
692 lyxerr << "MathErr: Bad place to set labels." << endl;
696 void MathedCursor::setNumbered()
699 MathedRowContainer::iterator crow = cursor->currentRow();
701 crow->setNumbered(!crow->isNumbered());
705 void MathedCursor::Interpret(string const & s)
708 latexkeys const * l = 0;
709 MathedTextCodes tcode = LM_TC_INSET;
711 if (s[0] == '^' || s[0] == '_') {
712 char c = cursor->GetChar();
713 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
718 // A script may be followed by a script
719 if (MathIsUp(c) || MathIsDown(c)) {
722 c = cursor->GetChar();
723 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
729 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
730 insertInset(p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
733 if (s[0] == '!' || s[0] == ',' || s[0] == ':' || s[0] == ';') {
734 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
735 p = new MathSpaceInset(sp);
736 insertInset(p, LM_TC_INSET);
742 p = MathMacroTable::mathMTable.createMacro(s);
744 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
746 p = new MathRootInset;
747 tcode = LM_TC_ACTIVE_INSET;
749 p = new MathFuncInset(s, LM_OT_UNDEF);
751 tcode = static_cast<MathMacro*>(p)->getTCode();
752 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
755 MathedInsetTypes fractype = LM_OT_FRAC;
758 p = new MathBigopInset(l->name, l->id);
763 Insert(static_cast<byte>(l->id), MathIsBOPS(l->id) ?
764 LM_TC_BOPS: LM_TC_SYMB);
766 p = new MathFuncInset(l->name);
771 fractype = LM_OT_STACKREL;
772 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
775 p = new MathFracInset(fractype);
776 tcode = LM_TC_ACTIVE_INSET;
780 p = new MathSqrtInset;
781 tcode = LM_TC_ACTIVE_INSET;
785 p = new MathDecorationInset(l->id);
786 tcode = LM_TC_ACTIVE_INSET;
790 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
794 p = new MathSpaceInset(l->id);
798 p = new MathDotsInset(l->name, l->id);
806 p = MathMacroTable::mathMTable.createMacro(s);
807 tcode = static_cast<MathMacro*>(p)->getTCode();
808 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
812 p = new MathFuncInset(l->name);
818 insertInset(p, tcode);
824 bool MathedCursor::pullArg()
826 if (cursor->IsActive()) {
827 MathParInset * p = cursor->GetActiveInset();
831 MathedArray a = p->GetData();
845 void MathedCursor::MacroModeOpen()
848 imacro = new MathFuncInset("");
849 insertInset(imacro, LM_TC_INSET);
852 lyxerr << "Mathed Warning: Already in macro mode" << endl;
856 void MathedCursor::MacroModeClose()
860 latexkeys const * l = in_word_set(imacro->GetName());
861 if (!imacro->GetName().empty()
862 && (!l || (l && IsMacro(l->token, l->id))) &&
863 !MathMacroTable::mathMTable.createMacro(imacro->GetName())) {
865 //imacro->SetName(macrobf);
866 // This guarantees that the string will be removed by destructor
867 imacro->SetType(LM_OT_UNDEF);
869 imacro->SetName(l->name);
872 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
874 static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
877 if (l || MathMacroTable::mathMTable.createMacro(imacro->GetName())) {
878 Interpret(imacro->GetName());
887 void MathedCursor::SelCopy()
890 int const p1 = (cursor->getPos() < selpos) ?
891 cursor->getPos() : selpos;
892 int const p2 = (cursor->getPos() > selpos) ?
893 cursor->getPos() : selpos;
894 selarray = *(cursor->GetData());
895 selarray.shrink(p1, p2);
902 void MathedCursor::SelCut()
905 if (cursor->getPos() == selpos)
908 int const p1 = (cursor->getPos() < selpos) ?
909 cursor->getPos() : selpos;
910 int const p2 = (cursor->getPos() > selpos) ?
911 cursor->getPos() : selpos;
912 selarray = *(cursor->GetData());
913 selarray.shrink(p1, p2);
914 cursor->Clean(selpos);
921 void MathedCursor::SelDel()
923 // lyxerr << "Deleting sel "
925 if (cursor->getPos() == selpos)
927 cursor->Clean(selpos);
934 void MathedCursor::SelPaste()
936 // lyxerr << "paste " << selarray << " " << curor->pos;
940 if (!selarray.empty()) {
941 cursor->Merge(selarray);
947 void MathedCursor::SelStart()
949 lyxerr[Debug::MATHED] << "Starting sel " << endl;
951 selpos = cursor->getPos();
953 anchor = selstk.Item(-1);
954 anchor->SetData(cursor->getPar());
956 anchor->goPosAbs(selpos);
962 void MathedCursor::SelClear()
964 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
971 // Anchor position must be at the same level that stack.
972 void MathedCursor::SelBalance()
974 int d = mathstk.Level() - selstk.Level();
976 // If unbalanced, balance them
979 // lyxerr << "b[" << mathstk.Level() << " " << selstk.Level
980 // << " " << anchor->GetX() << " " << cursor->GetX() << "]";
981 anchor = selstk.pop();
982 if (anchor->GetX() >= cursor->GetX())
985 // lyxerr <<"a[" << mathstk.Level() << " " << selstk.Level() <<"]";
988 d = mathstk.Level() - selstk.Level();
991 // Once balanced the levels, check that they are at the same paragraph
992 selpos = anchor->getPos();
996 void MathedCursor::SelGetArea(int ** xp, int ** yp, int & np)
998 static int xpoint[10];
999 static int ypoint[10];
1010 // Balance anchor and cursor
1015 cursor->getPar()->GetXY(xo, yo);
1016 int w = cursor->getPar()->Width();
1019 cursor->GetPos(x1, y1);
1022 cursor->getAD(a1, d1);
1025 anchor->GetPos(x, y);
1028 anchor->getAD(a, d);
1030 // single row selection
1033 ypoint[i++] = y + d;
1035 ypoint[i++] = y - a;
1039 ypoint[i++] = y - a;
1043 ypoint[i++] = y1 - a;
1048 ypoint[i++] = y1 - a;
1050 ypoint[i++] = y1 + d;
1054 ypoint[i++] = y1 + d;
1057 ypoint[i++] = y + d;
1060 xpoint[i] = xpoint[0];
1061 ypoint[i++] = ypoint[0];
1066 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
1067 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
1068 // for (i = 0; i < np; ++i)
1069 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
1073 void MathedCursor::setAccent(int ac)
1075 if (ac > 0 && accent < 8) {
1076 nestaccent[accent++] = ac;
1078 accent = 0; // consumed!
1082 int MathedCursor::getAccent() const
1084 return (accent > 0) ? nestaccent[accent - 1]: 0;
1088 void MathedCursor::doAccent(byte c, MathedTextCodes t)
1090 MathedInset * ac = 0;
1092 for (int i = accent - 1; i >= 0; --i) {
1093 if (i == accent - 1)
1094 ac = new MathAccentInset(c, t, nestaccent[i]);
1096 ac = new MathAccentInset(ac, nestaccent[i]);
1100 cursor->insertInset(ac, LM_TC_INSET);
1102 accent = 0; // consumed!
1106 void MathedCursor::doAccent(MathedInset * p)
1108 MathedInset * ac = 0;
1110 for (int i = accent - 1; i >= 0; --i) {
1111 if (i == accent - 1)
1112 ac = new MathAccentInset(p, nestaccent[i]);
1114 ac = new MathAccentInset(ac, nestaccent[i]);
1118 cursor->insertInset(ac, LM_TC_INSET);
1120 accent = 0; // consumed!
1124 void MathedCursor::toggleLastCode(MathedTextCodes t)
1127 lastcode = LM_TC_VAR;
1133 void MathedCursor::GetPos(int & x, int & y)
1135 cursor->GetPos(x, y);
1139 short MathedCursor::GetFCode()
1141 return cursor->fcode();
1145 MathParInset * MathedCursor::GetPar()
1151 MathParInset * MathedCursor::getCurrentPar() const
1153 return cursor->getPar();
1157 string const & MathedCursor::getLabel() const
1159 return cursor->getLabel();
1163 bool MathedCursor::IsEnd() const
1165 return !cursor->OK();
1169 bool MathedCursor::InMacroMode()
1175 bool MathedCursor::Selection()
1181 void MathedCursor::clearLastCode()
1183 lastcode = LM_TC_MIN;
1187 void MathedCursor::setLastCode(MathedTextCodes t)
1193 MathedTextCodes MathedCursor::getLastCode() const