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())
82 lyxerr << "MathNestInset::getCursorPos: cur: " << cur
83 << " x: " << x << " y: " << y << endl;
84 BOOST_ASSERT(x < 100000);
88 void MathNestInset::substitute(MathMacro const & m)
90 for (idx_type i = 0; i < nargs(); ++i)
91 cell(i).substitute(m);
95 void MathNestInset::metrics(MetricsInfo const & mi) const
98 for (idx_type i = 0; i < nargs(); ++i)
103 bool MathNestInset::idxNext(LCursor & cur) const
105 BOOST_ASSERT(cur.inset() == this);
106 if (cur.idx() + 1 >= nargs())
114 bool MathNestInset::idxRight(LCursor & cur) const
120 bool MathNestInset::idxPrev(LCursor & cur) const
122 BOOST_ASSERT(cur.inset() == this);
126 cur.pos() = cur.lastpos();
131 bool MathNestInset::idxLeft(LCursor & cur) const
137 bool MathNestInset::idxFirst(LCursor & cur) const
139 BOOST_ASSERT(cur.inset() == this);
148 bool MathNestInset::idxLast(LCursor & cur) const
150 BOOST_ASSERT(cur.inset() == this);
153 cur.idx() = nargs() - 1;
154 cur.pos() = cur.lastpos();
159 bool MathNestInset::idxHome(LCursor & cur) const
161 BOOST_ASSERT(cur.inset() == this);
169 bool MathNestInset::idxEnd(LCursor & cur) const
171 BOOST_ASSERT(cur.inset() == this);
172 if (cur.lastpos() == cur.lastpos())
174 cur.pos() = cur.lastpos();
179 void MathNestInset::dump() const
181 WriteStream os(lyxerr);
182 os << "---------------------------------------------\n";
185 for (idx_type i = 0; i < nargs(); ++i)
186 os << cell(i) << "\n";
187 os << "---------------------------------------------\n";
191 //void MathNestInset::draw(PainterInfo & pi, int x, int y) const
192 void MathNestInset::draw(PainterInfo &, int, int) const
196 pi.pain.fillRectangle(x, y - ascent(), width(), height(),
202 void MathNestInset::drawSelection(PainterInfo & pi, int x, int y) const
204 // this should use the x/y values given, not the cached values
205 LCursor & cur = pi.base.bv->cursor();
206 if (!cur.selection())
208 if (cur.inset() != this)
210 CursorSlice & s1 = cur.selBegin();
211 CursorSlice & s2 = cur.selEnd();
212 if (s1.idx() == s2.idx()) {
213 MathArray const & c = s1.cell();
214 lyxerr << "###### c.xo(): " << c.xo() << " c.yo(): " << c.yo() << endl;
215 int x1 = c.xo() + c.pos2x(s1.pos());
216 int y1 = c.yo() - c.ascent();
217 int x2 = c.xo() + c.pos2x(s2.pos());
218 int y2 = c.yo() + c.descent();
219 //pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
220 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::red);
222 for (idx_type i = 0; i < nargs(); ++i) {
223 if (idxBetween(i, s1.idx(), s2.idx())) {
224 MathArray const & c = cell(i);
226 int y1 = c.yo() - c.ascent();
227 int x2 = c.xo() + c.width();
228 int y2 = c.yo() + c.descent();
229 //pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
230 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::red);
237 void MathNestInset::validate(LaTeXFeatures & features) const
239 for (idx_type i = 0; i < nargs(); ++i)
240 cell(i).validate(features);
244 void MathNestInset::replace(ReplaceData & rep)
246 for (idx_type i = 0; i < nargs(); ++i)
247 cell(i).replace(rep);
251 bool MathNestInset::contains(MathArray const & ar) const
253 for (idx_type i = 0; i < nargs(); ++i)
254 if (cell(i).contains(ar))
260 bool MathNestInset::lock() const
266 void MathNestInset::lock(bool l)
272 bool MathNestInset::isActive() const
278 MathArray MathNestInset::glue() const
281 for (size_t i = 0; i < nargs(); ++i)
287 void MathNestInset::write(WriteStream & os) const
289 os << '\\' << name().c_str();
290 for (size_t i = 0; i < nargs(); ++i)
291 os << '{' << cell(i) << '}';
293 os.pendingSpace(true);
294 if (lock_ && !os.latex()) {
296 os.pendingSpace(true);
301 void MathNestInset::normalize(NormalStream & os) const
303 os << '[' << name().c_str();
304 for (size_t i = 0; i < nargs(); ++i)
305 os << ' ' << cell(i);
310 void MathNestInset::notifyCursorLeaves(idx_type idx)
312 cell(idx).notifyCursorLeaves();
316 void MathNestInset::handleFont
317 (LCursor & cur, string const & arg, string const & font)
319 // this whole function is a hack and won't work for incremental font
321 recordUndo(cur, Undo::ATOMIC);
323 if (cur.inset()->asMathInset()->name() == font)
324 cur.handleFont(font);
326 cur.handleNest(createMathInset(font));
332 void MathNestInset::handleFont2(LCursor & cur, string const & arg)
334 recordUndo(cur, Undo::ATOMIC);
337 bv_funcs::string2font(arg, font, b);
338 if (font.color() != LColor::inherit) {
339 MathAtom at = createMathInset("color");
340 asArray(lcolor.getGUIName(font.color()), at.nucleus()->cell(0));
341 cur.handleNest(at, 1);
347 MathNestInset::priv_dispatch(LCursor & cur, FuncRequest const & cmd)
349 lyxerr << "*** MathNestInset: request: " << cmd << std::endl;
350 //lyxerr << "InsetFormulaBase::localDispatch: act: " << cmd.action
351 // << " arg: '" << cmd.argument
352 // << "' x: '" << cmd.x
353 // << " y: '" << cmd.y
354 // << "' button: " << cmd.button() << endl;
356 switch (cmd.action) {
359 if (!cmd.argument.empty()) {
361 mathed_parse_cell(ar, cmd.argument);
362 cur.cell().insert(cur.pos(), ar);
363 cur.pos() += ar.size();
365 return DispatchResult(true, true);
369 istringstream is(cmd.argument.c_str());
372 cur.macroModeClose();
373 recordUndo(cur, Undo::ATOMIC);
375 return DispatchResult(true, true);
379 case LFUN_PASTESELECTION:
380 return dispatch(cur, FuncRequest(LFUN_PASTE, cur.bv().getClipboard()));
382 case LFUN_MOUSE_PRESS:
383 if (cmd.button() == mouse_button::button2)
384 return priv_dispatch(cur, FuncRequest(LFUN_PASTESELECTION));
385 return DispatchResult(false);
388 cur.selection() = true; // fall through...
391 DispatchResult(true, true) : DispatchResult(false, FINISHED_RIGHT);
392 //lyxerr << "calling scroll 20" << endl;
393 //scroll(&cur.bv(), 20);
394 // write something to the minibuffer
395 //cur.bv().owner()->message(cur.info());
398 cur.selection() = true; // fall through
401 DispatchResult(true, true) : DispatchResult(false, FINISHED);
404 cur.selection() = true; // fall through
407 DispatchResult(true, true) : DispatchResult(false, FINISHED_UP);
410 cur.selection() = true; // fall through
413 DispatchResult(true, true) : DispatchResult(false, FINISHED_DOWN);
417 cur.selection() = true;
419 return DispatchResult(true, true);
421 case LFUN_UP_PARAGRAPHSEL:
422 case LFUN_UP_PARAGRAPH:
423 case LFUN_DOWN_PARAGRAPHSEL:
424 case LFUN_DOWN_PARAGRAPH:
425 return DispatchResult(true, FINISHED);
428 case LFUN_WORDLEFTSEL:
429 cur.selection() = true; // fall through
433 ? DispatchResult(true, true) : DispatchResult(true, FINISHED);
436 case LFUN_WORDRIGHTSEL:
437 cur.selection() = true; // fall through
441 ? DispatchResult(true, true) : DispatchResult(false, FINISHED_RIGHT);
445 case LFUN_BEGINNINGBUFSEL:
446 case LFUN_BEGINNINGBUF:
447 return DispatchResult(true, FINISHED);
453 return DispatchResult(false, FINISHED_RIGHT);
455 case LFUN_CELL_FORWARD:
456 cur.inset()->idxNext(cur);
457 return DispatchResult(true, true);
459 case LFUN_CELL_BACKWARD:
460 cur.inset()->idxPrev(cur);
461 return DispatchResult(true, true);
463 case LFUN_DELETE_WORD_BACKWARD:
465 recordUndo(cur, Undo::ATOMIC);
467 return DispatchResult(true, true);
469 case LFUN_DELETE_WORD_FORWARD:
471 recordUndo(cur, Undo::ATOMIC);
473 return DispatchResult(true, FINISHED);
476 if (!cur.selection())
477 return DispatchResult(true, true);
479 return DispatchResult(false);
481 case LFUN_INSET_TOGGLE:
483 return DispatchResult(true, true);
485 case LFUN_SELFINSERT:
486 if (!cmd.argument.empty()) {
487 recordUndo(cur, Undo::ATOMIC);
488 if (cmd.argument.size() == 1) {
489 if (cur.interpret(cmd.argument[0]))
490 return DispatchResult(true, true);
492 return DispatchResult(false, FINISHED_RIGHT);
494 cur.insert(cmd.argument);
496 return DispatchResult(false, FINISHED_RIGHT);
501 // this needs to be incorporated
503 //lyxerr << "InsetFormulaBase::localDispatch: act: " << cmd.action
504 // << " arg: '" << cmd.argument
505 // << "' x: '" << cmd.x
506 // << " y: '" << cmd.y
507 // << "' button: " << cmd.button() << endl;
509 // delete empty mathbox (LFUN_BACKSPACE and LFUN_DELETE)
510 bool remove_inset = false;
512 switch (cmd.action) {
513 case LFUN_MOUSE_PRESS:
514 //lyxerr << "Mouse single press" << endl;
515 return lfunMousePress(cur, cmd);
516 case LFUN_MOUSE_MOTION:
517 //lyxerr << "Mouse motion" << endl;
518 return lfunMouseMotion(cur, cmd);
519 case LFUN_MOUSE_RELEASE:
520 //lyxerr << "Mouse single release" << endl;
521 return lfunMouseRelease(cur, cmd);
522 case LFUN_MOUSE_DOUBLE:
523 //lyxerr << "Mouse double" << endl;
524 return dispatch(cur, FuncRequest(LFUN_WORDSEL));
529 DispatchResult result(true);
530 bool was_macro = cur.inMacroMode();
535 switch (cmd.action) {
537 case LFUN_MATH_LIMITS:
538 recordUndo(cur, Undo::ATOMIC);
544 // sprintf(dispatch_buffer, "%d %d",);
545 // DispatchResult= dispatch_buffer;
548 lyxerr << "LFUN_SETXY broken!" << endl;
551 istringstream is(cmd.argument.c_str());
553 cur.setScreenPos(x, y);
554 return DispatchResult(true, true);
558 recordUndo(cur, Undo::DELETE);
560 return DispatchResult(true, true);
564 return DispatchResult(true, true);
567 // Special casing for superscript in case of LyX handling
569 case LFUN_CIRCUMFLEX:
570 if (cmd.argument.empty()) {
571 // do superscript if LyX handles
573 recordUndo(cur, Undo::ATOMIC);
576 return DispatchResult(true, true);
591 case LFUN_HUNG_UMLAUT:
592 return DispatchResult(true, true);
595 case LFUN_FREEFONT_APPLY:
596 case LFUN_FREEFONT_UPDATE:
597 handleFont2(cur, cmd.argument);
598 return DispatchResult(true, true);
601 handleFont(cur, cmd.argument, "mathbf");
602 return DispatchResult(true, true);
604 handleFont(cur, cmd.argument, "mathsf");
605 return DispatchResult(true, true);
607 handleFont(cur, cmd.argument, "mathcal");
608 return DispatchResult(true, true);
610 handleFont(cur, cmd.argument, "mathrm");
611 return DispatchResult(true, true);
613 handleFont(cur, cmd.argument, "texttt");
614 return DispatchResult(true, true);
616 handleFont(cur, cmd.argument, "mathfrak");
617 return DispatchResult(true, true);
619 handleFont(cur, cmd.argument, "mathit");
620 return DispatchResult(true, true);
622 handleFont(cur, cmd.argument, "mathbb");
623 return DispatchResult(true, true);
624 //case LFUN_FREEFONT_APPLY:
625 handleFont(cur, cmd.argument, "textrm");
626 return DispatchResult(true, true);
628 handleFont(cur, cmd.argument, "textnormal");
629 return DispatchResult(true, true);
633 cur.macroModeClose();
635 cur.plainInsert(MathAtom(new MathMBoxInset(cur.bv())));
637 cur.pushLeft(cur.nextAtom().nucleus());
639 if (cur.currentMode() == InsetBase::TEXT_MODE)
640 cur.niceInsert(MathAtom(new MathHullInset("simple")));
642 handleFont(cur, cmd.argument, "textrm");
643 //cur.owner()->message(_("math text mode toggled"));
645 return DispatchResult(true, true);
650 recordUndo(cur, Undo::ATOMIC);
654 return DispatchResult(true, true);
656 case LFUN_INSERT_MATRIX: {
657 recordUndo(cur, Undo::ATOMIC);
662 istringstream is(cmd.argument);
663 is >> m >> n >> v_align >> h_align;
670 MathAtom(new MathArrayInset("array", m, n, v_align[0], h_align)));
671 return DispatchResult(true, true);
674 case LFUN_MATH_DELIM: {
675 //lyxerr << "formulabase::LFUN_MATH_DELIM, arg: '" << arg << "'" << endl;
677 string rs = lyx::support::split(cmd.argument, ls, ' ');
678 // Reasonable default values
683 recordUndo(cur, Undo::ATOMIC);
684 cur.handleNest(MathAtom(new MathDelimInset(ls, rs)));
685 return DispatchResult(true, true);
688 case LFUN_SPACE_INSERT:
689 case LFUN_MATH_SPACE:
690 recordUndo(cur, Undo::ATOMIC);
691 cur.insert(MathAtom(new MathSpaceInset(",")));
692 return DispatchResult(true, true);
696 //cur.bv().owner()->message(_("Invalid action in math mode!"));
697 return DispatchResult(true, true);
700 // interpret this as if a backslash was typed
701 recordUndo(cur, Undo::ATOMIC);
703 return DispatchResult(true, true);
706 case LFUN_BREAKPARAGRAPH:
707 case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
708 case LFUN_BREAKPARAGRAPH_SKIP:
710 recordUndo(cur, Undo::ATOMIC);
711 cur.niceInsert(argument);
712 return DispatchResult(true, true);
715 // FIXME: We probably should swap parts of "math-insert" and "self-insert"
716 // handling such that "self-insert" works on "arbitrary stuff" too, and
717 // math-insert only handles special math things like "matrix".
718 case LFUN_INSERT_MATH:
719 recordUndo(cur, Undo::ATOMIC);
720 cur.niceInsert(cmd.argument);
721 return DispatchResult(true, true);
723 case LFUN_DIALOG_SHOW:
724 return DispatchResult(false);
726 case LFUN_DIALOG_SHOW_NEW_INSET: {
727 string const & name = cmd.argument;
732 data = tmp.createDialogStr(name);
736 return DispatchResult(false);
737 cur.bv().owner()->getDialogs().show(name, data, 0);
738 return DispatchResult(true, true);
741 case LFUN_INSET_APPLY: {
742 string const name = cmd.getArg(0);
743 InsetBase * base = cur.bv().owner()->getDialogs().getOpenInset(name);
746 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument);
747 return base->dispatch(cur, fr);
750 if (createMathInset_fromDialogStr(cmd.argument, ar)) {
752 return DispatchResult(true, true);
754 return DispatchResult(false);
760 case LFUN_WORD_REPLACE:
763 searchForward(&cur.bv(), cmd.getArg(0), false, false)
764 ? DispatchResult(true, true) : DispatchResult(false);
766 case LFUN_INSERT_MATH:
767 case LFUN_INSERT_MATRIX:
768 case LFUN_MATH_DELIM: {
769 MathHullInset * f = new MathHullInset;
770 if (openNewInset(cur, f)) {
771 cur.inset()->dispatch(cur, FuncRequest(LFUN_MATH_MUTATE, "simple"));
772 cur.inset()->dispatch(cur, cmd);
774 return DispatchResult(true, true);
780 BOOST_ASSERT(cur.inMathed());
782 if (result.dispatched()) {
784 cur.bv().stuffClipboard(cur.grabSelection());
786 cur.releaseMathCursor();
788 cur.bv().owner()->dispatch(FuncRequest(LFUN_DELETE));
791 return result; // original version
795 return MathDimInset::priv_dispatch(cur, cmd);
800 void MathNestInset::edit(LCursor & cur, int x, int y)
802 lyxerr << "Called MathNestInset::edit with '" << x << ' ' << y << "'" << endl;
805 int dist_min = 1000000;
806 for (idx_type i = 0; i < nargs(); ++i) {
807 int d = cell(i).dist(x, y);
813 MathArray & ar = cell(idx_min);
816 cur.pos() = ar.x2pos(x - ar.xo());
817 lyxerr << "found cell : " << idx_min << " pos: " << cur.pos() << endl;
820 for (pos_type i = 0, n = ar.size(); i < n; ++i)
821 if (ar[i]->covers(x, y))
822 ar[i].nucleus()->edit(cur, x, y);