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