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;
51 // This was very smaller, I'll change it later
53 bool IsMacro(short tok, int id)
55 return tok != LM_TK_STACK &&
61 tok != LM_TK_FUNCLIM &&
62 tok != LM_TK_BIGSYM &&
63 tok != LM_TK_ACCENT &&
64 !(tok == LM_TK_SYM && id < 255);
67 static int const MAX_STACK_ITEMS = 32;
69 struct MathStackXIter {
70 std::vector<MathedXIter> item;
73 MathStackXIter(int n = MAX_STACK_ITEMS)
77 MathedXIter * push() {
85 MathedXIter * Item(int idx) {
87 cerr << "Wrong index: " << idx << " i: " << i << endl;
88 return &item[i - idx - 1];
96 return i >= MAX_STACK_ITEMS;
103 int Level() { return i; }
109 /***---------------- Mathed Cursor ---------------------------***/
111 MathedCursor::MathedCursor(MathParInset * p) // : par(p)
115 lastcode = LM_TC_MIN;
117 if (!MathMacroTable::built)
118 MathMacroTable::mathMTable.builtinMacros();
122 void MathedCursor::SetPar(MathParInset * p)
125 selection = false; // not SelClear() ?
127 cursor = mathstk.push();
129 cursor->SetData(par);
133 void MathedCursor::draw(Painter & pain, int x, int y)
135 // lyxerr << "Cursor[" << x << " " << y << "] ";
136 //win = pm; // win = (mathedCanvas) ? mathedCanvas: pm;
139 int w = par->Width() + 2;
140 int a = par->Ascent() + 1;
141 int h = par->Height() + 1;
143 if (par->GetType() > LM_OT_PAR) {
148 pain.rectangle(x - 1, y - a, w, h, LColor::mathframe);
150 par->draw(pain, x, y);
155 void MathedCursor::Redraw(Painter & pain)
157 lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
159 int w = par->Width();
160 int h = par->Height();
164 //mathed_set_font(LM_TC_VAR, 1);
165 pain.fillRectangle(x, y - par->Ascent(),
166 x + w, y - par->Ascent() + h,
168 par->draw(pain, x, y);
172 bool MathedCursor::Left(bool sel)
175 // was MacroModeBack()
176 if (!imacro->GetName().empty()) {
177 imacro->SetName(imacro->GetName()
178 .substr(0, imacro->GetName()
188 if (sel && !selection)
191 if (!sel && selection)
194 bool result = cursor->Prev();
196 if (!result && !mathstk.Empty()) {
197 cursor = mathstk.pop();
202 } else if (result && cursor->IsActive()) {
203 if (cursor->IsScript()) {
205 if (!cursor->IsScript())
212 MathParInset * p = cursor->GetActiveInset();
216 p->setArgumentIdx(p->getMaxArgumentIdx());
217 cursor = mathstk.push();
227 bool MathedCursor::Pop()
229 if (!mathstk.Empty()) {
230 cursor = mathstk.pop();
239 bool MathedCursor::Push()
241 if (cursor->IsActive()) {
242 MathParInset * p = cursor->GetActiveInset();
245 cursor = mathstk.push();
253 bool MathedCursor::Right(bool sel)
262 if (sel && !selection)
265 if (!sel && selection)
270 if (cursor->IsActive()) {
271 if (cursor->IsScript()) {
273 // A script may be followed by another script
274 if (cursor->IsScript())
280 MathParInset *p = cursor->GetActiveInset();
282 lyxerr << "Math error: Inset expected." << endl;
283 return cursor->Next();
285 p->setArgumentIdx(0);
286 cursor = mathstk.push();
290 result = cursor->Next();
293 if (cursor->GetChar()!= LM_TC_CR)
294 result = cursor->Next();
295 if (!result && !mathstk.Empty()) {
296 cursor = mathstk.pop();
308 void MathedCursor::SetPos(int x, int y)
315 lastcode = LM_TC_MIN;
317 cursor = mathstk.push();
318 cursor->SetData(par);
319 cursor->fitCoord(x, y);
321 while (cursor->GetX()<x && cursor->OK()) {
322 if (cursor->IsActive()) {
323 MathParInset * p = cursor->GetActiveInset();
324 if (p->Inside(x, y)) {
326 cursor = mathstk.push();
328 cursor->fitCoord(x, y);
334 if (!cursor->Next() && !Pop())
337 if (x - xp < cursor->GetX() - x)
343 void MathedCursor::Home()
349 cursor = mathstk.push();
354 void MathedCursor::End()
360 cursor = mathstk.push();
365 MathMatrixInset * create_multiline(short int type, int cols)
376 for (int i = 0; i < cols; ++i)
383 for (int i = 0; i < cols; ++i)
388 case LM_OT_MULTLINEN:
401 MathMatrixInset * mt = new MathMatrixInset(columns, -1);
402 mt->SetAlign(' ', align);
407 void MathedCursor::Insert(byte c, MathedTextCodes t)
415 if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
419 MathParInset * p = cursor->getPar();
420 if (p == par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
421 short int type = LM_OT_MPAR;
423 if (c >= '1' && c <= '9') {
426 } else if (c >= 'A' && c <= 'I') {
427 type = LM_OT_ALIGNAT;
430 type = LM_OT_MULTLINE;
434 if (p->GetType() == LM_OT_PARN)
436 MathMatrixInset * mt = create_multiline(type, cols);
437 mt->SetStyle(LM_ST_DISPLAY);
439 mt->setData(p->GetData());
444 int pos = cursor->getPos();
445 cursor->SetData(par);
446 cursor->goPosAbs(pos);
448 if (p && p->Permit(LMPF_ALLOW_CR)) {
451 } else if (t == LM_TC_TAB) {
452 MathParInset * p = cursor->getPar();
453 if (p && p->Permit(LMPF_ALLOW_TAB)) {
455 cursor->insert(c, t);
458 cursor->goNextColumn();
459 } else // Navigate between arguments
460 if (p && p->GetType() == LM_OT_MACRO) {
461 if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
462 p->setArgumentIdx(p->getArgumentIdx() + 1);
469 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
470 // was MacroModeInsert(c);
471 imacro->SetName(imacro->GetName() + static_cast<char>(c));
479 cursor->insert(c, t);
488 void MathedCursor::insertInset(MathedInset * p, int t)
494 if (MathIsActive(t)) {
496 static_cast<MathParInset*>(p)->setData(selarray);
501 if (mathstk.i < MAX_STACK_ITEMS - 1) {
502 if (accent && !MathIsActive(t)) {
505 cursor->insertInset(p, t);
506 if (MathIsActive(t)) {
512 lyxerr << "Math error: Full stack." << endl;
516 void MathedCursor::Delete()
526 if (cursor->Empty() && !mathstk.Empty())
527 cursor = mathstk.pop();
529 // if (cursor->GetChar()!= LM_TC_TAB)
535 void MathedCursor::DelLine()
545 MathParInset * p = cursor->getPar();
547 if (p && p->GetType() <= LM_OT_MATRIX && p->GetType() >= LM_OT_MPAR) {
553 bool MathedCursor::Up(bool sel)
560 if (sel && !selection)
563 if (!sel && selection)
566 if (cursor->IsScript()) {
567 char cd = cursor->GetChar();
572 // A subscript may be followed by a superscript
575 if (MathIsUp(cursor->GetChar())) {
578 } else // return to the previous state
583 result = cursor->Up();
584 if (!result && cursor->getPar()) {
585 MathParInset * p = cursor->getPar();
587 if (p->GetType() == LM_OT_SCRIPT) {
588 MathedXIter * cx = mathstk.Item(1);
589 bool is_down = (cx->GetChar() == LM_TC_DOWN);
590 cursor = mathstk.pop();
592 result = (is_down) ? true: Up();
594 result = (p->getArgumentIdx() > 0);
596 p->setArgumentIdx(p->getArgumentIdx() - 1);
601 if (!result && !mathstk.Empty()) {
602 cursor = mathstk.pop();
610 bool MathedCursor::Down(bool sel)
617 if (sel && !selection)
620 if (!sel && selection)
623 if (cursor->IsScript()) {
624 char cd = cursor->GetChar();
625 if (MathIsDown(cd)) {
629 // A superscript may be followed by a subscript
632 if (MathIsDown(cursor->GetChar())) {
640 result = cursor->Down();
641 if (!result && cursor->getPar()) {
642 MathParInset * p= cursor->getPar();
643 if (p->GetType() == LM_OT_SCRIPT) {
644 MathedXIter * cx = mathstk.Item(1);
645 bool is_up = (cx->GetChar() == LM_TC_UP);
646 cursor = mathstk.pop();
648 result = (is_up) ? true : Down();
650 result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
652 p->setArgumentIdx(p->getArgumentIdx() + 1);
656 if (!result && !mathstk.Empty()) {
657 cursor = mathstk.pop();
665 bool MathedCursor::Limits()
667 if (cursor->IsInset()) {
668 MathedInset * p = cursor->GetInset();
669 bool ol = p->GetLimits();
671 return (ol!= p->GetLimits());
677 void MathedCursor::SetSize(short size)
679 MathParInset * p = cursor->getPar();
680 p->UserSetSize(size);
685 void MathedCursor::setLabel(string const & label)
687 // ugly hack and possible bug
688 if (!cursor->setLabel(label))
689 lyxerr << "MathErr: Bad place to set labels." << endl;
693 void MathedCursor::setNumbered()
696 MathedRowContainer::iterator crow = cursor->currentRow();
698 crow->setNumbered(!crow->isNumbered());
702 void MathedCursor::Interpret(string const & s)
705 latexkeys const * l = 0;
706 MathedTextCodes tcode = LM_TC_INSET;
708 if (s[0] == '^' || s[0] == '_') {
709 char c = cursor->GetChar();
710 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
715 // A script may be followed by a script
716 if (MathIsUp(c) || MathIsDown(c)) {
719 c = cursor->GetChar();
720 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
726 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
727 insertInset(p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
730 if (s[0] == '!' || s[0] == ',' || s[0] == ':' || s[0] == ';') {
731 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
732 p = new MathSpaceInset(sp);
733 insertInset(p, LM_TC_INSET);
739 p = MathMacroTable::mathMTable.createMacro(s);
741 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
743 p = new MathRootInset;
744 tcode = LM_TC_ACTIVE_INSET;
746 p = new MathFuncInset(s, LM_OT_UNDEF);
748 tcode = static_cast<MathMacro*>(p)->getTCode();
749 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
752 MathedInsetTypes fractype = LM_OT_FRAC;
755 p = new MathBigopInset(l->name, l->id);
760 Insert(static_cast<byte>(l->id), MathIsBOPS(l->id) ?
761 LM_TC_BOPS: LM_TC_SYMB);
763 p = new MathFuncInset(l->name);
768 fractype = LM_OT_STACKREL;
769 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
772 p = new MathFracInset(fractype);
773 tcode = LM_TC_ACTIVE_INSET;
777 p = new MathSqrtInset;
778 tcode = LM_TC_ACTIVE_INSET;
782 p = new MathDecorationInset(l->id);
783 tcode = LM_TC_ACTIVE_INSET;
787 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
791 p = new MathSpaceInset(l->id);
795 p = new MathDotsInset(l->name, l->id);
803 p = MathMacroTable::mathMTable.createMacro(s);
804 tcode = static_cast<MathMacro*>(p)->getTCode();
805 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
809 p = new MathFuncInset(l->name);
815 insertInset(p, tcode);
821 bool MathedCursor::pullArg()
823 if (cursor->IsActive()) {
824 MathParInset * p = cursor->GetActiveInset();
828 MathedArray a = p->GetData();
842 void MathedCursor::MacroModeOpen()
845 imacro = new MathFuncInset("");
846 insertInset(imacro, LM_TC_INSET);
849 lyxerr << "Mathed Warning: Already in macro mode" << endl;
853 void MathedCursor::MacroModeClose()
857 latexkeys const * l = in_word_set(imacro->GetName());
858 if (!imacro->GetName().empty()
859 && (!l || (l && IsMacro(l->token, l->id))) &&
860 !MathMacroTable::mathMTable.createMacro(imacro->GetName())) {
862 //imacro->SetName(macrobf);
863 // This guarantees that the string will be removed by destructor
864 imacro->SetType(LM_OT_UNDEF);
866 imacro->SetName(l->name);
869 if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
871 static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
874 if (l || MathMacroTable::mathMTable.createMacro(imacro->GetName())) {
875 Interpret(imacro->GetName());
884 void MathedCursor::SelCopy()
887 int const p1 = (cursor->getPos() < selpos) ?
888 cursor->getPos() : selpos;
889 int const p2 = (cursor->getPos() > selpos) ?
890 cursor->getPos() : selpos;
891 selarray = *(cursor->GetData());
892 selarray.shrink(p1, p2);
899 void MathedCursor::SelCut()
902 if (cursor->getPos() == selpos)
905 int const p1 = (cursor->getPos() < selpos) ?
906 cursor->getPos() : selpos;
907 int const p2 = (cursor->getPos() > selpos) ?
908 cursor->getPos() : selpos;
909 selarray = *(cursor->GetData());
910 selarray.shrink(p1, p2);
911 cursor->Clean(selpos);
918 void MathedCursor::SelDel()
920 // lyxerr << "Deleting sel "
922 if (cursor->getPos() == selpos)
924 cursor->Clean(selpos);
931 void MathedCursor::SelPaste()
933 // lyxerr << "paste " << selarray << " " << curor->pos;
937 if (!selarray.empty()) {
938 cursor->Merge(selarray);
944 void MathedCursor::SelStart()
946 lyxerr[Debug::MATHED] << "Starting sel " << endl;
948 selpos = cursor->getPos();
950 anchor = selstk.Item(-1);
951 anchor->SetData(cursor->getPar());
953 anchor->goPosAbs(selpos);
959 void MathedCursor::SelClear()
961 lyxerr[Debug::MATHED] << "Clearing sel " << endl;
968 // Anchor position must be at the same level that stack.
969 void MathedCursor::SelBalance()
971 int d = mathstk.Level() - selstk.Level();
973 // If unbalanced, balance them
976 // lyxerr << "b[" << mathstk.Level() << " " << selstk.Level
977 // << " " << anchor->GetX() << " " << cursor->GetX() << "]";
978 anchor = selstk.pop();
979 if (anchor->GetX() >= cursor->GetX())
982 // lyxerr <<"a[" << mathstk.Level() << " " << selstk.Level() <<"]";
985 d = mathstk.Level() - selstk.Level();
988 // Once balanced the levels, check that they are at the same paragraph
989 selpos = anchor->getPos();
993 void MathedCursor::SelGetArea(int ** xp, int ** yp, int & np)
995 static int xpoint[10];
996 static int ypoint[10];
1007 // Balance anchor and cursor
1012 cursor->getPar()->GetXY(xo, yo);
1013 int w = cursor->getPar()->Width();
1016 cursor->GetPos(x1, y1);
1019 cursor->getAD(a1, d1);
1022 anchor->GetPos(x, y);
1025 anchor->getAD(a, d);
1027 // single row selection
1030 ypoint[i++] = y + d;
1032 ypoint[i++] = y - a;
1036 ypoint[i++] = y - a;
1040 ypoint[i++] = y1 - a;
1045 ypoint[i++] = y1 - a;
1047 ypoint[i++] = y1 + d;
1051 ypoint[i++] = y1 + d;
1054 ypoint[i++] = y + d;
1057 xpoint[i] = xpoint[0];
1058 ypoint[i++] = ypoint[0];
1063 // lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
1064 // lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
1065 // for (i = 0; i < np; ++i)
1066 // lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
1070 void MathedCursor::setAccent(int ac)
1072 if (ac > 0 && accent < 8) {
1073 nestaccent[accent++] = ac;
1075 accent = 0; // consumed!
1079 int MathedCursor::getAccent() const
1081 return (accent > 0) ? nestaccent[accent - 1]: 0;
1085 void MathedCursor::doAccent(byte c, MathedTextCodes t)
1087 MathedInset * ac = 0;
1089 for (int i = accent - 1; i >= 0; --i) {
1090 if (i == accent - 1)
1091 ac = new MathAccentInset(c, t, nestaccent[i]);
1093 ac = new MathAccentInset(ac, nestaccent[i]);
1097 cursor->insertInset(ac, LM_TC_INSET);
1099 accent = 0; // consumed!
1103 void MathedCursor::doAccent(MathedInset * p)
1105 MathedInset * ac = 0;
1107 for (int i = accent - 1; i >= 0; --i) {
1108 if (i == accent - 1)
1109 ac = new MathAccentInset(p, nestaccent[i]);
1111 ac = new MathAccentInset(ac, nestaccent[i]);
1115 cursor->insertInset(ac, LM_TC_INSET);
1117 accent = 0; // consumed!
1121 void MathedCursor::toggleLastCode(MathedTextCodes t)
1124 lastcode = LM_TC_VAR;
1130 void MathedCursor::GetPos(int & x, int & y)
1132 cursor->GetPos(x, y);
1136 short MathedCursor::GetFCode()
1138 return cursor->fcode();
1142 MathParInset * MathedCursor::GetPar()
1148 MathParInset * MathedCursor::getCurrentPar() const
1150 return cursor->getPar();
1154 string const & MathedCursor::getLabel() const
1156 return cursor->getLabel();
1160 bool MathedCursor::IsEnd() const
1162 return !cursor->OK();
1166 bool MathedCursor::InMacroMode()
1172 bool MathedCursor::Selection()
1178 void MathedCursor::clearLastCode()
1180 lastcode = LM_TC_MIN;
1184 void MathedCursor::setLastCode(MathedTextCodes t)
1190 MathedTextCodes MathedCursor::getLastCode() const