]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathScript.C
* output_plaintext.C: cosmetics in comment: line length cannot be < 0
[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 bool 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         if (dim_ == dim)
242                 return false;
243         dim_ = dim;
244         return true;
245 }
246
247
248 void InsetMathScript::draw(PainterInfo & pi, int x, int y) const
249 {
250         if (nuc().size())
251                 nuc().draw(pi, x + dxx(), y);
252         else {
253                 nuc().setXY(*pi.base.bv, x + dxx(), y);
254                 if (editing(pi.base.bv))
255                         pi.draw(x + dxx(), y, char_type('.'));
256         }
257         ScriptChanger dummy(pi.base);
258         if (hasUp())
259                 up().draw(pi, x + dx1(), y - dy1());
260         if (hasDown())
261                 down().draw(pi, x + dx0(), y + dy0());
262         drawMarkers(pi, x, y);
263 }
264
265
266 void InsetMathScript::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
267 {
268         if (hasUp())
269                 up().metricsT(mi, dim);
270         if (hasDown())
271                 down().metricsT(mi, dim);
272         nuc().metricsT(mi, dim);
273 }
274
275
276 void InsetMathScript::drawT(TextPainter & pain, int x, int y) const
277 {
278         if (nuc().size())
279                 nuc().drawT(pain, x + dxx(), y);
280         if (hasUp())
281                 up().drawT(pain, x + dx1(), y - dy1());
282         if (hasDown())
283                 down().drawT(pain, x + dx0(), y + dy0());
284 }
285
286
287
288 bool InsetMathScript::hasLimits() const
289 {
290         // obvious cases
291         if (limits_ == 1)
292                 return true;
293         if (limits_ == -1)
294                 return false;
295
296         // we can only display limits if the nucleus wants some
297         if (!nuc().size())
298                 return false;
299         if (!nuc().back()->isScriptable())
300                 return false;
301
302         if (nuc().back()->asSymbolInset()) {
303                 // \intop is an alias for \int\limits, \ointop == \oint\limits
304                 if (nuc().back()->asSymbolInset()->name().find(from_ascii("intop")) != string::npos)
305                         return true;
306                 // per default \int has limits beside the \int even in displayed formulas
307                 if (nuc().back()->asSymbolInset()->name().find(from_ascii("int")) != string::npos)
308                         return false;
309         }
310
311         // assume "real" limits for everything else
312         return true;
313 }
314
315
316 void InsetMathScript::removeScript(bool up)
317 {
318         if (nargs() == 2) {
319                 if (up == cell_1_is_up_)
320                         cells_.pop_back();
321         } else if (nargs() == 3) {
322                 if (up == true) {
323                         swap(cells_[1], cells_[2]);
324                         cell_1_is_up_ = false;
325                 } else {
326                         cell_1_is_up_ = true;
327                 }
328                 cells_.pop_back();
329         }
330 }
331
332
333 bool InsetMathScript::has(bool up) const
334 {
335         return idxOfScript(up);
336 }
337
338
339 bool InsetMathScript::hasUp() const
340 {
341         //lyxerr << "1up: " << bool(cell_1_is_up_) << endl;
342         //lyxerr << "hasUp: " << bool(idxOfScript(true)) << endl;
343         return idxOfScript(true);
344 }
345
346
347 bool InsetMathScript::hasDown() const
348 {
349         //lyxerr << "1up: " << bool(cell_1_is_up_) << endl;
350         //lyxerr << "hasDown: " << bool(idxOfScript(false)) << endl;
351         return idxOfScript(false);
352 }
353
354
355 InsetBase::idx_type InsetMathScript::idxOfScript(bool up) const
356 {
357         if (nargs() == 1)
358                 return 0;
359         if (nargs() == 2)
360                 return (cell_1_is_up_ == up) ? 1 : 0;
361         if (nargs() == 3)
362                 return up ? 1 : 2;
363         BOOST_ASSERT(false);
364         // Silence compiler
365         return 0;
366 }
367
368
369 bool InsetMathScript::idxRight(LCursor &) const
370 {
371         return false;
372 }
373
374
375 bool InsetMathScript::idxLeft(LCursor &) const
376 {
377         return false;
378 }
379
380
381 bool InsetMathScript::idxUpDown(LCursor & cur, bool up) const
382 {
383         // in nucleus?
384         if (cur.idx() == 0) {
385                 // don't go up/down if there is no cell in this direction
386                 if (!has(up))
387                         return false;
388                 // go up/down only if in the last position
389                 // or in the first position of something with displayed limits
390                 if (cur.pos() == cur.lastpos() || (cur.pos() == 0 && hasLimits())) {
391                         cur.idx() = idxOfScript(up);
392                         cur.pos() = 0;
393                         return true;
394                 }
395                 return false;
396         }
397
398         // Are we 'up'?
399         if (has(up) && cur.idx() == idxOfScript(true)) {
400                 // can't go further up
401                 if (up)
402                         return false;
403                 // otherwise go to last position in the nucleus
404                 cur.idx() = 0;
405                 cur.pos() = cur.lastpos();
406                 return true;
407         }
408
409         // Are we 'down'?
410         if (has(up) && cur.idx() == idxOfScript(false)) {
411                 // can't go further down
412                 if (!up)
413                         return false;
414                 // otherwise go to last position in the nucleus
415                 cur.idx() = 0;
416                 cur.pos() = cur.lastpos();
417                 return true;
418         }
419
420         return false;
421 }
422
423
424 void InsetMathScript::write(WriteStream & os) const
425 {
426         if (nuc().size()) {
427                 os << nuc();
428                 //if (nuc().back()->takesLimits()) {
429                         if (limits_ == -1)
430                                 os << "\\nolimits ";
431                         if (limits_ == 1)
432                                 os << "\\limits ";
433                 //}
434         } else {
435                 if (os.firstitem())
436                         lyxerr[Debug::MATHED] << "suppressing {} when writing"
437                                               << endl;
438                 else
439                         os << "{}";
440         }
441
442         if (hasDown() /*&& down().size()*/)
443                 os << "_{" << down() << '}';
444
445         if (hasUp() /*&& up().size()*/)
446                 os << "^{" << up() << '}';
447
448         if (lock_ && !os.latex())
449                 os << "\\lyxlock ";
450 }
451
452
453 void InsetMathScript::normalize(NormalStream & os) const
454 {
455         bool d = hasDown() && down().size();
456         bool u = hasUp() && up().size();
457
458         if (u && d)
459                 os << "[subsup ";
460         else if (u)
461                 os << "[sup ";
462         else if (d)
463                 os << "[sub ";
464
465         if (nuc().size())
466                 os << nuc() << ' ';
467         else
468                 os << "[par]";
469
470         if (u && d)
471                 os << down() << ' ' << up() << ']';
472         else if (d)
473                 os << down() << ']';
474         else if (u)
475                 os << up() << ']';
476 }
477
478
479 void InsetMathScript::maple(MapleStream & os) const
480 {
481         if (nuc().size())
482                 os << nuc();
483         if (hasDown() && down().size())
484                 os << '[' << down() << ']';
485         if (hasUp() && up().size())
486                 os << "^(" << up() << ')';
487 }
488
489
490 void InsetMathScript::mathematica(MathematicaStream & os) const
491 {
492         bool d = hasDown() && down().size();
493         bool u = hasUp() && up().size();
494
495         if (nuc().size()) {
496                 if (d)
497                         os << "Subscript[" << nuc();
498                 else
499                         os << nuc();
500         }
501
502         if (u)
503                 os << "^(" << up() << ')';
504
505         if (nuc().size()) {
506                 if (d)
507                         os << ',' << down() << ']';
508         }
509 }
510
511
512 void InsetMathScript::mathmlize(MathStream & os) const
513 {
514         bool d = hasDown() && down().size();
515         bool u = hasUp() && up().size();
516
517         if (u && d)
518                 os << MTag("msubsup");
519         else if (u)
520                 os << MTag("msup");
521         else if (d)
522                 os << MTag("msub");
523
524         if (nuc().size())
525                 os << nuc();
526         else
527                 os << "<mrow/>";
528
529         if (u && d)
530                 os << down() << up() << ETag("msubsup");
531         else if (u)
532                 os << up() << ETag("msup");
533         else if (d)
534                 os << down() << ETag("msub");
535 }
536
537
538 void InsetMathScript::octave(OctaveStream & os) const
539 {
540         if (nuc().size())
541                 os << nuc();
542         if (hasDown() && down().size())
543                 os << '[' << down() << ']';
544         if (hasUp() && up().size())
545                 os << "^(" << up() << ')';
546 }
547
548
549 void InsetMathScript::infoize(odocstream & os) const
550 {
551         os << "Scripts";
552 }
553
554
555 void InsetMathScript::infoize2(odocstream & os) const
556 {
557         if (limits_)
558                 os << (limits_ == 1 ? ", Displayed limits" : ", Inlined limits");
559 }
560
561
562 bool InsetMathScript::notifyCursorLeaves(LCursor & cur)
563 {
564         InsetMathNest::notifyCursorLeaves(cur);
565
566         //lyxerr << "InsetMathScript::notifyCursorLeaves: 1 " << cur << endl;
567
568         // remove empty scripts if possible
569         if (nargs() > 2) {
570                 // Case of two scripts. In this case, 1 = super, 2 = sub
571                 if (cur.idx() == 2 && cell(2).empty()) {
572                         // must be a subscript...
573                         recordUndoInset(cur);
574                         removeScript(false);
575                         return true;
576                 } else if (cur.idx() == 1 && cell(1).empty()) {
577                         // must be a superscript...
578                         recordUndoInset(cur);
579                         removeScript(true);
580                         return true;
581                 }
582         } else if (nargs() > 1 && cur.idx() == 1 && cell(1).empty()) {
583                 // could be either subscript or super script
584                 recordUndoInset(cur);
585                 removeScript(cell_1_is_up_);
586                 // Let the script inset commit suicide. This is
587                 // modelled on LCursor.pullArg(), but tries not to
588                 // invoke notifyCursorLeaves again and does not touch
589                 // cur (since the top slice will be deleted
590                 // afterwards))
591                 MathArray ar = cell(0);
592                 LCursor tmpcur = cur;
593                 tmpcur.pop();
594                 tmpcur.cell().erase(tmpcur.pos());
595                 tmpcur.cell().insert(tmpcur.pos(), ar);
596                 return true;
597         }
598
599         //lyxerr << "InsetMathScript::notifyCursorLeaves: 2 " << cur << endl;
600         return false;
601 }
602
603
604 void InsetMathScript::doDispatch(LCursor & cur, FuncRequest & cmd)
605 {
606         //lyxerr << "InsetMathScript: request: " << cmd << std::endl;
607
608         if (cmd.action == LFUN_MATH_LIMITS) {
609                 if (!cmd.argument().empty()) {
610                         if (cmd.argument() == "limits")
611                                 limits_ = 1;
612                         else if (cmd.argument() == "nolimits")
613                                 limits_ = -1;
614                         else
615                                 limits_ = 0;
616                 } else if (limits_ == 0)
617                         limits_ = hasLimits() ? -1 : 1;
618                 else
619                         limits_ = 0;
620                 return;
621         }
622
623         InsetMathNest::doDispatch(cur, cmd);
624 }
625
626
627 } // namespace lyx