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