001    /*
002     * Copyright 2007 - 2007 JEuclid, http://jeuclid.sf.net
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    /* $Id: AbstractRoot.java,v bc1d5fde7b73 2009/06/01 14:40:54 maxberger $ */
018    
019    package net.sourceforge.jeuclid.elements.presentation.general;
020    
021    import java.awt.Color;
022    import java.awt.FontMetrics;
023    import java.awt.Graphics2D;
024    import java.util.List;
025    
026    import net.sourceforge.jeuclid.LayoutContext;
027    import net.sourceforge.jeuclid.context.Parameter;
028    import net.sourceforge.jeuclid.elements.AbstractJEuclidElement;
029    import net.sourceforge.jeuclid.elements.support.GraphicsSupport;
030    import net.sourceforge.jeuclid.elements.support.attributes.AttributesHelper;
031    import net.sourceforge.jeuclid.layout.GraphicsObject;
032    import net.sourceforge.jeuclid.layout.LayoutInfo;
033    import net.sourceforge.jeuclid.layout.LayoutStage;
034    import net.sourceforge.jeuclid.layout.LayoutView;
035    import net.sourceforge.jeuclid.layout.LayoutableNode;
036    import net.sourceforge.jeuclid.layout.LineObject;
037    
038    import org.apache.batik.dom.AbstractDocument;
039    import org.w3c.dom.mathml.MathMLRadicalElement;
040    
041    /**
042     * common superclass for root like elements (root, sqrt).
043     * 
044     * @version $Revision: bc1d5fde7b73 $
045     */
046    public abstract class AbstractRoot extends AbstractJEuclidElement implements
047            MathMLRadicalElement {
048    
049        private static final String EXTRA_SPACE = "0.1ex";
050    
051        private static final String ROOT_WIDTH = "0.5em";
052    
053        /**
054         * Default constructor. Sets MathML Namespace.
055         * 
056         * @param qname
057         *            Qualified name.
058         * @param odoc
059         *            Owner Document.
060         */
061        public AbstractRoot(final String qname, final AbstractDocument odoc) {
062            super(qname, odoc);
063        }
064    
065        /**
066         * retrieve the content of this radical element.
067         * 
068         * @return A List<MathElement> with the contents for this element.
069         */
070        protected abstract List<LayoutableNode> getContent();
071    
072        /** {@inheritDoc} */
073        // CHECKSTYLE:OFF
074        // This function is too long, but it depends on too many parameters.
075        @Override
076        protected void layoutStageInvariant(final LayoutView view,
077                final LayoutInfo info, final LayoutStage stage,
078                final LayoutContext context) {
079            // CHECKSTYLE:ON
080    
081            // Basic Calculations
082            final Graphics2D g = view.getGraphics();
083            final LayoutContext now = this.applyLocalAttributesToContext(context);
084            final float middleShift = this.getMiddleShift(g, context);
085            final float linethickness = GraphicsSupport.lineWidth(now);
086            final float extraSpace = AttributesHelper.convertSizeToPt(
087                    AbstractRoot.EXTRA_SPACE, now, "");
088            final float rootwidth = AttributesHelper.convertSizeToPt(
089                    AbstractRoot.ROOT_WIDTH, context, "");
090            final Color color = (Color) now.getParameter(Parameter.MATHCOLOR);
091            float xPos = linethickness;
092            final LayoutableNode index = (LayoutableNode) this.getIndex();
093            final List<GraphicsObject> graphicObjects = info.getGraphicObjects();
094            graphicObjects.clear();
095    
096            // Draw Index
097            float indexAscent;
098            if (index == null) {
099                indexAscent = 0.0f;
100            } else {
101                final LayoutInfo indexInfo = view.getInfo(index);
102                final float indexPos = middleShift + linethickness / 2.0f
103                        + extraSpace + indexInfo.getDescentHeight(stage);
104                indexInfo.moveTo(xPos, -indexPos, stage);
105                xPos += indexInfo.getWidth(stage);
106                graphicObjects.add(new LineObject(linethickness, -middleShift,
107                        xPos, -middleShift, linethickness, color));
108                indexAscent = indexPos + indexInfo.getAscentHeight(stage);
109            }
110    
111            // Skip Root Space
112            xPos += rootwidth;
113    
114            // Draw Content below Root
115            final float contentStartX = xPos;
116            final FontMetrics metrics = this
117                    .getFontMetrics(view.getGraphics(), now);
118            float maxAscent = metrics.getAscent();
119            float maxDescent = metrics.getDescent();
120            for (final LayoutableNode child : this.getContent()) {
121                final LayoutInfo childInfo = view.getInfo(child);
122                childInfo.moveTo(xPos, 0, stage);
123                maxAscent = Math.max(maxAscent, childInfo.getAscentHeight(stage));
124                maxDescent = Math
125                        .max(maxDescent, childInfo.getDescentHeight(stage));
126                xPos += childInfo.getWidth(stage);
127            }
128            xPos += 2 * extraSpace;
129            final float topLinePos = maxAscent + 2 * extraSpace + linethickness
130                    / 2.0f;
131    
132            // Fill in Info
133            info.setAscentHeight(Math.max(topLinePos + linethickness / 2.0f,
134                    indexAscent), stage);
135            info.setDescentHeight(maxDescent + linethickness / 2.0f, stage);
136            info.setHorizontalCenterOffset(xPos / 2.0f, stage);
137            info.setWidth(xPos + linethickness, stage);
138            info.setStretchAscent(maxAscent);
139            info.setStretchDescent(maxDescent);
140    
141            // Add Root Glyph
142            graphicObjects.add(new LineObject(contentStartX - rootwidth,
143                    -middleShift, contentStartX - rootwidth / 2.0f, maxDescent,
144                    linethickness, color));
145            graphicObjects.add(new LineObject(contentStartX - rootwidth / 2.0f,
146                    maxDescent, contentStartX, -topLinePos, linethickness, color));
147            graphicObjects.add(new LineObject(contentStartX, -topLinePos, xPos,
148                    -topLinePos, linethickness, color));
149        }
150    }