3 * Purpose: Implementation of common parts of the LyX math insets
4 * Author: Alejandro Aguilar Sierra <asierra@servidor.unam.mx>
5 * Created: January 1996
7 * Copyright: 1996-1998 Alejandro Aguilar Sierra
9 * Version: 0.4, Lyx project.
11 * You are free to use and modify this code under the terms of
12 * the GNU General Public Licence version 2 or later.
19 #include "support/LAssert.h"
22 #pragma implementation
26 #include "formulamacro.h"
27 #include "commandtags.h"
28 #include "BufferView.h"
32 #include "LaTeXFeatures.h"
34 #include "math_support.h"
35 #include "support/lstrings.h"
36 #include "frontends/LyXView.h"
37 #include "frontends/font_metrics.h"
38 #include "frontends/mouse_state.h"
40 #include "math_arrayinset.h"
41 #include "math_charinset.h"
42 #include "math_cursor.h"
43 #include "math_factory.h"
44 #include "math_fontinset.h"
45 #include "math_hullinset.h"
46 #include "math_iterator.h"
47 #include "math_macrotable.h"
48 #include "math_parser.h"
50 #include "math_spaceinset.h"
51 #include "undo_funcs.h"
52 #include "textpainter.h"
53 #include "frontends/Dialogs.h"
61 MathCursor * mathcursor = 0;
72 bool openNewInset(BufferView * bv, UpdatableInset * new_inset)
74 if (!bv->insertInset(new_inset)) {
78 new_inset->edit(bv, 0, 0, mouse_button::none);
87 InsetFormulaBase::InsetFormulaBase()
88 : view_(0), font_(), xo_(0), yo_(0)
90 // This is needed as long the math parser is not re-entrant
91 MathMacroTable::builtinMacros();
92 //lyxerr << "sizeof MathInset: " << sizeof(MathInset) << "\n";
93 //lyxerr << "sizeof(MathMetricsInfo): " << sizeof(MathMetricsInfo) << "\n";
94 //lyxerr << "sizeof(MathCharInset): " << sizeof(MathCharInset) << "\n";
95 //lyxerr << "sizeof(LyXFont): " << sizeof(LyXFont) << "\n";
99 // simply scrap this function if you want
100 void InsetFormulaBase::mutateToText()
103 // translate to latex
105 latex(NULL, os, false, false);
106 string str = os.str();
109 LyXText * lt = view_->getLyXText();
110 string::const_iterator cit = str.begin();
111 string::const_iterator end = str.end();
112 for (; cit != end; ++cit)
113 view_->owner()->getIntl()->getTrans().TranslateAndInsert(*cit, lt);
116 //view_->owner()->getLyXFunc()->dispatch(LFUN_ESCAPE);
121 void InsetFormulaBase::handleFont
122 (BufferView * bv, string const & arg, string const & font)
124 bv->lockedInsetStoreUndo(Undo::EDIT);
125 bool sel = mathcursor->selection();
127 updateLocal(bv, true);
128 mathcursor->handleNest(new MathFontInset(font));
129 for (string::const_iterator it = arg.begin(); it != arg.end(); ++it)
130 mathcursor->insert(*it);
132 updateLocal(bv, false);
136 // Check if uses AMS macros
137 void InsetFormulaBase::validate(LaTeXFeatures &) const
141 void InsetFormulaBase::metrics(BufferView * bv, LyXFont const & f) const
148 void InsetFormulaBase::metrics(BufferView * bv) const
154 mi.base.style = display() ? LM_ST_DISPLAY : LM_ST_TEXT;
155 mi.base.font = font_;
156 mi.base.font.setColor(LColor::math);
161 string const InsetFormulaBase::editMessage() const
163 return _("Math editor mode");
167 void InsetFormulaBase::edit(BufferView * bv, int x, int y, mouse_button::state)
169 if (!bv->lockInset(this))
170 lyxerr[Debug::MATHED] << "Cannot lock inset!!!" << endl;
172 mathcursor = new MathCursor(this, true);
174 mathcursor->setPos(x, y);
175 //lyxerr << "setting pos to " << x << "," << y << "\n";
177 // if that is removed, we won't get the magenta box when entering an
178 // inset for the first time
179 bv->updateInset(this, false);
183 void InsetFormulaBase::edit(BufferView * bv, bool front)
185 if (!bv->lockInset(this))
186 lyxerr[Debug::MATHED] << "Cannot lock inset!!!" << endl;
188 mathcursor = new MathCursor(this, front);
190 bv->updateInset(this, false);
194 void InsetFormulaBase::insetUnlock(BufferView * bv)
197 if (mathcursor->inMacroMode()) {
198 mathcursor->macroModeClose();
199 updateLocal(bv, true);
204 bv->updateInset(this, false);
208 void InsetFormulaBase::getCursorPos(BufferView *, int & x, int & y) const
210 mathcursor->getPos(x, y);
213 //lyxerr << "getCursorPos: " << x << " " << y << "\n";
217 void InsetFormulaBase::toggleInsetCursor(BufferView * bv)
222 if (isCursorVisible())
223 bv->hideLockedInsetCursor();
228 mathcursor->getPos(x, y);
232 math_font_max_dim(font_, asc, des);
233 bv->showLockedInsetCursor(x, y, asc, des);
234 //lyxerr << "toggleInsetCursor: " << x << " " << y << "\n";
237 toggleCursorVisible();
241 void InsetFormulaBase::showInsetCursor(BufferView * bv, bool)
243 if (isCursorVisible())
249 mathcursor->getPos(x, y);
253 math_font_max_dim(font_, asc, des);
254 bv->fitLockedInsetCursor(x, y, asc, des);
255 //lyxerr << "showInsetCursor: x: " << x << " y: " << y << " yo: " << yo_ << "\n";
257 toggleInsetCursor(bv);
261 void InsetFormulaBase::hideInsetCursor(BufferView * bv)
263 if (isCursorVisible())
264 toggleInsetCursor(bv);
268 void InsetFormulaBase::toggleInsetSelection(BufferView * bv)
271 bv->updateInset(this, false);
275 vector<string> const InsetFormulaBase::getLabelList() const
277 return vector<string>();
281 void InsetFormulaBase::updateLocal(BufferView * bv, bool dirty)
284 bv->updateInset(this, dirty);
288 bool InsetFormulaBase::insetButtonRelease(BufferView * bv,
289 int /*x*/, int /*y*/, mouse_button::state button)
291 //lyxerr << "insetButtonRelease: " << x << " " << y << "\n";
297 bv->updateInset(this, false);
299 if (button == mouse_button::button3) {
300 // launch math panel for right mouse button
301 bv->owner()->getDialogs()->showMathPanel();
308 void InsetFormulaBase::insetButtonPress(BufferView * bv,
309 int x, int y, mouse_button::state button)
311 //lyxerr << "insetButtonPress: "
312 // << x << " " << y << " but: " << button << "\n";
319 mathcursor = new MathCursor(this, x == 0);
323 mathcursor->selClear();
324 mathcursor->setPos(x + xo_, y + yo_);
328 lyxerr << "insetButtonPress: 2\n";
331 bv->lockedInsetStoreUndo(Undo::EDIT);
333 mathcursor->selGet(ar);
334 mathcursor->setPos(x + xo_, y + yo_);
336 bv->getLyXText()->selectionAsString(bv->buffer(), false);
337 mathed_parse_cell(ar, sel);
338 mathcursor->insert(ar);
343 // launch math panel for right mouse button
344 bv->owner()->getDialogs()->showMathPanel();
348 if (button == mouse_button::button1 || !mathcursor) {
350 mathcursor = new MathCursor(this, x == 0);
354 mathcursor->selClear();
355 mathcursor->setPos(x + xo_, y + yo_);
358 #warning Never launch a Dialog on "Press" event ONLY on "Release" event!
360 // launch math panel for right mouse button
361 bv->owner()->getDialogs()->showMathPanel();
365 bv->updateInset(this, false);
369 void InsetFormulaBase::insetMotionNotify(BufferView * bv,
370 int x, int y, mouse_button::state)
375 if (abs(x - first_x) < 2 && abs(y - first_y) < 2) {
376 //lyxerr << "insetMotionNotify: ignored\n";
382 if (!mathcursor->selection())
383 mathcursor->selStart();
385 //lyxerr << "insetMotionNotify: " << x + xo_ << ' ' << y + yo_
386 // << ' ' << button << "\n";
388 mathcursor->setPos(x + xo_, y + yo_);
390 bv->updateInset(this, false);
394 UpdatableInset::RESULT
395 InsetFormulaBase::localDispatch(BufferView * bv, kb_action action,
398 //lyxerr << "InsetFormulaBase::localDispatch: act: " << action
399 // << " arg: '" << arg << "' cursor: " << mathcursor << "\n";
404 RESULT result = DISPATCHED;
406 bool was_macro = mathcursor->inMacroMode();
407 bool was_selection = mathcursor->selection();
411 mathcursor->normalize();
416 // --- Cursor Movements ---------------------------------------------
419 sel = true; // fall through...
422 result = mathcursor->right(sel) ? DISPATCHED : FINISHED_RIGHT;
423 //lyxerr << "calling scroll 20\n";
425 updateLocal(bv, false);
426 // write something to the minibuffer
427 //bv->owner()->message(mathcursor->info());
432 sel = true; // fall through
435 result = mathcursor->left(sel) ? DISPATCHED : FINISHED;
436 updateLocal(bv, false);
444 result = mathcursor->up(sel) ? DISPATCHED : FINISHED_UP;
445 updateLocal(bv, false);
453 result = mathcursor->down(sel) ? DISPATCHED : FINISHED_DOWN;
454 updateLocal(bv, false);
461 mathcursor->home(sel);
462 updateLocal(bv, false);
469 mathcursor->end(sel);
470 updateLocal(bv, false);
473 case LFUN_DELETE_LINE_FORWARD:
474 bv->lockedInsetStoreUndo(Undo::DELETE);
475 mathcursor->delLine();
476 updateLocal(bv, true);
480 mathcursor->idxNext();
481 updateLocal(bv, false);
485 mathcursor->idxPrev();
486 updateLocal(bv, false);
490 bv->lockedInsetStoreUndo(Undo::EDIT);
491 mathcursor->splitCell();
492 updateLocal(bv, true);
495 case LFUN_DELETE_WORD_BACKWARD:
497 bv->lockedInsetStoreUndo(Undo::DELETE);
498 mathcursor->backspace();
499 bv->updateInset(this, true);
502 case LFUN_DELETE_WORD_FORWARD:
504 bv->lockedInsetStoreUndo(Undo::DELETE);
506 bv->updateInset(this, true);
510 // sprintf(dispatch_buffer, "%d %d",);
511 // dispatch_result = dispatch_buffer;
514 lyxerr << "LFUN_SETXY broken!\n";
517 istringstream is(arg.c_str());
519 mathcursor->setPos(x, y);
520 updateLocal(bv, false);
526 mathcursor->macroModeClose();
527 bv->lockedInsetStoreUndo(Undo::EDIT);
528 mathcursor->selPaste();
529 updateLocal(bv, true);
533 bv->lockedInsetStoreUndo(Undo::DELETE);
534 mathcursor->selCut();
535 updateLocal(bv, true);
539 mathcursor->selCopy();
542 case LFUN_WORDRIGHTSEL:
543 case LFUN_WORDLEFTSEL:
546 // Special casing for superscript in case of LyX handling
548 case LFUN_CIRCUMFLEX:
550 // do superscript if LyX handles
552 bv->lockedInsetStoreUndo(Undo::EDIT);
553 mathcursor->script(true);
554 updateLocal(bv, true);
570 case LFUN_HUNG_UMLAUT:
574 case LFUN_GREEK_TOGGLE: handleFont(bv, arg, "lyxgreek"); break;
575 case LFUN_BOLD: handleFont(bv, arg, "textbf"); break;
576 case LFUN_SANS: handleFont(bv, arg, "textsf"); break;
577 case LFUN_EMPH: handleFont(bv, arg, "mathcal"); break;
578 case LFUN_ROMAN: handleFont(bv, arg, "mathrm"); break;
579 case LFUN_CODE: handleFont(bv, arg, "texttt"); break;
580 case LFUN_FRAK: handleFont(bv, arg, "mathfrak"); break;
581 case LFUN_ITAL: handleFont(bv, arg, "mathit"); break;
582 case LFUN_NOUN: handleFont(bv, arg, "mathbb"); break;
583 case LFUN_DEFAULT: handleFont(bv, arg, "textnormal"); break;
584 case LFUN_FREE: handleFont(bv, arg, "textrm"); break;
587 handleFont(bv, arg, "lyxgreek1");
589 mathcursor->interpret(arg);
593 if (mathcursor->inMathMode()) {
594 handleFont(bv, arg, "textrm");
596 mathcursor->niceInsert(MathAtom(new MathHullInset(LM_OT_SIMPLE)));
597 updateLocal(bv, true);
599 //bv->owner()->message(_("math text mode toggled"));
602 case LFUN_MATH_LIMITS:
603 bv->lockedInsetStoreUndo(Undo::EDIT);
604 if (mathcursor->toggleLimits())
605 updateLocal(bv, true);
611 bv->lockedInsetStoreUndo(Undo::EDIT);
612 mathcursor->setSize(arg);
613 updateLocal(bv, true);
618 case LFUN_INSERT_MATRIX:
620 bv->lockedInsetStoreUndo(Undo::EDIT);
621 mathcursor->interpret("matrix " + arg);
622 updateLocal(bv, true);
626 case LFUN_SUPERSCRIPT:
629 bv->lockedInsetStoreUndo(Undo::EDIT);
630 mathcursor->script(action == LFUN_SUPERSCRIPT);
631 updateLocal(bv, true);
635 case LFUN_MATH_DELIM:
637 //lyxerr << "formulabase::LFUN_MATH_DELIM, arg: '" << arg << "'\n";
639 string rs = split(arg, ls, ' ');
640 // Reasonable default values
646 bv->lockedInsetStoreUndo(Undo::EDIT);
647 mathcursor->handleDelim(ls, rs);
648 updateLocal(bv, true);
652 case LFUN_PROTECTEDSPACE:
653 case LFUN_MATH_SPACE:
654 bv->lockedInsetStoreUndo(Undo::EDIT);
655 mathcursor->insert(MathAtom(new MathSpaceInset(1)));
656 updateLocal(bv, true);
660 bv->owner()->message(_("Invalid action in math mode!"));
664 case LFUN_MATH_HALIGN:
665 case LFUN_MATH_VALIGN:
666 case LFUN_MATH_ROW_INSERT:
667 case LFUN_MATH_ROW_DELETE:
668 case LFUN_MATH_COLUMN_INSERT:
669 case LFUN_MATH_COLUMN_DELETE:
671 MathInset::idx_type idx = 0;
672 MathGridInset * p = mathcursor ? mathcursor->enclosingGrid(idx) : 0;
674 mathcursor->popToEnclosingGrid();
675 bv->lockedInsetStoreUndo(Undo::EDIT);
676 char align = arg.size() ? arg[0] : 'c';
678 case LFUN_MATH_HALIGN: p->halign(align, p->col(idx)); break;
679 case LFUN_MATH_VALIGN: p->valign(align); break;
680 case LFUN_MATH_ROW_INSERT: p->addRow(p->row(idx)); break;
681 case LFUN_MATH_ROW_DELETE: p->delRow(p->row(idx)); break;
682 case LFUN_MATH_COLUMN_INSERT: p->addFancyCol(p->col(idx)); break;
683 case LFUN_MATH_COLUMN_DELETE: p->delFancyCol(p->col(idx)); break;
686 updateLocal(bv, true);
691 case LFUN_EXEC_COMMAND:
692 result = UNDISPATCHED;
695 case LFUN_BREAKPARAGRAPH:
696 case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
697 //lyxerr << "LFUN ignored\n";
701 // interpret this as if a backslash was typed
702 bv->lockedInsetStoreUndo(Undo::EDIT);
703 mathcursor->interpret('\\');
704 updateLocal(bv, true);
708 case LFUN_INSERT_MATH:
709 case LFUN_SELFINSERT:
711 bv->lockedInsetStoreUndo(Undo::EDIT);
713 result = mathcursor->interpret(arg[0]) ? DISPATCHED : FINISHED_RIGHT;
715 result = mathcursor->interpret(arg) ? DISPATCHED : FINISHED_RIGHT;
716 updateLocal(bv, true);
720 case LFUN_MATH_PANEL:
721 result = UNDISPATCHED;
725 if (mathcursor->selection())
726 mathcursor->selClear();
728 result = UNDISPATCHED;
731 case LFUN_INSET_TOGGLE:
732 mathcursor->insetToggle();
736 result = UNDISPATCHED;
739 mathcursor->normalize();
742 lyx::Assert(mathcursor);
744 if (mathcursor->selection() || was_selection)
745 toggleInsetSelection(bv);
747 if (result == DISPATCHED || result == DISPATCHED_NOUPDATE ||
748 result == UNDISPATCHED)
751 bv->unlockInset(this);
755 return result; // original version
759 void InsetFormulaBase::revealCodes(BufferView * bv) const
763 bv->owner()->message(mathcursor->info());
766 // write something to the minibuffer
767 // translate to latex
768 mathcursor->markInsert();
771 string str = os.str();
772 mathcursor->markErase();
773 string::size_type pos = 0;
775 for (string::iterator it = str.begin(); it != str.end(); ++it) {
778 else if (*it == '\0') {
780 pos = it - str.begin();
786 res = res.substr(pos - 30);
788 res = res.substr(0, 60);
789 bv->owner()->message(res);
794 Inset::Code InsetFormulaBase::lyxCode() const
796 return Inset::MATH_CODE;
800 int InsetFormulaBase::ylow() const
802 return yo_ - ascent(view_, font_);
806 int InsetFormulaBase::yhigh() const
808 return yo_ + descent(view_, font_);
812 int InsetFormulaBase::xlow() const
818 int InsetFormulaBase::xhigh() const
820 return xo_ + width(view_, font_);
824 /////////////////////////////////////////////////////////////////////
827 bool InsetFormulaBase::searchForward(BufferView * bv, string const & str,
833 static InsetFormulaBase * lastformula = 0;
834 static MathIterator current = MathIterator(ibegin(par().nucleus()));
836 static string laststr;
838 if (lastformula != this || laststr != str) {
839 //lyxerr << "reset lastformula to " << this << "\n";
842 current = ibegin(par().nucleus());
844 mathed_parse_cell(ar, str);
848 //lyxerr << "searching '" << str << "' in " << this << ar << endl;
850 for (MathIterator it = current; it != iend(par().nucleus()); ++it) {
851 if (it.cell().matchpart(ar, it.position().pos_)) {
852 mathcursor->setSelection(it.cursor(), ar.size());
855 // I guess some of the following can go
856 bv->toggleSelection(true);
858 updateLocal(bv, true);
865 //lyxerr << "not found!\n";
867 // we have to unlock ourself in this function by default!
868 // don't ask me why...
869 bv->unlockInset(this);
874 bool InsetFormulaBase::searchBackward(BufferView * bv, string const & what,
877 lyxerr << "searching backward not implemented in mathed" << endl;
878 return searchForward(bv, what, a, b);
882 /////////////////////////////////////////////////////////////////////
885 void mathDispatchCreation(BufferView * bv, string const & arg, bool display)
887 if (bv->available()) {
888 // use selection if available..
890 //if (action == LFUN_MATH_IMPORT_SELECTION)
894 string sel = bv->getLyXText()->selectionAsString(bv->buffer(), false);
896 InsetFormulaBase * f;
898 f = new InsetFormula;
899 if (openNewInset(bv, f)) {
900 // don't do that also for LFUN_MATH_MODE unless you want end up with
901 // always changing to mathrm when opening an inlined inset
902 // -- I really hate "LyXfunc overloading"...
904 f->localDispatch(bv, LFUN_MATH_DISPLAY, string());
905 f->localDispatch(bv, LFUN_INSERT_MATH, arg);
908 // create a macro if we see "\\newcommand" somewhere, and an ordinary
910 if (sel.find("\\newcommand") == string::npos &&
911 sel.find("\\def") == string::npos)
913 f = new InsetFormula(sel);
916 if (!mathed_parse_macro(name, sel))
918 f = new InsetFormulaMacro(sel);
920 bv->getLyXText()->cutSelection(bv);
924 bv->owner()->getLyXFunc()->setMessage(N_("Math editor mode"));
928 void mathDispatchMathDisplay(BufferView * bv, string const & arg)
930 mathDispatchCreation(bv, arg, true);
934 void mathDispatchMathMode(BufferView * bv, string const & arg)
936 mathDispatchCreation(bv, arg, false);
940 void mathDispatchMathImportSelection(BufferView * bv, string const & arg)
942 mathDispatchCreation(bv, arg, true);
946 void mathDispatchMathMacro(BufferView * bv, string const & arg)
948 if (bv->available()) {
950 bv->owner()->getLyXFunc()->setErrorMessage(N_("Missing argument"));
953 string const s1 = token(s, ' ', 1);
954 int const na = s1.empty() ? 0 : lyx::atoi(s1);
955 openNewInset(bv, new InsetFormulaMacro(token(s, ' ', 0), na));
961 void mathDispatchMathDelim(BufferView * bv, string const & arg)
963 if (bv->available()) {
964 if (openNewInset(bv, new InsetFormula))
965 bv->theLockingInset()->localDispatch(bv, LFUN_MATH_DELIM, arg);
970 void mathDispatchInsertMatrix(BufferView * bv, string const & arg)
972 if (bv->available()) {
973 if (openNewInset(bv, new InsetFormula))
974 bv->theLockingInset()->localDispatch(bv, LFUN_INSERT_MATRIX, arg);
979 void mathDispatchInsertMath(BufferView * bv, string const & arg)
981 if (bv->available()) {
982 if (arg.size() && arg[0] == '\\') {
983 InsetFormula * f = new InsetFormula(arg);
984 if (!bv->insertInset(f))
986 else if (!mathcursor) // hotfix
987 bv->getLyXText()->cursorRight(bv);
989 mathDispatchMathMode(bv, arg);
995 void mathDispatchGreek(BufferView * bv, string const & arg)
997 if (bv->available()) {
998 InsetFormula * f = new InsetFormula;
999 if (openNewInset(bv, f)) {
1000 bv->theLockingInset()->localDispatch(bv, LFUN_GREEK, arg);
1007 void mathDispatch(BufferView *, kb_action, string const &)