]> git.lyx.org Git - features.git/blob - src/mathed/InsetMathSideset.cpp
9d1e51bd1b1812cd7be3bcc1ec4828d7e43c080b
[features.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)
43         : InsetMathNest(buf, 5)
44 {}
45
46
47 InsetMathSideset::InsetMathSideset(Buffer * buf, MathAtom const & at)
48         : InsetMathNest(buf, 5)
49 {
50         nuc().push_back(at);
51 }
52
53
54 Inset * InsetMathSideset::clone() const
55 {
56         return new InsetMathSideset(*this);
57 }
58
59
60 bool InsetMathSideset::idxFirst(Cursor & cur) const
61 {
62         cur.idx() = 0;
63         cur.pos() = 0;
64         return true;
65 }
66
67
68 bool InsetMathSideset::idxLast(Cursor & cur) const
69 {
70         cur.idx() = 0;
71         cur.pos() = nuc().size();
72         return true;
73 }
74
75
76 int InsetMathSideset::dybt(BufferView const & bv, int asc, int des, bool top) const
77 {
78         bool isCharBox = nuc().empty() ? false : isAlphaSymbol(nuc().back());
79         int dasc = max(bl().dimension(bv).ascent(), br().dimension(bv).ascent());
80         int slevel = nuc().slevel();
81         int ascdrop = dasc - slevel;
82         int desdrop = isCharBox ? 0 : des + nuc().sshift();
83         int mindes = nuc().mindes();
84         des = max(desdrop, ascdrop);
85         des = max(mindes, des);
86         int minasc = nuc().minasc();
87         ascdrop = isCharBox ? 0 : asc - min(tl().mindes(), tr().mindes());
88         int udes = max(bl().dimension(bv).descent(), tr().dimension(bv).descent());
89         asc = udes + nuc().sshift();
90         asc = max(ascdrop, asc);
91         asc = max(minasc, asc);
92         int del = asc - udes - dasc;
93         if (del + des <= 2) {
94                 int newdes = 2 - del;
95                 del = slevel - asc + udes;
96                 if (del > 0) {
97                         asc += del;
98                         newdes -= del;
99                 }
100                 des = max(des, newdes);
101         }
102         return top ? asc : des;
103 }
104
105
106 int InsetMathSideset::dyb(BufferView const & bv) const
107 {
108         int nd = ndes(bv);
109         int des = max(bl().dimension(bv).ascent(), br().dimension(bv).ascent());
110         int na = nasc(bv);
111         des = dybt(bv, na, nd, false);
112         return des;
113 }
114
115
116 int InsetMathSideset::dyt(BufferView const & bv) const
117 {
118         int na = nasc(bv);
119         int asc = max(tl().dimension(bv).descent(), tr().dimension(bv).descent());
120         int nd = ndes(bv);
121         asc = dybt(bv, na, nd, true);
122         return asc;
123 }
124
125
126 int InsetMathSideset::dxr(BufferView const & bv) const
127 {
128         return dxn(bv) + nwid(bv) + dx;
129 }
130
131
132 int InsetMathSideset::dxn(BufferView const & bv) const
133 {
134         Dimension const dimb = bl().dimension(bv);
135         Dimension const dimt = tl().dimension(bv);
136         return max(dimb.width(), dimt.width()) + dx;
137 }
138
139
140 int InsetMathSideset::nwid(BufferView const & bv) const
141 {
142         return nuc().dimension(bv).width();
143 }
144
145
146 int InsetMathSideset::nasc(BufferView const & bv) const
147 {
148         return nuc().dimension(bv).ascent();
149 }
150
151
152 int InsetMathSideset::ndes(BufferView const & bv) const
153 {
154         return nuc().dimension(bv).descent();
155 }
156
157
158 int InsetMathSideset::nker(BufferView const * bv) const
159 {
160         int const kerning = nuc().kerning(bv);
161         return max(kerning, 0);
162 }
163
164
165 void InsetMathSideset::metrics(MetricsInfo & mi, Dimension & dim) const
166 {
167         Dimension dimn;
168         Dimension dimbl;
169         Dimension dimtl;
170         Dimension dimbr;
171         Dimension dimtr;
172         nuc().metrics(mi, dimn);
173         ScriptChanger dummy(mi.base);
174         bl().metrics(mi, dimbl);
175         tl().metrics(mi, dimtl);
176         br().metrics(mi, dimbr);
177         tr().metrics(mi, dimtr);
178
179         BufferView & bv = *mi.base.bv;
180         // FIXME: data copying... not very efficient.
181
182         dim.wid = nwid(bv) + nker(mi.base.bv) + 2 * dx;
183         dim.wid += max(dimbl.width(), dimtl.width());
184         dim.wid += max(dimbr.width(), dimtr.width());
185         int na = nasc(bv);
186         int asc = dyt(bv) + max(dimtl.ascent(), dimtr.ascent());
187         dim.asc = max(na, asc);
188         int nd = ndes(bv);
189         int des = dyb(bv) + max(dimbl.descent(), dimbr.descent());
190         dim.des = max(nd, des);
191         metricsMarkers(dim);
192 }
193
194
195 void InsetMathSideset::draw(PainterInfo & pi, int x, int y) const
196 {
197         BufferView & bv = *pi.base.bv;
198         nuc().draw(pi, x + dxn(bv), y);
199         ScriptChanger dummy(pi.base);
200         bl().draw(pi, x          , y + dyb(bv));
201         tl().draw(pi, x          , y - dyt(bv));
202         br().draw(pi, x + dxr(bv), y + dyb(bv));
203         tr().draw(pi, x + dxr(bv), y - dyt(bv));
204         drawMarkers(pi, x, y);
205 }
206
207
208 void InsetMathSideset::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
209 {
210         bl().metricsT(mi, dim);
211         tl().metricsT(mi, dim);
212         br().metricsT(mi, dim);
213         tr().metricsT(mi, dim);
214         nuc().metricsT(mi, dim);
215 }
216
217
218 void InsetMathSideset::drawT(TextPainter & pain, int x, int y) const
219 {
220         // FIXME: BROKEN
221         nuc().drawT(pain, x + 1, y);
222         bl().drawT(pain, x + 1, y + 1 /*dy0()*/);
223         tl().drawT(pain, x + 1, y - 1 /*dy1()*/);
224         br().drawT(pain, x + 1, y + 1 /*dy0()*/);
225         tr().drawT(pain, x + 1, y - 1 /*dy1()*/);
226 }
227
228
229
230 bool InsetMathSideset::idxForward(Cursor &) const
231 {
232         return false;
233 }
234
235
236 bool InsetMathSideset::idxBackward(Cursor &) const
237 {
238         return false;
239 }
240
241
242 bool InsetMathSideset::idxUpDown(Cursor & cur, bool up) const
243 {
244         // in nucleus?
245         if (cur.idx() == 0) {
246                 // go up/down only if in the last position
247                 // or in the first position
248                 if (cur.pos() == cur.lastpos() || cur.pos() == 0) {
249                         if (cur.pos() == 0)
250                                 cur.idx() = up ? 2 : 1;
251                         else
252                                 cur.idx() = up ? 4 : 3;
253                         cur.pos() = 0;
254                         return true;
255                 }
256                 return false;
257         }
258
259         // Are we 'up'?
260         if (cur.idx() == 2 || cur.idx() == 4) {
261                 // can't go further up
262                 if (up)
263                         return false;
264                 // otherwise go to first or last position in the nucleus
265                 cur.idx() = 0;
266                 if (cur.idx() == 2)
267                         cur.pos() = 0;
268                 else
269                         cur.pos() = cur.lastpos();
270                 return true;
271         }
272
273         // Are we 'down'?
274         if (cur.idx() == 1 || cur.idx() == 3) {
275                 // can't go further down
276                 if (!up)
277                         return false;
278                 // otherwise go to first or last position in the nucleus
279                 cur.idx() = 0;
280                 if (cur.idx() == 1)
281                         cur.pos() = 0;
282                 else
283                         cur.pos() = cur.lastpos();
284                 return true;
285         }
286
287         return false;
288 }
289
290
291 void InsetMathSideset::write(WriteStream & os) const
292 {
293         MathEnsurer ensurer(os);
294
295         os << "\\sideset";
296         for (int i = 0; i < 2; ++i) {
297                 os << '{';
298                 if (!cell(2*i+1).empty())
299                         os << "_{" << cell(2*i+1) << '}';
300                 if (!cell(2*i+2).empty())
301                         os << "^{" << cell(2*i+2) << '}';
302                 os << '}';
303         }
304         os << '{' << nuc() << '}';
305
306         if (lock_ && !os.latex())
307                 os << "\\lyxlock ";
308 }
309
310
311 void InsetMathSideset::normalize(NormalStream & os) const
312 {
313         os << "[sideset ";
314
315         if (!bl().empty())
316                 os << bl() << ' ';
317         if (!tl().empty())
318                 os << tl() << ' ';
319
320         if (!nuc().empty())
321                 os << nuc() << ' ';
322         else
323                 os << "[par]";
324
325         if (!br().empty())
326                 os << br() << ' ';
327         if (!tr().empty())
328                 os << tr() << ' ';
329         os << ']';
330 }
331
332
333 void InsetMathSideset::mathmlize(MathStream & os) const
334 {
335         os << MTag("mmultiscripts");
336
337         if (nuc().empty())
338                 os << "<mrow />";
339         else
340                 os << MTag("mrow") << nuc() << ETag("mrow");
341
342         if (br().empty())
343                 os << "<none />";
344         else
345                 os << MTag("mrow") << br() << ETag("mrow");
346         if (tr().empty())
347                 os << "<none />";
348         else
349                 os << MTag("mrow") << tr() << ETag("mrow");
350
351         if (bl().empty())
352                 os << "<none />";
353         else
354                 os << MTag("mrow") << bl() << ETag("mrow");
355         if (tl().empty())
356                 os << "<none />";
357         else
358                 os << MTag("mrow") << tl() << ETag("mrow");
359
360         os << ETag("mmultiscripts");
361 }
362
363
364 void InsetMathSideset::htmlize(HtmlStream & os) const
365 {
366         bool const havebl = !bl().empty();
367         bool const havetl = !tl().empty();
368         bool const havebr = !br().empty();
369         bool const havetr = !tr().empty();
370
371         if (havebl && havetl)
372                 os << MTag("span", "class='scripts'")
373                          << MTag("span") << tl() << ETag("span")
374                          << MTag("span") << bl() << ETag("span")
375                          << ETag("span");
376         else if (havebl)
377                 os << MTag("sub", "class='math'") << bl() << ETag("sub");
378         else if (havetl)
379                 os << MTag("sup", "class='math'") << tl() << ETag("sup");
380
381         if (!nuc().empty())
382                 os << nuc();
383
384         if (havebr && havetr)
385                 os << MTag("span", "class='scripts'")
386                          << MTag("span") << tr() << ETag("span")
387                          << MTag("span") << br() << ETag("span")
388                          << ETag("span");
389         else if (havebr)
390                 os << MTag("sub", "class='math'") << br() << ETag("sub");
391         else if (havetr)
392                 os << MTag("sup", "class='math'") << tr() << ETag("sup");
393 }
394
395
396 void InsetMathSideset::infoize(odocstream & os) const
397 {
398         os << "Sideset";
399 }
400
401
402 // the idea for dual scripts came from the eLyXer code
403 void InsetMathSideset::validate(LaTeXFeatures & features) const
404 {
405         if (features.runparams().math_flavor == OutputParams::MathAsHTML)
406                 features.addCSSSnippet(
407                         "span.scripts{display: inline-block; vertical-align: middle; text-align:center; font-size: 75%;}\n"
408                         "span.scripts span {display: block;}\n"
409                         "sub.math{font-size: 75%;}\n"
410                         "sup.math{font-size: 75%;}");
411         features.require("amsmath");
412         InsetMathNest::validate(features);
413 }
414
415 } // namespace lyx