]> git.lyx.org Git - lyx.git/blob - src/mathed/MathData.cpp
86741882bdaf4bcc83230ece1b1ac654a81de0f1
[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  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "MathData.h"
14 #include "InsetMathFont.h"
15 #include "InsetMathScript.h"
16 #include "MathMacro.h"
17 #include "MacroTable.h"
18 #include "MathStream.h"
19 #include "MathSupport.h"
20 #include "ReplaceData.h"
21
22 #include "BufferView.h"
23 #include "Buffer.h"
24 #include "Cursor.h"
25 #include "debug.h"
26 #include "Color.h"
27
28 #include "frontends/FontMetrics.h"
29 #include "frontends/Painter.h"
30
31 #include <boost/assert.hpp>
32 #include <boost/next_prior.hpp>
33
34
35 namespace lyx {
36
37 using std::abs;
38 using std::endl;
39 using std::min;
40 using std::ostringstream;
41 using std::string;
42 using std::vector;
43
44
45 MathData::MathData(const_iterator from, const_iterator to)
46         : base_type(from, to)
47 {}
48
49
50 MathAtom & MathData::operator[](pos_type pos)
51 {
52         BOOST_ASSERT(pos < size());
53         return base_type::operator[](pos);
54 }
55
56
57 MathAtom const & MathData::operator[](pos_type pos) const
58 {
59         BOOST_ASSERT(pos < size());
60         return base_type::operator[](pos);
61 }
62
63
64 void MathData::insert(size_type pos, MathAtom const & t)
65 {
66         base_type::insert(begin() + pos, t);
67 }
68
69
70 void MathData::insert(size_type pos, MathData const & ar)
71 {
72         BOOST_ASSERT(pos <= size());
73         base_type::insert(begin() + pos, ar.begin(), ar.end());
74 }
75
76
77 void MathData::append(MathData const & ar)
78 {
79         insert(size(), ar);
80 }
81
82
83 void MathData::erase(size_type pos)
84 {
85         if (pos < size())
86                 erase(pos, pos + 1);
87 }
88
89
90 void MathData::erase(iterator pos1, iterator pos2)
91 {
92         base_type::erase(pos1, pos2);
93 }
94
95
96 void MathData::erase(iterator pos)
97 {
98         base_type::erase(pos);
99 }
100
101
102 void MathData::erase(size_type pos1, size_type pos2)
103 {
104         base_type::erase(begin() + pos1, begin() + pos2);
105 }
106
107
108 void MathData::dump2() const
109 {
110         odocstringstream os;
111         NormalStream ns(os);
112         for (const_iterator it = begin(); it != end(); ++it)
113                 ns << *it << ' ';
114         lyxerr << to_utf8(os.str());
115 }
116
117
118 void MathData::dump() const
119 {
120         odocstringstream os;
121         NormalStream ns(os);
122         for (const_iterator it = begin(); it != end(); ++it)
123                 ns << '<' << *it << '>';
124         lyxerr << to_utf8(os.str());
125 }
126
127
128 void MathData::validate(LaTeXFeatures & features) const
129 {
130         for (const_iterator it = begin(); it != end(); ++it)
131                 (*it)->validate(features);
132 }
133
134
135 bool MathData::match(MathData const & ar) const
136 {
137         return size() == ar.size() && matchpart(ar, 0);
138 }
139
140
141 bool MathData::matchpart(MathData const & ar, pos_type pos) const
142 {
143         if (size() < ar.size() + pos)
144                 return false;
145         const_iterator it = begin() + pos;
146         for (const_iterator jt = ar.begin(); jt != ar.end(); ++jt, ++it)
147                 if (asString(*it) != asString(*jt))
148                         return false;
149         return true;
150 }
151
152
153 void MathData::replace(ReplaceData & rep)
154 {
155         for (size_type i = 0; i < size(); ++i) {
156                 if (find1(rep.from, i)) {
157                         // match found
158                         lyxerr << "match found!" << endl;
159                         erase(i, i + rep.from.size());
160                         insert(i, rep.to);
161                 }
162         }
163
164         // FIXME: temporarily disabled
165         // for (const_iterator it = begin(); it != end(); ++it)
166         //      it->nucleus()->replace(rep);
167 }
168
169
170 bool MathData::find1(MathData const & ar, size_type pos) const
171 {
172         lyxerr << "finding '" << ar << "' in '" << *this << "'" << endl;
173         for (size_type i = 0, n = ar.size(); i < n; ++i)
174                 if (asString(operator[](pos + i)) != asString(ar[i]))
175                         return false;
176         return true;
177 }
178
179
180 MathData::size_type MathData::find(MathData const & ar) const
181 {
182         for (int i = 0, last = size() - ar.size(); i < last; ++i)
183                 if (find1(ar, i))
184                         return i;
185         return size();
186 }
187
188
189 MathData::size_type MathData::find_last(MathData const & ar) const
190 {
191         for (int i = size() - ar.size(); i >= 0; --i)
192                 if (find1(ar, i))
193                         return i;
194         return size();
195 }
196
197
198 bool MathData::contains(MathData const & ar) const
199 {
200         if (find(ar) != size())
201                 return true;
202         for (const_iterator it = begin(); it != end(); ++it)
203                 if ((*it)->contains(ar))
204                         return true;
205         return false;
206 }
207
208
209 void MathData::touch() const
210 {
211 }
212
213
214 namespace {
215
216 bool isInside(DocIterator const & it, MathData const & ar,
217         pos_type p1, pos_type p2)
218 {
219         for (size_t i = 0; i != it.depth(); ++i) {
220                 CursorSlice const & sl = it[i];
221                 if (sl.inset().inMathed() && &sl.cell() == &ar)
222                         return p1 <= sl.pos() && sl.pos() < p2;
223         }
224         return false;
225 }
226
227 }
228
229
230
231 void MathData::metrics(MetricsInfo & mi, Dimension & dim) const
232 {
233         frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
234         dim = fm.dimension('I');
235         int xascent = fm.dimension('x').ascent();
236         if (xascent >= dim.asc)
237                 xascent = (2 * dim.asc) / 3;
238         minasc_ = xascent;
239         mindes_ = (3 * xascent) / 4;
240         slevel_ = (4 * xascent) / 5;
241         sshift_ = xascent / 4;
242         kerning_ = 0;
243
244         if (empty())
245                 return;
246
247         dim.asc = 0;
248         dim.wid = 0;
249         Dimension d;
250         atom_dims_.clear();
251         //BufferView & bv  = *mi.base.bv;
252         //Buffer const & buf = bv.buffer();
253         for (size_t i = 0, n = size(); i != n; ++i) {
254                 MathAtom const & at = operator[](i);
255 #if 0
256                 MathMacro const * mac = at->asMacro();
257                 if (mac && buf.hasMacro(mac->name())) {
258                         MacroData const & tmpl = buf.getMacro(mac->name());
259                         int numargs = tmpl.numargs();
260                         if (i + numargs > n)
261                                 numargs = n - i - 1;
262                         lyxerr << "metrics:found macro: " << mac->name()
263                                 << " numargs: " << numargs << endl;
264                         if (!isInside(bv.cursor(), *this, i + 1, i + numargs + 1)) {
265                                 MathData args(begin() + i + 1, begin() + i + numargs + 1);
266                                 MathData exp;
267                                 tmpl.expand(args, exp);
268                                 mac->setExpansion(exp, args);
269                                 mac->metricsExpanded(mi, d);
270                                 dim.wid += mac->widthExpanded();
271                                 i += numargs;
272                                 continue;
273                         }
274                 }
275 #endif
276                 at->metrics(mi, d);
277                 atom_dims_.push_back(d);
278                 dim += d;
279                 if (i == n - 1)
280                         kerning_ = at->kerning();
281         }
282         // Cache the dimension.
283         mi.base.bv->coordCache().arrays().add(this, dim);
284 }
285
286
287 void MathData::draw(PainterInfo & pi, int x, int 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         if (empty()) {
296                 pi.pain.rectangle(x, y - dim.ascent(), dim.width(), dim.height(), Color::mathline);
297                 return;
298         }
299
300         // don't draw outside the workarea
301         if (y + dim.descent() <= 0
302                 || y - dim.ascent() >= bv.workHeight()
303                 || x + dim.width() <= 0
304                 || x >= bv. workWidth())
305                 return;
306
307         for (size_t i = 0, n = size(); i != n; ++i) {
308                 MathAtom const & at = operator[](i);
309 #if 0
310         Buffer const & buf = bv.buffer();
311                 // special macro handling
312                 MathMacro const * mac = at->asMacro();
313                 if (mac && buf.hasMacro(mac->name())) {
314                         MacroData const & tmpl = buf.getMacro(mac->name());
315                         int numargs = tmpl.numargs();
316                         if (i + numargs > n)
317                                 numargs = n - i - 1;
318                         if (!isInside(bv.cursor(), *this, i + 1, i + numargs + 1)) {
319                                 mac->drawExpanded(pi, x, y);
320                                 x += mac->widthExpanded();
321                                 i += numargs;
322                                 continue;
323                         }
324                 }
325 #endif
326                 bv.coordCache().insets().add(at.nucleus(), x, y);
327                 at->drawSelection(pi, x, y);
328                 at->draw(pi, x, y);
329                 x += atom_dims_[i].wid;
330         }
331 }
332
333
334 void MathData::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
335 {
336         dim.clear();
337         Dimension d;
338         for (const_iterator it = begin(); it != end(); ++it) {
339                 (*it)->metricsT(mi, d);
340                 dim += d;
341         }
342 }
343
344
345 void MathData::drawT(TextPainter & pain, int x, int y) const
346 {
347         //lyxerr << "x: " << x << " y: " << y << ' ' << pain.workAreaHeight() << endl;
348
349         // FIXME: Abdel 16/10/2006
350         // This drawT() method is never used, this is dead code.
351
352         for (const_iterator it = begin(), et = end(); it != et; ++it) {
353                 (*it)->drawT(pain, x, y);
354                 //x += (*it)->width_;
355                 x += 2;
356         }
357 }
358
359
360 int MathData::pos2x(size_type pos) const
361 {
362         return pos2x(pos, 0);
363 }
364
365
366 int MathData::pos2x(size_type pos, int glue) const
367 {
368         int x = 0;
369         size_type target = min(pos, size());
370         for (size_type i = 0; i < target; ++i) {
371                 const_iterator it = begin() + i;
372                 if ((*it)->getChar() == ' ')
373                         x += glue;
374                 //lyxerr << "char: " << (*it)->getChar()
375                 //      << "width: " << (*it)->width() << std::endl;
376                 x += atom_dims_[i].wid;
377         }
378         return x;
379 }
380
381
382 MathData::size_type MathData::x2pos(int targetx) const
383 {
384         return x2pos(targetx, 0);
385 }
386
387
388 MathData::size_type MathData::x2pos(int targetx, int glue) const
389 {
390         const_iterator it = begin();
391         int lastx = 0;
392         int currx = 0;
393         // find first position after targetx
394         for (; currx < targetx && it < end(); ++it) {
395                 lastx = currx;
396                 if ((*it)->getChar() == ' ')
397                         currx += glue;
398                 currx += atom_dims_[it - begin()].wid;
399         }
400
401         /**
402          * If we are not at the beginning of the array, go to the left
403          * of the inset if one of the following two condition holds:
404          * - the current inset is editable (so that the cursor tip is
405          *   deeper than us): in this case, we want all intermediate
406          *   cursor slices to be before insets;
407          * - the mouse is closer to the left side of the inset than to
408          *   the right one.
409          * See bug 1918 for details.
410          **/
411         if (it != begin() && currx >= targetx
412             && ((*boost::prior(it))->asNestInset()
413                 || abs(lastx - targetx) < abs(currx - targetx))) {
414                 --it;
415         }
416
417         return it - begin();
418 }
419
420
421 int MathData::dist(BufferView const & bv, int x, int y) const
422 {
423         return bv.coordCache().getArrays().squareDistance(this, x, y);
424 }
425
426
427 void MathData::setXY(BufferView & bv, int x, int y) const
428 {
429         //lyxerr << "setting position cache for MathData " << this << std::endl;
430         bv.coordCache().arrays().add(this, x, y);
431 }
432
433
434 Dimension const & MathData::dimension(BufferView const & bv) const
435 {
436         if (empty()) {
437                 static Dimension dummy;
438                 return dummy;
439         }
440
441         return bv.coordCache().getArrays().dim(this);
442 }
443
444
445 int MathData::xm(BufferView const & bv) const
446 {
447         Geometry const & g = bv.coordCache().getArrays().geometry(this);
448
449         return g.pos.x_ + g.dim.wid / 2;
450 }
451
452
453 int MathData::ym(BufferView const & bv) const
454 {
455         Geometry const & g = bv.coordCache().getArrays().geometry(this);
456
457         return g.pos.y_ + (g.dim.des - g.dim.asc) / 2;
458 }
459
460
461 int MathData::xo(BufferView const & bv) const
462 {
463         return bv.coordCache().getArrays().x(this);
464 }
465
466
467 int MathData::yo(BufferView const & bv) const
468 {
469         return bv.coordCache().getArrays().y(this);
470 }
471
472
473 std::ostream & operator<<(std::ostream & os, MathData const & ar)
474 {
475         odocstringstream oss;
476         NormalStream ns(oss);
477         ns << ar;
478         return os << to_utf8(oss.str());
479 }
480
481
482 odocstream & operator<<(odocstream & os, MathData const & ar)
483 {
484         NormalStream ns(os);
485         ns << ar;
486         return os;
487 }
488
489
490 } // namespace lyx