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