]> git.lyx.org Git - lyx.git/blob - src/mathed/math_scriptinset.C
and some mathed de-uglyfication
[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(LCursor & cur) const
76 {
77         cur.idx() = 2;
78         cur.pos() = 0;
79         return true;
80 }
81
82
83 bool MathScriptInset::idxLast(LCursor & cur) const
84 {
85         cur.idx() = 2;
86         cur.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(LCursor &) const
317 {
318         return false;
319 }
320
321
322 bool MathScriptInset::idxLeft(LCursor &) const
323 {
324         return false;
325 }
326
327
328 bool MathScriptInset::idxUpDown(LCursor & cur, bool up, int) const
329 {
330         if (cur.idx() == 1) {
331                 // if we are 'up' we can't go further up
332                 if (up)
333                         return false;
334                 // otherwise go to last base position
335                 cur.idx() = 2;
336                 cur.pos() = cur.lastpos();
337         }
338
339         else if (cur.idx() == 0) {
340                 // if we are 'down' we can't go further down
341                 if (!up)
342                         return false;
343                 cur.idx() = 2;
344                 cur.pos() = cur.lastpos();
345         }
346
347         else {
348                 // in nucleus
349                 // don't go up/down if there is no cell.
350                 if (!has(up))
351                         return false;
352                 // go up/down only if in the last position
353                 // or in the first position of something with displayed limits
354                 if (cur.pos() == cur.lastpos() || (cur.pos() == 0 && hasLimits())) {
355                         cur.idx() = up;
356                         cur.pos() = 0;
357                         return true;
358                 }
359                 return false;
360         }
361         return true;
362 }
363
364
365 void MathScriptInset::write(WriteStream & os) const
366 {
367         if (nuc().size()) {
368                 os << nuc();
369                 //if (nuc().back()->takesLimits()) {
370                         if (limits_ == -1)
371                                 os << "\\nolimits ";
372                         if (limits_ == 1)
373                                 os << "\\limits ";
374                 //}
375         } else {
376                 if (os.firstitem())
377                         lyxerr[Debug::MATHED] << "suppressing {} when writing"
378                                               << endl;
379                 else
380                         os << "{}";
381         }
382
383         if (hasDown() && down().size())
384                 os << "_{" << down() << '}';
385
386         if (hasUp() && up().size())
387                 os << "^{" << up() << '}';
388
389         if (lock_ && !os.latex())
390                 os << "\\lyxlock ";
391 }
392
393
394 void MathScriptInset::normalize(NormalStream & os) const
395 {
396         bool d = hasDown() && down().size();
397         bool u = hasUp() && up().size();
398
399         if (u && d)
400                 os << "[subsup ";
401         else if (u)
402                 os << "[sup ";
403         else if (d)
404                 os << "[sub ";
405
406         if (nuc().size())
407                 os << nuc() << ' ';
408         else
409                 os << "[par]";
410
411         if (u && d)
412                 os << down() << ' ' << up() << ']';
413         else if (d)
414                 os << down() << ']';
415         else if (u)
416                 os << up() << ']';
417 }
418
419
420 void MathScriptInset::maple(MapleStream & os) const
421 {
422         if (nuc().size())
423                 os << nuc();
424         if (hasDown() && down().size())
425                 os << '[' << down() << ']';
426         if (hasUp() && up().size())
427                 os << "^(" << up() << ')';
428 }
429
430
431 void MathScriptInset::mathematica(MathematicaStream & os) const
432 {
433         bool d = hasDown() && down().size();
434         bool u = hasUp() && up().size();
435
436         if (nuc().size()) {
437                 if (d)
438                         os << "Subscript[" << nuc();
439                 else
440                         os << nuc();
441         }
442
443         if (u)
444                 os << "^(" << up() << ')';
445
446         if (nuc().size())
447                 if (d)
448                         os << ',' << down() << ']';
449 }
450
451
452 void MathScriptInset::mathmlize( MathMLStream & os) const
453 {
454         bool d = hasDown() && down().size();
455         bool u = hasUp() && up().size();
456
457         if (u && d)
458                 os << MTag("msubsup");
459         else if (u)
460                 os << MTag("msup");
461         else if (d)
462                 os << MTag("msub");
463
464         if (nuc().size())
465                 os << nuc();
466         else
467                 os << "<mrow/>";
468
469         if (u && d)
470                 os << down() << up() << ETag("msubsup");
471         else if (u)
472                 os << up() << ETag("msup");
473         else if (d)
474                 os << down() << ETag("msub");
475 }
476
477
478 void MathScriptInset::octave(OctaveStream & os) const
479 {
480         if (nuc().size())
481                 os << nuc();
482         if (hasDown() && down().size())
483                 os << '[' << down() << ']';
484         if (hasUp() && up().size())
485                 os << "^(" << up() << ')';
486 }
487
488
489 void MathScriptInset::infoize(std::ostream & os) const
490 {
491         os << "Scripts";
492 }
493
494
495 void MathScriptInset::infoize2(std::ostream & os) const
496 {
497         if (limits_)
498                 os << (limits_ == 1 ? ", Displayed limits" : ", Inlined limits");
499 }
500
501
502 void MathScriptInset::notifyCursorLeaves(idx_type idx)
503 {
504         MathNestInset::notifyCursorLeaves(idx);
505
506         // remove empty scripts if possible
507         if (idx != 2 && script_[idx] && cell(idx).empty()) {
508                 cell(idx).clear();
509                 script_[idx] = false;
510         }
511 }
512
513
514 DispatchResult
515 MathScriptInset::priv_dispatch(BufferView & bv, FuncRequest const & cmd)
516 {
517         if (cmd.action == LFUN_MATH_LIMITS) {
518                 if (!cmd.argument.empty()) {
519                         if (cmd.argument == "limits")
520                                 limits_ = 1;
521                         else if (cmd.argument == "nolimits")
522                                 limits_ = -1;
523                         else
524                                 limits_ = 0;
525                 } else if (limits_ == 0)
526                         limits_ = hasLimits() ? -1 : 1;
527                 else
528                         limits_ = 0;
529                 return DispatchResult(true, true);
530         }
531
532         return MathNestInset::priv_dispatch(bv, cmd);
533 }