3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alejandro Aguilar Sierra
9 * Full author contact details are available in file CREDITS.
15 #include "formulabase.h"
17 #include "formulamacro.h"
18 #include "math_support.h"
19 #include "math_arrayinset.h"
20 #include "math_deliminset.h"
21 #include "math_cursor.h"
22 #include "math_factory.h"
23 #include "math_hullinset.h"
24 #include "math_parser.h"
25 #include "math_spaceinset.h"
26 #include "ref_inset.h"
28 #include "BufferView.h"
29 #include "bufferview_funcs.h"
30 #include "dispatchresult.h"
32 #include "funcrequest.h"
38 #include "frontends/LyXView.h"
39 #include "frontends/Dialogs.h"
41 #include "support/std_sstream.h"
42 #include "support/lstrings.h"
43 #include "support/lyxlib.h"
45 using lyx::support::atoi;
46 using lyx::support::split;
47 using lyx::support::token;
53 using std::istringstream;
54 using std::ostringstream;
63 bool openNewInset(LCursor & cur, UpdatableInset * inset)
65 if (!cur.bv().insertInset(inset)) {
69 inset->edit(cur, true);
78 InsetFormulaBase::InsetFormulaBase()
80 // This is needed as long the math parser is not re-entrant
82 //lyxerr << "sizeof MathInset: " << sizeof(MathInset) << endl;
83 //lyxerr << "sizeof MetricsInfo: " << sizeof(MetricsInfo) << endl;
84 //lyxerr << "sizeof MathCharInset: " << sizeof(MathCharInset) << endl;
85 //lyxerr << "sizeof LyXFont: " << sizeof(LyXFont) << endl;
89 // simply scrap this function if you want
90 void InsetFormulaBase::mutateToText()
95 latex(NULL, os, false, false);
96 string str = os.str();
99 LyXText * lt = view_->getLyXText();
100 string::const_iterator cit = str.begin();
101 string::const_iterator end = str.end();
102 for (; cit != end; ++cit)
103 view_->owner()->getIntl()->getTransManager().TranslateAndInsert(*cit, lt);
106 //view_->owner()->dispatch(LFUN_ESCAPE);
111 void InsetFormulaBase::handleFont
112 (LCursor & cur, string const & arg, string const & font)
114 // this whole function is a hack and won't work for incremental font
116 recordUndo(cur, Undo::ATOMIC);
118 if (cur.inset()->asMathInset()->name() == font)
119 mathcursor::handleFont(cur, font);
121 mathcursor::handleNest(cur, createMathInset(font));
122 mathcursor::insert(cur, arg);
127 void InsetFormulaBase::handleFont2(LCursor & cur, string const & arg)
129 recordUndo(cur, Undo::ATOMIC);
132 bv_funcs::string2font(arg, font, b);
133 if (font.color() != LColor::inherit) {
134 MathAtom at = createMathInset("color");
135 asArray(lcolor.getGUIName(font.color()), at.nucleus()->cell(0));
136 mathcursor::handleNest(cur, at, 1);
142 void InsetFormulaBase::validate(LaTeXFeatures &) const
146 string const InsetFormulaBase::editMessage() const
148 return _("Math editor mode");
152 void InsetFormulaBase::insetUnlock(BufferView & bv)
155 if (mathcursor::inMacroMode(bv.cursor()))
156 mathcursor::macroModeClose(bv.cursor());
157 releaseMathCursor(bv.cursor());
160 generatePreview(*bv.buffer());
165 void InsetFormulaBase::getCursorPos(BufferView & bv, int & x, int & y) const
168 mathcursor::getScreenPos(bv.cursor(), x, y);
169 x = mathcursor::targetX(bv.cursor());
172 lyxerr << "InsetFormulaBase::getCursorPos: " << x << ' ' << y << endl;
176 lyxerr << "getCursorPos - should not happen";
181 void InsetFormulaBase::getCursorDim(int & asc, int & desc) const
186 //math_font_max_dim(font_, asc, des);
192 InsetFormulaBase::lfunMouseRelease(LCursor & cur, FuncRequest const & cmd)
195 return DispatchResult(false);
197 //lyxerr << "lfunMouseRelease: buttons: " << cmd.button() << endl;
199 if (cmd.button() == mouse_button::button3) {
200 // try to dispatch to enclosed insets first
201 if (!mathcursor::dispatch(cur, cmd).dispatched()) {
202 // launch math panel for right mouse button
203 lyxerr << "lfunMouseRelease: undispatched: " << cmd.button() << endl;
204 cur.bv().owner()->getDialogs().show("mathpanel");
206 return DispatchResult(true, true);
209 if (cmd.button() == mouse_button::button2) {
211 asArray(cur.bv().getClipboard(), ar);
212 mathcursor::selClear(cur);
213 mathcursor::setScreenPos(cur, cmd.x + xo_, cmd.y + yo_);
214 mathcursor::insert(cur, ar);
216 return DispatchResult(true, true);
219 if (cmd.button() == mouse_button::button1) {
220 // try to dispatch to enclosed insets first
221 mathcursor::dispatch(cur, cmd);
222 cur.bv().stuffClipboard(mathcursor::grabSelection(cur));
223 // try to set the cursor
225 //mathcursor = new MathCursor(bv, this, x == 0);
227 //mathcursor::setScreenPos(x + xo_, y + yo_);
228 return DispatchResult(true, true);
231 return DispatchResult(false);
236 InsetFormulaBase::lfunMousePress(LCursor & cur, FuncRequest const & cmd)
238 //lyxerr << "lfunMousePress: buttons: " << cmd.button() << endl;
240 if (!inMathed() || mathcursor::formula() != this) {
241 lyxerr[Debug::MATHED] << "re-create cursor" << endl;
242 releaseMathCursor(cur);
243 mathcursor::formula_ = this;
246 mathcursor::setScreenPos(cur, cmd.x + xo_, cmd.y + yo_);
249 if (cmd.button() == mouse_button::button3) {
250 mathcursor::dispatch(cur, cmd);
251 return DispatchResult(true, true);
254 if (cmd.button() == mouse_button::button1) {
257 mathcursor::selClear(cur);
258 mathcursor::setScreenPos(cur, cmd.x + xo_, cmd.y + yo_);
259 mathcursor::dispatch(cur, cmd);
260 return DispatchResult(true, true);
264 return DispatchResult(true, true);
269 InsetFormulaBase::lfunMouseMotion(LCursor & cur, FuncRequest const & cmd)
272 return DispatchResult(true, true);
274 if (mathcursor::dispatch(cur, FuncRequest(cmd)).dispatched())
275 return DispatchResult(true, true);
277 // only select with button 1
278 if (cmd.button() != mouse_button::button1)
279 return DispatchResult(true, true);
281 if (abs(cmd.x - first_x) < 2 && abs(cmd.y - first_y) < 2)
282 return DispatchResult(true, true);
287 if (!cur.selection())
288 mathcursor::selStart(cur);
290 mathcursor::setScreenPos(cur, cmd.x + xo_, cmd.y + yo_);
292 return DispatchResult(true, true);
296 void InsetFormulaBase::edit(LCursor & cur, bool /*left*/)
298 lyxerr << "Called FormulaBase::edit" << endl;
299 mathcursor::formula_ = this;
304 cur.push(par().nucleus()->asHullInset());
305 //cur.idx() = left ? 0 : cur.lastidx();
312 void InsetFormulaBase::edit(LCursor & cur, int x, int y)
314 lyxerr << "Called FormulaBase::EDIT with '" << x << ' ' << y << "'" << endl;
315 releaseMathCursor(cur);
320 mathcursor::setScreenPos(cur, x + xo_, y + yo_);
321 cur.push(par().nucleus()->asHullInset());
322 //cur.idx() = left ? 0 : cur.lastidx();
325 // if that is removed, we won't get the magenta box when entering an
326 // inset for the first time
332 InsetFormulaBase::priv_dispatch(LCursor & cur, FuncRequest const & cmd)
334 return par().nucleus()->dispatch(cur, cmd);
336 //lyxerr << "InsetFormulaBase::localDispatch: act: " << cmd.action
337 // << " arg: '" << cmd.argument
338 // << "' x: '" << cmd.x
339 // << " y: '" << cmd.y
340 // << "' button: " << cmd.button() << endl;
343 // delete empty mathbox (LFUN_BACKSPACE and LFUN_DELETE)
344 bool remove_inset = false;
346 switch (cmd.action) {
347 case LFUN_MOUSE_PRESS:
348 //lyxerr << "Mouse single press" << endl;
349 return lfunMousePress(cur, cmd);
350 case LFUN_MOUSE_MOTION:
351 //lyxerr << "Mouse motion" << endl;
352 return lfunMouseMotion(cur, cmd);
353 case LFUN_MOUSE_RELEASE:
354 //lyxerr << "Mouse single release" << endl;
355 return lfunMouseRelease(cur, cmd);
356 case LFUN_MOUSE_DOUBLE:
357 //lyxerr << "Mouse double" << endl;
358 return dispatch(cur, FuncRequest(LFUN_WORDSEL));
363 DispatchResult result(true);
364 string argument = cmd.argument;
366 bool was_macro = mathcursor::inMacroMode(cur);
368 mathcursor::normalize(cur);
371 switch (cmd.action) {
373 case LFUN_MATH_MUTATE:
374 case LFUN_MATH_DISPLAY:
375 case LFUN_MATH_NUMBER:
376 case LFUN_MATH_NONUMBER:
377 case LFUN_CELL_SPLIT:
379 case LFUN_DELETE_LINE_FORWARD:
380 case LFUN_INSERT_LABEL:
381 case LFUN_MATH_EXTERN:
382 case LFUN_TABULAR_FEATURE:
383 case LFUN_PASTESELECTION:
384 case LFUN_MATH_LIMITS:
385 recordUndo(cur, Undo::ATOMIC);
386 mathcursor::dispatch(cur, cmd);
390 mathcursor::home(cur, false);
391 mathcursor::end(cur, true);
394 case LFUN_UP_PARAGRAPHSEL:
395 case LFUN_UP_PARAGRAPH:
396 case LFUN_DOWN_PARAGRAPHSEL:
397 case LFUN_DOWN_PARAGRAPH:
398 result = DispatchResult(true, FINISHED);
402 case LFUN_WORDLEFTSEL:
403 sel = true; // fall through
406 result = mathcursor::home(cur, sel)
407 ? DispatchResult(true, true) : DispatchResult(true, FINISHED);
411 case LFUN_WORDRIGHTSEL:
412 sel = true; // fall through
415 result = mathcursor::end(cur, sel)
416 ? DispatchResult(true, true) : DispatchResult(false, FINISHED_RIGHT);
421 case LFUN_BEGINNINGBUFSEL:
422 case LFUN_BEGINNINGBUF:
423 result = DispatchResult(true, FINISHED);
430 result = DispatchResult(false, FINISHED_RIGHT);
433 case LFUN_CELL_FORWARD:
434 mathcursor::idxNext(cur);
437 case LFUN_CELL_BACKWARD:
438 mathcursor::idxPrev(cur);
441 case LFUN_DELETE_WORD_BACKWARD:
443 recordUndo(cur, Undo::ATOMIC);
444 if (!mathcursor::backspace(cur)) {
445 result = DispatchResult(true, FINISHED);
450 case LFUN_DELETE_WORD_FORWARD:
452 recordUndo(cur, Undo::ATOMIC);
453 if (!mathcursor::erase(cur)) {
454 result = DispatchResult(true, FINISHED);
460 // sprintf(dispatch_buffer, "%d %d",);
461 // DispatchResult= dispatch_buffer;
464 lyxerr << "LFUN_SETXY broken!" << endl;
467 istringstream is(cmd.argument.c_str());
469 mathcursor::setScreenPos(cur, x, y);
475 istringstream is(cmd.argument.c_str());
478 mathcursor::macroModeClose(cur);
479 recordUndo(cur, Undo::ATOMIC);
480 mathcursor::selPaste(cur, n);
485 recordUndo(cur, Undo::DELETE);
486 mathcursor::selCut(cur);
490 mathcursor::selCopy(cur);
494 // Special casing for superscript in case of LyX handling
496 case LFUN_CIRCUMFLEX:
497 if (cmd.argument.empty()) {
498 // do superscript if LyX handles
500 recordUndo(cur, Undo::ATOMIC);
501 mathcursor::script(cur, true);
518 case LFUN_HUNG_UMLAUT:
522 case LFUN_FREEFONT_APPLY:
523 case LFUN_FREEFONT_UPDATE:
524 handleFont2(cur, cmd.argument);
527 case LFUN_BOLD: handleFont(cur, cmd.argument, "mathbf"); break;
528 case LFUN_SANS: handleFont(cur, cmd.argument, "mathsf"); break;
529 case LFUN_EMPH: handleFont(cur, cmd.argument, "mathcal"); break;
530 case LFUN_ROMAN: handleFont(cur, cmd.argument, "mathrm"); break;
531 case LFUN_CODE: handleFont(cur, cmd.argument, "texttt"); break;
532 case LFUN_FRAK: handleFont(cur, cmd.argument, "mathfrak"); break;
533 case LFUN_ITAL: handleFont(cur, cmd.argument, "mathit"); break;
534 case LFUN_NOUN: handleFont(cur, cmd.argument, "mathbb"); break;
535 //case LFUN_FREEFONT_APPLY: handleFont(cur, cmd.argument, "textrm"); break;
536 case LFUN_DEFAULT: handleFont(cur, cmd.argument, "textnormal"); break;
539 if (mathcursor::currentMode(cur) == MathInset::TEXT_MODE)
540 mathcursor::niceInsert(cur, MathAtom(new MathHullInset("simple")));
542 handleFont(cur, cmd.argument, "textrm");
543 //cur.owner()->message(_("math text mode toggled"));
549 recordUndo(cur, Undo::ATOMIC);
550 mathcursor::setSize(arg);
555 case LFUN_INSERT_MATRIX: {
556 recordUndo(cur, Undo::ATOMIC);
561 istringstream is(argument);
562 is >> m >> n >> v_align >> h_align;
566 mathcursor::niceInsert(cur,
567 MathAtom(new MathArrayInset("array", m, n, v_align[0], h_align)));
571 case LFUN_MATH_DELIM: {
572 //lyxerr << "formulabase::LFUN_MATH_DELIM, arg: '" << arg << "'" << endl;
574 string rs = split(cmd.argument, ls, ' ');
575 // Reasonable default values
580 recordUndo(cur, Undo::ATOMIC);
581 mathcursor::handleNest(cur, MathAtom(new MathDelimInset(ls, rs)));
585 case LFUN_SPACE_INSERT:
586 case LFUN_MATH_SPACE:
587 recordUndo(cur, Undo::ATOMIC);
588 mathcursor::insert(cur, MathAtom(new MathSpaceInset(",")));
592 cur.bv().owner()->message(_("Invalid action in math mode!"));
596 case LFUN_EXEC_COMMAND:
597 result = DispatchResult(false);
601 // interpret this as if a backslash was typed
602 recordUndo(cur, Undo::ATOMIC);
603 mathcursor::interpret(cur, '\\');
606 case LFUN_BREAKPARAGRAPH:
607 case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
608 case LFUN_BREAKPARAGRAPH_SKIP:
612 // FIXME: We probably should swap parts of "math-insert" and "self-insert"
613 // handling such that "self-insert" works on "arbitrary stuff" too, and
614 // math-insert only handles special math things like "matrix".
615 case LFUN_INSERT_MATH:
616 recordUndo(cur, Undo::ATOMIC);
617 mathcursor::niceInsert(cur, argument);
621 case LFUN_SELFINSERT:
622 if (!argument.empty()) {
623 recordUndo(cur, Undo::ATOMIC);
624 if (argument.size() == 1)
625 result = mathcursor::interpret(cur, argument[0])
626 ? DispatchResult(true, true) : DispatchResult(false, FINISHED_RIGHT);
628 mathcursor::insert(cur, argument);
634 mathcursor::selClear(cur);
636 result = DispatchResult(false);
639 case LFUN_INSET_TOGGLE:
640 mathcursor::insetToggle(cur);
643 case LFUN_DIALOG_SHOW:
644 result = DispatchResult(false);
647 case LFUN_DIALOG_SHOW_NEW_INSET: {
648 string const & name = argument;
652 data = tmp.createDialogStr(name);
656 result = DispatchResult(false);
658 cur.bv().owner()->getDialogs().show(name, data, 0);
662 case LFUN_INSET_APPLY: {
663 string const name = cmd.getArg(0);
664 InsetBase * base = cur.bv().owner()->getDialogs().getOpenInset(name);
667 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument);
668 result = base->dispatch(cur, fr);
671 if (createMathInset_fromDialogStr(cmd.argument, ar)) {
672 mathcursor::insert(cur, ar);
673 result = DispatchResult(true, true);
675 result = DispatchResult(false);
681 case LFUN_WORD_REPLACE:
682 case LFUN_WORD_FIND: {
684 searchForward(&cur.bv(), cmd.getArg(0), false, false)
685 ? DispatchResult(true, true) : DispatchResult(false);
690 result = DispatchResult(false);
693 if (result == DispatchResult(true, true))
696 mathcursor::normalize(cur);
699 BOOST_ASSERT(inMathed());
701 if (result.dispatched()) {
703 cur.bv().stuffClipboard(mathcursor::grabSelection(cur));
705 releaseMathCursor(cur);
707 cur.bv().owner()->dispatch(FuncRequest(LFUN_DELETE));
710 return result; // original version
715 void InsetFormulaBase::revealCodes(LCursor & cur) const
721 cur.bv().owner()->message(os.str());
723 // write something to the minibuffer
724 // translate to latex
725 mathcursor::markInsert(bv);
728 string str = os.str();
729 mathcursor::markErase(bv);
730 string::size_type pos = 0;
732 for (string::iterator it = str.begin(); it != str.end(); ++it) {
735 else if (*it == '\0') {
737 pos = it - str.begin();
743 res = res.substr(pos - 30);
745 res = res.substr(0, 60);
746 bv.owner()->message(res);
751 InsetOld::Code InsetFormulaBase::lyxCode() const
753 return InsetOld::MATH_CODE;
757 int InsetFormulaBase::ylow() const
759 return yo_ - dim_.asc;
763 int InsetFormulaBase::yhigh() const
765 return yo_ + dim_.des;
769 int InsetFormulaBase::xlow() const
775 int InsetFormulaBase::xhigh() const
777 return xo_ + dim_.wid;
781 /////////////////////////////////////////////////////////////////////
784 bool InsetFormulaBase::searchForward(BufferView * bv, string const & str,
791 static InsetFormulaBase * lastformula = 0;
792 static CursorBase current = CursorBase(ibegin(par().nucleus()));
794 static string laststr;
796 if (lastformula != this || laststr != str) {
797 //lyxerr << "reset lastformula to " << this << endl;
800 current = ibegin(par().nucleus());
802 mathed_parse_cell(ar, str);
806 //lyxerr << "searching '" << str << "' in " << this << ar << endl;
808 for (CursorBase it = current; it != iend(par().nucleus()); increment(it)) {
809 CursorSlice & top = it.back();
810 MathArray const & a = top.asMathInset()->cell(top.idx_);
811 if (a.matchpart(ar, top.pos_)) {
812 mathcursor::formula_ = this;
813 mathcursor::setSelection(bv->cursor(), it, ar.size());
815 top.pos_ += ar.size();
821 //lyxerr << "not found!" << endl;
827 bool InsetFormulaBase::searchBackward(BufferView * bv, string const & what,
830 lyxerr[Debug::MATHED] << "searching backward not implemented in mathed" << endl;
831 return searchForward(bv, what, a, b);
835 bool InsetFormulaBase::display() const
837 return par()->asHullInset() && par()->asHullInset()->display();
841 string InsetFormulaBase::selectionAsString(BufferView & bv) const
843 return inMathed() ? mathcursor::grabSelection(bv.cursor()) : string();
846 /////////////////////////////////////////////////////////////////////
849 void mathDispatchCreation(LCursor & cur, FuncRequest const & cmd,
852 // use selection if available..
854 //if (action == LFUN_MATH_IMPORT_SELECTION)
859 cur.bv().getLyXText()->selectionAsString(*cur.bv().buffer(), false);
862 InsetFormula * f = new InsetFormula;
863 if (openNewInset(cur, f)) {
864 cur.inset()->dispatch(cur, FuncRequest(LFUN_MATH_MUTATE, "simple"));
865 // don't do that also for LFUN_MATH_MODE unless you want end up with
866 // always changing to mathrm when opening an inlined inset
867 // -- I really hate "LyXfunc overloading"...
869 f->dispatch(cur, FuncRequest(LFUN_MATH_DISPLAY));
870 f->dispatch(cur, FuncRequest(LFUN_INSERT_MATH, cmd.argument));
873 // create a macro if we see "\\newcommand" somewhere, and an ordinary
875 InsetFormulaBase * f;
876 if (sel.find("\\newcommand") == string::npos &&
877 sel.find("\\def") == string::npos)
878 f = new InsetFormula(sel);
880 f = new InsetFormulaMacro(sel);
881 cur.bv().getLyXText()->cutSelection(true, false);
882 openNewInset(cur, f);
884 cmd.message(N_("Math editor mode"));
888 void mathDispatch(LCursor & cur, FuncRequest const & cmd)
890 if (!cur.bv().available())
893 switch (cmd.action) {
895 case LFUN_MATH_DISPLAY:
896 mathDispatchCreation(cur, cmd, true);
900 mathDispatchCreation(cur, cmd, false);
903 case LFUN_MATH_IMPORT_SELECTION:
904 mathDispatchCreation(cur, cmd, false);
907 case LFUN_MATH_MACRO:
908 if (cmd.argument.empty())
909 cmd.errorMessage(N_("Missing argument"));
911 string s = cmd.argument;
912 string const s1 = token(s, ' ', 1);
913 int const nargs = s1.empty() ? 0 : atoi(s1);
914 string const s2 = token(s, ' ', 2);
915 string const type = s2.empty() ? "newcommand" : s2;
916 openNewInset(cur, new InsetFormulaMacro(token(s, ' ', 0), nargs, s2));
920 case LFUN_INSERT_MATH:
921 case LFUN_INSERT_MATRIX:
922 case LFUN_MATH_DELIM: {
923 InsetFormula * f = new InsetFormula;
924 if (openNewInset(cur, f)) {
925 cur.inset()->dispatch(cur, FuncRequest(LFUN_MATH_MUTATE, "simple"));
926 cur.inset()->dispatch(cur, cmd);