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