View Javadoc

1   /*
2    * Copyright 2007 - 2007 JEuclid, http://jeuclid.sf.net
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  /* $Id: AbstractUnderOver.java 795 2008-06-21 10:53:35Z maxberger $ */
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   * Implementation and helper methods for munder, mover, and munderover.
43   * 
44   * @todo some operators should "default" to being an accent, but currently they
45   *       don't
46   * @version $Revision: 795 $
47   */
48  public abstract class AbstractUnderOver extends AbstractJEuclidElement
49          implements MathMLUnderOverElement {
50  
51      /**
52       * Space between base and under/over for accents.
53       */
54      public static final String UNDER_OVER_SPACE = "0.1ex";
55  
56      /** Space for non-accents multiplied by this value. */
57      public static final float NON_ACCENT_MULTIPLIER = 2.5f;
58  
59      /** attribute for accent property. */
60      public static final String ATTR_ACCENT = "accent";
61  
62      /** attribute for accentunder property. */
63      public static final String ATTR_ACCENTUNDER = "accentunder";
64  
65      /**
66       * default constructor.
67       */
68      public AbstractUnderOver() {
69          super();
70      }
71  
72      /** {@inheritDoc} */
73      public String getAccent() {
74          // TODO: Accent also depends on the content. See spec 3.4.4 - 3.4.6
75          return this.getMathAttribute(AbstractUnderOver.ATTR_ACCENT);
76      }
77  
78      /**
79       * returns the accent property as boolean.
80       * 
81       * @return accent
82       */
83      protected boolean getAccentAsBoolean() {
84          return Boolean.parseBoolean(this.getAccent());
85      }
86  
87      /**
88       * @param context
89       *            LayoutContext
90       * @return true if limits are moved (behave like under/over).
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     /** {@inheritDoc} */
103     public String getAccentunder() {
104         // TODO: Accent also depends on the content. See spec 3.4.4 - 3.4.6
105         return this.getMathAttribute(AbstractUnderOver.ATTR_ACCENTUNDER);
106     }
107 
108     /** {@inheritDoc} */
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             // TODO: Should depend on type and accent
117             return new RelativeScriptlevelLayoutContext(
118                     new InlineLayoutContext(now), 1);
119         }
120     }
121 
122     /**
123      * returns the accentunder property as boolean.
124      * 
125      * @return accentunder
126      */
127     protected boolean getAccentunderAsBoolean() {
128         return Boolean.parseBoolean(this.getAccentunder());
129     }
130 
131     /** {@inheritDoc} */
132     public JEuclidElement getBase() {
133         return this.getMathElement(0);
134     }
135 
136     /** {@inheritDoc} */
137     public abstract JEuclidElement getOverscript();
138 
139     /** {@inheritDoc} */
140     public abstract JEuclidElement getUnderscript();
141 
142     /** {@inheritDoc} */
143     public void setAccent(final String accent) {
144         this.setAttribute(AbstractUnderOver.ATTR_ACCENT, accent);
145     }
146 
147     /** {@inheritDoc} */
148     public void setAccentunder(final String accentunder) {
149         this.setAttribute(AbstractUnderOver.ATTR_ACCENTUNDER, accentunder);
150     }
151 
152     /** {@inheritDoc} */
153     public void setBase(final MathMLElement base) {
154         this.setMathElement(0, base);
155     }
156 
157     /** {@inheritDoc} */
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     /** {@inheritDoc} */
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 }