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