2 * \file math_nestinset.C
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * Full author contact details are available in file CREDITS.
13 #include "math_nestinset.h"
15 #include "math_arrayinset.h"
16 #include "math_data.h"
17 #include "math_deliminset.h"
18 #include "math_factory.h"
19 #include "math_hullinset.h"
20 #include "math_mathmlstream.h"
21 #include "math_parser.h"
22 #include "math_spaceinset.h"
23 #include "math_support.h"
24 #include "math_mboxinset.h"
26 #include "BufferView.h"
27 #include "bufferview_funcs.h"
30 #include "dispatchresult.h"
31 #include "funcrequest.h"
36 #include "support/std_sstream.h"
37 #include "support/lstrings.h"
39 #include "frontends/Dialogs.h"
40 #include "frontends/LyXView.h"
41 #include "frontends/Painter.h"
46 using std::istringstream;
49 MathNestInset::MathNestInset(idx_type nargs)
50 : cells_(nargs), lock_(false)
54 MathInset::idx_type MathNestInset::nargs() const
60 MathArray & MathNestInset::cell(idx_type i)
66 MathArray const & MathNestInset::cell(idx_type i) const
72 void MathNestInset::getCursorPos(CursorSlice const & cur,
73 int & x, int & y) const
75 BOOST_ASSERT(cur.inset() == this);
76 MathArray const & ar = cur.cell();
77 x = ar.xo() + ar.pos2x(cur.pos());
79 // move cursor visually into empty cells ("blue rectangles");
80 if (cur.cell().empty())
85 void MathNestInset::substitute(MathMacro const & m)
87 for (idx_type i = 0; i < nargs(); ++i)
88 cell(i).substitute(m);
92 void MathNestInset::metrics(MetricsInfo const & mi) const
95 for (idx_type i = 0; i < nargs(); ++i)
100 bool MathNestInset::idxNext(LCursor & cur) const
102 BOOST_ASSERT(cur.inset() == this);
103 if (cur.idx() == cur.lastidx())
111 bool MathNestInset::idxRight(LCursor & cur) const
117 bool MathNestInset::idxPrev(LCursor & cur) const
119 BOOST_ASSERT(cur.inset() == this);
123 cur.pos() = cur.lastpos();
128 bool MathNestInset::idxLeft(LCursor & cur) const
134 bool MathNestInset::idxFirst(LCursor & cur) const
136 BOOST_ASSERT(cur.inset() == this);
145 bool MathNestInset::idxLast(LCursor & cur) const
147 BOOST_ASSERT(cur.inset() == this);
150 cur.idx() = cur.lastidx();
151 cur.pos() = cur.lastpos();
156 bool MathNestInset::idxHome(LCursor & cur) const
158 BOOST_ASSERT(cur.inset() == this);
166 bool MathNestInset::idxEnd(LCursor & cur) const
168 BOOST_ASSERT(cur.inset() == this);
169 if (cur.lastpos() == cur.lastpos())
171 cur.pos() = cur.lastpos();
176 void MathNestInset::dump() const
178 WriteStream os(lyxerr);
179 os << "---------------------------------------------\n";
182 for (idx_type i = 0; i < nargs(); ++i)
183 os << cell(i) << "\n";
184 os << "---------------------------------------------\n";
188 //void MathNestInset::draw(PainterInfo & pi, int x, int y) const
189 void MathNestInset::draw(PainterInfo &, int, int) const
193 pi.pain.fillRectangle(x, y - ascent(), width(), height(),
199 void MathNestInset::drawSelection(PainterInfo & pi, int x, int y) const
201 // this should use the x/y values given, not the cached values
202 LCursor & cur = pi.base.bv->cursor();
203 if (!cur.selection())
205 if (cur.inset() != this)
207 CursorSlice & s1 = cur.selBegin();
208 CursorSlice & s2 = cur.selEnd();
209 if (s1.idx() == s2.idx()) {
210 MathArray const & c = s1.cell();
211 lyxerr << "###### c.xo(): " << c.xo() << " c.yo(): " << c.yo() << endl;
212 int x1 = c.xo() + c.pos2x(s1.pos());
213 int y1 = c.yo() - c.ascent();
214 int x2 = c.xo() + c.pos2x(s2.pos());
215 int y2 = c.yo() + c.descent();
216 //pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
217 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::red);
219 for (idx_type i = 0; i < nargs(); ++i) {
220 if (idxBetween(i, s1.idx(), s2.idx())) {
221 MathArray const & c = cell(i);
223 int y1 = c.yo() - c.ascent();
224 int x2 = c.xo() + c.width();
225 int y2 = c.yo() + c.descent();
226 //pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
227 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::red);
234 void MathNestInset::validate(LaTeXFeatures & features) const
236 for (idx_type i = 0; i < nargs(); ++i)
237 cell(i).validate(features);
241 void MathNestInset::replace(ReplaceData & rep)
243 for (idx_type i = 0; i < nargs(); ++i)
244 cell(i).replace(rep);
248 bool MathNestInset::contains(MathArray const & ar) const
250 for (idx_type i = 0; i < nargs(); ++i)
251 if (cell(i).contains(ar))
257 bool MathNestInset::lock() const
263 void MathNestInset::lock(bool l)
269 bool MathNestInset::isActive() const
275 MathArray MathNestInset::glue() const
278 for (size_t i = 0; i < nargs(); ++i)
284 void MathNestInset::write(WriteStream & os) const
286 os << '\\' << name().c_str();
287 for (size_t i = 0; i < nargs(); ++i)
288 os << '{' << cell(i) << '}';
290 os.pendingSpace(true);
291 if (lock_ && !os.latex()) {
293 os.pendingSpace(true);
298 void MathNestInset::normalize(NormalStream & os) const
300 os << '[' << name().c_str();
301 for (size_t i = 0; i < nargs(); ++i)
302 os << ' ' << cell(i);
307 void MathNestInset::notifyCursorLeaves(idx_type idx)
309 cell(idx).notifyCursorLeaves();
313 void MathNestInset::handleFont
314 (LCursor & cur, string const & arg, string const & font)
316 // this whole function is a hack and won't work for incremental font
318 //recordUndo(cur, Undo::ATOMIC);
320 if (cur.inset()->asMathInset()->name() == font)
321 cur.handleFont(font);
323 cur.handleNest(createMathInset(font));
329 void MathNestInset::handleFont2(LCursor & cur, string const & arg)
331 //recordUndo(cur, Undo::ATOMIC);
334 bv_funcs::string2font(arg, font, b);
335 if (font.color() != LColor::inherit) {
336 MathAtom at = createMathInset("color");
337 asArray(lcolor.getGUIName(font.color()), at.nucleus()->cell(0));
338 cur.handleNest(at, 1);
344 MathNestInset::priv_dispatch(LCursor & cur, FuncRequest const & cmd)
346 lyxerr << "*** MathNestInset: request: " << cmd << std::endl;
347 //lyxerr << "InsetFormulaBase::localDispatch: act: " << cmd.action
348 // << " arg: '" << cmd.argument
349 // << "' x: '" << cmd.x
350 // << " y: '" << cmd.y
351 // << "' button: " << cmd.button() << endl;
353 switch (cmd.action) {
356 if (!cmd.argument.empty()) {
358 mathed_parse_cell(ar, cmd.argument);
359 cur.cell().insert(cur.pos(), ar);
360 cur.pos() += ar.size();
362 return DispatchResult(true, true);
366 istringstream is(cmd.argument.c_str());
369 cur.macroModeClose();
370 //recordUndo(cur, Undo::ATOMIC);
372 return DispatchResult(true, true);
376 case LFUN_PASTESELECTION:
377 return dispatch(cur, FuncRequest(LFUN_PASTE, cur.bv().getClipboard()));
379 case LFUN_MOUSE_PRESS:
380 if (cmd.button() == mouse_button::button2)
381 return priv_dispatch(cur, FuncRequest(LFUN_PASTESELECTION));
382 return DispatchResult(false);
385 cur.selection() = true; // fall through...
388 DispatchResult(true, true) : DispatchResult(false, FINISHED_RIGHT);
389 //lyxerr << "calling scroll 20" << endl;
390 //scroll(&cur.bv(), 20);
391 // write something to the minibuffer
392 //cur.bv().owner()->message(cur.info());
395 cur.selection() = true; // fall through
398 DispatchResult(true, true) : DispatchResult(false, FINISHED);
401 cur.selection() = true; // fall through
404 DispatchResult(true, true) : DispatchResult(false, FINISHED_UP);
407 cur.selection() = true; // fall through
410 DispatchResult(true, true) : DispatchResult(false, FINISHED_DOWN);
414 cur.selection() = true;
416 return DispatchResult(true, true);
418 case LFUN_UP_PARAGRAPHSEL:
419 case LFUN_UP_PARAGRAPH:
420 case LFUN_DOWN_PARAGRAPHSEL:
421 case LFUN_DOWN_PARAGRAPH:
422 return DispatchResult(true, FINISHED);
425 case LFUN_WORDLEFTSEL:
426 cur.selection() = true; // fall through
430 ? DispatchResult(true, true) : DispatchResult(true, FINISHED);
433 case LFUN_WORDRIGHTSEL:
434 cur.selection() = true; // fall through
438 ? DispatchResult(true, true) : DispatchResult(false, FINISHED_RIGHT);
442 case LFUN_BEGINNINGBUFSEL:
443 case LFUN_BEGINNINGBUF:
444 return DispatchResult(true, FINISHED);
450 return DispatchResult(false, FINISHED_RIGHT);
452 case LFUN_CELL_FORWARD:
453 cur.inset()->idxNext(cur);
454 return DispatchResult(true, true);
456 case LFUN_CELL_BACKWARD:
457 cur.inset()->idxPrev(cur);
458 return DispatchResult(true, true);
460 case LFUN_DELETE_WORD_BACKWARD:
462 //recordUndo(cur, Undo::ATOMIC);
464 return DispatchResult(true, true);
466 case LFUN_DELETE_WORD_FORWARD:
468 //recordUndo(cur, Undo::ATOMIC);
470 return DispatchResult(true, FINISHED);
473 if (!cur.selection())
474 return DispatchResult(true, true);
476 return DispatchResult(false);
478 case LFUN_INSET_TOGGLE:
480 return DispatchResult(true, true);
482 case LFUN_SELFINSERT:
483 if (!cmd.argument.empty()) {
484 //recordUndo(cur, Undo::ATOMIC);
485 if (cmd.argument.size() == 1) {
486 if (cur.interpret(cmd.argument[0]))
487 return DispatchResult(true, true);
489 return DispatchResult(false, FINISHED_RIGHT);
491 cur.insert(cmd.argument);
493 return DispatchResult(false, FINISHED_RIGHT);
498 // this needs to be incorporated
500 //lyxerr << "InsetFormulaBase::localDispatch: act: " << cmd.action
501 // << " arg: '" << cmd.argument
502 // << "' x: '" << cmd.x
503 // << " y: '" << cmd.y
504 // << "' button: " << cmd.button() << endl;
506 // delete empty mathbox (LFUN_BACKSPACE and LFUN_DELETE)
507 bool remove_inset = false;
509 switch (cmd.action) {
510 case LFUN_MOUSE_PRESS:
511 //lyxerr << "Mouse single press" << endl;
512 return lfunMousePress(cur, cmd);
513 case LFUN_MOUSE_MOTION:
514 //lyxerr << "Mouse motion" << endl;
515 return lfunMouseMotion(cur, cmd);
516 case LFUN_MOUSE_RELEASE:
517 //lyxerr << "Mouse single release" << endl;
518 return lfunMouseRelease(cur, cmd);
519 case LFUN_MOUSE_DOUBLE:
520 //lyxerr << "Mouse double" << endl;
521 return dispatch(cur, FuncRequest(LFUN_WORDSEL));
526 DispatchResult result(true);
527 bool was_macro = cur.inMacroMode();
532 switch (cmd.action) {
534 case LFUN_MATH_LIMITS:
535 //recordUndo(cur, Undo::ATOMIC);
541 // sprintf(dispatch_buffer, "%d %d",);
542 // DispatchResult= dispatch_buffer;
545 lyxerr << "LFUN_SETXY broken!" << endl;
548 istringstream is(cmd.argument.c_str());
550 cur.setScreenPos(x, y);
551 return DispatchResult(true, true);
555 //recordUndo(cur, Undo::DELETE);
557 return DispatchResult(true, true);
561 return DispatchResult(true, true);
564 // Special casing for superscript in case of LyX handling
566 case LFUN_CIRCUMFLEX:
567 if (cmd.argument.empty()) {
568 // do superscript if LyX handles
570 //recordUndo(cur, Undo::ATOMIC);
573 return DispatchResult(true, true);
588 case LFUN_HUNG_UMLAUT:
589 return DispatchResult(true, true);
592 case LFUN_FREEFONT_APPLY:
593 case LFUN_FREEFONT_UPDATE:
594 handleFont2(cur, cmd.argument);
595 return DispatchResult(true, true);
598 handleFont(cur, cmd.argument, "mathbf");
599 return DispatchResult(true, true);
601 handleFont(cur, cmd.argument, "mathsf");
602 return DispatchResult(true, true);
604 handleFont(cur, cmd.argument, "mathcal");
605 return DispatchResult(true, true);
607 handleFont(cur, cmd.argument, "mathrm");
608 return DispatchResult(true, true);
610 handleFont(cur, cmd.argument, "texttt");
611 return DispatchResult(true, true);
613 handleFont(cur, cmd.argument, "mathfrak");
614 return DispatchResult(true, true);
616 handleFont(cur, cmd.argument, "mathit");
617 return DispatchResult(true, true);
619 handleFont(cur, cmd.argument, "mathbb");
620 return DispatchResult(true, true);
621 //case LFUN_FREEFONT_APPLY:
622 handleFont(cur, cmd.argument, "textrm");
623 return DispatchResult(true, true);
625 handleFont(cur, cmd.argument, "textnormal");
626 return DispatchResult(true, true);
630 cur.macroModeClose();
632 cur.plainInsert(MathAtom(new MathMBoxInset(cur.bv())));
634 cur.pushLeft(cur.nextInset());
636 if (cur.currentMode() == InsetBase::TEXT_MODE)
637 cur.niceInsert(MathAtom(new MathHullInset("simple")));
639 handleFont(cur, cmd.argument, "textrm");
640 //cur.owner()->message(_("math text mode toggled"));
642 return DispatchResult(true, true);
647 //recordUndo(cur, Undo::ATOMIC);
651 return DispatchResult(true, true);
653 case LFUN_INSERT_MATRIX: {
654 //recordUndo(cur, Undo::ATOMIC);
659 istringstream is(cmd.argument);
660 is >> m >> n >> v_align >> h_align;
667 MathAtom(new MathArrayInset("array", m, n, v_align[0], h_align)));
668 return DispatchResult(true, true);
671 case LFUN_MATH_DELIM: {
672 //lyxerr << "formulabase::LFUN_MATH_DELIM, arg: '" << arg << "'" << endl;
674 string rs = lyx::support::split(cmd.argument, ls, ' ');
675 // Reasonable default values
680 //recordUndo(cur, Undo::ATOMIC);
681 cur.handleNest(MathAtom(new MathDelimInset(ls, rs)));
682 return DispatchResult(true, true);
685 case LFUN_SPACE_INSERT:
686 case LFUN_MATH_SPACE:
687 //recordUndo(cur, Undo::ATOMIC);
688 cur.insert(MathAtom(new MathSpaceInset(",")));
689 return DispatchResult(true, true);
693 //cur.bv().owner()->message(_("Invalid action in math mode!"));
694 return DispatchResult(true, true);
697 // interpret this as if a backslash was typed
698 //recordUndo(cur, Undo::ATOMIC);
700 return DispatchResult(true, true);
703 case LFUN_BREAKPARAGRAPH:
704 case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
705 case LFUN_BREAKPARAGRAPH_SKIP:
707 //recordUndo(cur, Undo::ATOMIC);
708 cur.niceInsert(argument);
709 return DispatchResult(true, true);
712 // FIXME: We probably should swap parts of "math-insert" and "self-insert"
713 // handling such that "self-insert" works on "arbitrary stuff" too, and
714 // math-insert only handles special math things like "matrix".
715 case LFUN_INSERT_MATH:
716 //recordUndo(cur, Undo::ATOMIC);
717 cur.niceInsert(cmd.argument);
718 return DispatchResult(true, true);
720 case LFUN_DIALOG_SHOW:
721 return DispatchResult(false);
723 case LFUN_DIALOG_SHOW_NEW_INSET: {
724 string const & name = cmd.argument;
729 data = tmp.createDialogStr(name);
733 return DispatchResult(false);
734 cur.bv().owner()->getDialogs().show(name, data, 0);
735 return DispatchResult(true, true);
738 case LFUN_INSET_APPLY: {
739 string const name = cmd.getArg(0);
740 InsetBase * base = cur.bv().owner()->getDialogs().getOpenInset(name);
743 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument);
744 return base->dispatch(cur, fr);
747 if (createMathInset_fromDialogStr(cmd.argument, ar)) {
749 return DispatchResult(true, true);
751 return DispatchResult(false);
757 case LFUN_WORD_REPLACE:
760 searchForward(&cur.bv(), cmd.getArg(0), false, false)
761 ? DispatchResult(true, true) : DispatchResult(false);
763 case LFUN_INSERT_MATH:
764 case LFUN_INSERT_MATRIX:
765 case LFUN_MATH_DELIM: {
766 MathHullInset * f = new MathHullInset;
767 if (openNewInset(cur, f)) {
768 cur.inset()->dispatch(cur, FuncRequest(LFUN_MATH_MUTATE, "simple"));
769 cur.inset()->dispatch(cur, cmd);
771 return DispatchResult(true, true);
777 BOOST_ASSERT(cur.inMathed());
779 if (result.dispatched()) {
781 cur.bv().stuffClipboard(cur.grabSelection());
783 cur.releaseMathCursor();
785 cur.bv().owner()->dispatch(FuncRequest(LFUN_DELETE));
788 return result; // original version
792 return MathDimInset::priv_dispatch(cur, cmd);
797 void MathNestInset::edit(LCursor & cur, int x, int y)
799 lyxerr << "Called MathNestInset::edit with '" << x << ' ' << y << "'" << endl;
802 int dist_min = 1000000;
803 for (idx_type i = 0; i < nargs(); ++i) {
804 int d = cell(i).dist(x, y);
810 MathArray & ar = cell(idx_min);
813 cur.pos() = ar.x2pos(x - ar.xo());
814 lyxerr << "found cell : " << idx_min << " pos: " << cur.pos() << endl;
817 for (pos_type i = 0, n = ar.size(); i < n; ++i)
818 if (ar[i]->covers(x, y))
819 ar[i].nucleus()->edit(cur, x, y);