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