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