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