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