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 MathArray const & ar = cur.cell();
76 x = ar.xo() + ar.pos2x(cur.pos());
78 // move cursor visually into empty cells ("blue rectangles");
79 if (cur.cell().empty())
84 void MathNestInset::substitute(MathMacro const & m)
86 for (idx_type i = 0; i < nargs(); ++i)
87 cell(i).substitute(m);
91 void MathNestInset::metrics(MetricsInfo const & mi) const
94 for (idx_type i = 0; i < nargs(); ++i)
99 bool MathNestInset::idxNext(LCursor & cur) const
101 if (cur.idx() + 1 >= nargs())
109 bool MathNestInset::idxRight(LCursor & cur) const
115 bool MathNestInset::idxPrev(LCursor & cur) const
120 cur.pos() = cur.lastpos();
125 bool MathNestInset::idxLeft(LCursor & cur) const
131 bool MathNestInset::idxFirst(LCursor & cur) const
141 bool MathNestInset::idxLast(LCursor & cur) const
145 cur.idx() = nargs() - 1;
146 cur.pos() = cur.lastpos();
151 bool MathNestInset::idxHome(LCursor & cur) const
160 bool MathNestInset::idxEnd(LCursor & cur) const
162 if (cur.lastpos() == cur.lastpos())
164 cur.pos() = cur.lastpos();
169 void MathNestInset::dump() const
171 WriteStream os(lyxerr);
172 os << "---------------------------------------------\n";
175 for (idx_type i = 0; i < nargs(); ++i)
176 os << cell(i) << "\n";
177 os << "---------------------------------------------\n";
181 //void MathNestInset::draw(PainterInfo & pi, int x, int y) const
182 void MathNestInset::draw(PainterInfo &, int, int) const
186 pi.pain.fillRectangle(x, y - ascent(), width(), height(),
192 void MathNestInset::drawSelection(PainterInfo & pi,
193 idx_type idx1, pos_type pos1, idx_type idx2, pos_type pos2) const
196 MathArray const & c = cell(idx1);
197 int x1 = c.xo() + c.pos2x(pos1);
198 int y1 = c.yo() - c.ascent();
199 int x2 = c.xo() + c.pos2x(pos2);
200 int y2 = c.yo() + c.descent();
201 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
203 for (idx_type i = 0; i < nargs(); ++i) {
204 if (idxBetween(i, idx1, idx2)) {
205 MathArray const & c = cell(i);
207 int y1 = c.yo() - c.ascent();
208 int x2 = c.xo() + c.width();
209 int y2 = c.yo() + c.descent();
210 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
217 void MathNestInset::validate(LaTeXFeatures & features) const
219 for (idx_type i = 0; i < nargs(); ++i)
220 cell(i).validate(features);
224 void MathNestInset::replace(ReplaceData & rep)
226 for (idx_type i = 0; i < nargs(); ++i)
227 cell(i).replace(rep);
231 bool MathNestInset::contains(MathArray const & ar) const
233 for (idx_type i = 0; i < nargs(); ++i)
234 if (cell(i).contains(ar))
240 bool MathNestInset::lock() const
246 void MathNestInset::lock(bool l)
252 bool MathNestInset::isActive() const
258 MathArray MathNestInset::glue() const
261 for (size_t i = 0; i < nargs(); ++i)
267 void MathNestInset::write(WriteStream & os) const
269 os << '\\' << name().c_str();
270 for (size_t i = 0; i < nargs(); ++i)
271 os << '{' << cell(i) << '}';
273 os.pendingSpace(true);
274 if (lock_ && !os.latex()) {
276 os.pendingSpace(true);
281 void MathNestInset::normalize(NormalStream & os) const
283 os << '[' << name().c_str();
284 for (size_t i = 0; i < nargs(); ++i)
285 os << ' ' << cell(i);
290 void MathNestInset::notifyCursorLeaves(idx_type idx)
292 cell(idx).notifyCursorLeaves();
296 void MathNestInset::handleFont
297 (LCursor & cur, string const & arg, string const & font)
299 // this whole function is a hack and won't work for incremental font
301 recordUndo(cur, Undo::ATOMIC);
303 if (cur.inset()->asMathInset()->name() == font)
304 cur.handleFont(font);
306 cur.handleNest(createMathInset(font));
312 void MathNestInset::handleFont2(LCursor & cur, string const & arg)
314 recordUndo(cur, Undo::ATOMIC);
317 bv_funcs::string2font(arg, font, b);
318 if (font.color() != LColor::inherit) {
319 MathAtom at = createMathInset("color");
320 asArray(lcolor.getGUIName(font.color()), at.nucleus()->cell(0));
321 cur.handleNest(at, 1);
327 MathNestInset::priv_dispatch(LCursor & cur, FuncRequest const & cmd)
329 lyxerr << "*** MathNestInset: request: " << cmd << std::endl;
330 //lyxerr << "InsetFormulaBase::localDispatch: act: " << cmd.action
331 // << " arg: '" << cmd.argument
332 // << "' x: '" << cmd.x
333 // << " y: '" << cmd.y
334 // << "' button: " << cmd.button() << endl;
336 switch (cmd.action) {
339 if (!cmd.argument.empty()) {
341 mathed_parse_cell(ar, cmd.argument);
342 cur.cell().insert(cur.pos(), ar);
343 cur.pos() += ar.size();
345 return DispatchResult(true, true);
349 istringstream is(cmd.argument.c_str());
352 cur.macroModeClose();
353 recordUndo(cur, Undo::ATOMIC);
355 return DispatchResult(true, true);
359 case LFUN_PASTESELECTION:
360 return dispatch(cur, FuncRequest(LFUN_PASTE, cur.bv().getClipboard()));
362 case LFUN_MOUSE_PRESS:
363 if (cmd.button() == mouse_button::button2)
364 return priv_dispatch(cur, FuncRequest(LFUN_PASTESELECTION));
365 return DispatchResult(false);
368 cur.selection() = true; // fall through...
371 DispatchResult(true, true) : DispatchResult(false, FINISHED_RIGHT);
372 //lyxerr << "calling scroll 20" << endl;
373 //scroll(&cur.bv(), 20);
374 // write something to the minibuffer
375 //cur.bv().owner()->message(cur.info());
378 cur.selection() = true; // fall through
381 DispatchResult(true, true) : DispatchResult(false, FINISHED);
384 cur.selection() = true; // fall through
387 DispatchResult(true, true) : DispatchResult(false, FINISHED_UP);
390 cur.selection() = true; // fall through
393 DispatchResult(true, true) : DispatchResult(false, FINISHED_DOWN);
397 cur.selection() = true;
399 return DispatchResult(true, true);
401 case LFUN_UP_PARAGRAPHSEL:
402 case LFUN_UP_PARAGRAPH:
403 case LFUN_DOWN_PARAGRAPHSEL:
404 case LFUN_DOWN_PARAGRAPH:
405 return DispatchResult(true, FINISHED);
408 case LFUN_WORDLEFTSEL:
409 cur.selection() = true; // fall through
413 ? DispatchResult(true, true) : DispatchResult(true, FINISHED);
416 case LFUN_WORDRIGHTSEL:
417 cur.selection() = true; // fall through
421 ? DispatchResult(true, true) : DispatchResult(false, FINISHED_RIGHT);
425 case LFUN_BEGINNINGBUFSEL:
426 case LFUN_BEGINNINGBUF:
427 return DispatchResult(true, FINISHED);
433 return DispatchResult(false, FINISHED_RIGHT);
435 case LFUN_CELL_FORWARD:
436 cur.inset()->idxNext(cur);
437 return DispatchResult(true, true);
439 case LFUN_CELL_BACKWARD:
440 cur.inset()->idxPrev(cur);
441 return DispatchResult(true, true);
443 case LFUN_DELETE_WORD_BACKWARD:
445 recordUndo(cur, Undo::ATOMIC);
447 return DispatchResult(true, true);
449 case LFUN_DELETE_WORD_FORWARD:
451 recordUndo(cur, Undo::ATOMIC);
453 return DispatchResult(true, FINISHED);
456 if (!cur.selection())
457 return DispatchResult(true, true);
459 return DispatchResult(false);
461 case LFUN_INSET_TOGGLE:
463 return DispatchResult(true, true);
465 case LFUN_SELFINSERT:
466 if (!cmd.argument.empty()) {
467 recordUndo(cur, Undo::ATOMIC);
468 if (cmd.argument.size() == 1) {
469 if (cur.interpret(cmd.argument[0]))
470 return DispatchResult(true, true);
472 return DispatchResult(false, FINISHED_RIGHT);
474 cur.insert(cmd.argument);
476 return DispatchResult(false, FINISHED_RIGHT);
481 // this needs to be incorporated
483 //lyxerr << "InsetFormulaBase::localDispatch: act: " << cmd.action
484 // << " arg: '" << cmd.argument
485 // << "' x: '" << cmd.x
486 // << " y: '" << cmd.y
487 // << "' button: " << cmd.button() << endl;
489 // delete empty mathbox (LFUN_BACKSPACE and LFUN_DELETE)
490 bool remove_inset = false;
492 switch (cmd.action) {
493 case LFUN_MOUSE_PRESS:
494 //lyxerr << "Mouse single press" << endl;
495 return lfunMousePress(cur, cmd);
496 case LFUN_MOUSE_MOTION:
497 //lyxerr << "Mouse motion" << endl;
498 return lfunMouseMotion(cur, cmd);
499 case LFUN_MOUSE_RELEASE:
500 //lyxerr << "Mouse single release" << endl;
501 return lfunMouseRelease(cur, cmd);
502 case LFUN_MOUSE_DOUBLE:
503 //lyxerr << "Mouse double" << endl;
504 return dispatch(cur, FuncRequest(LFUN_WORDSEL));
509 DispatchResult result(true);
510 bool was_macro = cur.inMacroMode();
515 switch (cmd.action) {
517 case LFUN_MATH_LIMITS:
518 recordUndo(cur, Undo::ATOMIC);
524 // sprintf(dispatch_buffer, "%d %d",);
525 // DispatchResult= dispatch_buffer;
528 lyxerr << "LFUN_SETXY broken!" << endl;
531 istringstream is(cmd.argument.c_str());
533 cur.setScreenPos(x, y);
534 return DispatchResult(true, true);
538 recordUndo(cur, Undo::DELETE);
540 return DispatchResult(true, true);
544 return DispatchResult(true, true);
547 // Special casing for superscript in case of LyX handling
549 case LFUN_CIRCUMFLEX:
550 if (cmd.argument.empty()) {
551 // do superscript if LyX handles
553 recordUndo(cur, Undo::ATOMIC);
556 return DispatchResult(true, true);
571 case LFUN_HUNG_UMLAUT:
572 return DispatchResult(true, true);
575 case LFUN_FREEFONT_APPLY:
576 case LFUN_FREEFONT_UPDATE:
577 handleFont2(cur, cmd.argument);
578 return DispatchResult(true, true);
581 handleFont(cur, cmd.argument, "mathbf");
582 return DispatchResult(true, true);
584 handleFont(cur, cmd.argument, "mathsf");
585 return DispatchResult(true, true);
587 handleFont(cur, cmd.argument, "mathcal");
588 return DispatchResult(true, true);
590 handleFont(cur, cmd.argument, "mathrm");
591 return DispatchResult(true, true);
593 handleFont(cur, cmd.argument, "texttt");
594 return DispatchResult(true, true);
596 handleFont(cur, cmd.argument, "mathfrak");
597 return DispatchResult(true, true);
599 handleFont(cur, cmd.argument, "mathit");
600 return DispatchResult(true, true);
602 handleFont(cur, cmd.argument, "mathbb");
603 return DispatchResult(true, true);
604 //case LFUN_FREEFONT_APPLY:
605 handleFont(cur, cmd.argument, "textrm");
606 return DispatchResult(true, true);
608 handleFont(cur, cmd.argument, "textnormal");
609 return DispatchResult(true, true);
613 cur.macroModeClose();
615 cur.plainInsert(MathAtom(new MathMBoxInset(cur.bv())));
617 cur.pushLeft(cur.nextAtom().nucleus());
619 if (cur.currentMode() == InsetBase::TEXT_MODE)
620 cur.niceInsert(MathAtom(new MathHullInset("simple")));
622 handleFont(cur, cmd.argument, "textrm");
623 //cur.owner()->message(_("math text mode toggled"));
625 return DispatchResult(true, true);
630 recordUndo(cur, Undo::ATOMIC);
634 return DispatchResult(true, true);
636 case LFUN_INSERT_MATRIX: {
637 recordUndo(cur, Undo::ATOMIC);
642 istringstream is(cmd.argument);
643 is >> m >> n >> v_align >> h_align;
650 MathAtom(new MathArrayInset("array", m, n, v_align[0], h_align)));
651 return DispatchResult(true, true);
654 case LFUN_MATH_DELIM: {
655 //lyxerr << "formulabase::LFUN_MATH_DELIM, arg: '" << arg << "'" << endl;
657 string rs = lyx::support::split(cmd.argument, ls, ' ');
658 // Reasonable default values
663 recordUndo(cur, Undo::ATOMIC);
664 cur.handleNest(MathAtom(new MathDelimInset(ls, rs)));
665 return DispatchResult(true, true);
668 case LFUN_SPACE_INSERT:
669 case LFUN_MATH_SPACE:
670 recordUndo(cur, Undo::ATOMIC);
671 cur.insert(MathAtom(new MathSpaceInset(",")));
672 return DispatchResult(true, true);
676 //cur.bv().owner()->message(_("Invalid action in math mode!"));
677 return DispatchResult(true, true);
680 // interpret this as if a backslash was typed
681 recordUndo(cur, Undo::ATOMIC);
683 return DispatchResult(true, true);
686 case LFUN_BREAKPARAGRAPH:
687 case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
688 case LFUN_BREAKPARAGRAPH_SKIP:
690 recordUndo(cur, Undo::ATOMIC);
691 cur.niceInsert(argument);
692 return DispatchResult(true, true);
695 // FIXME: We probably should swap parts of "math-insert" and "self-insert"
696 // handling such that "self-insert" works on "arbitrary stuff" too, and
697 // math-insert only handles special math things like "matrix".
698 case LFUN_INSERT_MATH:
699 recordUndo(cur, Undo::ATOMIC);
700 cur.niceInsert(cmd.argument);
701 return DispatchResult(true, true);
703 case LFUN_DIALOG_SHOW:
704 return DispatchResult(false);
706 case LFUN_DIALOG_SHOW_NEW_INSET: {
707 string const & name = cmd.argument;
712 data = tmp.createDialogStr(name);
716 return DispatchResult(false);
717 cur.bv().owner()->getDialogs().show(name, data, 0);
718 return DispatchResult(true, true);
721 case LFUN_INSET_APPLY: {
722 string const name = cmd.getArg(0);
723 InsetBase * base = cur.bv().owner()->getDialogs().getOpenInset(name);
726 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument);
727 return base->dispatch(cur, fr);
730 if (createMathInset_fromDialogStr(cmd.argument, ar)) {
732 return DispatchResult(true, true);
734 return DispatchResult(false);
740 case LFUN_WORD_REPLACE:
743 searchForward(&cur.bv(), cmd.getArg(0), false, false)
744 ? DispatchResult(true, true) : DispatchResult(false);
746 case LFUN_INSERT_MATH:
747 case LFUN_INSERT_MATRIX:
748 case LFUN_MATH_DELIM: {
749 MathHullInset * f = new MathHullInset;
750 if (openNewInset(cur, f)) {
751 cur.inset()->dispatch(cur, FuncRequest(LFUN_MATH_MUTATE, "simple"));
752 cur.inset()->dispatch(cur, cmd);
754 return DispatchResult(true, true);
760 BOOST_ASSERT(cur.inMathed());
762 if (result.dispatched()) {
764 cur.bv().stuffClipboard(cur.grabSelection());
766 cur.releaseMathCursor();
768 cur.bv().owner()->dispatch(FuncRequest(LFUN_DELETE));
771 return result; // original version
775 return MathDimInset::priv_dispatch(cur, cmd);
780 void MathNestInset::edit(LCursor & cur, int x, int y)
782 lyxerr << "Called MathHullInset::edit with '" << x << ' ' << y << "'" << endl;
785 int dist_min = 1000000;
786 for (idx_type i = 0; i < nargs(); ++i) {
787 int d = cell(i).dist(x, y);
793 MathArray & ar = cell(idx_min);
796 cur.pos() = ar.x2pos(x - ar.xo());
797 lyxerr << "found cell : " << idx_min << " pos: " << cur.pos() << endl;
800 for (pos_type i = 0, n = ar.size(); i < n; ++i)
801 if (ar[i]->covers(x, y))
802 ar[i].nucleus()->edit(cur, x, y);