]> git.lyx.org Git - lyx.git/blob - src/mathed/math_scriptinset.C
Change Assert to BOOST_ASSERT.
[lyx.git] / src / mathed / math_scriptinset.C
1 /**
2  * \file math_scriptinset.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 "math_scriptinset.h"
14 #include "math_data.h"
15 #include "math_mathmlstream.h"
16 #include "math_support.h"
17 #include "math_symbolinset.h"
18 #include "debug.h"
19 #include "funcrequest.h"
20
21 using std::max;
22 using std::auto_ptr;
23 using std::endl;
24
25
26 MathScriptInset::MathScriptInset()
27         : MathNestInset(3), limits_(0)
28 {
29         script_[0] = false;
30         script_[1] = false;
31 }
32
33
34 MathScriptInset::MathScriptInset(bool up)
35         : MathNestInset(3), limits_(0)
36 {
37         script_[0] = !up;
38         script_[1] = up;
39 }
40
41
42 MathScriptInset::MathScriptInset(MathAtom const & at, bool up)
43         : MathNestInset(3), limits_(0)
44 {
45         script_[0] = !up;
46         script_[1] = up;
47         cell(2).push_back(at);
48 }
49
50
51
52 auto_ptr<InsetBase> MathScriptInset::clone() const
53 {
54         return auto_ptr<InsetBase>(new MathScriptInset(*this));
55 }
56
57
58 MathScriptInset const * MathScriptInset::asScriptInset() const
59 {
60         return this;
61 }
62
63
64 MathScriptInset * MathScriptInset::asScriptInset()
65 {
66         return this;
67 }
68
69
70 bool MathScriptInset::idxFirst(idx_type & idx, pos_type & pos) const
71 {
72         idx = 2;
73         pos = 0;
74         return true;
75 }
76
77
78 bool MathScriptInset::idxLast(idx_type & idx, pos_type & pos) const
79 {
80         idx = 2;
81         pos = nuc().size();
82         return true;
83 }
84
85
86 MathArray const & MathScriptInset::down() const
87 {
88         return cell(0);
89 }
90
91
92 MathArray & MathScriptInset::down()
93 {
94         return cell(0);
95 }
96
97
98 MathArray const & MathScriptInset::up() const
99 {
100         return cell(1);
101 }
102
103
104 MathArray & MathScriptInset::up()
105 {
106         return cell(1);
107 }
108
109
110 void MathScriptInset::ensure(bool up)
111 {
112         script_[up] = true;
113 }
114
115
116 MathArray const & MathScriptInset::nuc() const
117 {
118         return cell(2);
119 }
120
121
122 MathArray & MathScriptInset::nuc()
123 {
124         return cell(2);
125 }
126
127
128 int MathScriptInset::dy0() const
129 {
130         int nd = ndes();
131         if (!hasDown())
132                 return nd;
133         int des = down().ascent();
134         if (hasLimits())
135                 des += nd + 2;
136         else
137                 des = max(des, nd);
138         return des;
139 }
140
141
142 int MathScriptInset::dy1() const
143 {
144         int na = nasc();
145         if (!hasUp())
146                 return na;
147         int asc = up().descent();
148         if (hasLimits())
149                 asc += na + 2;
150         else
151                 asc = max(asc, na);
152         asc = max(asc, 5);
153         return asc;
154 }
155
156
157 int MathScriptInset::dx0() const
158 {
159         BOOST_ASSERT(hasDown());
160         return hasLimits() ? (dim_.wid - down().width()) / 2 : nwid();
161 }
162
163
164 int MathScriptInset::dx1() const
165 {
166         BOOST_ASSERT(hasUp());
167         return hasLimits() ? (dim_.wid - up().width()) / 2 : nwid();
168 }
169
170
171 int MathScriptInset::dxx() const
172 {
173         return hasLimits() ? (dim_.wid - nwid()) / 2  :  0;
174 }
175
176
177 int MathScriptInset::nwid() const
178 {
179         return nuc().size() ? nuc().width() : 2;
180 }
181
182
183 int MathScriptInset::nasc() const
184 {
185         return nuc().size() ? nuc().ascent() : 5;
186 }
187
188
189 int MathScriptInset::ndes() const
190 {
191         return nuc().size() ? nuc().descent() : 0;
192 }
193
194
195 void MathScriptInset::metrics(MetricsInfo & mi, Dimension & dim) const
196 {
197         cell(2).metrics(mi);
198         ScriptChanger dummy(mi.base);
199         cell(0).metrics(mi);
200         cell(1).metrics(mi);
201         dim_.wid = 0;
202         if (hasLimits()) {
203                 dim_.wid = nwid();
204                 if (hasUp())
205                         dim_.wid = max(dim_.wid, up().width());
206                 if (hasDown())
207                         dim_.wid = max(dim_.wid, down().width());
208         } else {
209                 if (hasUp())
210                         dim_.wid = max(dim_.wid, up().width());
211                 if (hasDown())
212                         dim_.wid = max(dim_.wid, down().width());
213                 dim_.wid += nwid();
214         }
215         dim_.asc = dy1() + (hasUp() ? up().ascent() : 0);
216         dim_.des = dy0() + (hasDown() ? down().descent() : 0);
217         metricsMarkers();
218         dim = dim_;
219 }
220
221
222 void MathScriptInset::draw(PainterInfo & pi, int x, int y) const
223 {
224         if (nuc().size())
225                 nuc().draw(pi, x + dxx(), y);
226         else {
227                 nuc().setXY(x + dxx(), y);
228                 if (editing())
229                         drawStr(pi, pi.base.font, x + dxx(), y, ".");
230         }
231         ScriptChanger dummy(pi.base);
232         if (hasUp())
233                 up().draw(pi, x + dx1(), y - dy1());
234         if (hasDown())
235                 down().draw(pi, x + dx0(), y + dy0());
236         drawMarkers(pi, x, y);
237 }
238
239
240 void MathScriptInset::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
241 {
242         if (hasUp())
243                 up().metricsT(mi, dim);
244         if (hasDown())
245                 down().metricsT(mi, dim);
246         nuc().metricsT(mi, dim);
247 }
248
249
250 void MathScriptInset::drawT(TextPainter & pain, int x, int y) const
251 {
252         if (nuc().size())
253                 nuc().drawT(pain, x + dxx(), y);
254         if (hasUp())
255                 up().drawT(pain, x + dx1(), y - dy1());
256         if (hasDown())
257                 down().drawT(pain, x + dx0(), y + dy0());
258 }
259
260
261
262 bool MathScriptInset::hasLimits() const
263 {
264         // obvious cases
265         if (limits_ == 1)
266                 return true;
267         if (limits_ == -1)
268                 return false;
269
270         // we can only display limits if the nucleus wants some
271         if (!nuc().size())
272                 return false;
273         if (!nuc().back()->isScriptable())
274                 return false;
275
276         // per default \int has limits beside the \int even in displayed formulas
277         if (nuc().back()->asSymbolInset())
278                 if (nuc().back()->asSymbolInset()->name().find("int") != string::npos)
279                         return false;
280
281         // assume "real" limits for everything else
282         return true;
283 }
284
285
286 void MathScriptInset::removeScript(bool up)
287 {
288         cell(up).clear();
289         script_[up] = false;
290 }
291
292
293 bool MathScriptInset::has(bool up) const
294 {
295         return script_[up];
296 }
297
298
299 bool MathScriptInset::hasUp() const
300 {
301         return script_[1];
302 }
303
304
305 bool MathScriptInset::hasDown() const
306 {
307         return script_[0];
308 }
309
310
311 bool MathScriptInset::idxRight(idx_type &, pos_type &) const
312 {
313         return false;
314 }
315
316
317 bool MathScriptInset::idxLeft(idx_type &, pos_type &) const
318 {
319         return false;
320 }
321
322
323 bool MathScriptInset::idxUpDown(idx_type & idx, pos_type & pos, bool up,
324         int) const
325 {
326         if (idx == 1) {
327                 // if we are 'up' we can't go further up
328                 if (up)
329                         return false;
330                 // otherwise go to last base position
331                 idx = 2;
332                 pos = cell(2).size();
333         }
334
335         else if (idx == 0) {
336                 // if we are 'down' we can't go further down
337                 if (!up)
338                         return false;
339                 idx = 2;
340                 pos = cell(2).size();
341         }
342
343         else {
344                 // in nucleus
345                 // don't go up/down if there is no cell.
346                 if (!has(up))
347                         return false;
348                 // go up/down only if in the last position
349                 // or in the first position of something with displayed limits
350                 if (pos == cell(2).size() || (pos == 0 && hasLimits())) {
351                         idx = up;
352                         pos = 0;
353                         return true;
354                 }
355                 return false;
356         }
357         return true;
358 }
359
360
361 void MathScriptInset::write(WriteStream & os) const
362 {
363         if (nuc().size()) {
364                 os << nuc();
365                 //if (nuc().back()->takesLimits()) {
366                         if (limits_ == -1)
367                                 os << "\\nolimits ";
368                         if (limits_ == 1)
369                                 os << "\\limits ";
370                 //}
371         } else {
372                 if (os.firstitem())
373                         lyxerr[Debug::MATHED] << "suppressing {} when writing"
374                                               << endl;
375                 else
376                         os << "{}";
377         }
378
379         if (hasDown() && down().size())
380                 os << "_{" << down() << '}';
381
382         if (hasUp() && up().size())
383                 os << "^{" << up() << '}';
384
385         if (lock_ && !os.latex())
386                 os << "\\lyxlock ";
387 }
388
389
390 void MathScriptInset::normalize(NormalStream & os) const
391 {
392         bool d = hasDown() && down().size();
393         bool u = hasUp() && up().size();
394
395         if (u && d)
396                 os << "[subsup ";
397         else if (u)
398                 os << "[sup ";
399         else if (d)
400                 os << "[sub ";
401
402         if (nuc().size())
403                 os << nuc() << ' ';
404         else
405                 os << "[par]";
406
407         if (u && d)
408                 os << down() << ' ' << up() << ']';
409         else if (d)
410                 os << down() << ']';
411         else if (u)
412                 os << up() << ']';
413 }
414
415
416 void MathScriptInset::maple(MapleStream & os) const
417 {
418         if (nuc().size())
419                 os << nuc();
420         if (hasDown() && down().size())
421                 os << '[' << down() << ']';
422         if (hasUp() && up().size())
423                 os << "^(" << up() << ')';
424 }
425
426
427 void MathScriptInset::mathematica(MathematicaStream & os) const
428 {
429         bool d = hasDown() && down().size();
430         bool u = hasUp() && up().size();
431
432         if (nuc().size()) {
433                 if (d)
434                         os << "Subscript[" << nuc();
435                 else
436                         os << nuc();
437         }
438
439         if (u)
440                 os << "^(" << up() << ')';
441
442         if (nuc().size())
443                 if (d)
444                         os << ',' << down() << ']';
445 }
446
447
448 void MathScriptInset::mathmlize( MathMLStream & os) const
449 {
450         bool d = hasDown() && down().size();
451         bool u = hasUp() && up().size();
452
453         if (u && d)
454                 os << MTag("msubsup");
455         else if (u)
456                 os << MTag("msup");
457         else if (d)
458                 os << MTag("msub");
459
460         if (nuc().size())
461                 os << nuc();
462         else
463                 os << "<mrow/>";
464
465         if (u && d)
466                 os << down() << up() << ETag("msubsup");
467         else if (u)
468                 os << up() << ETag("msup");
469         else if (d)
470                 os << down() << ETag("msub");
471 }
472
473
474 void MathScriptInset::octave(OctaveStream & os) const
475 {
476         if (nuc().size())
477                 os << nuc();
478         if (hasDown() && down().size())
479                 os << '[' << down() << ']';
480         if (hasUp() && up().size())
481                 os << "^(" << up() << ')';
482 }
483
484
485 void MathScriptInset::infoize(std::ostream & os) const
486 {
487         os << "Scripts";
488 }
489
490
491 void MathScriptInset::infoize2(std::ostream & os) const
492 {
493         if (limits_)
494                 os << (limits_ == 1 ? ", Displayed limits" : ", Inlined limits");
495 }
496
497
498 void MathScriptInset::notifyCursorLeaves(idx_type idx)
499 {
500         MathNestInset::notifyCursorLeaves(idx);
501
502         // remove empty scripts if possible
503         if (idx != 2 && script_[idx] && cell(idx).empty()) {
504                 cell(idx).clear();
505                 script_[idx] = false;
506         }
507 }
508
509
510 dispatch_result MathScriptInset::dispatch
511         (FuncRequest const & cmd, idx_type & idx, pos_type & pos)
512 {
513         if (cmd.action == LFUN_MATH_LIMITS) {
514                 if (!cmd.argument.empty()) {
515                         if (cmd.argument == "limits")
516                                 limits_ = 1;
517                         else if (cmd.argument == "nolimits")
518                                 limits_ = -1;
519                         else
520                                 limits_ = 0;
521                 } else if (limits_ == 0)
522                         limits_ =  (hasLimits()) ? -1 : 1;
523                 else
524                         limits_ = 0;
525                 return DISPATCHED;
526         }
527
528         return MathNestInset::dispatch(cmd, idx, pos);
529 }