001    /*
002     * Copyright 2002 - 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: MathComponentUI.java 442 2007-08-21 12:47:57Z maxberger $ */
018    
019    package net.sourceforge.jeuclid.swing;
020    
021    import java.awt.Color;
022    import java.awt.Dimension;
023    import java.awt.Graphics;
024    import java.awt.Graphics2D;
025    import java.awt.Insets;
026    import java.awt.Point;
027    import java.awt.geom.Point2D;
028    import java.beans.PropertyChangeEvent;
029    import java.beans.PropertyChangeListener;
030    import java.util.Map;
031    
032    import javax.swing.JComponent;
033    import javax.swing.LookAndFeel;
034    import javax.swing.SwingConstants;
035    import javax.swing.border.Border;
036    import javax.swing.plaf.ComponentUI;
037    
038    import net.sourceforge.jeuclid.DOMBuilder;
039    import net.sourceforge.jeuclid.Defense;
040    import net.sourceforge.jeuclid.MathBase;
041    import net.sourceforge.jeuclid.ParameterKey;
042    
043    import org.apache.commons.logging.Log;
044    import org.apache.commons.logging.LogFactory;
045    import org.w3c.dom.Document;
046    
047    /**
048     * See
049     * http://today.java.net/pub/a/today/2007/02/22/how-to-write-custom-swing-component.html
050     * for details.
051     * 
052     * @author putrycze
053     * @version $Revision: 442 $
054     * 
055     */
056    public class MathComponentUI extends ComponentUI implements
057            PropertyChangeListener {
058    
059        /**
060         * Logger for this class
061         */
062        private static final Log LOGGER = LogFactory
063                .getLog(MathComponentUI.class);
064    
065        private JMathComponent mathComponent;
066    
067        /**
068         * Reference to the MathBase class.
069         */
070        private MathBase base;
071    
072        /**
073         * Creates a new UI.
074         * 
075         */
076        public MathComponentUI() {
077            // nothing to do
078        }
079    
080        /** {@inheritDoc} */
081        @Override
082        public void paint(final Graphics g, final JComponent c) {
083            final Graphics2D g2 = (Graphics2D) g;
084            final Dimension dim = new Dimension(this.mathComponent.getWidth(),
085                    this.mathComponent.getHeight());
086            final Point start = this
087                    .getStartPointWithBordersAndAdjustDimension(dim);
088            this.paintBackground(g, dim, start);
089            if (this.base != null) {
090                final Point2D alignOffset = this.calucateAlignmentOffset(g2, dim);
091                this.base.paint((Graphics2D) g, (float) alignOffset.getX()
092                        + start.x, (float) alignOffset.getY() + start.y);
093            }
094    
095        }
096    
097        private Point2D calucateAlignmentOffset(final Graphics2D g2,
098                final Dimension dim) {
099            final float xo;
100            if ((this.mathComponent.getHorizontalAlignment() == SwingConstants.LEADING)
101                    || (this.mathComponent.getHorizontalAlignment() == SwingConstants.LEFT)) {
102                xo = 0.0f;
103            } else if ((this.mathComponent.getHorizontalAlignment() == SwingConstants.TRAILING)
104                    || (this.mathComponent.getHorizontalAlignment() == SwingConstants.RIGHT)) {
105                xo = dim.width - this.base.getWidth(g2);
106            } else {
107                xo = (dim.width - this.base.getWidth(g2)) / 2.0f;
108            }
109            final float yo;
110            if (this.mathComponent.getVerticalAlignment() == SwingConstants.TOP) {
111                yo = 0.0f;
112            } else if (this.mathComponent.getVerticalAlignment() == SwingConstants.BOTTOM) {
113                yo = dim.height - this.base.getHeight(g2);
114            } else {
115                yo = (dim.height - this.base.getHeight(g2)) / 2.0f;
116            }
117            final Point2D alignOffset = new Point2D.Float(xo, yo);
118            return alignOffset;
119        }
120    
121        private void paintBackground(final Graphics g, final Dimension dim,
122                final Point start) {
123            final Color back = this.getRealBackgroundColor();
124            if (back != null) {
125                g.setColor(back);
126                g.fillRect(start.x, start.y, dim.width, dim.height);
127            }
128        }
129    
130        private Point getStartPointWithBordersAndAdjustDimension(
131                final Dimension dim) {
132            Point start = new Point(0, 0);
133            final Border border = this.mathComponent.getBorder();
134            if (border != null) {
135                final Insets insets = border.getBorderInsets(this.mathComponent);
136                if (insets != null) {
137                    dim.width -= insets.left + insets.right;
138                    dim.height -= insets.top + insets.bottom;
139                    start = new Point(insets.left, insets.top);
140                }
141            }
142            return start;
143        }
144    
145        private Color getRealBackgroundColor() {
146            Color back = this.mathComponent.getBackground();
147            if (this.mathComponent.isOpaque()) {
148                if (back == null) {
149                    back = Color.WHITE;
150                }
151                // Remove Alpha
152                back = new Color(back.getRGB());
153            }
154            return back;
155        }
156    
157        /** {@inheritDoc} */
158        @Override
159        public void installUI(final JComponent c) {
160            this.mathComponent = (JMathComponent) c;
161            c.addPropertyChangeListener(this);
162            this.installDefaults(this.mathComponent);
163        }
164    
165        /**
166         * Configures the default properties from L&F.
167         * 
168         * @param c
169         *            the component
170         */
171        protected void installDefaults(final JMathComponent c) {
172            // LookAndFeel.installColorsAndFont(c, "Label.background",
173            // "Label.foreground", "Label.font");
174            LookAndFeel.installProperty(c, "opaque", Boolean.FALSE);
175        }
176    
177        /** {@inheritDoc} */
178        @Override
179        public void uninstallUI(final JComponent c) {
180            c.removePropertyChangeListener(this);
181            this.mathComponent = null;
182        }
183    
184        /** {@inheritDoc} */
185        public void propertyChange(final PropertyChangeEvent evt) {
186            final String name = evt.getPropertyName();
187            if (name.equals("document") || name.equals("property")) {
188                final JMathComponent jc = (JMathComponent) evt.getSource();
189                this.redo((Document) evt.getNewValue(), jc.getParameters());
190                // jc.repaint();
191            } else {
192                try {
193                    final ParameterKey key = ParameterKey.valueOf(name);
194                    this.base.setParam(key, evt.getNewValue().toString());
195                } catch (final IllegalArgumentException ia) {
196                    MathComponentUI.LOGGER.debug(ia);
197                }
198            }
199        }
200    
201        private void redo(final Document doc,
202                final Map<ParameterKey, String> parameters) {
203            if (doc != null) {
204                this.base = new MathBase(parameters);
205                new DOMBuilder(doc, this.base);
206            } else {
207                this.base = null;
208            }
209        }
210    
211        /** {@inheritDoc} */
212        @Override
213        public Dimension getMinimumSize(final JComponent c) {
214            final Dimension dim;
215            if (this.base == null || c.getGraphics() == null) {
216                dim = this.getPreferredSize(c);
217            } else {
218                final Graphics2D g2d = (Graphics2D) c.getGraphics();
219                Defense.notNull(g2d, "g2d");
220                dim = new Dimension((int) Math.ceil(this.base.getWidth(g2d)),
221                        (int) Math.ceil(this.base.getHeight(g2d)));
222            }
223            final Border border = c.getBorder();
224            if (border != null) {
225                final Insets insets = border.getBorderInsets(c);
226                if (insets != null) {
227                    dim.width += insets.left + insets.right; // here +
228                    dim.height += insets.top + insets.bottom; // here +
229                }
230            }
231            return dim;
232    
233        }
234    
235    }