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