]> git.lyx.org Git - lyx.git/blob - src/mathed/MathData.cpp
last commit was incomplete... not sure how I managed this..
[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                 // Cache the dimension.
246                 mi.base.bv->coordCache().arrays().add(this, dim);
247                 return;
248         }
249
250         dim.asc = 0;
251         dim.wid = 0;
252         Dimension d;
253         atom_dims_.clear();
254         //BufferView & bv  = *mi.base.bv;
255         //Buffer const & buf = bv.buffer();
256         for (size_t i = 0, n = size(); i != n; ++i) {
257                 MathAtom const & at = operator[](i);
258 #if 0
259                 MathMacro const * mac = at->asMacro();
260                 if (mac && buf.hasMacro(mac->name())) {
261                         MacroData const & tmpl = buf.getMacro(mac->name());
262                         int numargs = tmpl.numargs();
263                         if (i + numargs > n)
264                                 numargs = n - i - 1;
265                         lyxerr << "metrics:found macro: " << mac->name()
266                                 << " numargs: " << numargs << endl;
267                         if (!isInside(bv.cursor(), *this, i + 1, i + numargs + 1)) {
268                                 MathData args(begin() + i + 1, begin() + i + numargs + 1);
269                                 MathData exp;
270                                 tmpl.expand(args, exp);
271                                 mac->setExpansion(exp, args);
272                                 mac->metricsExpanded(mi, d);
273                                 dim.wid += mac->widthExpanded();
274                                 i += numargs;
275                                 continue;
276                         }
277                 }
278 #endif
279                 at->metrics(mi, d);
280                 atom_dims_.push_back(d);
281                 dim += d;
282                 if (i == n - 1)
283                         kerning_ = at->kerning();
284         }
285         // Cache the dimension.
286         mi.base.bv->coordCache().arrays().add(this, dim);
287 }
288
289
290 void MathData::draw(PainterInfo & pi, int x, int y) const
291 {
292         //lyxerr << "MathData::draw: x: " << x << " y: " << y << endl;
293         BufferView & bv  = *pi.base.bv;
294         setXY(bv, x, y);
295
296         Dimension const & dim = bv.coordCache().getArrays().dim(this);
297
298         if (empty()) {
299                 pi.pain.rectangle(x, y - dim.ascent(), dim.width(), dim.height(), Color::mathline);
300                 return;
301         }
302
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;
309
310         for (size_t i = 0, n = size(); i != n; ++i) {
311                 MathAtom const & at = operator[](i);
312 #if 0
313         Buffer const & buf = bv.buffer();
314                 // special macro handling
315                 MathMacro const * mac = at->asMacro();
316                 if (mac && buf.hasMacro(mac->name())) {
317                         MacroData const & tmpl = buf.getMacro(mac->name());
318                         int numargs = tmpl.numargs();
319                         if (i + numargs > n)
320                                 numargs = n - i - 1;
321                         if (!isInside(bv.cursor(), *this, i + 1, i + numargs + 1)) {
322                                 mac->drawExpanded(pi, x, y);
323                                 x += mac->widthExpanded();
324                                 i += numargs;
325                                 continue;
326                         }
327                 }
328 #endif
329                 bv.coordCache().insets().add(at.nucleus(), x, y);
330                 at->drawSelection(pi, x, y);
331                 at->draw(pi, x, y);
332                 x += atom_dims_[i].wid;
333         }
334 }
335
336
337 void MathData::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
338 {
339         dim.clear();
340         Dimension d;
341         for (const_iterator it = begin(); it != end(); ++it) {
342                 (*it)->metricsT(mi, d);
343                 dim += d;
344         }
345 }
346
347
348 void MathData::drawT(TextPainter & pain, int x, int y) const
349 {
350         //lyxerr << "x: " << x << " y: " << y << ' ' << pain.workAreaHeight() << endl;
351
352         // FIXME: Abdel 16/10/2006
353         // This drawT() method is never used, this is dead code.
354
355         for (const_iterator it = begin(), et = end(); it != et; ++it) {
356                 (*it)->drawT(pain, x, y);
357                 //x += (*it)->width_;
358                 x += 2;
359         }
360 }
361
362
363 int MathData::pos2x(size_type pos) const
364 {
365         return pos2x(pos, 0);
366 }
367
368
369 int MathData::pos2x(size_type pos, int glue) const
370 {
371         int x = 0;
372         size_type target = min(pos, size());
373         for (size_type i = 0; i < target; ++i) {
374                 const_iterator it = begin() + i;
375                 if ((*it)->getChar() == ' ')
376                         x += glue;
377                 //lyxerr << "char: " << (*it)->getChar()
378                 //      << "width: " << (*it)->width() << std::endl;
379                 x += atom_dims_[i].wid;
380         }
381         return x;
382 }
383
384
385 MathData::size_type MathData::x2pos(int targetx) const
386 {
387         return x2pos(targetx, 0);
388 }
389
390
391 MathData::size_type MathData::x2pos(int targetx, int glue) const
392 {
393         const_iterator it = begin();
394         int lastx = 0;
395         int currx = 0;
396         // find first position after targetx
397         for (; currx < targetx && it < end(); ++it) {
398                 lastx = currx;
399                 if ((*it)->getChar() == ' ')
400                         currx += glue;
401                 currx += atom_dims_[it - begin()].wid;
402         }
403
404         /**
405          * If we are not at the beginning of the array, go to the left
406          * of the inset if one of the following two condition holds:
407          * - the current inset is editable (so that the cursor tip is
408          *   deeper than us): in this case, we want all intermediate
409          *   cursor slices to be before insets;
410          * - the mouse is closer to the left side of the inset than to
411          *   the right one.
412          * See bug 1918 for details.
413          **/
414         if (it != begin() && currx >= targetx
415             && ((*boost::prior(it))->asNestInset()
416                 || abs(lastx - targetx) < abs(currx - targetx))) {
417                 --it;
418         }
419
420         return it - begin();
421 }
422
423
424 int MathData::dist(BufferView const & bv, int x, int y) const
425 {
426         return bv.coordCache().getArrays().squareDistance(this, x, y);
427 }
428
429
430 void MathData::setXY(BufferView & bv, int x, int y) const
431 {
432         //lyxerr << "setting position cache for MathData " << this << std::endl;
433         bv.coordCache().arrays().add(this, x, y);
434 }
435
436
437 Dimension const & MathData::dimension(BufferView const & bv) const
438 {
439         if (empty()) {
440                 static Dimension dummy;
441                 return dummy;
442         }
443
444         return bv.coordCache().getArrays().dim(this);
445 }
446
447
448 int MathData::xm(BufferView const & bv) const
449 {
450         Geometry const & g = bv.coordCache().getArrays().geometry(this);
451
452         return g.pos.x_ + g.dim.wid / 2;
453 }
454
455
456 int MathData::ym(BufferView const & bv) const
457 {
458         Geometry const & g = bv.coordCache().getArrays().geometry(this);
459
460         return g.pos.y_ + (g.dim.des - g.dim.asc) / 2;
461 }
462
463
464 int MathData::xo(BufferView const & bv) const
465 {
466         return bv.coordCache().getArrays().x(this);
467 }
468
469
470 int MathData::yo(BufferView const & bv) const
471 {
472         return bv.coordCache().getArrays().y(this);
473 }
474
475
476 std::ostream & operator<<(std::ostream & os, MathData const & ar)
477 {
478         odocstringstream oss;
479         NormalStream ns(oss);
480         ns << ar;
481         return os << to_utf8(oss.str());
482 }
483
484
485 odocstream & operator<<(odocstream & os, MathData const & ar)
486 {
487         NormalStream ns(os);
488         ns << ar;
489         return os;
490 }
491
492
493 } // namespace lyx