]> git.lyx.org Git - features.git/blob - src/mathed/MathData.C
70a256c70812a9af1c39042820e5b75171b495ee
[features.git] / src / mathed / MathData.C
1 /**
2  * \file MathData.C
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 "InsetMathMacro.h"
17 #include "InsetMathBrace.h"
18 #include "MathMacroTable.h"
19 #include "MathStream.h"
20 #include "MathSupport.h"
21 #include "MathReplace.h"
22
23 #include "BufferView.h"
24 #include "buffer.h"
25 #include "cursor.h"
26 #include "debug.h"
27 #include "LColor.h"
28
29 #include "frontends/FontMetrics.h"
30 #include "frontends/Painter.h"
31
32 #include <boost/assert.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 MathArray::MathArray(const_iterator from, const_iterator to)
46         : base_type(from, to)
47 {}
48
49
50 MathAtom & MathArray::operator[](pos_type pos)
51 {
52         BOOST_ASSERT(pos < size());
53         return base_type::operator[](pos);
54 }
55
56
57 MathAtom const & MathArray::operator[](pos_type pos) const
58 {
59         BOOST_ASSERT(pos < size());
60         return base_type::operator[](pos);
61 }
62
63
64 void MathArray::insert(size_type pos, MathAtom const & t)
65 {
66         base_type::insert(begin() + pos, t);
67 }
68
69
70 void MathArray::insert(size_type pos, MathArray const & ar)
71 {
72         BOOST_ASSERT(pos <= size());
73         base_type::insert(begin() + pos, ar.begin(), ar.end());
74 }
75
76
77 void MathArray::append(MathArray const & ar)
78 {
79         insert(size(), ar);
80 }
81
82
83 void MathArray::erase(size_type pos)
84 {
85         if (pos < size())
86                 erase(pos, pos + 1);
87 }
88
89
90 void MathArray::erase(iterator pos1, iterator pos2)
91 {
92         base_type::erase(pos1, pos2);
93 }
94
95
96 void MathArray::erase(iterator pos)
97 {
98         base_type::erase(pos);
99 }
100
101
102 void MathArray::erase(size_type pos1, size_type pos2)
103 {
104         base_type::erase(begin() + pos1, begin() + pos2);
105 }
106
107
108 void MathArray::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 MathArray::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 MathArray::validate(LaTeXFeatures & features) const
129 {
130         for (const_iterator it = begin(); it != end(); ++it)
131                 (*it)->validate(features);
132 }
133
134
135 bool MathArray::match(MathArray const & ar) const
136 {
137         return size() == ar.size() && matchpart(ar, 0);
138 }
139
140
141 bool MathArray::matchpart(MathArray 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 MathArray::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 #ifdef WITH_WARNINGS
165 #warning temporarily disabled
166         // for (const_iterator it = begin(); it != end(); ++it)
167         //      it->nucleus()->replace(rep);
168 #endif
169 }
170
171
172 bool MathArray::find1(MathArray const & ar, size_type pos) const
173 {
174         lyxerr << "finding '" << ar << "' in '" << *this << "'" << endl;
175         for (size_type i = 0, n = ar.size(); i < n; ++i)
176                 if (asString(operator[](pos + i)) != asString(ar[i]))
177                         return false;
178         return true;
179 }
180
181
182 MathArray::size_type MathArray::find(MathArray const & ar) const
183 {
184         for (int i = 0, last = size() - ar.size(); i < last; ++i)
185                 if (find1(ar, i))
186                         return i;
187         return size();
188 }
189
190
191 MathArray::size_type MathArray::find_last(MathArray const & ar) const
192 {
193         for (int i = size() - ar.size(); i >= 0; --i)
194                 if (find1(ar, i))
195                         return i;
196         return size();
197 }
198
199
200 bool MathArray::contains(MathArray const & ar) const
201 {
202         if (find(ar) != size())
203                 return true;
204         for (const_iterator it = begin(); it != end(); ++it)
205                 if ((*it)->contains(ar))
206                         return true;
207         return false;
208 }
209
210
211 void MathArray::touch() const
212 {
213 }
214
215
216 bool MathArray::metrics(MetricsInfo & mi, Dimension & dim) const
217 {
218         dim = dim_;
219         metrics(mi);
220         if (dim_ == dim)
221                 return false;
222         dim = dim_;
223         return true;
224 }
225
226
227 namespace {
228
229 bool isInside(DocIterator const & it, MathArray const & ar,
230         pos_type p1, pos_type p2)
231 {
232         for (size_t i = 0; i != it.depth(); ++i) {
233                 CursorSlice const & sl = it[i];
234                 if (sl.inset().inMathed() && &sl.cell() == &ar)
235                         return p1 <= sl.pos() && sl.pos() < p2;
236         }
237         return false;
238 }
239
240 }
241
242
243 void MathArray::metrics(MetricsInfo & mi) const
244 {
245         frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
246         dim_ = fm.dimension('I');
247         int xascent = fm.dimension('x').ascent();
248         if (xascent >= dim_.asc)
249                 xascent = (2 * dim_.asc) / 3;
250         minasc_ = xascent;
251         mindes_ = (3 * xascent) / 4;
252         slevel_ = (4 * xascent) / 5;
253         sshift_ = xascent / 4;
254         kerning_ = 0;
255
256         if (empty())
257                 return;
258
259         const_cast<MathArray*>(this)->updateMacros( mi );
260         
261         dim_.asc = 0;
262         dim_.wid = 0;
263         Dimension d;    
264         for (size_t i = 0; i != size(); ++i) {
265                 MathAtom const & at = operator[](i);
266                 at->metrics(mi, d);
267                 dim_ += d;
268                 if (i == size() - 1)
269                         kerning_ = at->kerning();
270         }
271 }
272
273
274 void MathArray::draw(PainterInfo & pi, int x, int y) const
275 {
276         //lyxerr << "MathArray::draw: x: " << x << " y: " << y << endl;
277         BufferView & bv  = *pi.base.bv;
278         setXY(bv, x, y);
279
280         if (empty()) {
281                 pi.pain.rectangle(x, y - ascent(), width(), height(), LColor::mathline);
282                 return;
283         }
284
285         // don't draw outside the workarea
286         if (y + descent() <= 0
287                 || y - ascent() >= bv.workHeight()
288                 || x + width() <= 0
289                 || x >= bv. workWidth())
290                 return;
291
292         for (size_t i = 0, n = size(); i != n; ++i) {
293                 MathAtom const & at = operator[](i);
294                 bv.coordCache().insets().add(at.nucleus(), x, y);
295                 at->drawSelection(pi, x, y);
296                 at->draw(pi, x, y);
297                 x += at->width();
298         }
299 }
300
301
302 void MathArray::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
303 {
304         dim.clear();
305         Dimension d;
306         for (const_iterator it = begin(); it != end(); ++it) {
307                 (*it)->metricsT(mi, d);
308                 dim += d;
309         }
310 }
311
312
313 void MathArray::drawT(TextPainter & pain, int x, int y) const
314 {
315         //lyxerr << "x: " << x << " y: " << y << ' ' << pain.workAreaHeight() << endl;
316
317         // FIXME: Abdel 16/10/2006
318         // This drawT() method is never used, this is dead code.
319
320         for (const_iterator it = begin(), et = end(); it != et; ++it) {
321                 (*it)->drawT(pain, x, y);
322                 //x += (*it)->width_;
323                 x += 2;
324         }
325 }
326
327
328 void MathArray::updateMacros(MetricsInfo & mi) {
329         Buffer *buf = mi.base.bv->buffer(); 
330         
331         // go over the array and look for macros
332         for (size_t i = 0; i != size(); ++i) {
333                 InsetMath * at = operator[](i).nucleus();
334                 MathMacro * macroInset = at->asMacro();
335                 if (macroInset) {
336                         // get arity of macro or 0 if unknown
337                         size_t numargs = 0;
338                         if (buf->hasMacro(macroInset->name())) {
339                                 MacroData const & macro = buf->getMacro(macroInset->name());
340                                 numargs = macro.numargs();
341                         }
342                         
343                         // arity of macro changed?
344                         if (macroInset->nargs() != numargs) {
345                                 // detach all arguments
346                                 std::vector<MathArray> detachedArgs;
347                                 macroInset->detachArguments( detachedArgs );
348                                 
349                                 // too many arguments in the macro inset?
350                                 if (detachedArgs.size() > numargs) {
351                                         // insert overlap back as braces
352                                         std::vector<MathArray> overlap(detachedArgs.begin()+numargs, detachedArgs.end());
353                                         detachedArgs.erase(detachedArgs.begin()+numargs, detachedArgs.end());
354                                         for (size_t j = 0; j < overlap.size(); ++j) {
355                                                 MathArray const & arg = overlap[j];
356                                                 if (arg.size() == 1)
357                                                         insert(i+j+1, MathAtom(new InsetMathBrace(arg)));
358                                                 else
359                                                         insert(i+j+1, arg[0]);
360                                         }
361                                         i += overlap.size();
362                                 } else {
363                                         // insert some cells from the array into the macro inset
364                                         size_t missingArgs = numargs-detachedArgs.size();
365                                         size_t j;
366                                         for (j = 0; j < missingArgs && i+1+j < size(); ++j) {
367                                                 MathAtom & cell = operator[](i+1+j);
368                                                 InsetMathBrace const * brace = cell->asBraceInset();
369                                                 if (brace) {
370                                                         // found brace, convert into argument
371                                                         detachedArgs.push_back(brace->cell(0));
372                                                 } else {
373                                                         MathArray array;
374                                                         array.insert(0, cell);
375                                                         detachedArgs.push_back(array);
376                                                 }
377                                         }
378                                         
379                                         // remove them from the array
380                                         erase(begin()+i+1, begin()+i+1+j);
381                                         
382                                         // enough for the macro inset now?
383                                         // Add some empty ones of necessary
384                                         for (; j < missingArgs; ++j)
385                                                 detachedArgs.insert(detachedArgs.end(), MathArray());
386                                 }
387                                 
388                                 // attach arguments back to macro inset
389                                 macroInset->attachArguments(detachedArgs);
390                         }
391                 }
392         }
393 }
394
395
396 int MathArray::pos2x(size_type pos) const
397 {
398         return pos2x(pos, 0);
399 }
400
401
402 int MathArray::pos2x(size_type pos, int glue) const
403 {
404         int x = 0;
405         size_type target = min(pos, size());
406         for (size_type i = 0; i < target; ++i) {
407                 const_iterator it = begin() + i;
408                 if ((*it)->getChar() == ' ')
409                         x += glue;
410                 //lyxerr << "char: " << (*it)->getChar()
411                 //      << "width: " << (*it)->width() << std::endl;
412                 x += (*it)->width();
413         }
414         return x;
415 }
416
417
418 MathArray::size_type MathArray::x2pos(int targetx) const
419 {
420         return x2pos(targetx, 0);
421 }
422
423
424 MathArray::size_type MathArray::x2pos(int targetx, int glue) const
425 {
426         const_iterator it = begin();
427         int lastx = 0;
428         int currx = 0;
429         // find first position after targetx
430         for (; currx < targetx && it < end(); ++it) {
431                 lastx = currx;
432                 if ((*it)->getChar() == ' ')
433                         currx += glue;
434                 currx += (*it)->width();
435         }
436
437         /**
438          * If we are not at the beginning of the array, go to the left
439          * of the inset if one of the following two condition holds:
440          * - the current inset is editable (so that the cursor tip is
441          *   deeper than us): in this case, we want all intermediate
442          *   cursor slices to be before insets;
443          * - the mouse is closer to the left side of the inset than to
444          *   the right one.
445          * See bug 1918 for details.
446          **/
447         if (it != begin() && currx >= targetx
448             && ((*boost::prior(it))->asNestInset()
449                 || abs(lastx - targetx) < abs(currx - targetx))) {
450                 --it;
451         }
452
453         return it - begin();
454 }
455
456
457 int MathArray::dist(BufferView const & bv, int x, int y) const
458 {
459         int xx = 0;
460         int yy = 0;
461
462         const int xo_ = xo(bv);
463         const int yo_ = yo(bv);
464
465         if (x < xo_)
466                 xx = xo_ - x;
467         else if (x > xo_ + width())
468                 xx = x - xo_ - width();
469
470         if (y < yo_ - ascent())
471                 yy = yo_ - ascent() - y;
472         else if (y > yo_ + descent())
473                 yy = y - yo_ - descent();
474
475         return xx + yy;
476 }
477
478
479 void MathArray::setXY(BufferView & bv, int x, int y) const
480 {
481         //lyxerr << "setting position cache for MathArray " << this << std::endl;
482         bv.coordCache().arrays().add(this, x, y);
483 }
484
485
486 int MathArray::xo(BufferView const & bv) const
487 {
488         return bv.coordCache().getArrays().x(this);
489 }
490
491
492 int MathArray::yo(BufferView const & bv) const
493 {
494         return bv.coordCache().getArrays().y(this);
495 }
496
497
498 std::ostream & operator<<(std::ostream & os, MathArray const & ar)
499 {
500         odocstringstream oss;
501         NormalStream ns(oss);
502         ns << ar;
503         return os << to_utf8(oss.str());
504 }
505
506
507 odocstream & operator<<(odocstream & os, MathArray const & ar)
508 {
509         NormalStream ns(os);
510         ns << ar;
511         return os;
512 }
513
514
515 } // namespace lyx