3 * Purpose: Implementation of formula inset
4 * Author: Alejandro Aguilar Sierra <asierra@servidor.unam.mx>
5 * Created: January 1996
6 * Description: Allows the edition of math paragraphs inside Lyx.
8 * Copyright: 1996-1998 Alejandro Aguilar Sierra
10 * Version: 0.4, Lyx project.
12 * You are free to use and modify this code under the terms of
13 * the GNU General Public Licence version 2 or later.
22 #pragma implementation
26 #include "commandtags.h"
27 #include "math_cursor.h"
28 #include "math_parser.h"
29 #include "BufferView.h"
32 #include "LaTeXFeatures.h"
34 #include "support/LOstream.h"
38 #include "math_arrayinset.h"
39 #include "math_spaceinset.h"
40 #include "math_deliminset.h"
41 #include "support/lyxlib.h"
42 #include "mathed/support.h"
48 extern char const * latex_special_chars;
50 int greek_kb_flag = 0;
51 extern char const * latex_mathenv[];
52 LyXFont * Math_Fonts = 0;
53 MathCursor * mathcursor = 0;
64 void mathed_init_fonts();
66 string nicelabel(string const & label)
68 return label.empty() ? string("(#)") : "(" + label + ")";
75 LyXFont WhichFont(short type, int size)
112 case LM_TC_SPECIAL: //f = Math_Fonts[0]; break;
125 if (type == LM_TC_BSYM) {
138 case LM_ST_SCRIPTSCRIPT:
144 lyxerr << "Math Error: wrong font size: " << size << endl;
148 if (type != LM_TC_TEXTRM)
149 f.setColor(LColor::math);
157 void mathed_init_fonts()
159 Math_Fonts = new LyXFont[8]; //DEC cxx cannot initialize all fonts
162 for (int i = 0 ; i < 8 ; ++i) {
163 Math_Fonts[i] = LyXFont(LyXFont::ALL_SANE);
166 Math_Fonts[0].setShape(LyXFont::ITALIC_SHAPE);
168 Math_Fonts[1].setFamily(LyXFont::SYMBOL_FAMILY);
170 Math_Fonts[2].setFamily(LyXFont::SYMBOL_FAMILY);
171 Math_Fonts[2].setShape(LyXFont::ITALIC_SHAPE);
173 Math_Fonts[3].setSeries(LyXFont::BOLD_SERIES);
175 Math_Fonts[4].setFamily(LyXFont::SANS_FAMILY);
176 Math_Fonts[4].setShape(LyXFont::ITALIC_SHAPE);
178 Math_Fonts[5].setFamily(LyXFont::TYPEWRITER_FAMILY);
180 Math_Fonts[6].setFamily(LyXFont::ROMAN_FAMILY);
182 Math_Fonts[7].setFamily(LyXFont::SANS_FAMILY);
186 // returns the nearest enclosing matrix
187 MathArrayInset * matrixpar(int & idx)
191 static_cast<MathArrayInset *>
192 (mathcursor ? mathcursor->enclosing(LM_OT_MATRIX, idx) : 0);
199 InsetFormulaBase::InsetFormulaBase(MathInset * par)
203 InsetFormulaBase::InsetFormulaBase(InsetFormulaBase const & f)
204 : UpdatableInset(f), par_(static_cast<MathInset *>(f.par_->Clone()))
207 InsetFormulaBase::~InsetFormulaBase()
210 #warning leak this for a while...
216 void InsetFormulaBase::Write(Buffer const * buf, ostream & os) const
219 Latex(buf, os, false, false);
223 int InsetFormulaBase::Ascii(Buffer const *, ostream & os, int) const
225 par_->Write(os, false);
230 int InsetFormulaBase::Linuxdoc(Buffer const * buf, ostream & os) const
232 return Ascii(buf, os, 0);
236 int InsetFormulaBase::DocBook(Buffer const * buf, ostream & os) const
238 return Ascii(buf, os, 0);
242 // Check if uses AMS macros
243 void InsetFormulaBase::Validate(LaTeXFeatures &) const
247 string const InsetFormulaBase::EditMessage() const
249 return _("Math editor mode");
253 void InsetFormulaBase::Edit(BufferView * bv, int x, int /*y*/, unsigned int)
255 mathcursor = new MathCursor(this);
257 if (!bv->lockInset(this))
258 lyxerr[Debug::MATHED] << "Cannot lock inset!!!" << endl;
260 par_->Metrics(LM_ST_TEXT);
261 bv->updateInset(this, false);
273 void InsetFormulaBase::InsetUnlock(BufferView * bv)
276 if (mathcursor->InMacroMode()) {
277 mathcursor->MacroModeClose();
283 bv->updateInset(this, false);
287 void InsetFormulaBase::GetCursorPos(BufferView *, int & x, int & y) const
289 mathcursor->GetPos(x, y);
295 void InsetFormulaBase::ToggleInsetCursor(BufferView * bv)
300 if (isCursorVisible())
301 bv->hideLockedInsetCursor();
305 mathcursor->GetPos(x, y);
309 LyXFont font = WhichFont(LM_TC_TEXTRM, LM_ST_TEXT);
310 int const asc = lyxfont::maxAscent(font);
311 int const desc = lyxfont::maxDescent(font);
313 bv->showLockedInsetCursor(x, y, asc, desc);
316 toggleCursorVisible();
320 void InsetFormulaBase::ShowInsetCursor(BufferView * bv, bool)
322 if (!isCursorVisible()) {
326 mathcursor->GetPos(x, y);
329 LyXFont font = WhichFont(LM_TC_TEXTRM, LM_ST_TEXT);
330 int const asc = lyxfont::maxAscent(font);
331 int const desc = lyxfont::maxDescent(font);
332 bv->fitLockedInsetCursor(x, y, asc, desc);
334 ToggleInsetCursor(bv);
339 void InsetFormulaBase::HideInsetCursor(BufferView * bv)
341 if (isCursorVisible())
342 ToggleInsetCursor(bv);
346 void InsetFormulaBase::ToggleInsetSelection(BufferView * bv)
351 bv->updateInset(this, false);
355 vector<string> const InsetFormulaBase::getLabelList() const
357 return std::vector<string>();
361 void InsetFormulaBase::UpdateLocal(BufferView * bv)
363 par_->Metrics(LM_ST_TEXT);
364 bv->updateInset(this, true);
368 void InsetFormulaBase::InsetButtonRelease(BufferView * bv,
369 int x, int y, int /*button*/)
375 mathcursor->SetPos(x, y);
381 bv->updateInset(this, false);
387 void InsetFormulaBase::InsetButtonPress(BufferView * bv,
388 int x, int y, int /*button*/)
393 if (mathcursor && mathcursor->Selection()) {
394 mathcursor->SelClear();
395 bv->updateInset(this, false);
400 void InsetFormulaBase::InsetMotionNotify(BufferView * bv,
401 int x, int y, int /*button*/)
403 if (sel_x && sel_y && abs(x-sel_x) > 4 && !sel_flag) {
406 mathcursor->SetPos(sel_x + par_->xo(), sel_y + par_->yo());
407 mathcursor->SelStart();
409 mathcursor->GetPos(sel_x, sel_y);
410 } else if (sel_flag) {
414 mathcursor->SetPos(x, y);
416 mathcursor->GetPos(x, y);
417 if (sel_x != x || sel_y != y)
418 bv->updateInset(this, false);
425 void InsetFormulaBase::InsetKeyPress(XKeyEvent *)
427 lyxerr[Debug::MATHED] << "Used InsetFormulaBase::InsetKeyPress." << endl;
431 UpdatableInset::RESULT
432 InsetFormulaBase::LocalDispatch(BufferView * bv, kb_action action,
435 //lyxerr << "InsetFormulaBase::LocalDispatch: act: " << action
436 // << " arg: '" << arg << "' cursor: " << mathcursor << "\n";
437 // extern char *dispatch_result;
442 MathTextCodes varcode = LM_TC_MIN;
443 bool was_macro = mathcursor->InMacroMode();
445 bool space_on = false;
446 bool was_selection = mathcursor->Selection();
447 RESULT result = DISPATCHED;
448 static MathSpaceInset * sp = 0;
452 if (mathcursor->getLastCode() == LM_TC_TEX)
455 mathcursor->normalize();
459 // --- Cursor Movements ---------------------------------------------
462 sel = true; // fall through...
465 result = DISPATCH_RESULT(mathcursor->Right(sel));
471 sel = true; // fall through
474 result = DISPATCH_RESULT(mathcursor->Left(sel));
483 result = DISPATCH_RESULT(mathcursor->Up(sel));
492 result = DISPATCH_RESULT(mathcursor->Down(sel));
499 result = DISPATCHED_NOUPDATE;
504 result = DISPATCHED_NOUPDATE;
507 case LFUN_DELETE_LINE_FORWARD:
508 bv->lockedInsetStoreUndo(Undo::DELETE);
509 mathcursor->DelLine();
514 bv->lockedInsetStoreUndo(Undo::INSERT);
515 mathcursor->idxRight();
520 bv->lockedInsetStoreUndo(Undo::INSERT);
521 mathcursor->idxRight();
526 if (!mathcursor->InMacroMode() && mathcursor->pos() == 0) {
527 bv->lockedInsetStoreUndo(Undo::DELETE);
528 mathcursor->pullArg();
529 bv->updateInset(this, true);
532 if (!mathcursor->Left())
537 bv->lockedInsetStoreUndo(Undo::DELETE);
538 mathcursor->Delete();
539 bv->updateInset(this, true);
543 // sprintf(dispatch_buffer, "%d %d",);
544 // dispatch_result = dispatch_buffer;
548 lyxerr << "LFUN_SETXY broken!\n";
550 istringstream is(arg.c_str());
552 lyxerr << "LFUN_SETXY: x: " << x << " y: " << y << "\n";
554 mathcursor->SetPos(x1 + x, y1 + y);
558 // cursor selection ----------------------------
562 mathcursor->MacroModeClose();
563 bv->lockedInsetStoreUndo(Undo::INSERT);
564 mathcursor->SelPaste();
569 bv->lockedInsetStoreUndo(Undo::DELETE);
570 mathcursor->SelCut();
575 mathcursor->SelCopy();
580 case LFUN_WORDRIGHTSEL:
581 case LFUN_WORDLEFTSEL:
584 // --- accented characters ------------------------------
586 case LFUN_UMLAUT: mathcursor->setAccent(LM_ddot); break;
587 case LFUN_CIRCUMFLEX: mathcursor->setAccent(LM_hat); break;
588 case LFUN_GRAVE: mathcursor->setAccent(LM_grave); break;
589 case LFUN_ACUTE: mathcursor->setAccent(LM_acute); break;
590 case LFUN_TILDE: mathcursor->setAccent(LM_tilde); break;
591 case LFUN_MACRON: mathcursor->setAccent(LM_bar); break;
592 case LFUN_DOT: mathcursor->setAccent(LM_dot); break;
593 case LFUN_CARON: mathcursor->setAccent(LM_check); break;
594 case LFUN_BREVE: mathcursor->setAccent(LM_breve); break;
595 case LFUN_VECTOR: mathcursor->setAccent(LM_vec); break;
599 if (!greek_kb_flag) {
601 bv->owner()->message(_("Math greek mode on"));
607 case LFUN_GREEK_TOGGLE:
608 greek_kb_flag = greek_kb_flag ? 0 : 2;
610 bv->owner()->message(_("Math greek keyboard on"));
612 bv->owner()->message(_("Math greek keyboard off"));
616 case LFUN_BOLD: mathcursor->toggleLastCode(LM_TC_BF); break;
617 case LFUN_SANS: mathcursor->toggleLastCode(LM_TC_SF); break;
618 case LFUN_EMPH: mathcursor->toggleLastCode(LM_TC_CAL); break;
619 case LFUN_ROMAN: mathcursor->toggleLastCode(LM_TC_RM); break;
620 case LFUN_CODE: mathcursor->toggleLastCode(LM_TC_TT); break;
621 case LFUN_DEFAULT: mathcursor->setLastCode(LM_TC_VAR); break;
625 #warning This needs a fix.
626 // Can we use the ERT inset here? (Lgb)
629 // varcode = LM_TC_TEX;
630 mathcursor->setLastCode(LM_TC_TEX);
631 bv->owner()->message(_("TeX mode"));
634 case LFUN_MATH_LIMITS:
635 bv->lockedInsetStoreUndo(Undo::INSERT);
636 if (mathcursor->toggleLimits())
642 bv->lockedInsetStoreUndo(Undo::INSERT);
643 latexkeys const * l = in_word_set(arg);
644 mathcursor->SetSize(MathStyles(l ? l->id : static_cast<unsigned int>(-1)));
649 case LFUN_INSERT_MATH:
651 bv->lockedInsetStoreUndo(Undo::INSERT);
652 mathcursor->Interpret(arg);
657 case LFUN_INSERT_MATRIX:
659 bv->lockedInsetStoreUndo(Undo::INSERT);
662 string v_align, h_align;
663 istringstream is(arg.c_str());
664 is >> m >> n >> v_align >> h_align;
665 MathArrayInset * p = new MathArrayInset(m, n);
666 p->valign(v_align[0]);
668 mathcursor->insert(p);
673 case LFUN_MATH_DELIM:
675 bv->lockedInsetStoreUndo(Undo::INSERT);
678 static const string vdelim("(){}[]./|");
679 lyxerr << "formulabase::LFUN_MATH_DELIM, arg: '" << arg << "'\n";
684 istringstream is(arg.c_str());
687 lyxerr << "formulabase::LFUN_MATH_DELIM, lt: '" << lt << "'\n";
688 lyxerr << "formulabase::LFUN_MATH_DELIM, rt: '" << rt << "'\n";
691 latexkeys const * l = in_word_set(lt);
694 } else if (vdelim.find(lt[0]) != string::npos)
698 latexkeys const * l = in_word_set(rt);
701 } else if (vdelim.find(rt[0]) != string::npos)
704 if (mathcursor->selection) {
705 MathDelimInset * p = new MathDelimInset(ilt, irt);
707 mathcursor->selArray(ar);
708 lyxerr << "selarray: " << ar << "\n";
710 mathcursor->insert(p);
712 mathcursor->insert(new MathDelimInset(ilt, irt));
718 case LFUN_PROTECTEDSPACE:
719 bv->lockedInsetStoreUndo(Undo::INSERT);
720 mathcursor->insert(new MathSpaceInset(1));
725 // Invalid actions under math mode
727 if (mathcursor->getLastCode() != LM_TC_TEXTRM) {
728 bv->owner()->message(_("math text mode"));
729 varcode = LM_TC_TEXTRM;
732 mathcursor->setLastCode(varcode);
736 bv->owner()->message(_("Invalid action in math mode!"));
740 case LFUN_MATH_HALIGN:
742 bv->lockedInsetStoreUndo(Undo::INSERT);
743 lyxerr << "handling halign '" << arg << "'\n";
745 MathArrayInset * p = matrixpar(idx);
748 p->halign(arg.size() ? arg[0] : 'c', p->col(idx));
753 case LFUN_MATH_VALIGN:
755 bv->lockedInsetStoreUndo(Undo::INSERT);
756 lyxerr << "handling valign '" << arg << "'\n";
758 MathArrayInset * p = matrixpar(idx);
761 p->valign(arg.size() ? arg[0] : 'c');
766 case LFUN_MATH_ROW_INSERT:
768 bv->lockedInsetStoreUndo(Undo::INSERT);
770 MathArrayInset * p = matrixpar(idx);
771 lyxerr << " calling LFUN_MATH_ROW_INSERT on " << p << endl;
774 p->addRow(p->row(idx));
779 case LFUN_MATH_ROW_DELETE:
781 bv->lockedInsetStoreUndo(Undo::INSERT);
783 MathArrayInset * p = matrixpar(idx);
784 lyxerr << " calling LFUN_MATH_ROW_DELETE on " << p << endl;
787 p->delRow(p->row(idx));
792 case LFUN_MATH_COLUMN_INSERT:
794 bv->lockedInsetStoreUndo(Undo::INSERT);
796 MathArrayInset * p = matrixpar(idx);
799 p->addCol(p->col(idx));
804 case LFUN_MATH_COLUMN_DELETE:
806 bv->lockedInsetStoreUndo(Undo::INSERT);
808 MathArrayInset * p = matrixpar(idx);
811 p->delCol(p->col(idx));
816 case LFUN_EXEC_COMMAND:
817 result = UNDISPATCHED;
821 if ((action == -1 || action == LFUN_SELFINSERT) && !arg.empty()) {
822 unsigned char c = arg[0];
823 bv->lockedInsetStoreUndo(Undo::INSERT);
825 if (c == ' ' && mathcursor->getAccent() == LM_hat) {
827 mathcursor->setAccent(0);
830 if (c == 0) { // Dead key, do nothing
831 //lyxerr << "deadkey" << endl;
836 if (mathcursor->getLastCode() == LM_TC_TEX) {
837 mathcursor->MacroModeOpen();
838 mathcursor->clearLastCode();
840 } else if (!varcode) {
841 short f = mathcursor->getLastCode() ?
842 mathcursor->getLastCode() :
843 mathcursor->nextCode();
844 varcode = MathIsAlphaFont(f) ?
845 static_cast<MathTextCodes>(f) :
849 // lyxerr << "Varcode << vardoce;
850 MathTextCodes char_code = varcode;
853 {'A', 'B', 'X', 0 , 'E', 0 , 0 , 'H', 'I', 0 ,
854 'K', 0 , 'M', 'N', 'O', 0 , 0 , 'P', 0 , 'T',
855 'Y', 0, 0, 0, 0 , 'Z' };
857 if ('A' <= c && c <= 'Z' && greek[c - 'A']) {
858 char_code = LM_TC_RM;
861 char_code = LM_TC_SYMB;
864 mathcursor->insert(c, char_code);
866 if (greek_kb_flag && char_code == LM_TC_RM )
867 mathcursor->setLastCode(LM_TC_VAR);
871 if (greek_kb_flag < 2)
874 } else if (strchr("!,:;{}", c) && (varcode == LM_TC_TEX||was_macro)) {
875 mathcursor->insert(c, LM_TC_TEX);
877 mathcursor->insert('}', LM_TC_TEX);
880 mathcursor->clearLastCode();
881 // varcode = LM_TC_MIN;
882 } else if (c == '_' && varcode == LM_TC_TEX) {
883 mathcursor->insert(c, LM_TC_SPECIAL);
884 mathcursor->clearLastCode();
885 // varcode = LM_TC_MIN;
886 } else if ('0' <= c && c <= '9' && (varcode == LM_TC_TEX||was_macro)) {
887 mathcursor->MacroModeOpen();
888 mathcursor->clearLastCode();
889 mathcursor->insert(c, LM_TC_MIN);
890 } else if (('0' <= c && c <= '9') || strchr(";:!|[]().,?", c)) {
891 MathTextCodes code = mathcursor->getLastCode();
892 if (code != LM_TC_TEXTRM)
894 mathcursor->insert(c, code);
895 } else if (strchr("+/-*<>=", c)) {
896 MathTextCodes code = mathcursor->getLastCode();
897 if (code != LM_TC_TEXTRM)
899 mathcursor->insert(c, code);
900 } else if (strchr(latex_special_chars, c) && c!= '_') {
901 MathTextCodes code = mathcursor->getLastCode();
902 if (code != LM_TC_TEXTRM)
903 code = LM_TC_SPECIAL;
904 mathcursor->insert(c, code);
905 } else if (c == '_' || c == '^') {
909 mathcursor->Interpret(s);
910 } else if (c == ' ') {
912 short f = (mathcursor->getLastCode()) ?
913 mathcursor->getLastCode() :
914 mathcursor->nextCode();
915 varcode = MathIsAlphaFont(f) ?
916 static_cast<MathTextCodes>(f) :
920 if (varcode == LM_TC_TEXTRM) {
921 mathcursor->insert(c, LM_TC_TEXTRM);
922 } else if (was_macro) {
923 mathcursor->MacroModeClose();
925 int isp = (sp->GetSpace()<5) ? sp->GetSpace()+1: 0;
929 if (!mathcursor->pop())
931 mathcursor->plainRight();
933 } else if (c == '\'' || c == '@') {
934 mathcursor->insert (c, LM_TC_VAR);
935 } else if (c == '\\') {
937 mathcursor->MacroModeClose();
938 bv->owner()->message(_("TeX mode"));
939 mathcursor->setLastCode(LM_TC_TEX);
942 } else if (action == LFUN_MATH_PANEL) {
943 result = UNDISPATCHED;
945 lyxerr << "Closed by action " << action << endl;
951 mathcursor->normalize();
953 if (mathcursor && was_macro != mathcursor->InMacroMode()
955 && action != LFUN_BACKSPACE)
964 if (mathcursor && (mathcursor->Selection() || was_selection))
965 ToggleInsetSelection(bv);
967 if (result == DISPATCHED || result == DISPATCHED_NOUPDATE ||
968 result == UNDISPATCHED)
971 bv->unlockInset(this);
973 return result; // original version
978 /* FIXME: math-greek-toggle seems to work OK, but math-greek doesn't turn
980 bool math_insert_greek(BufferView * bv, char c)
982 if (!bv->available())
990 if (!bv->theLockingInset() || bv->theLockingInset()->IsTextInset()) {
991 int greek_kb_flag_save = greek_kb_flag;
992 InsetFormula * new_inset = new InsetFormula();
993 bv->beforeChange(bv->text);
994 if (!bv->insertInset(new_inset)) {
999 new_inset->Edit(bv, 0, 0, 0);
1000 new_inset->LocalDispatch(bv, LFUN_SELFINSERT, tmp);
1001 if (greek_kb_flag_save < 2) {
1002 bv->unlockInset(new_inset); // bv->theLockingInset());
1003 bv->text->cursorRight(bv, true);
1006 if (bv->theLockingInset()->LyxCode() == Inset::MATH_CODE ||
1007 bv->theLockingInset()->LyxCode() == Inset::MATHMACRO_CODE)
1008 static_cast<InsetFormula*>(bv->theLockingInset())->LocalDispatch(bv, LFUN_SELFINSERT, tmp);
1010 lyxerr << "Math error: attempt to write on a wrong "
1011 "class of inset." << endl;
1017 Inset::Code InsetFormulaBase::LyxCode() const
1019 return Inset::MATH_CODE;
1023 LyXFont const InsetFormulaBase::ConvertFont(LyXFont const & f) const
1025 // We have already discussed what was here
1028 font.setLatex(LyXFont::OFF);
1033 MathInset * InsetFormulaBase::par() const