]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathSideset.cpp
Rename some LFUN names to match their text name
[lyx.git] / src / mathed / InsetMathSideset.cpp
1 /**
2  * \file InsetMathSideset.cpp
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  * \author Georg Baum
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "BufferView.h"
15 #include "Cursor.h"
16 #include "DispatchResult.h"
17 #include "FuncRequest.h"
18 #include "FuncStatus.h"
19 #include "InsetMathSideset.h"
20 #include "InsetMathSymbol.h"
21 #include "LaTeXFeatures.h"
22 #include "MathData.h"
23 #include "MathStream.h"
24 #include "MathSupport.h"
25
26 #include "support/debug.h"
27
28 #include "support/lassert.h"
29
30
31 using namespace std;
32
33
34 namespace {
35         /// x spacing between the nucleus and the scripts
36         int const dx = 2;
37 }
38
39
40 namespace lyx {
41
42 InsetMathSideset::InsetMathSideset(Buffer * buf, bool scriptl, bool scriptr)
43         : InsetMathNest(buf, 3 + scriptl + scriptr), scriptl_(scriptl),
44           scriptr_(scriptr)
45 {}
46
47
48 InsetMathSideset::InsetMathSideset(Buffer * buf, bool scriptl, bool scriptr,
49                                    MathAtom const & at)
50         : InsetMathNest(buf, 3 + scriptl + scriptr), scriptl_(scriptl),
51           scriptr_(scriptr)
52 {
53         nuc().push_back(at);
54 }
55
56
57 Inset * InsetMathSideset::clone() const
58 {
59         return new InsetMathSideset(*this);
60 }
61
62
63 bool InsetMathSideset::idxFirst(Cursor & cur) const
64 {
65         cur.idx() = 0;
66         cur.pos() = 0;
67         return true;
68 }
69
70
71 bool InsetMathSideset::idxLast(Cursor & cur) const
72 {
73         cur.idx() = 0;
74         cur.pos() = nuc().size();
75         return true;
76 }
77
78
79 int InsetMathSideset::dybt(BufferView const & bv, int asc, int des, bool top) const
80 {
81         bool isCharBox = nuc().empty() ? false : isAlphaSymbol(nuc().back());
82         int dasc = 0;
83         if (scriptl_ && scriptr_)
84                 dasc = max(bl().dimension(bv).ascent(), br().dimension(bv).ascent());
85         else if (scriptl_)
86                 dasc = bl().dimension(bv).ascent();
87         else if (scriptr_)
88                 dasc = br().dimension(bv).ascent();
89         int slevel = nuc().slevel();
90         int ascdrop = dasc - slevel;
91         int desdrop = isCharBox ? 0 : des + nuc().sshift();
92         int mindes = nuc().mindes();
93         des = max(desdrop, ascdrop);
94         des = max(mindes, des);
95         int minasc = nuc().minasc();
96         ascdrop = 0;
97         if (!isCharBox && (scriptl_ || scriptr_)) {
98                 if (scriptl_ && scriptr_)
99                         ascdrop = asc - min(tl().mindes(), tr().mindes());
100                 else if (scriptl_)
101                         ascdrop = asc - tl().mindes();
102                 else if (scriptr_)
103                         ascdrop = asc - tr().mindes();
104         }
105         int udes = 0;
106         if (scriptl_)
107                 udes = bl().dimension(bv).descent();
108         if (scriptr_)
109                 udes = max(udes, br().dimension(bv).descent());
110         asc = udes + nuc().sshift();
111         asc = max(ascdrop, asc);
112         asc = max(minasc, asc);
113         int del = asc - udes - dasc;
114         if (del + des <= 2) {
115                 int newdes = 2 - del;
116                 del = slevel - asc + udes;
117                 if (del > 0) {
118                         asc += del;
119                         newdes -= del;
120                 }
121                 des = max(des, newdes);
122         }
123         return top ? asc : des;
124 }
125
126
127 int InsetMathSideset::dyb(BufferView const & bv) const
128 {
129         int nd = ndes(bv);
130         int des = 0;
131         if (scriptl_ && scriptr_)
132                 des = max(bl().dimension(bv).ascent(), br().dimension(bv).ascent());
133         else if (scriptl_)
134                 des = bl().dimension(bv).ascent();
135         else if (scriptr_)
136                 des = br().dimension(bv).ascent();
137         int na = nasc(bv);
138         des = dybt(bv, na, nd, false);
139         return des;
140 }
141
142
143 int InsetMathSideset::dyt(BufferView const & bv) const
144 {
145         int na = nasc(bv);
146         int asc = 0;
147         if (scriptl_ && scriptr_)
148                 asc = max(tl().dimension(bv).descent(), tr().dimension(bv).descent());
149         else if (scriptl_)
150                 asc = tl().dimension(bv).descent();
151         else if (scriptr_)
152                 asc = tr().dimension(bv).descent();
153         int nd = ndes(bv);
154         asc = dybt(bv, na, nd, true);
155         return asc;
156 }
157
158
159 int InsetMathSideset::dxr(BufferView const & bv) const
160 {
161         return dxn(bv) + nwid(bv) + dx;
162 }
163
164
165 int InsetMathSideset::dxn(BufferView const & bv) const
166 {
167         Dimension const dimb = bl().dimension(bv);
168         Dimension const dimt = tl().dimension(bv);
169         return max(dimb.width(), dimt.width()) + dx;
170 }
171
172
173 int InsetMathSideset::nwid(BufferView const & bv) const
174 {
175         return nuc().dimension(bv).width();
176 }
177
178
179 int InsetMathSideset::nasc(BufferView const & bv) const
180 {
181         return nuc().dimension(bv).ascent();
182 }
183
184
185 int InsetMathSideset::ndes(BufferView const & bv) const
186 {
187         return nuc().dimension(bv).descent();
188 }
189
190
191 int InsetMathSideset::nker(BufferView const * bv) const
192 {
193         int const kerning = nuc().kerning(bv);
194         return max(kerning, 0);
195 }
196
197
198 void InsetMathSideset::metrics(MetricsInfo & mi, Dimension & dim) const
199 {
200         Dimension dimn;
201         Dimension dimbl;
202         Dimension dimtl;
203         Dimension dimbr;
204         Dimension dimtr;
205         nuc().metrics(mi, dimn);
206         if (!scriptl_) {
207                 bl().metrics(mi, dimbl);
208                 dimtl = dimbl;
209         }
210         if (!scriptr_) {
211                 br().metrics(mi, dimbr);
212                 dimtr = dimbr;
213         }
214         ScriptChanger dummy(mi.base);
215         if (scriptl_) {
216                 bl().metrics(mi, dimbl);
217                 tl().metrics(mi, dimtl);
218         }
219         if (scriptr_) {
220                 br().metrics(mi, dimbr);
221                 tr().metrics(mi, dimtr);
222         }
223
224         BufferView & bv = *mi.base.bv;
225         // FIXME: data copying... not very efficient.
226
227         dim.wid = nwid(bv) + nker(mi.base.bv) + 2 * dx;
228         dim.wid += max(dimbl.width(), dimtl.width());
229         dim.wid += max(dimbr.width(), dimtr.width());
230         int na = nasc(bv);
231         int asc = dyt(bv) + max(dimtl.ascent(), dimtr.ascent());
232         dim.asc = max(na, asc);
233         int nd = ndes(bv);
234         int des = dyb(bv) + max(dimbl.descent(), dimbr.descent());
235         dim.des = max(nd, des);
236         metricsMarkers(dim);
237 }
238
239
240 void InsetMathSideset::draw(PainterInfo & pi, int x, int y) const
241 {
242         BufferView & bv = *pi.base.bv;
243         nuc().draw(pi, x + dxn(bv), y);
244         if (!scriptl_)
245                 bl().draw(pi, x          , y);
246         if (!scriptr_)
247                 br().draw(pi, x + dxr(bv), y);
248         ScriptChanger dummy(pi.base);
249         if (scriptl_) {
250                 bl().draw(pi, x          , y + dyb(bv));
251                 tl().draw(pi, x          , y - dyt(bv));
252         }
253         if (scriptr_) {
254                 br().draw(pi, x + dxr(bv), y + dyb(bv));
255                 tr().draw(pi, x + dxr(bv), y - dyt(bv));
256         }
257         drawMarkers(pi, x, y);
258 }
259
260
261 void InsetMathSideset::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
262 {
263         bl().metricsT(mi, dim);
264         tl().metricsT(mi, dim);
265         br().metricsT(mi, dim);
266         tr().metricsT(mi, dim);
267         nuc().metricsT(mi, dim);
268 }
269
270
271 void InsetMathSideset::drawT(TextPainter & pain, int x, int y) const
272 {
273         // FIXME: BROKEN
274         nuc().drawT(pain, x + 1, y);
275         bl().drawT(pain, x + 1, y + 1 /*dy0()*/);
276         tl().drawT(pain, x + 1, y - 1 /*dy1()*/);
277         br().drawT(pain, x + 1, y + 1 /*dy0()*/);
278         tr().drawT(pain, x + 1, y - 1 /*dy1()*/);
279 }
280
281
282
283 bool InsetMathSideset::idxForward(Cursor & cur) const
284 {
285         if (!scriptl_ && cur.idx() == 1) {
286                 // left => nucleus
287                 cur.idx() = 0;
288                 return true;
289         } else if (!scriptr_ && cur.idx() == 0) {
290                 // nucleus => right
291                 cur.idx() = 2 + scriptl_;
292                 return true;
293         }
294         return false;
295 }
296
297
298 bool InsetMathSideset::idxBackward(Cursor & cur) const
299 {
300         if (!scriptr_ && cur.idx() == (scriptl_ ? 3 : 2)) {
301                 // right => nucleus
302                 cur.idx() = 0;
303                 return true;
304         } else if (!scriptl_ && cur.idx() == 0) {
305                 // nucleus => left
306                 cur.idx() = 1;
307                 return true;
308         }
309         return false;
310 }
311
312
313 bool InsetMathSideset::idxUpDown(Cursor & cur, bool up) const
314 {
315         // in nucleus?
316         if (cur.idx() == 0) {
317                 // go up/down only if in the last position
318                 // or in the first position
319                 if ((scriptr_ && cur.pos() == cur.lastpos()) ||
320                     (scriptl_ && cur.pos() == 0)) {
321                         if (cur.pos() == 0)
322                                 cur.idx() = up ? 2 : 1;
323                         else
324                                 cur.idx() = (up ? 3 : 2) + scriptl_;
325                         cur.pos() = 0;
326                         return true;
327                 }
328                 return false;
329         }
330
331         // Are we 'up'?
332         if ((scriptl_ && cur.idx() == 2) ||
333             (scriptr_ && cur.idx() == (scriptl_ ? 4 : 3))) {
334                 // can't go further up
335                 if (up)
336                         return false;
337                 // otherwise go to first or last position in the nucleus
338                 cur.idx() = 0;
339                 if (cur.idx() == 2)
340                         cur.pos() = 0;
341                 else
342                         cur.pos() = cur.lastpos();
343                 return true;
344         }
345
346         // Are we 'down'?
347         if ((scriptl_ && cur.idx() == 1) ||
348             (scriptr_ && cur.idx() == (scriptl_ ? 3 : 2))) {
349                 // can't go further down
350                 if (!up)
351                         return false;
352                 // otherwise go to first or last position in the nucleus
353                 cur.idx() = 0;
354                 if (cur.idx() == 1)
355                         cur.pos() = 0;
356                 else
357                         cur.pos() = cur.lastpos();
358                 return true;
359         }
360
361         return false;
362 }
363
364
365 void InsetMathSideset::write(WriteStream & os) const
366 {
367         MathEnsurer ensurer(os);
368
369         os << "\\sideset";
370         os << '{';
371         if (scriptl_) {
372                 if (!bl().empty())
373                         os << "_{" << bl() << '}';
374                 if (!tl().empty())
375                         os << "^{" << tl() << '}';
376         } else
377                 os << bl();
378         os << "}{";
379         if (scriptr_) {
380                 if (!br().empty())
381                         os << "_{" << br() << '}';
382                 if (!tr().empty())
383                         os << "^{" << tr() << '}';
384         } else
385                 os << br();
386         os << '}';
387         os << nuc();
388
389         if (lock_ && !os.latex())
390                 os << "\\lyxlock ";
391 }
392
393
394 void InsetMathSideset::normalize(NormalStream & os) const
395 {
396         os << "[sideset ";
397
398         if (!bl().empty())
399                 os << bl() << ' ';
400         if (scriptl_ && !tl().empty())
401                 os << tl() << ' ';
402
403         if (!nuc().empty())
404                 os << nuc() << ' ';
405         else
406                 os << "[par]";
407
408         if (!br().empty())
409                 os << br() << ' ';
410         if (scriptr_ && !tr().empty())
411                 os << tr() << ' ';
412         os << ']';
413 }
414
415
416 void InsetMathSideset::mathmlize(MathStream & os) const
417 {
418         // FIXME This is only accurate if both scriptl_ and scriptr_ are true
419         if (!scriptl_)
420                 os << MTag("mrow") << bl() << ETag("mrow");
421         if (scriptl_ || scriptr_) {
422                 os << MTag("mmultiscripts");
423
424                 if (nuc().empty())
425                         os << "<mrow />";
426                 else
427                         os << MTag("mrow") << nuc() << ETag("mrow");
428
429                 if (br().empty() || !scriptr_)
430                         os << "<none />";
431                 else
432                         os << MTag("mrow") << br() << ETag("mrow");
433                 if (tr().empty() || !scriptr_)
434                         os << "<none />";
435                 else
436                         os << MTag("mrow") << tr() << ETag("mrow");
437
438                 if (bl().empty() || !scriptl_)
439                         os << "<none />";
440                 else
441                         os << MTag("mrow") << bl() << ETag("mrow");
442                 if (tl().empty() || !scriptl_)
443                         os << "<none />";
444                 else
445                         os << MTag("mrow") << tl() << ETag("mrow");
446
447                 os << ETag("mmultiscripts");
448         }
449         if (!scriptr_)
450                 os << MTag("mrow") << br() << ETag("mrow");
451 }
452
453
454 void InsetMathSideset::htmlize(HtmlStream & os) const
455 {
456         // FIXME This is only accurate if both scriptl_ and scriptr_ are true
457         bool const havebl = scriptl_ && !bl().empty();
458         bool const havetl = scriptl_ && !tl().empty();
459         bool const havebr = scriptr_ && !br().empty();
460         bool const havetr = scriptr_ && !tr().empty();
461
462         if (!scriptl_ && !bl().empty())
463                 os << bl();
464
465         if (havebl && havetl)
466                 os << MTag("span", "class='scripts'")
467                          << MTag("span") << tl() << ETag("span")
468                          << MTag("span") << bl() << ETag("span")
469                          << ETag("span");
470         else if (havebl)
471                 os << MTag("sub", "class='math'") << bl() << ETag("sub");
472         else if (havetl)
473                 os << MTag("sup", "class='math'") << tl() << ETag("sup");
474
475         if (!nuc().empty())
476                 os << nuc();
477
478         if (havebr && havetr)
479                 os << MTag("span", "class='scripts'")
480                          << MTag("span") << tr() << ETag("span")
481                          << MTag("span") << br() << ETag("span")
482                          << ETag("span");
483         else if (havebr)
484                 os << MTag("sub", "class='math'") << br() << ETag("sub");
485         else if (havetr)
486                 os << MTag("sup", "class='math'") << tr() << ETag("sup");
487
488         if (!scriptr_ && !br().empty())
489                 os << br();
490 }
491
492
493 void InsetMathSideset::infoize(odocstream & os) const
494 {
495         os << "Sideset";
496 }
497
498
499 // the idea for dual scripts came from the eLyXer code
500 void InsetMathSideset::validate(LaTeXFeatures & features) const
501 {
502         if (features.runparams().math_flavor == OutputParams::MathAsHTML)
503                 features.addCSSSnippet(
504                         "span.scripts{display: inline-block; vertical-align: middle; text-align:center; font-size: 75%;}\n"
505                         "span.scripts span {display: block;}\n"
506                         "sub.math{font-size: 75%;}\n"
507                         "sup.math{font-size: 75%;}");
508         features.require("amsmath");
509         InsetMathNest::validate(features);
510 }
511
512 } // namespace lyx