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 + ")";
71 void handleFont(BufferView * bv, MathTextCodes t)
73 if (mathcursor->Selection())
74 bv->lockedInsetStoreUndo(Undo::EDIT);
75 mathcursor->handleFont(t);
82 LyXFont WhichFont(short type, int size)
119 case LM_TC_SPECIAL: //f = Math_Fonts[0]; break;
132 if (type == LM_TC_BSYM) {
145 case LM_ST_SCRIPTSCRIPT:
151 lyxerr << "Math Error: wrong font size: " << size << endl;
155 if (type != LM_TC_TEXTRM)
156 f.setColor(LColor::math);
164 void mathed_init_fonts()
166 Math_Fonts = new LyXFont[8]; //DEC cxx cannot initialize all fonts
169 for (int i = 0 ; i < 8 ; ++i) {
170 Math_Fonts[i] = LyXFont(LyXFont::ALL_SANE);
173 Math_Fonts[0].setShape(LyXFont::ITALIC_SHAPE);
175 Math_Fonts[1].setFamily(LyXFont::SYMBOL_FAMILY);
177 Math_Fonts[2].setFamily(LyXFont::SYMBOL_FAMILY);
178 Math_Fonts[2].setShape(LyXFont::ITALIC_SHAPE);
180 Math_Fonts[3].setSeries(LyXFont::BOLD_SERIES);
182 Math_Fonts[4].setFamily(LyXFont::SANS_FAMILY);
183 Math_Fonts[4].setShape(LyXFont::ITALIC_SHAPE);
185 Math_Fonts[5].setFamily(LyXFont::TYPEWRITER_FAMILY);
187 Math_Fonts[6].setFamily(LyXFont::ROMAN_FAMILY);
189 Math_Fonts[7].setFamily(LyXFont::SANS_FAMILY);
193 // returns the nearest enclosing matrix
194 MathArrayInset * matrixpar(int & idx)
198 static_cast<MathArrayInset *>
199 (mathcursor ? mathcursor->enclosing(LM_OT_MATRIX, idx) : 0);
206 InsetFormulaBase::InsetFormulaBase(MathInset * par)
211 InsetFormulaBase::InsetFormulaBase(InsetFormulaBase const & f)
212 : UpdatableInset(f), par_(static_cast<MathInset *>(f.par_->clone()))
216 InsetFormulaBase::~InsetFormulaBase()
219 #warning leak this for a while...
225 void InsetFormulaBase::write(Buffer const * buf, ostream & os) const
228 latex(buf, os, false, false);
232 int InsetFormulaBase::ascii(Buffer const *, ostream & os, int) const
234 par_->Write(os, false);
239 int InsetFormulaBase::linuxdoc(Buffer const * buf, ostream & os) const
241 return ascii(buf, os, 0);
245 int InsetFormulaBase::docBook(Buffer const * buf, ostream & os) const
247 return ascii(buf, os, 0);
251 // Check if uses AMS macros
252 void InsetFormulaBase::validate(LaTeXFeatures &) const
256 string const InsetFormulaBase::editMessage() const
258 return _("Math editor mode");
262 void InsetFormulaBase::edit(BufferView * bv, int x, int /*y*/, unsigned int)
264 mathcursor = new MathCursor(this);
266 if (!bv->lockInset(this))
267 lyxerr[Debug::MATHED] << "Cannot lock inset!!!" << endl;
269 par_->Metrics(LM_ST_TEXT);
270 bv->updateInset(this, false);
282 void InsetFormulaBase::insetUnlock(BufferView * bv)
285 if (mathcursor->InMacroMode()) {
286 mathcursor->MacroModeClose();
292 bv->updateInset(this, false);
296 void InsetFormulaBase::getCursorPos(BufferView *, int & x, int & y) const
298 mathcursor->GetPos(x, y);
304 void InsetFormulaBase::toggleInsetCursor(BufferView * bv)
309 if (isCursorVisible())
310 bv->hideLockedInsetCursor();
314 mathcursor->GetPos(x, y);
318 LyXFont font = WhichFont(LM_TC_TEXTRM, LM_ST_TEXT);
319 int const asc = lyxfont::maxAscent(font);
320 int const desc = lyxfont::maxDescent(font);
322 bv->showLockedInsetCursor(x, y, asc, desc);
325 toggleCursorVisible();
329 void InsetFormulaBase::showInsetCursor(BufferView * bv, bool)
331 if (!isCursorVisible()) {
335 mathcursor->GetPos(x, y);
338 LyXFont font = WhichFont(LM_TC_TEXTRM, LM_ST_TEXT);
339 int const asc = lyxfont::maxAscent(font);
340 int const desc = lyxfont::maxDescent(font);
341 bv->fitLockedInsetCursor(x, y, asc, desc);
343 toggleInsetCursor(bv);
348 void InsetFormulaBase::hideInsetCursor(BufferView * bv)
350 if (isCursorVisible())
351 toggleInsetCursor(bv);
355 void InsetFormulaBase::toggleInsetSelection(BufferView * bv)
360 bv->updateInset(this, false);
364 vector<string> const InsetFormulaBase::getLabelList() const
366 return std::vector<string>();
370 void InsetFormulaBase::updateLocal(BufferView * bv)
372 par_->Metrics(LM_ST_TEXT);
373 bv->updateInset(this, true);
377 void InsetFormulaBase::insetButtonRelease(BufferView * bv,
378 int x, int y, int /*button*/)
384 mathcursor->SetPos(x, y);
390 bv->updateInset(this, false);
396 void InsetFormulaBase::insetButtonPress(BufferView * bv,
397 int x, int y, int /*button*/)
402 if (mathcursor && mathcursor->Selection()) {
403 mathcursor->SelClear();
404 bv->updateInset(this, false);
409 void InsetFormulaBase::insetMotionNotify(BufferView * bv,
410 int x, int y, int /*button*/)
412 if (sel_x && sel_y && abs(x-sel_x) > 4 && !sel_flag) {
415 mathcursor->SetPos(sel_x + par_->xo(), sel_y + par_->yo());
416 mathcursor->SelStart();
418 mathcursor->GetPos(sel_x, sel_y);
419 } else if (sel_flag) {
423 mathcursor->SetPos(x, y);
425 mathcursor->GetPos(x, y);
426 if (sel_x != x || sel_y != y)
427 bv->updateInset(this, false);
434 void InsetFormulaBase::insetKeyPress(XKeyEvent *)
436 lyxerr[Debug::MATHED]
437 << "Used InsetFormulaBase::InsetKeyPress." << endl;
442 UpdatableInset::RESULT
443 InsetFormulaBase::localDispatch(BufferView * bv, kb_action action,
446 //lyxerr << "InsetFormulaBase::LocalDispatch: act: " << action
447 // << " arg: '" << arg << "' cursor: " << mathcursor << "\n";
448 // extern char *dispatch_result;
453 MathTextCodes varcode = LM_TC_MIN;
454 bool was_macro = mathcursor->InMacroMode();
456 bool space_on = false;
457 bool was_selection = mathcursor->Selection();
458 RESULT result = DISPATCHED;
459 static MathSpaceInset * sp = 0;
463 if (mathcursor->getLastCode() == LM_TC_TEX)
466 mathcursor->normalize();
470 // --- Cursor Movements ---------------------------------------------
473 sel = true; // fall through...
476 result = DISPATCH_RESULT(mathcursor->Right(sel));
482 sel = true; // fall through
485 result = DISPATCH_RESULT(mathcursor->Left(sel));
494 result = DISPATCH_RESULT(mathcursor->Up(sel));
503 result = DISPATCH_RESULT(mathcursor->Down(sel));
510 result = DISPATCHED_NOUPDATE;
515 result = DISPATCHED_NOUPDATE;
518 case LFUN_DELETE_LINE_FORWARD:
519 bv->lockedInsetStoreUndo(Undo::DELETE);
520 mathcursor->DelLine();
525 bv->lockedInsetStoreUndo(Undo::INSERT);
526 mathcursor->idxRight();
531 bv->lockedInsetStoreUndo(Undo::INSERT);
532 mathcursor->idxRight();
537 if (!mathcursor->InMacroMode() && mathcursor->pos() == 0) {
538 bv->lockedInsetStoreUndo(Undo::DELETE);
539 mathcursor->pullArg();
540 bv->updateInset(this, true);
543 if (!mathcursor->Left())
548 bv->lockedInsetStoreUndo(Undo::DELETE);
549 mathcursor->Delete();
550 bv->updateInset(this, true);
554 // sprintf(dispatch_buffer, "%d %d",);
555 // dispatch_result = dispatch_buffer;
559 lyxerr << "LFUN_SETXY broken!\n";
564 istringstream is(arg.c_str());
566 lyxerr << "LFUN_SETXY: x: " << x << " y: " << y << "\n";
568 mathcursor->SetPos(x1 + x, y1 + y);
572 // cursor selection ----------------------------
576 mathcursor->MacroModeClose();
577 bv->lockedInsetStoreUndo(Undo::INSERT);
578 mathcursor->SelPaste();
583 bv->lockedInsetStoreUndo(Undo::DELETE);
584 mathcursor->SelCut();
589 mathcursor->SelCopy();
594 case LFUN_WORDRIGHTSEL:
595 case LFUN_WORDLEFTSEL:
598 // --- accented characters ------------------------------
600 case LFUN_UMLAUT: mathcursor->setAccent(LM_ddot); break;
601 case LFUN_CIRCUMFLEX: mathcursor->setAccent(LM_hat); break;
602 case LFUN_GRAVE: mathcursor->setAccent(LM_grave); break;
603 case LFUN_ACUTE: mathcursor->setAccent(LM_acute); break;
604 case LFUN_TILDE: mathcursor->setAccent(LM_tilde); break;
605 case LFUN_MACRON: mathcursor->setAccent(LM_bar); break;
606 case LFUN_DOT: mathcursor->setAccent(LM_dot); break;
607 case LFUN_CARON: mathcursor->setAccent(LM_check); break;
608 case LFUN_BREVE: mathcursor->setAccent(LM_breve); break;
609 case LFUN_VECTOR: mathcursor->setAccent(LM_vec); break;
613 if (!greek_kb_flag) {
615 bv->owner()->message(_("Math greek mode on"));
621 case LFUN_GREEK_TOGGLE:
622 greek_kb_flag = greek_kb_flag ? 0 : 2;
624 bv->owner()->message(_("Math greek keyboard on"));
626 bv->owner()->message(_("Math greek keyboard off"));
630 case LFUN_BOLD: handleFont(bv, LM_TC_BF); break;
631 case LFUN_SANS: handleFont(bv, LM_TC_SF); break;
632 case LFUN_EMPH: handleFont(bv, LM_TC_CAL); break;
633 case LFUN_ROMAN: handleFont(bv, LM_TC_RM); break;
634 case LFUN_CODE: handleFont(bv, LM_TC_TT); break;
635 case LFUN_DEFAULT: handleFont(bv, LM_TC_VAR); break;
638 handleFont(bv, LM_TC_TEXTRM);
639 //bv->owner()->message(_("math text mode toggled"));
644 if (!mathcursor->Selection()) {
645 mathcursor->handleFont(LM_TC_TEX);
646 //bv->owner()->message(_("TeX mode toggled"));
651 case LFUN_MATH_LIMITS:
652 bv->lockedInsetStoreUndo(Undo::INSERT);
653 if (mathcursor->toggleLimits())
659 bv->lockedInsetStoreUndo(Undo::INSERT);
660 latexkeys const * l = in_word_set(arg);
661 mathcursor->SetSize(MathStyles(l ? l->id : static_cast<unsigned int>(-1)));
666 case LFUN_INSERT_MATH:
668 bv->lockedInsetStoreUndo(Undo::INSERT);
669 mathcursor->Interpret(arg);
674 case LFUN_INSERT_MATRIX:
676 bv->lockedInsetStoreUndo(Undo::INSERT);
681 istringstream is(arg.c_str());
682 is >> m >> n >> v_align >> h_align;
683 MathArrayInset * p = new MathArrayInset(m, n);
684 p->valign(v_align[0]);
686 mathcursor->insert(p);
691 case LFUN_MATH_DELIM:
693 bv->lockedInsetStoreUndo(Undo::INSERT);
696 static const string vdelim("(){}[]./|");
697 lyxerr << "formulabase::LFUN_MATH_DELIM, arg: '" << arg << "'\n";
702 istringstream is(arg.c_str());
706 lyxerr << "formulabase::LFUN_MATH_DELIM, lt: '" << lt << "'\n";
707 lyxerr << "formulabase::LFUN_MATH_DELIM, rt: '" << rt << "'\n";
710 latexkeys const * l = in_word_set(lt);
713 } else if (vdelim.find(lt[0]) != string::npos)
717 latexkeys const * l = in_word_set(rt);
720 } else if (vdelim.find(rt[0]) != string::npos)
723 if (mathcursor->selection) {
724 MathDelimInset * p = new MathDelimInset(ilt, irt);
726 mathcursor->selArray(ar);
727 lyxerr << "selarray: " << ar << "\n";
729 mathcursor->insert(p);
731 mathcursor->insert(new MathDelimInset(ilt, irt));
737 case LFUN_PROTECTEDSPACE:
738 bv->lockedInsetStoreUndo(Undo::INSERT);
739 mathcursor->insert(new MathSpaceInset(1));
745 bv->owner()->message(_("Invalid action in math mode!"));
749 case LFUN_MATH_HALIGN:
751 bv->lockedInsetStoreUndo(Undo::INSERT);
752 lyxerr << "handling halign '" << arg << "'\n";
754 MathArrayInset * p = matrixpar(idx);
757 p->halign(arg.size() ? arg[0] : 'c', p->col(idx));
762 case LFUN_MATH_VALIGN:
764 bv->lockedInsetStoreUndo(Undo::INSERT);
765 lyxerr << "handling valign '" << arg << "'\n";
767 MathArrayInset * p = matrixpar(idx);
770 p->valign(arg.size() ? arg[0] : 'c');
775 case LFUN_MATH_ROW_INSERT:
777 bv->lockedInsetStoreUndo(Undo::INSERT);
779 MathArrayInset * p = matrixpar(idx);
780 lyxerr << " calling LFUN_MATH_ROW_INSERT on " << p << endl;
783 p->addRow(p->row(idx));
788 case LFUN_MATH_ROW_DELETE:
790 bv->lockedInsetStoreUndo(Undo::INSERT);
792 MathArrayInset * p = matrixpar(idx);
793 lyxerr << " calling LFUN_MATH_ROW_DELETE on " << p << endl;
796 p->delRow(p->row(idx));
801 case LFUN_MATH_COLUMN_INSERT:
803 bv->lockedInsetStoreUndo(Undo::INSERT);
805 MathArrayInset * p = matrixpar(idx);
808 p->addCol(p->col(idx));
813 case LFUN_MATH_COLUMN_DELETE:
815 bv->lockedInsetStoreUndo(Undo::INSERT);
817 MathArrayInset * p = matrixpar(idx);
820 p->delCol(p->col(idx));
825 case LFUN_EXEC_COMMAND:
826 result = UNDISPATCHED;
830 if ((action == -1 || action == LFUN_SELFINSERT) && !arg.empty()) {
831 unsigned char c = arg[0];
832 bv->lockedInsetStoreUndo(Undo::INSERT);
834 if (c == ' ' && mathcursor->getAccent() == LM_hat) {
836 mathcursor->setAccent(0);
839 if (c == 0) { // Dead key, do nothing
840 //lyxerr << "deadkey" << endl;
845 if (mathcursor->getLastCode() == LM_TC_TEX) {
846 mathcursor->MacroModeOpen();
847 mathcursor->clearLastCode();
849 } else if (!varcode) {
850 short f = mathcursor->getLastCode() ?
851 mathcursor->getLastCode() :
852 mathcursor->nextCode();
853 varcode = MathIsAlphaFont(f) ?
854 static_cast<MathTextCodes>(f) :
858 // lyxerr << "Varcode << vardoce;
859 MathTextCodes char_code = varcode;
862 {'A', 'B', 'X', 0 , 'E', 0 , 0 , 'H', 'I', 0 ,
863 'K', 0 , 'M', 'N', 'O', 0 , 0 , 'P', 0 , 'T',
864 'Y', 0, 0, 0, 0 , 'Z' };
866 if ('A' <= c && c <= 'Z' && greek[c - 'A']) {
867 char_code = LM_TC_RM;
870 char_code = LM_TC_SYMB;
873 mathcursor->insert(c, char_code);
875 if (greek_kb_flag && char_code == LM_TC_RM )
876 mathcursor->setLastCode(LM_TC_VAR);
880 if (greek_kb_flag < 2)
883 } else if (strchr("!,:;{}", c) && (varcode == LM_TC_TEX||was_macro)) {
884 mathcursor->insert(c, LM_TC_TEX);
886 mathcursor->insert('}', LM_TC_TEX);
889 mathcursor->clearLastCode();
890 // varcode = LM_TC_MIN;
891 } else if (c == '_' && varcode == LM_TC_TEX) {
892 mathcursor->insert(c, LM_TC_SPECIAL);
893 mathcursor->clearLastCode();
894 // varcode = LM_TC_MIN;
895 } else if ('0' <= c && c <= '9' && (varcode == LM_TC_TEX||was_macro)) {
896 mathcursor->MacroModeOpen();
897 mathcursor->clearLastCode();
898 mathcursor->insert(c, LM_TC_MIN);
899 } else if (('0' <= c && c <= '9') || strchr(";:!|[]().,?", c)) {
900 MathTextCodes code = mathcursor->getLastCode();
901 if (code != LM_TC_TEXTRM)
903 mathcursor->insert(c, code);
904 } else if (strchr("+/-*<>=", c)) {
905 MathTextCodes code = mathcursor->getLastCode();
906 if (code != LM_TC_TEXTRM)
908 mathcursor->insert(c, code);
909 } else if (strchr(latex_special_chars, c) && c!= '_') {
910 MathTextCodes code = mathcursor->getLastCode();
911 if (code != LM_TC_TEXTRM)
912 code = LM_TC_SPECIAL;
913 mathcursor->insert(c, code);
914 } else if (c == '_' || c == '^') {
918 mathcursor->Interpret(s);
919 } else if (c == ' ') {
921 short f = (mathcursor->getLastCode()) ?
922 mathcursor->getLastCode() :
923 mathcursor->nextCode();
924 varcode = MathIsAlphaFont(f) ?
925 static_cast<MathTextCodes>(f) :
929 if (varcode == LM_TC_TEXTRM) {
930 mathcursor->insert(c, LM_TC_TEXTRM);
931 } else if (was_macro) {
932 mathcursor->MacroModeClose();
934 int isp = (sp->GetSpace()<5) ? sp->GetSpace()+1: 0;
938 if (!mathcursor->pop())
940 mathcursor->plainRight();
942 } else if (c == '\'' || c == '@') {
943 mathcursor->insert (c, LM_TC_VAR);
944 } else if (c == '\\') {
946 mathcursor->MacroModeClose();
947 bv->owner()->message(_("TeX mode"));
948 mathcursor->setLastCode(LM_TC_TEX);
951 } else if (action == LFUN_MATH_PANEL) {
952 result = UNDISPATCHED;
954 lyxerr << "Closed by action " << action << endl;
960 mathcursor->normalize();
962 if (mathcursor && was_macro != mathcursor->InMacroMode()
964 && action != LFUN_BACKSPACE)
973 if (mathcursor && (mathcursor->Selection() || was_selection))
974 toggleInsetSelection(bv);
976 if (result == DISPATCHED || result == DISPATCHED_NOUPDATE ||
977 result == UNDISPATCHED)
980 bv->unlockInset(this);
982 return result; // original version
987 /* FIXME: math-greek-toggle seems to work OK, but math-greek doesn't turn
989 bool math_insert_greek(BufferView * bv, char c)
991 if (!bv->available())
999 if (!bv->theLockingInset() || bv->theLockingInset()->isTextInset()) {
1000 int greek_kb_flag_save = greek_kb_flag;
1001 InsetFormula * new_inset = new InsetFormula();
1002 bv->beforeChange(bv->text);
1003 if (!bv->insertInset(new_inset)) {
1008 new_inset->edit(bv, 0, 0, 0);
1009 new_inset->localDispatch(bv, LFUN_SELFINSERT, tmp);
1010 if (greek_kb_flag_save < 2) {
1011 bv->unlockInset(new_inset); // bv->theLockingInset());
1012 bv->text->cursorRight(bv, true);
1015 if (bv->theLockingInset()->lyxCode() == Inset::MATH_CODE ||
1016 bv->theLockingInset()->lyxCode() == Inset::MATHMACRO_CODE)
1017 static_cast<InsetFormula*>(bv->theLockingInset())->localDispatch(bv, LFUN_SELFINSERT, tmp);
1019 lyxerr << "Math error: attempt to write on a wrong "
1020 "class of inset." << endl;
1026 Inset::Code InsetFormulaBase::lyxCode() const
1028 return Inset::MATH_CODE;
1032 LyXFont const InsetFormulaBase::convertFont(LyXFont const & f) const
1034 // We have already discussed what was here
1037 font.setLatex(LyXFont::OFF);
1042 MathInset * InsetFormulaBase::par() const