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