A bit more re-organization.
[lyx.git] / src / mathed / MathData.cpp
blobb52c34f5d27089837bd9f18552b79d44deea8b71
1 /**
2 * \file MathData.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author André Pönitz
7 * \author Stefan Schimanski
9 * Full author contact details are available in file CREDITS.
12 #include <config.h>
14 #include "MathData.h"
16 #include "InsetMathBrace.h"
17 #include "InsetMathFont.h"
18 #include "InsetMathScript.h"
19 #include "MacroTable.h"
20 #include "MathMacro.h"
21 #include "MathStream.h"
22 #include "MathSupport.h"
23 #include "MetricsInfo.h"
24 #include "ReplaceData.h"
26 #include "Buffer.h"
27 #include "BufferView.h"
28 #include "CoordCache.h"
29 #include "Cursor.h"
31 #include "mathed/InsetMathUnknown.h"
33 #include "support/debug.h"
34 #include "support/docstream.h"
36 #include "frontends/FontMetrics.h"
37 #include "frontends/Painter.h"
39 #include "support/lassert.h"
40 #include <boost/next_prior.hpp>
42 #include <cstdlib>
44 using namespace std;
46 namespace lyx {
49 MathData::MathData(Buffer * buf, const_iterator from, const_iterator to)
50 : base_type(from, to), buffer_(buf)
54 MathAtom & MathData::operator[](pos_type pos)
56 LASSERT(pos < size(), /**/);
57 return base_type::operator[](pos);
61 MathAtom const & MathData::operator[](pos_type pos) const
63 LASSERT(pos < size(), /**/);
64 return base_type::operator[](pos);
68 void MathData::insert(size_type pos, MathAtom const & t)
70 base_type::insert(begin() + pos, t);
74 void MathData::insert(size_type pos, MathData const & ar)
76 LASSERT(pos <= size(), /**/);
77 base_type::insert(begin() + pos, ar.begin(), ar.end());
81 void MathData::append(MathData const & ar)
83 insert(size(), ar);
87 void MathData::erase(size_type pos)
89 if (pos < size())
90 erase(pos, pos + 1);
94 void MathData::erase(iterator pos1, iterator pos2)
96 base_type::erase(pos1, pos2);
100 void MathData::erase(iterator pos)
102 base_type::erase(pos);
106 void MathData::erase(size_type pos1, size_type pos2)
108 base_type::erase(begin() + pos1, begin() + pos2);
112 void MathData::dump2() const
114 odocstringstream os;
115 NormalStream ns(os);
116 for (const_iterator it = begin(); it != end(); ++it)
117 ns << *it << ' ';
118 lyxerr << to_utf8(os.str());
122 void MathData::dump() const
124 odocstringstream os;
125 NormalStream ns(os);
126 for (const_iterator it = begin(); it != end(); ++it)
127 ns << '<' << *it << '>';
128 lyxerr << to_utf8(os.str());
132 void MathData::validate(LaTeXFeatures & features) const
134 for (const_iterator it = begin(); it != end(); ++it)
135 (*it)->validate(features);
139 bool MathData::match(MathData const & ar) const
141 return size() == ar.size() && matchpart(ar, 0);
145 bool MathData::matchpart(MathData const & ar, pos_type pos) const
147 if (size() < ar.size() + pos)
148 return false;
149 const_iterator it = begin() + pos;
150 for (const_iterator jt = ar.begin(); jt != ar.end(); ++jt, ++it)
151 if (asString(*it) != asString(*jt))
152 return false;
153 return true;
157 void MathData::replace(ReplaceData & rep)
159 for (size_type i = 0; i < size(); ++i) {
160 if (find1(rep.from, i)) {
161 // match found
162 lyxerr << "match found!" << endl;
163 erase(i, i + rep.from.size());
164 insert(i, rep.to);
168 // FIXME: temporarily disabled
169 // for (const_iterator it = begin(); it != end(); ++it)
170 // it->nucleus()->replace(rep);
174 bool MathData::find1(MathData const & ar, size_type pos) const
176 lyxerr << "finding '" << ar << "' in '" << *this << "'" << endl;
177 for (size_type i = 0, n = ar.size(); i < n; ++i)
178 if (asString(operator[](pos + i)) != asString(ar[i]))
179 return false;
180 return true;
184 MathData::size_type MathData::find(MathData const & ar) const
186 for (int i = 0, last = size() - ar.size(); i < last; ++i)
187 if (find1(ar, i))
188 return i;
189 return size();
193 MathData::size_type MathData::find_last(MathData const & ar) const
195 for (int i = size() - ar.size(); i >= 0; --i)
196 if (find1(ar, i))
197 return i;
198 return size();
202 bool MathData::contains(MathData const & ar) const
204 if (find(ar) != size())
205 return true;
206 for (const_iterator it = begin(); it != end(); ++it)
207 if ((*it)->contains(ar))
208 return true;
209 return false;
213 void MathData::touch() const
218 namespace {
220 bool isInside(DocIterator const & it, MathData const & ar,
221 pos_type p1, pos_type p2)
223 for (size_t i = 0; i != it.depth(); ++i) {
224 CursorSlice const & sl = it[i];
225 if (sl.inset().inMathed() && &sl.cell() == &ar)
226 return p1 <= sl.pos() && sl.pos() < p2;
228 return false;
235 void MathData::metrics(MetricsInfo & mi, Dimension & dim) const
237 frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
238 dim = fm.dimension('I');
239 int xascent = fm.dimension('x').ascent();
240 if (xascent >= dim.asc)
241 xascent = (2 * dim.asc) / 3;
242 minasc_ = xascent;
243 mindes_ = (3 * xascent) / 4;
244 slevel_ = (4 * xascent) / 5;
245 sshift_ = xascent / 4;
246 kerning_ = 0;
248 if (empty()) {
249 // Cache the dimension.
250 mi.base.bv->coordCache().arrays().add(this, dim);
251 return;
254 Cursor & cur = mi.base.bv->cursor();
255 const_cast<MathData*>(this)->updateMacros(&cur, mi.macrocontext);
257 DocIterator const & inlineCompletionPos = mi.base.bv->inlineCompletionPos();
258 MathData const * inlineCompletionData = 0;
259 if (inlineCompletionPos.inMathed())
260 inlineCompletionData = &inlineCompletionPos.cell();
262 dim.asc = 0;
263 dim.wid = 0;
264 Dimension d;
265 CoordCacheBase<Inset> & coords = mi.base.bv->coordCache().insets();
266 for (pos_type i = 0, n = size(); i != n; ++i) {
267 MathAtom const & at = operator[](i);
268 at->metrics(mi, d);
269 coords.add(at.nucleus(), d);
270 dim += d;
271 if (i == n - 1)
272 kerning_ = at->kerning(mi.base.bv);
274 // HACK to draw completion suggestion inline
275 if (inlineCompletionData != this
276 || size_t(inlineCompletionPos.pos()) != i + 1)
277 continue;
279 docstring const & completion = mi.base.bv->inlineCompletion();
280 if (completion.length() == 0)
281 continue;
283 dim.wid += mathed_string_width(mi.base.font, completion);
285 // Cache the dimension.
286 mi.base.bv->coordCache().arrays().add(this, dim);
290 void MathData::draw(PainterInfo & pi, int x, int y) const
292 //lyxerr << "MathData::draw: x: " << x << " y: " << y << endl;
293 BufferView & bv = *pi.base.bv;
294 setXY(bv, x, y);
296 Dimension const & dim = bv.coordCache().getArrays().dim(this);
298 if (empty()) {
299 pi.pain.rectangle(x, y - dim.ascent(), dim.width(), dim.height(), Color_mathline);
300 return;
303 // don't draw outside the workarea
304 if (y + dim.descent() <= 0
305 || y - dim.ascent() >= bv.workHeight()
306 || x + dim.width() <= 0
307 || x >= bv. workWidth())
308 return;
310 DocIterator const & inlineCompletionPos = bv.inlineCompletionPos();
311 MathData const * inlineCompletionData = 0;
312 if (inlineCompletionPos.inMathed())
313 inlineCompletionData = &inlineCompletionPos.cell();
315 CoordCacheBase<Inset> & coords = pi.base.bv->coordCache().insets();
316 for (size_t i = 0, n = size(); i != n; ++i) {
317 MathAtom const & at = operator[](i);
318 coords.add(at.nucleus(), x, y);
319 at->drawSelection(pi, x, y);
320 at->draw(pi, x, y);
321 x += coords.dim(at.nucleus()).wid;
323 // Is the inline completion here?
324 if (inlineCompletionData != this
325 || size_t(inlineCompletionPos.pos()) != i + 1)
326 continue;
327 docstring const & completion = bv.inlineCompletion();
328 if (completion.length() == 0)
329 continue;
330 FontInfo f = pi.base.font;
332 // draw the unique and the non-unique completion part
333 // Note: this is not time-critical as it is
334 // only done once per screen.
335 size_t uniqueTo = bv.inlineCompletionUniqueChars();
336 docstring s1 = completion.substr(0, uniqueTo);
337 docstring s2 = completion.substr(uniqueTo);
339 if (s1.size() > 0) {
340 f.setColor(Color_inlinecompletion);
341 pi.pain.text(x, y, s1, f);
342 x += mathed_string_width(f, s1);
345 if (s2.size() > 0) {
346 f.setColor(Color_nonunique_inlinecompletion);
347 pi.pain.text(x, y, s2, f);
348 x += mathed_string_width(f, s2);
354 void MathData::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
356 dim.clear();
357 Dimension d;
358 for (const_iterator it = begin(); it != end(); ++it) {
359 (*it)->metricsT(mi, d);
360 dim += d;
365 void MathData::drawT(TextPainter & pain, int x, int y) const
367 //lyxerr << "x: " << x << " y: " << y << ' ' << pain.workAreaHeight() << endl;
369 // FIXME: Abdel 16/10/2006
370 // This drawT() method is never used, this is dead code.
372 for (const_iterator it = begin(), et = end(); it != et; ++it) {
373 (*it)->drawT(pain, x, y);
374 //x += (*it)->width_;
375 x += 2;
380 void MathData::updateMacros(Cursor * cur, MacroContext const & mc)
382 // go over the array and look for macros
383 for (size_t i = 0; i < size(); ++i) {
384 MathMacro * macroInset = operator[](i).nucleus()->asMacro();
385 if (!macroInset || macroInset->name_[0] == '^'
386 || macroInset->name_[0] == '_')
387 continue;
389 // get macro
390 macroInset->updateMacro(mc);
391 size_t macroNumArgs = 0;
392 size_t macroOptionals = 0;
393 MacroData const * macro = macroInset->macro();
394 if (macro) {
395 macroNumArgs = macro->numargs();
396 macroOptionals = macro->optionals();
399 // store old and compute new display mode
400 MathMacro::DisplayMode newDisplayMode;
401 MathMacro::DisplayMode oldDisplayMode = macroInset->displayMode();
402 newDisplayMode = macroInset->computeDisplayMode();
404 // arity changed or other reason to detach?
405 if (oldDisplayMode == MathMacro::DISPLAY_NORMAL
406 && (macroInset->arity() != macroNumArgs
407 || macroInset->optionals() != macroOptionals
408 || newDisplayMode == MathMacro::DISPLAY_UNFOLDED)) {
410 detachMacroParameters(cur, i);
413 // the macro could have been copied while resizing this
414 macroInset = operator[](i).nucleus()->asMacro();
416 // Cursor in \label?
417 if (newDisplayMode != MathMacro::DISPLAY_UNFOLDED
418 && oldDisplayMode == MathMacro::DISPLAY_UNFOLDED) {
419 // put cursor in front of macro
420 if (cur) {
421 int macroSlice = cur->find(macroInset);
422 if (macroSlice != -1)
423 cur->cutOff(macroSlice - 1);
427 // update the display mode
428 size_t appetite = macroInset->appetite();
429 macroInset->setDisplayMode(newDisplayMode);
431 // arity changed?
432 if (newDisplayMode == MathMacro::DISPLAY_NORMAL
433 && (macroInset->arity() != macroNumArgs
434 || macroInset->optionals() != macroOptionals)) {
435 // is it a virgin macro which was never attached to parameters?
436 bool fromInitToNormalMode
437 = (oldDisplayMode == MathMacro::DISPLAY_INIT
438 || oldDisplayMode == MathMacro::DISPLAY_INTERACTIVE_INIT)
439 && newDisplayMode == MathMacro::DISPLAY_NORMAL;
441 // if the macro was entered interactively (i.e. not by paste or during
442 // loading), it should not be greedy, but the cursor should
443 // automatically jump into the macro when behind
444 bool interactive = (oldDisplayMode == MathMacro::DISPLAY_INTERACTIVE_INIT);
446 // attach parameters
447 attachMacroParameters(cur, i, macroNumArgs, macroOptionals,
448 fromInitToNormalMode, interactive, appetite);
450 if (cur) {
451 // FIXME: proper anchor handling, this removes the selection
452 cur->updateInsets(&cur->bottom().inset());
453 cur->clearSelection();
457 // Give macro the chance to adapt to new situation.
458 // The macroInset could be invalid now because it was put into a script
459 // inset and therefore "deep" copied. So get it again from the MathData.
460 InsetMath * inset = operator[](i).nucleus();
461 if (inset->asScriptInset())
462 inset = inset->asScriptInset()->nuc()[0].nucleus();
463 LASSERT(inset->asMacro(), /**/);
464 inset->asMacro()->updateRepresentation();
469 void MathData::detachMacroParameters(Cursor * cur, const size_type macroPos)
471 MathMacro * macroInset = operator[](macroPos).nucleus()->asMacro();
472 Buffer * buf = cur->buffer();
474 // detach all arguments
475 vector<MathData> detachedArgs;
476 if (macroPos + 1 == size())
477 // strip arguments if we are at the MathData end
478 macroInset->detachArguments(detachedArgs, true);
479 else
480 macroInset->detachArguments(detachedArgs, false);
482 // find cursor slice
483 int curMacroSlice = -1;
484 if (cur)
485 curMacroSlice = cur->find(macroInset);
486 idx_type curMacroIdx = -1;
487 pos_type curMacroPos = -1;
488 vector<CursorSlice> argSlices;
489 if (curMacroSlice != -1) {
490 curMacroPos = (*cur)[curMacroSlice].pos();
491 curMacroIdx = (*cur)[curMacroSlice].idx();
492 cur->cutOff(curMacroSlice, argSlices);
493 cur->pop_back();
496 // only [] after the last non-empty argument can be dropped later
497 size_t lastNonEmptyOptional = 0;
498 for (size_t l = 0; l < detachedArgs.size() && l < macroInset->optionals(); ++l) {
499 if (!detachedArgs[l].empty())
500 lastNonEmptyOptional = l;
503 // optional arguments to be put back?
504 pos_type p = macroPos + 1;
505 size_t j = 0;
506 for (; j < detachedArgs.size() && j < macroInset->optionals(); ++j) {
507 // another non-empty parameter follows?
508 bool canDropEmptyOptional = j >= lastNonEmptyOptional;
510 // then we can drop empty optional parameters
511 if (detachedArgs[j].empty() && canDropEmptyOptional) {
512 if (curMacroIdx == j)
513 (*cur)[curMacroSlice - 1].pos() = macroPos + 1;
514 continue;
517 // Otherwise we don't drop an empty optional, put it back normally
518 MathData optarg;
519 asArray(from_ascii("[]"), optarg);
520 MathData & arg = detachedArgs[j];
522 // look for "]", i.e. put a brace around?
523 InsetMathBrace * brace = 0;
524 for (size_t q = 0; q < arg.size(); ++q) {
525 if (arg[q]->getChar() == ']') {
526 // put brace
527 brace = new InsetMathBrace(buf);
528 break;
532 // put arg between []
533 if (brace) {
534 brace->cell(0) = arg;
535 optarg.insert(1, MathAtom(brace));
536 } else
537 optarg.insert(1, arg);
539 // insert it into the array
540 insert(p, optarg);
541 p += optarg.size();
543 // cursor in macro?
544 if (curMacroSlice == -1)
545 continue;
547 // cursor in optional argument of macro?
548 if (curMacroIdx == j) {
549 if (brace) {
550 cur->append(0, curMacroPos);
551 (*cur)[curMacroSlice - 1].pos() = macroPos + 2;
552 } else
553 (*cur)[curMacroSlice - 1].pos() = macroPos + 2 + curMacroPos;
554 cur->append(argSlices);
555 } else if ((*cur)[curMacroSlice - 1].pos() >= int(p))
556 // cursor right of macro
557 (*cur)[curMacroSlice - 1].pos() += optarg.size();
560 // put them back into the MathData
561 for (; j < detachedArgs.size(); ++j, ++p) {
562 MathData const & arg = detachedArgs[j];
563 if (arg.size() == 1
564 && !arg[0]->asScriptInset()
565 && !(arg[0]->asMacro() && arg[0]->asMacro()->arity() > 0))
566 insert(p, arg[0]);
567 else
568 insert(p, MathAtom(new InsetMathBrace(arg)));
570 // cursor in macro?
571 if (curMacroSlice == -1)
572 continue;
574 // cursor in j-th argument of macro?
575 if (curMacroIdx == j) {
576 if (operator[](p).nucleus()->asBraceInset()) {
577 (*cur)[curMacroSlice - 1].pos() = p;
578 cur->append(0, curMacroPos);
579 cur->append(argSlices);
580 } else {
581 (*cur)[curMacroSlice - 1].pos() = p; // + macroPos;
582 cur->append(argSlices);
584 } else if ((*cur)[curMacroSlice - 1].pos() >= int(p))
585 ++(*cur)[curMacroSlice - 1].pos();
588 if (cur) {
589 // FIXME: proper anchor handling, this removes the selection
590 cur->clearSelection();
591 cur->updateInsets(&cur->bottom().inset());
596 void MathData::attachMacroParameters(Cursor * cur,
597 const size_type macroPos, const size_type macroNumArgs,
598 const int macroOptionals, const bool fromInitToNormalMode,
599 const bool interactiveInit, const size_t appetite)
601 MathMacro * macroInset = operator[](macroPos).nucleus()->asMacro();
603 // start at atom behind the macro again, maybe with some new arguments
604 // from the detach phase above, to add them back into the macro inset
605 size_t p = macroPos + 1;
606 vector<MathData> detachedArgs;
607 MathAtom scriptToPutAround;
609 // find cursor slice again of this MathData
610 int thisSlice = -1;
611 if (cur)
612 thisSlice = cur->find(*this);
613 int thisPos = -1;
614 if (thisSlice != -1)
615 thisPos = (*cur)[thisSlice].pos();
617 // find arguments behind the macro
618 if (!interactiveInit) {
619 collectOptionalParameters(cur, macroOptionals, detachedArgs, p,
620 scriptToPutAround, macroPos, thisPos, thisSlice);
622 collectParameters(cur, macroNumArgs, detachedArgs, p,
623 scriptToPutAround, macroPos, thisPos, thisSlice, appetite);
625 // attach arguments back to macro inset
626 macroInset->attachArguments(detachedArgs, macroNumArgs, macroOptionals);
628 // found tail script? E.g. \foo{a}b^x
629 if (scriptToPutAround.nucleus()) {
630 // put macro into a script inset
631 scriptToPutAround.nucleus()->asScriptInset()->nuc()[0]
632 = operator[](macroPos);
633 operator[](macroPos) = scriptToPutAround;
635 // go into the script inset nucleus
636 if (cur && thisPos == int(macroPos))
637 cur->append(0, 0);
639 // get pointer to "deep" copied macro inset
640 InsetMathScript * scriptInset
641 = operator[](macroPos).nucleus()->asScriptInset();
642 macroInset = scriptInset->nuc()[0].nucleus()->asMacro();
645 // remove them from the MathData
646 erase(begin() + macroPos + 1, begin() + p);
648 // cursor outside this MathData?
649 if (thisSlice == -1)
650 return;
652 // fix cursor if right of p
653 if (thisPos >= int(p))
654 (*cur)[thisSlice].pos() -= p - (macroPos + 1);
656 // was the macro inset just inserted interactively and was now folded
657 // and the cursor is just behind?
658 if ((*cur)[thisSlice].pos() == int(macroPos + 1)
659 && interactiveInit
660 && fromInitToNormalMode
661 && macroInset->arity() > 0
662 && thisSlice + 1 == int(cur->depth())) {
663 // then enter it if the cursor was just behind
664 (*cur)[thisSlice].pos() = macroPos;
665 cur->push_back(CursorSlice(*macroInset));
666 macroInset->idxFirst(*cur);
671 void MathData::collectOptionalParameters(Cursor * cur,
672 const size_type numOptionalParams, vector<MathData> & params,
673 size_t & pos, MathAtom & scriptToPutAround,
674 const pos_type macroPos, const int thisPos, const int thisSlice)
676 Buffer * buf = cur ? cur->buffer() : 0;
677 // insert optional arguments?
678 while (params.size() < numOptionalParams
679 && pos < size()
680 && !scriptToPutAround.nucleus()) {
681 // is a [] block following which could be an optional parameter?
682 if (operator[](pos)->getChar() != '[')
683 break;
685 // found possible optional argument, look for "]"
686 size_t right = pos + 1;
687 for (; right < size(); ++right) {
688 MathAtom & cell = operator[](right);
690 if (cell->getChar() == ']')
691 // found right end
692 break;
694 // maybe "]" with a script around?
695 InsetMathScript * script = cell.nucleus()->asScriptInset();
696 if (!script)
697 continue;
698 if (script->nuc().size() != 1)
699 continue;
700 if (script->nuc()[0]->getChar() == ']') {
701 // script will be put around the macro later
702 scriptToPutAround = cell;
703 break;
707 // found?
708 if (right >= size()) {
709 // no ] found, so it's not an optional argument
710 break;
713 // add everything between [ and ] as optional argument
714 MathData optarg(buf, begin() + pos + 1, begin() + right);
716 // a brace?
717 bool brace = false;
718 if (optarg.size() == 1 && optarg[0]->asBraceInset()) {
719 brace = true;
720 params.push_back(optarg[0]->asBraceInset()->cell(0));
721 } else
722 params.push_back(optarg);
724 // place cursor in optional argument of macro
725 if (thisSlice != -1
726 && thisPos >= int(pos) && thisPos <= int(right)) {
727 int paramPos = max(0, thisPos - int(pos) - 1);
728 vector<CursorSlice> x;
729 cur->cutOff(thisSlice, x);
730 (*cur)[thisSlice].pos() = macroPos;
731 if (brace) {
732 paramPos = x[0].pos();
733 x.erase(x.begin());
735 cur->append(0, paramPos);
736 cur->append(x);
738 pos = right + 1;
741 // fill up empty optional parameters
742 while (params.size() < numOptionalParams)
743 params.push_back(MathData());
747 void MathData::collectParameters(Cursor * cur,
748 const size_type numParams, vector<MathData> & params,
749 size_t & pos, MathAtom & scriptToPutAround,
750 const pos_type macroPos, const int thisPos, const int thisSlice,
751 const size_t appetite)
753 size_t startSize = params.size();
755 // insert normal arguments
756 while (params.size() < numParams
757 && params.size() - startSize < appetite
758 && pos < size()
759 && !scriptToPutAround.nucleus()) {
760 MathAtom & cell = operator[](pos);
762 // fix cursor
763 vector<CursorSlice> argSlices;
764 int argPos = 0;
765 if (thisSlice != -1 && thisPos == int(pos))
766 cur->cutOff(thisSlice, argSlices);
768 // which kind of parameter is it? In {}? With index x^n?
769 InsetMathBrace const * brace = cell->asBraceInset();
770 if (brace) {
771 // found brace, convert into argument
772 params.push_back(brace->cell(0));
774 // cursor inside of the brace or just in front of?
775 if (thisPos == int(pos) && !argSlices.empty()) {
776 argPos = argSlices[0].pos();
777 argSlices.erase(argSlices.begin());
779 } else if (cell->asScriptInset() && params.size() + 1 == numParams) {
780 // last inset with scripts without braces
781 // -> they belong to the macro, not the argument
782 InsetMathScript * script = cell.nucleus()->asScriptInset();
783 if (script->nuc().size() == 1 && script->nuc()[0]->asBraceInset())
784 // nucleus in brace? Unpack!
785 params.push_back(script->nuc()[0]->asBraceInset()->cell(0));
786 else
787 params.push_back(script->nuc());
789 // script will be put around below
790 scriptToPutAround = cell;
792 // this should only happen after loading, so make cursor handling simple
793 if (thisPos >= int(macroPos) && thisPos <= int(macroPos + numParams)) {
794 argSlices.clear();
795 if (cur)
796 cur->append(0, 0);
798 } else {
799 // the simplest case: plain inset
800 MathData array;
801 array.insert(0, cell);
802 params.push_back(array);
805 // put cursor in argument again
806 if (thisSlice != - 1 && thisPos == int(pos)) {
807 cur->append(params.size() - 1, argPos);
808 cur->append(argSlices);
809 (*cur)[thisSlice].pos() = macroPos;
812 ++pos;
817 int MathData::pos2x(BufferView const * bv, size_type pos) const
819 return pos2x(bv, pos, 0);
823 int MathData::pos2x(BufferView const * bv, size_type pos, int glue) const
825 int x = 0;
826 size_type target = min(pos, size());
827 CoordCacheBase<Inset> const & coords = bv->coordCache().getInsets();
828 for (size_type i = 0; i < target; ++i) {
829 const_iterator it = begin() + i;
830 if ((*it)->getChar() == ' ')
831 x += glue;
832 //lyxerr << "char: " << (*it)->getChar()
833 // << "width: " << (*it)->width() << endl;
834 x += coords.dim((*it).nucleus()).wid;
836 return x;
840 MathData::size_type MathData::x2pos(BufferView const * bv, int targetx) const
842 return x2pos(bv, targetx, 0);
846 MathData::size_type MathData::x2pos(BufferView const * bv, int targetx, int glue) const
848 const_iterator it = begin();
849 int lastx = 0;
850 int currx = 0;
851 CoordCacheBase<Inset> const & coords = bv->coordCache().getInsets();
852 // find first position after targetx
853 for (; currx < targetx && it < end(); ++it) {
854 lastx = currx;
855 if ((*it)->getChar() == ' ')
856 currx += glue;
857 currx += coords.dim((*it).nucleus()).wid;
861 * If we are not at the beginning of the array, go to the left
862 * of the inset if one of the following two condition holds:
863 * - the current inset is editable (so that the cursor tip is
864 * deeper than us): in this case, we want all intermediate
865 * cursor slices to be before insets;
866 * - the mouse is closer to the left side of the inset than to
867 * the right one.
868 * See bug 1918 for details.
870 if (it != begin() && currx >= targetx
871 && ((*boost::prior(it))->asNestInset()
872 || abs(lastx - targetx) < abs(currx - targetx))) {
873 --it;
876 return it - begin();
880 int MathData::dist(BufferView const & bv, int x, int y) const
882 return bv.coordCache().getArrays().squareDistance(this, x, y);
886 void MathData::setXY(BufferView & bv, int x, int y) const
888 //lyxerr << "setting position cache for MathData " << this << endl;
889 bv.coordCache().arrays().add(this, x, y);
893 Dimension const & MathData::dimension(BufferView const & bv) const
895 return bv.coordCache().getArrays().dim(this);
899 int MathData::xm(BufferView const & bv) const
901 Geometry const & g = bv.coordCache().getArrays().geometry(this);
903 return g.pos.x_ + g.dim.wid / 2;
907 int MathData::ym(BufferView const & bv) const
909 Geometry const & g = bv.coordCache().getArrays().geometry(this);
911 return g.pos.y_ + (g.dim.des - g.dim.asc) / 2;
915 int MathData::xo(BufferView const & bv) const
917 return bv.coordCache().getArrays().x(this);
921 int MathData::yo(BufferView const & bv) const
923 return bv.coordCache().getArrays().y(this);
927 ostream & operator<<(ostream & os, MathData const & ar)
929 odocstringstream oss;
930 NormalStream ns(oss);
931 ns << ar;
932 return os << to_utf8(oss.str());
936 odocstream & operator<<(odocstream & os, MathData const & ar)
938 NormalStream ns(os);
939 ns << ar;
940 return os;
944 } // namespace lyx