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