1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sourceforge.jeuclid.elements.presentation.script;
20
21 import java.awt.geom.Dimension2D;
22
23 import net.sourceforge.jeuclid.LayoutContext;
24 import net.sourceforge.jeuclid.context.Display;
25 import net.sourceforge.jeuclid.context.InlineLayoutContext;
26 import net.sourceforge.jeuclid.context.Parameter;
27 import net.sourceforge.jeuclid.context.RelativeScriptlevelLayoutContext;
28 import net.sourceforge.jeuclid.elements.AbstractJEuclidElement;
29 import net.sourceforge.jeuclid.elements.JEuclidElement;
30 import net.sourceforge.jeuclid.elements.support.Dimension2DImpl;
31 import net.sourceforge.jeuclid.elements.support.ElementListSupport;
32 import net.sourceforge.jeuclid.elements.support.attributes.AttributesHelper;
33 import net.sourceforge.jeuclid.layout.LayoutInfo;
34 import net.sourceforge.jeuclid.layout.LayoutStage;
35 import net.sourceforge.jeuclid.layout.LayoutView;
36
37 import org.apache.batik.dom.AbstractDocument;
38 import org.w3c.dom.mathml.MathMLElement;
39 import org.w3c.dom.mathml.MathMLOperatorElement;
40 import org.w3c.dom.mathml.MathMLUnderOverElement;
41
42
43
44
45
46
47
48
49
50 public abstract class AbstractUnderOver extends AbstractJEuclidElement
51 implements MathMLUnderOverElement {
52
53
54
55
56 public static final String UNDER_OVER_SPACE = "0.1ex";
57
58
59 public static final float NON_ACCENT_MULTIPLIER = 2.5f;
60
61
62 public static final String ATTR_ACCENT = "accent";
63
64
65 public static final String ATTR_ACCENTUNDER = "accentunder";
66
67
68 private static final long serialVersionUID = 1L;
69
70
71
72
73
74
75
76
77
78 public AbstractUnderOver(final String qname, final AbstractDocument odoc) {
79 super(qname, odoc);
80 }
81
82
83 public String getAccent() {
84
85 return this.getMathAttribute(AbstractUnderOver.ATTR_ACCENT);
86 }
87
88
89
90
91
92
93 protected boolean getAccentAsBoolean() {
94 return Boolean.parseBoolean(this.getAccent());
95 }
96
97
98
99
100
101
102 private boolean limitsAreMoved(final LayoutContext now) {
103 return (!this.getAccentAsBoolean())
104 && (this.getBase() instanceof MathMLOperatorElement)
105 && Boolean
106 .parseBoolean(((MathMLOperatorElement) this.getBase())
107 .getMovablelimits())
108 && (Display.INLINE.equals(now.getParameter(Parameter.DISPLAY)));
109 }
110
111
112 public String getAccentunder() {
113
114 return this.getMathAttribute(AbstractUnderOver.ATTR_ACCENTUNDER);
115 }
116
117
118 @Override
119 public LayoutContext getChildLayoutContext(final int childNum,
120 final LayoutContext context) {
121 final LayoutContext now = this.applyLocalAttributesToContext(context);
122 if (childNum == 0) {
123 return now;
124 } else {
125
126 return new RelativeScriptlevelLayoutContext(
127 new InlineLayoutContext(now), 1);
128 }
129 }
130
131
132
133
134
135
136 protected boolean getAccentunderAsBoolean() {
137 return Boolean.parseBoolean(this.getAccentunder());
138 }
139
140
141 public JEuclidElement getBase() {
142 return this.getMathElement(0);
143 }
144
145
146 public abstract JEuclidElement getOverscript();
147
148
149 public abstract JEuclidElement getUnderscript();
150
151
152 public void setAccent(final String accent) {
153 this.setAttribute(AbstractUnderOver.ATTR_ACCENT, accent);
154 }
155
156
157 public void setAccentunder(final String accentunder) {
158 this.setAttribute(AbstractUnderOver.ATTR_ACCENTUNDER, accentunder);
159 }
160
161
162 public void setBase(final MathMLElement base) {
163 this.setMathElement(0, base);
164 }
165
166
167 @Override
168 public boolean hasChildPostscripts(final JEuclidElement child,
169 final LayoutContext context) {
170 return this.limitsAreMoved(context) && child.isSameNode(this.getBase());
171 }
172
173
174 @Override
175 protected void layoutStageInvariant(final LayoutView view,
176 final LayoutInfo info, final LayoutStage stage,
177 final LayoutContext context) {
178 final LayoutContext now = this.applyLocalAttributesToContext(context);
179 if (this.limitsAreMoved(now)) {
180 ScriptSupport.layout(view, info, stage, now, this, this.getBase(),
181 this.getUnderscript(), this.getOverscript(), null, null);
182 } else {
183 this.layoutUnderOver(view, info, stage, now);
184 }
185 }
186
187 private void layoutUnderOver(final LayoutView view, final LayoutInfo info,
188 final LayoutStage stage, final LayoutContext now) {
189
190 final JEuclidElement base = this.getBase();
191 final JEuclidElement under = this.getUnderscript();
192 final JEuclidElement over = this.getOverscript();
193
194 final LayoutInfo baseInfo = view.getInfo(base);
195 final LayoutInfo underInfo;
196 final LayoutInfo overInfo;
197
198 float width = baseInfo.getWidth(stage);
199
200 final float extraShift = AttributesHelper.convertSizeToPt(
201 AbstractUnderOver.UNDER_OVER_SPACE, now, AttributesHelper.PT);
202
203 if (under == null) {
204 underInfo = null;
205 } else {
206 underInfo = view.getInfo(under);
207 width = Math.max(width, underInfo.getWidth(stage));
208 }
209 if (over == null) {
210 overInfo = null;
211 } else {
212 overInfo = view.getInfo(over);
213 width = Math.max(width, overInfo.getWidth(stage));
214 }
215 final float middle = width / 2.0f;
216
217 baseInfo.moveTo(middle - baseInfo.getHorizontalCenterOffset(stage), 0,
218 stage);
219
220 if (under != null) {
221 this.positionUnder(stage, baseInfo, underInfo, extraShift, middle);
222 }
223 if (over != null) {
224 this.positionOver(stage, baseInfo, overInfo, extraShift, middle);
225 }
226
227 final Dimension2D borderLeftTop = new Dimension2DImpl(0.0f, 0.0f);
228 final Dimension2D borderRightBottom = new Dimension2DImpl(0.0f, 0.0f);
229 ElementListSupport.fillInfoFromChildren(view, info, this, stage,
230 borderLeftTop, borderRightBottom);
231 info.setStretchWidth(width);
232 info.setStretchAscent(baseInfo.getStretchAscent());
233 info.setStretchDescent(baseInfo.getStretchDescent());
234 }
235
236 private void positionUnder(final LayoutStage stage,
237 final LayoutInfo baseInfo, final LayoutInfo underInfo,
238 final float extraShift, final float middle) {
239 final float underextra;
240 if (this.getAccentunderAsBoolean()) {
241 underextra = extraShift;
242 } else {
243 underextra = extraShift * AbstractUnderOver.NON_ACCENT_MULTIPLIER;
244 }
245 final float y = baseInfo.getDescentHeight(stage)
246 + underInfo.getAscentHeight(stage) + underextra;
247 underInfo.moveTo(middle - underInfo.getHorizontalCenterOffset(stage),
248 y, stage);
249 }
250
251 private void positionOver(final LayoutStage stage,
252 final LayoutInfo baseInfo, final LayoutInfo overInfo,
253 final float extraShift, final float middle) {
254 final float overextra;
255 if (this.getAccentAsBoolean()) {
256 overextra = extraShift;
257 } else {
258 overextra = extraShift * AbstractUnderOver.NON_ACCENT_MULTIPLIER;
259 }
260 final float y = baseInfo.getAscentHeight(stage)
261 + overInfo.getDescentHeight(stage) + overextra;
262 overInfo.moveTo(middle - overInfo.getHorizontalCenterOffset(stage), -y,
263 stage);
264 }
265 }