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