]> git.lyx.org Git - lyx.git/blob - src/mathed/MathData.cpp
Simplify Changers interface
[lyx.git] / src / mathed / MathData.cpp
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.
5  *
6  * \author André Pönitz
7  * \author Stefan Schimanski
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "MathData.h"
15
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"
25
26 #include "Buffer.h"
27 #include "BufferView.h"
28 #include "CoordCache.h"
29 #include "Cursor.h"
30
31 #include "mathed/InsetMathUnknown.h"
32
33 #include "frontends/FontMetrics.h"
34
35 #include "support/debug.h"
36 #include "support/docstream.h"
37 #include "support/gettext.h"
38 #include "support/lassert.h"
39 #include "support/lyxalgo.h"
40
41 #include <cstdlib>
42
43 using namespace std;
44
45 namespace lyx {
46
47
48 MathData::MathData(Buffer * buf, const_iterator from, const_iterator to)
49         : base_type(from, to), minasc_(0), mindes_(0), slevel_(0),
50           sshift_(0), kerning_(0), buffer_(buf)
51 {}
52
53
54 MathAtom & MathData::operator[](pos_type pos)
55 {
56         LBUFERR(pos < size());
57         return base_type::operator[](pos);
58 }
59
60
61 MathAtom const & MathData::operator[](pos_type pos) const
62 {
63         LBUFERR(pos < size());
64         return base_type::operator[](pos);
65 }
66
67
68 void MathData::insert(size_type pos, MathAtom const & t)
69 {
70         LBUFERR(pos <= size());
71         base_type::insert(begin() + pos, t);
72 }
73
74
75 void MathData::insert(size_type pos, MathData const & ar)
76 {
77         LBUFERR(pos <= size());
78         base_type::insert(begin() + pos, ar.begin(), ar.end());
79 }
80
81
82 void MathData::append(MathData const & ar)
83 {
84         insert(size(), ar);
85 }
86
87
88 void MathData::erase(size_type pos)
89 {
90         if (pos < size())
91                 erase(pos, pos + 1);
92 }
93
94
95 void MathData::erase(iterator pos1, iterator pos2)
96 {
97         base_type::erase(pos1, pos2);
98 }
99
100
101 void MathData::erase(iterator pos)
102 {
103         base_type::erase(pos);
104 }
105
106
107 void MathData::erase(size_type pos1, size_type pos2)
108 {
109         base_type::erase(begin() + pos1, begin() + pos2);
110 }
111
112
113 void MathData::dump2() const
114 {
115         odocstringstream os;
116         NormalStream ns(os);
117         for (const_iterator it = begin(); it != end(); ++it)
118                 ns << *it << ' ';
119         lyxerr << to_utf8(os.str());
120 }
121
122
123 void MathData::dump() const
124 {
125         odocstringstream os;
126         NormalStream ns(os);
127         for (const_iterator it = begin(); it != end(); ++it)
128                 ns << '<' << *it << '>';
129         lyxerr << to_utf8(os.str());
130 }
131
132
133 void MathData::validate(LaTeXFeatures & features) const
134 {
135         for (const_iterator it = begin(); it != end(); ++it)
136                 (*it)->validate(features);
137 }
138
139
140 bool MathData::match(MathData const & ar) const
141 {
142         return size() == ar.size() && matchpart(ar, 0);
143 }
144
145
146 bool MathData::matchpart(MathData const & ar, pos_type pos) const
147 {
148         if (size() < ar.size() + pos)
149                 return false;
150         const_iterator it = begin() + pos;
151         for (const_iterator jt = ar.begin(); jt != ar.end(); ++jt, ++it)
152                 if (asString(*it) != asString(*jt))
153                         return false;
154         return true;
155 }
156
157
158 void MathData::replace(ReplaceData & rep)
159 {
160         for (size_type i = 0; i < size(); ++i) {
161                 if (find1(rep.from, i)) {
162                         // match found
163                         lyxerr << "match found!" << endl;
164                         erase(i, i + rep.from.size());
165                         insert(i, rep.to);
166                 }
167         }
168
169         // FIXME: temporarily disabled
170         // for (const_iterator it = begin(); it != end(); ++it)
171         //      it->nucleus()->replace(rep);
172 }
173
174
175 bool MathData::find1(MathData const & ar, size_type pos) const
176 {
177         lyxerr << "finding '" << ar << "' in '" << *this << "'" << endl;
178         for (size_type i = 0, n = ar.size(); i < n; ++i)
179                 if (asString(operator[](pos + i)) != asString(ar[i]))
180                         return false;
181         return true;
182 }
183
184
185 MathData::size_type MathData::find(MathData const & ar) const
186 {
187         for (int i = 0, last = size() - ar.size(); i < last; ++i)
188                 if (find1(ar, i))
189                         return i;
190         return size();
191 }
192
193
194 MathData::size_type MathData::find_last(MathData const & ar) const
195 {
196         for (int i = size() - ar.size(); i >= 0; --i)
197                 if (find1(ar, i))
198                         return i;
199         return size();
200 }
201
202
203 bool MathData::contains(MathData const & ar) const
204 {
205         if (find(ar) != size())
206                 return true;
207         for (const_iterator it = begin(); it != end(); ++it)
208                 if ((*it)->contains(ar))
209                         return true;
210         return false;
211 }
212
213
214 void MathData::touch() const
215 {
216 }
217
218
219 bool MathData::addToMathRow(MathRow & mrow, MetricsInfo & mi) const
220 {
221         bool has_contents = false;
222         BufferView * bv = mi.base.bv;
223         MathData * ar = const_cast<MathData*>(this);
224         ar->updateMacros(&bv->cursor(), mi.macrocontext,
225                          InternalUpdate);
226
227         // FIXME: for completion, try to insert the relevant data in the
228         // mathrow (like is done for text rows). We could add a pair of
229         // InsetMathColor inset, but these come with extra spacing of
230         // their own.
231         DocIterator const & inlineCompletionPos = bv->inlineCompletionPos();
232         bool const has_completion = inlineCompletionPos.inMathed()
233                 && &inlineCompletionPos.cell() == this;
234         size_t const compl_pos = has_completion ? inlineCompletionPos.pos() : 0;
235
236         for (size_t i = 0 ; i < size() ; ++i) {
237                 has_contents |= (*this)[i]->addToMathRow(mrow, mi);
238                 if (i + 1 == compl_pos) {
239                         mrow.back().compl_text = bv->inlineCompletion();
240                         mrow.back().compl_unique_to = bv->inlineCompletionUniqueChars();
241                 }
242         }
243         return has_contents;
244 }
245
246
247 #if 0
248 namespace {
249
250 bool isInside(DocIterator const & it, MathData const & ar,
251         pos_type p1, pos_type p2)
252 {
253         for (size_t i = 0; i != it.depth(); ++i) {
254                 CursorSlice const & sl = it[i];
255                 if (sl.inset().inMathed() && &sl.cell() == &ar)
256                         return p1 <= sl.pos() && sl.pos() < p2;
257         }
258         return false;
259 }
260
261 }
262 #endif
263
264
265 void MathData::metrics(MetricsInfo & mi, Dimension & dim) const
266 {
267         frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
268         dim = fm.dimension('I');
269         int xascent = fm.dimension('x').ascent();
270         if (xascent >= dim.asc)
271                 xascent = (2 * dim.asc) / 3;
272         minasc_ = xascent;
273         mindes_ = (3 * xascent) / 4;
274         slevel_ = (4 * xascent) / 5;
275         sshift_ = xascent / 4;
276
277         MathRow mrow(mi, this);
278         mrow_cache_[mi.base.bv] = mrow;
279         mrow.metrics(mi, dim);
280         kerning_ = mrow.kerning(mi.base.bv);
281
282         // Cache the dimension.
283         mi.base.bv->coordCache().arrays().add(this, dim);
284 }
285
286
287 void MathData::draw(PainterInfo & pi, int const x, int const y) const
288 {
289         //lyxerr << "MathData::draw: x: " << x << " y: " << y << endl;
290         BufferView & bv  = *pi.base.bv;
291         setXY(bv, x, y);
292
293         Dimension const & dim = bv.coordCache().getArrays().dim(this);
294
295         // don't draw outside the workarea
296         if (y + dim.descent() <= 0
297                 || y - dim.ascent() >= bv.workHeight()
298                 || x + dim.width() <= 0
299                 || x >= bv. workWidth())
300                 return;
301
302         MathRow const & mrow = mrow_cache_[pi.base.bv];
303         mrow.draw(pi, x, y);
304 }
305
306
307 void MathData::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
308 {
309         dim.clear();
310         Dimension d;
311         for (const_iterator it = begin(); it != end(); ++it) {
312                 (*it)->metricsT(mi, d);
313                 dim += d;
314         }
315 }
316
317
318 void MathData::drawT(TextPainter & pain, int x, int y) const
319 {
320         //lyxerr << "x: " << x << " y: " << y << ' ' << pain.workAreaHeight() << endl;
321
322         // FIXME: Abdel 16/10/2006
323         // This drawT() method is never used, this is dead code.
324
325         for (const_iterator it = begin(), et = end(); it != et; ++it) {
326                 (*it)->drawT(pain, x, y);
327                 //x += (*it)->width_;
328                 x += 2;
329         }
330 }
331
332
333 void MathData::updateBuffer(ParIterator const & it, UpdateType utype)
334 {
335         // pass down
336         for (size_t i = 0, n = size(); i != n; ++i) {
337                 MathAtom & at = operator[](i);
338                 at.nucleus()->updateBuffer(it, utype);
339         }
340 }
341
342
343 void MathData::updateMacros(Cursor * cur, MacroContext const & mc,
344                 UpdateType utype)
345 {
346         // If we are editing a macro, we cannot update it immediately,
347         // otherwise wrong undo steps will be recorded (bug 6208).
348         InsetMath const * inmath = cur ? cur->inset().asInsetMath() : 0;
349         MathMacro const * inmacro = inmath ? inmath->asMacro() : 0;
350         docstring const edited_name = inmacro ? inmacro->name() : docstring();
351
352         // go over the array and look for macros
353         for (size_t i = 0; i < size(); ++i) {
354                 MathMacro * macroInset = operator[](i).nucleus()->asMacro();
355                 if (!macroInset || macroInset->macroName().empty()
356                                 || macroInset->macroName()[0] == '^'
357                                 || macroInset->macroName()[0] == '_'
358                                 || (macroInset->name() == edited_name
359                                     && macroInset->displayMode() ==
360                                                 MathMacro::DISPLAY_UNFOLDED))
361                         continue;
362
363                 // get macro
364                 macroInset->updateMacro(mc);
365                 size_t macroNumArgs = 0;
366                 size_t macroOptionals = 0;
367                 MacroData const * macro = macroInset->macro();
368                 if (macro) {
369                         macroNumArgs = macro->numargs();
370                         macroOptionals = macro->optionals();
371                 }
372
373                 // store old and compute new display mode
374                 MathMacro::DisplayMode newDisplayMode;
375                 MathMacro::DisplayMode oldDisplayMode = macroInset->displayMode();
376                 newDisplayMode = macroInset->computeDisplayMode();
377
378                 // arity changed or other reason to detach?
379                 if (oldDisplayMode == MathMacro::DISPLAY_NORMAL
380                     && (macroInset->arity() != macroNumArgs
381                         || macroInset->optionals() != macroOptionals
382                         || newDisplayMode == MathMacro::DISPLAY_UNFOLDED))
383                         detachMacroParameters(cur, i);
384
385                 // the macro could have been copied while resizing this
386                 macroInset = operator[](i).nucleus()->asMacro();
387
388                 // Cursor in \label?
389                 if (newDisplayMode != MathMacro::DISPLAY_UNFOLDED
390                     && oldDisplayMode == MathMacro::DISPLAY_UNFOLDED) {
391                         // put cursor in front of macro
392                         if (cur) {
393                                 int macroSlice = cur->find(macroInset);
394                                 if (macroSlice != -1)
395                                         cur->cutOff(macroSlice - 1);
396                         }
397                 }
398
399                 // update the display mode
400                 size_t appetite = macroInset->appetite();
401                 macroInset->setDisplayMode(newDisplayMode);
402
403                 // arity changed?
404                 if (newDisplayMode == MathMacro::DISPLAY_NORMAL
405                     && (macroInset->arity() != macroNumArgs
406                         || macroInset->optionals() != macroOptionals)) {
407                         // is it a virgin macro which was never attached to parameters?
408                         bool fromInitToNormalMode
409                         = (oldDisplayMode == MathMacro::DISPLAY_INIT
410                            || oldDisplayMode == MathMacro::DISPLAY_INTERACTIVE_INIT)
411                           && newDisplayMode == MathMacro::DISPLAY_NORMAL;
412
413                         // if the macro was entered interactively (i.e. not by paste or during
414                         // loading), it should not be greedy, but the cursor should
415                         // automatically jump into the macro when behind
416                         bool interactive = (oldDisplayMode == MathMacro::DISPLAY_INTERACTIVE_INIT);
417
418                         // attach parameters
419                         attachMacroParameters(cur, i, macroNumArgs, macroOptionals,
420                                 fromInitToNormalMode, interactive, appetite);
421
422                         if (cur)
423                                 cur->updateInsets(&cur->bottom().inset());
424                 }
425
426                 // Give macro the chance to adapt to new situation.
427                 // The macroInset could be invalid now because it was put into a script
428                 // inset and therefore "deep" copied. So get it again from the MathData.
429                 InsetMath * inset = operator[](i).nucleus();
430                 if (inset->asScriptInset())
431                         inset = inset->asScriptInset()->nuc()[0].nucleus();
432                 LASSERT(inset->asMacro(), continue);
433                 inset->asMacro()->updateRepresentation(cur, mc, utype);
434         }
435 }
436
437
438 void MathData::detachMacroParameters(DocIterator * cur, const size_type macroPos)
439 {
440         MathMacro * macroInset = operator[](macroPos).nucleus()->asMacro();
441         // We store this now, because the inset pointer will be invalidated in the scond loop below
442         size_t const optionals = macroInset->optionals();
443
444         // detach all arguments
445         vector<MathData> detachedArgs;
446         if (macroPos + 1 == size())
447                 // strip arguments if we are at the MathData end
448                 macroInset->detachArguments(detachedArgs, true);
449         else
450                 macroInset->detachArguments(detachedArgs, false);
451
452         // find cursor slice
453         int curMacroSlice = -1;
454         if (cur)
455                 curMacroSlice = cur->find(macroInset);
456         idx_type curMacroIdx = -1;
457         pos_type curMacroPos = -1;
458         vector<CursorSlice> argSlices;
459         if (curMacroSlice != -1) {
460                 curMacroPos = (*cur)[curMacroSlice].pos();
461                 curMacroIdx = (*cur)[curMacroSlice].idx();
462                 cur->cutOff(curMacroSlice, argSlices);
463                 cur->pop_back();
464         }
465
466         // only [] after the last non-empty argument can be dropped later
467         size_t lastNonEmptyOptional = 0;
468         for (size_t l = 0; l < detachedArgs.size() && l < optionals; ++l) {
469                 if (!detachedArgs[l].empty())
470                         lastNonEmptyOptional = l;
471         }
472
473         // optional arguments to be put back?
474         pos_type p = macroPos + 1;
475         size_t j = 0;
476         // We do not want to use macroInset below, the insert() call in
477         // the loop will invalidate it.
478         macroInset = 0;
479         for (; j < detachedArgs.size() && j < optionals; ++j) {
480                 // another non-empty parameter follows?
481                 bool canDropEmptyOptional = j >= lastNonEmptyOptional;
482
483                 // then we can drop empty optional parameters
484                 if (detachedArgs[j].empty() && canDropEmptyOptional) {
485                         if (curMacroIdx == j)
486                                 (*cur)[curMacroSlice - 1].pos() = macroPos + 1;
487                         continue;
488                 }
489
490                 // Otherwise we don't drop an empty optional, put it back normally
491                 MathData optarg;
492                 asArray(from_ascii("[]"), optarg);
493                 MathData & arg = detachedArgs[j];
494
495                 // look for "]", i.e. put a brace around?
496                 InsetMathBrace * brace = 0;
497                 for (size_t q = 0; q < arg.size(); ++q) {
498                         if (arg[q]->getChar() == ']') {
499                                 // put brace
500                                 brace = new InsetMathBrace(buffer_);
501                                 break;
502                         }
503                 }
504
505                 // put arg between []
506                 if (brace) {
507                         brace->cell(0) = arg;
508                         optarg.insert(1, MathAtom(brace));
509                 } else
510                         optarg.insert(1, arg);
511
512                 // insert it into the array
513                 insert(p, optarg);
514                 p += optarg.size();
515
516                 // cursor in macro?
517                 if (curMacroSlice == -1)
518                         continue;
519
520                 // cursor in optional argument of macro?
521                 if (curMacroIdx == j) {
522                         if (brace) {
523                                 cur->append(0, curMacroPos);
524                                 (*cur)[curMacroSlice - 1].pos() = macroPos + 2;
525                         } else
526                                 (*cur)[curMacroSlice - 1].pos() = macroPos + 2 + curMacroPos;
527                         cur->append(argSlices);
528                 } else if ((*cur)[curMacroSlice - 1].pos() >= int(p))
529                         // cursor right of macro
530                         (*cur)[curMacroSlice - 1].pos() += optarg.size();
531         }
532
533         // put them back into the MathData
534         for (; j < detachedArgs.size(); ++j, ++p) {
535                 MathData const & arg = detachedArgs[j];
536                 if (arg.size() == 1
537                     && !arg[0]->asScriptInset()
538                     && !(arg[0]->asMacro() && arg[0]->asMacro()->arity() > 0))
539                         insert(p, arg[0]);
540                 else
541                         insert(p, MathAtom(new InsetMathBrace(arg)));
542
543                 // cursor in macro?
544                 if (curMacroSlice == -1)
545                         continue;
546
547                 // cursor in j-th argument of macro?
548                 if (curMacroIdx == j) {
549                         if (operator[](p).nucleus()->asBraceInset()) {
550                                 (*cur)[curMacroSlice - 1].pos() = p;
551                                 cur->append(0, curMacroPos);
552                                 cur->append(argSlices);
553                         } else {
554                                 (*cur)[curMacroSlice - 1].pos() = p; // + macroPos;
555                                 cur->append(argSlices);
556                         }
557                 } else if ((*cur)[curMacroSlice - 1].pos() >= int(p))
558                         ++(*cur)[curMacroSlice - 1].pos();
559         }
560
561         if (cur)
562                 cur->updateInsets(&cur->bottom().inset());
563 }
564
565
566 void MathData::attachMacroParameters(Cursor * cur,
567         const size_type macroPos, const size_type macroNumArgs,
568         const int macroOptionals, const bool fromInitToNormalMode,
569         const bool interactiveInit, const size_t appetite)
570 {
571         MathMacro * macroInset = operator[](macroPos).nucleus()->asMacro();
572
573         // start at atom behind the macro again, maybe with some new arguments
574         // from the detach phase above, to add them back into the macro inset
575         size_t p = macroPos + 1;
576         vector<MathData> detachedArgs;
577         MathAtom scriptToPutAround;
578
579         // find cursor slice again of this MathData
580         int thisSlice = -1;
581         if (cur)
582                 thisSlice = cur->find(*this);
583         int thisPos = -1;
584         if (thisSlice != -1)
585                 thisPos = (*cur)[thisSlice].pos();
586
587         // find arguments behind the macro
588         if (!interactiveInit) {
589                 collectOptionalParameters(cur, macroOptionals, detachedArgs, p,
590                         scriptToPutAround, macroPos, thisPos, thisSlice);
591         }
592         collectParameters(cur, macroNumArgs, detachedArgs, p,
593                 scriptToPutAround, macroPos, thisPos, thisSlice, appetite);
594
595         // attach arguments back to macro inset
596         macroInset->attachArguments(detachedArgs, macroNumArgs, macroOptionals);
597
598         // found tail script? E.g. \foo{a}b^x
599         if (scriptToPutAround.nucleus()) {
600                 InsetMathScript * scriptInset =
601                         scriptToPutAround.nucleus()->asScriptInset();
602                 // In the math parser we remove empty braces in the base
603                 // of a script inset, but we have to restore them here.
604                 if (scriptInset->nuc().empty()) {
605                         MathData ar;
606                         scriptInset->nuc().push_back(
607                                         MathAtom(new InsetMathBrace(ar)));
608                 }
609                 // put macro into a script inset
610                 scriptInset->nuc()[0] = operator[](macroPos);
611                 operator[](macroPos) = scriptToPutAround;
612
613                 // go into the script inset nucleus
614                 if (cur && thisPos == int(macroPos))
615                         cur->append(0, 0);
616
617                 // get pointer to "deep" copied macro inset
618                 scriptInset = operator[](macroPos).nucleus()->asScriptInset();
619                 macroInset = scriptInset->nuc()[0].nucleus()->asMacro();
620         }
621
622         // remove them from the MathData
623         erase(macroPos + 1, p);
624
625         // cursor outside this MathData?
626         if (thisSlice == -1)
627                 return;
628
629         // fix cursor if right of p
630         if (thisPos >= int(p))
631                 (*cur)[thisSlice].pos() -= p - (macroPos + 1);
632
633         // was the macro inset just inserted interactively and was now folded
634         // and the cursor is just behind?
635         if ((*cur)[thisSlice].pos() == int(macroPos + 1)
636             && interactiveInit
637             && fromInitToNormalMode
638             && macroInset->arity() > 0
639             && thisSlice + 1 == int(cur->depth())) {
640                 // then enter it if the cursor was just behind
641                 (*cur)[thisSlice].pos() = macroPos;
642                 cur->push_back(CursorSlice(*macroInset));
643                 macroInset->idxFirst(*cur);
644         }
645 }
646
647
648 void MathData::collectOptionalParameters(Cursor * cur,
649         const size_type numOptionalParams, vector<MathData> & params,
650         size_t & pos, MathAtom & scriptToPutAround,
651         const pos_type macroPos, const int thisPos, const int thisSlice)
652 {
653         Buffer * buf = cur ? cur->buffer() : 0;
654         // insert optional arguments?
655         while (params.size() < numOptionalParams
656                && pos < size()
657                && !scriptToPutAround.nucleus()) {
658                 // is a [] block following which could be an optional parameter?
659                 if (operator[](pos)->getChar() != '[')
660                         break;
661
662                 // found possible optional argument, look for "]"
663                 size_t right = pos + 1;
664                 for (; right < size(); ++right) {
665                         MathAtom & cell = operator[](right);
666
667                         if (cell->getChar() == ']')
668                                 // found right end
669                                 break;
670
671                         // maybe "]" with a script around?
672                         InsetMathScript * script = cell.nucleus()->asScriptInset();
673                         if (!script)
674                                 continue;
675                         if (script->nuc().size() != 1)
676                                 continue;
677                         if (script->nuc()[0]->getChar() == ']') {
678                                 // script will be put around the macro later
679                                 scriptToPutAround = cell;
680                                 break;
681                         }
682                 }
683
684                 // found?
685                 if (right >= size()) {
686                         // no ] found, so it's not an optional argument
687                         break;
688                 }
689
690                 // add everything between [ and ] as optional argument
691                 MathData optarg(buf, begin() + pos + 1, begin() + right);
692
693                 // a brace?
694                 bool brace = false;
695                 if (optarg.size() == 1 && optarg[0]->asBraceInset()) {
696                         brace = true;
697                         params.push_back(optarg[0]->asBraceInset()->cell(0));
698                 } else
699                         params.push_back(optarg);
700
701                 // place cursor in optional argument of macro
702                 if (thisSlice != -1
703                     && thisPos >= int(pos) && thisPos <= int(right)) {
704                         int paramPos = max(0, thisPos - int(pos) - 1);
705                         vector<CursorSlice> x;
706                         cur->cutOff(thisSlice, x);
707                         (*cur)[thisSlice].pos() = macroPos;
708                         if (brace) {
709                                 paramPos = x[0].pos();
710                                 x.erase(x.begin());
711                         }
712                         cur->append(0, paramPos);
713                         cur->append(x);
714                 }
715                 pos = right + 1;
716         }
717
718         // fill up empty optional parameters
719         while (params.size() < numOptionalParams)
720                 params.push_back(MathData());
721 }
722
723
724 void MathData::collectParameters(Cursor * cur,
725         const size_type numParams, vector<MathData> & params,
726         size_t & pos, MathAtom & scriptToPutAround,
727         const pos_type macroPos, const int thisPos, const int thisSlice,
728         const size_t appetite)
729 {
730         size_t startSize = params.size();
731
732         // insert normal arguments
733         while (params.size() < numParams
734                && params.size() - startSize < appetite
735                && pos < size()
736                && !scriptToPutAround.nucleus()) {
737                 MathAtom & cell = operator[](pos);
738
739                 // fix cursor
740                 vector<CursorSlice> argSlices;
741                 int argPos = 0;
742                 if (thisSlice != -1 && thisPos == int(pos))
743                         cur->cutOff(thisSlice, argSlices);
744
745                 // which kind of parameter is it? In {}? With index x^n?
746                 InsetMathBrace const * brace = cell->asBraceInset();
747                 if (brace) {
748                         // found brace, convert into argument
749                         params.push_back(brace->cell(0));
750
751                         // cursor inside of the brace or just in front of?
752                         if (thisPos == int(pos) && !argSlices.empty()) {
753                                 argPos = argSlices[0].pos();
754                                 argSlices.erase(argSlices.begin());
755                         }
756                 } else if (cell->asScriptInset() && params.size() + 1 == numParams) {
757                         // last inset with scripts without braces
758                         // -> they belong to the macro, not the argument
759                         InsetMathScript * script = cell.nucleus()->asScriptInset();
760                         if (script->nuc().size() == 1 && script->nuc()[0]->asBraceInset())
761                                 // nucleus in brace? Unpack!
762                                 params.push_back(script->nuc()[0]->asBraceInset()->cell(0));
763                         else
764                                 params.push_back(script->nuc());
765
766                         // script will be put around below
767                         scriptToPutAround = cell;
768
769                         // this should only happen after loading, so make cursor handling simple
770                         if (thisPos >= int(macroPos) && thisPos <= int(macroPos + numParams)) {
771                                 argSlices.clear();
772                                 if (cur)
773                                         cur->append(0, 0);
774                         }
775                 } else {
776                         // the simplest case: plain inset
777                         MathData array;
778                         array.insert(0, cell);
779                         params.push_back(array);
780                 }
781
782                 // put cursor in argument again
783                 if (thisSlice != - 1 && thisPos == int(pos)) {
784                         cur->append(params.size() - 1, argPos);
785                         cur->append(argSlices);
786                         (*cur)[thisSlice].pos() = macroPos;
787                 }
788
789                 ++pos;
790         }
791 }
792
793
794 int MathData::pos2x(BufferView const * bv, size_type pos) const
795 {
796         int x = 0;
797         size_type target = min(pos, size());
798         CoordCache::Insets const & coords = bv->coordCache().getInsets();
799         for (size_type i = 0; i < target; ++i) {
800                 const_iterator it = begin() + i;
801                 //lyxerr << "char: " << (*it)->getChar()
802                 //      << "width: " << (*it)->width() << endl;
803                 x += coords.dim((*it).nucleus()).wid;
804         }
805         return x;
806 }
807
808
809 MathData::size_type MathData::x2pos(BufferView const * bv, int targetx) const
810 {
811         const_iterator it = begin();
812         int lastx = 0;
813         int currx = 0;
814         CoordCache::Insets const & coords = bv->coordCache().getInsets();
815         // find first position after targetx
816         for (; currx < targetx && it != end(); ++it) {
817                 lastx = currx;
818                 currx += coords.dim((*it).nucleus()).wid;
819         }
820
821         /**
822          * If we are not at the beginning of the array, go to the left
823          * of the inset if one of the following two condition holds:
824          * - the current inset is editable (so that the cursor tip is
825          *   deeper than us): in this case, we want all intermediate
826          *   cursor slices to be before insets;
827          * - the mouse is closer to the left side of the inset than to
828          *   the right one.
829          * See bug 1918 for details.
830          **/
831         if (it != begin() && currx >= targetx
832             && ((*prev(it, 1))->asNestInset()
833                 || abs(lastx - targetx) < abs(currx - targetx))) {
834                 --it;
835         }
836
837         return it - begin();
838 }
839
840
841 int MathData::dist(BufferView const & bv, int x, int y) const
842 {
843         return bv.coordCache().getArrays().squareDistance(this, x, y);
844 }
845
846
847 void MathData::setXY(BufferView & bv, int x, int y) const
848 {
849         //lyxerr << "setting position cache for MathData " << this << endl;
850         bv.coordCache().arrays().add(this, x, y);
851 }
852
853
854 Dimension const & MathData::dimension(BufferView const & bv) const
855 {
856         return bv.coordCache().getArrays().dim(this);
857 }
858
859
860 int MathData::xm(BufferView const & bv) const
861 {
862         Geometry const & g = bv.coordCache().getArrays().geometry(this);
863
864         return g.pos.x_ + g.dim.wid / 2;
865 }
866
867
868 int MathData::ym(BufferView const & bv) const
869 {
870         Geometry const & g = bv.coordCache().getArrays().geometry(this);
871
872         return g.pos.y_ + (g.dim.des - g.dim.asc) / 2;
873 }
874
875
876 int MathData::xo(BufferView const & bv) const
877 {
878         return bv.coordCache().getArrays().x(this);
879 }
880
881
882 int MathData::yo(BufferView const & bv) const
883 {
884         return bv.coordCache().getArrays().y(this);
885 }
886
887
888 ostream & operator<<(ostream & os, MathData const & ar)
889 {
890         odocstringstream oss;
891         NormalStream ns(oss);
892         ns << ar;
893         return os << to_utf8(oss.str());
894 }
895
896
897 odocstream & operator<<(odocstream & os, MathData const & ar)
898 {
899         NormalStream ns(os);
900         ns << ar;
901         return os;
902 }
903
904
905 } // namespace lyx