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