Coverage Report - net.sourceforge.jeuclid.swing.MathComponentUI
 
Classes in this File Line Coverage Branch Coverage Complexity
MathComponentUI
19%
27/137
8%
6/72
1,95
MathComponentUI$ViewContext
100%
10/10
100%
2/2
1,95
 
 1  
 /*
 2  
  * Copyright 2002 - 2008 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: MathComponentUI.java,v 950f3d880cc1 2010/08/09 21:18:26 max $ */
 18  
 
 19  
 package net.sourceforge.jeuclid.swing;
 20  
 
 21  
 import java.awt.Color;
 22  
 import java.awt.Dimension;
 23  
 import java.awt.Graphics;
 24  
 import java.awt.Graphics2D;
 25  
 import java.awt.Insets;
 26  
 import java.awt.Point;
 27  
 import java.awt.geom.Point2D;
 28  
 import java.beans.PropertyChangeEvent;
 29  
 import java.beans.PropertyChangeListener;
 30  
 import java.lang.ref.Reference;
 31  
 import java.lang.ref.SoftReference;
 32  
 import java.util.HashMap;
 33  
 import java.util.List;
 34  
 import java.util.Map;
 35  
 
 36  
 import javax.swing.JComponent;
 37  
 import javax.swing.LookAndFeel;
 38  
 import javax.swing.SwingConstants;
 39  
 import javax.swing.border.Border;
 40  
 import javax.swing.plaf.ComponentUI;
 41  
 
 42  
 import net.sourceforge.jeuclid.LayoutContext;
 43  
 import net.sourceforge.jeuclid.layout.JEuclidView;
 44  
 
 45  
 import org.w3c.dom.Node;
 46  
 
 47  
 /**
 48  
  * See http://today.java.net/pub/a/today/2007/02/22/how-to-write-custom-swing-
 49  
  * component.html for details.
 50  
  * 
 51  
  * @version $Revision: 950f3d880cc1 $
 52  
  * 
 53  
  */
 54  
 public class MathComponentUI extends ComponentUI implements
 55  
         PropertyChangeListener {
 56  
 
 57  
     // /**
 58  0
     // * Logger for this class
 59  
     // */
 60  
     // Currently Unused
 61  
     // private static final Log LOGGER =
 62  
     // LogFactory.getLog(MathComponentUI.class);
 63  
 
 64  4
     private final Map<JMathComponent, Reference<ViewContext>> contextCache = new HashMap<JMathComponent, Reference<ViewContext>>();
 65  
 
 66  
     /**
 67  
      * Default constructor.
 68  
      */
 69  
     public MathComponentUI() {
 70  4
         super();
 71  
         // nothing to do.
 72  4
     }
 73  
 
 74  
     private JEuclidView getJeuclidView(final Graphics g, final JComponent c) {
 75  12
         JMathComponent jc = (JMathComponent) c;
 76  12
         ViewContext cache = null;
 77  12
         Reference<ViewContext> ref = contextCache.get(jc);
 78  12
         if (ref != null) {
 79  4
             cache = ref.get();
 80  
         }
 81  
 
 82  12
         if (cache == null) {
 83  8
             cache = new ViewContext(jc);
 84  8
             contextCache.put(jc, new SoftReference<ViewContext>(cache));
 85  0
         }
 86  0
 
 87  12
         return cache.getJeculidView((Graphics2D) g);
 88  0
     }
 89  0
 
 90  0
     /** {@inheritDoc} */
 91  0
     @Override
 92  
     public void paint(final Graphics g, final JComponent c) {
 93  0
         JEuclidView jEuclidView = this.getJeuclidView(g, c);
 94  0
         final Dimension dim = this.calculatePreferredSize(c, jEuclidView);
 95  0
         final Point start = this.getStartPointWithBordersAndAdjustDimension(c,
 96  
                 dim);
 97  0
         this.paintBackground(g, c, dim, start);
 98  
 
 99  0
         final Point2D alignOffset = this.calculateAlignmentOffset(
 100  0
                 (JMathComponent) c, jEuclidView, dim);
 101  0
         jEuclidView.draw((Graphics2D) g, (float) alignOffset.getX() + start.x,
 102  0
                 (float) alignOffset.getY() + start.y);
 103  
 
 104  0
     }
 105  0
 
 106  
     /** {@inheritDoc} */
 107  
     @Override
 108  
     public void update(final Graphics g, final JComponent c) {
 109  0
         if (c.isOpaque()) {
 110  0
             g.setColor(c.getBackground());
 111  0
             g.fillRect(0, 0, c.getWidth(), c.getHeight());
 112  0
         }
 113  0
         this.paint(g, c);
 114  0
     }
 115  
 
 116  0
     private Point2D calculateAlignmentOffset(JMathComponent jc,
 117  
             JEuclidView jEuclidView, final Dimension dim) {
 118  
         final float xo;
 119  0
         if ((jc.getHorizontalAlignment() == SwingConstants.LEADING)
 120  0
                 || (jc.getHorizontalAlignment() == SwingConstants.LEFT)) {
 121  0
             xo = 0.0f;
 122  0
         } else if ((jc.getHorizontalAlignment() == SwingConstants.TRAILING)
 123  
                 || (jc.getHorizontalAlignment() == SwingConstants.RIGHT)) {
 124  0
             xo = dim.width - jEuclidView.getWidth();
 125  
         } else {
 126  0
             xo = (dim.width - jEuclidView.getWidth()) / 2.0f;
 127  0
         }
 128  
         final float yo;
 129  0
         if (jc.getVerticalAlignment() == SwingConstants.TOP) {
 130  0
             yo = jEuclidView.getAscentHeight();
 131  0
         } else if (jc.getVerticalAlignment() == SwingConstants.BOTTOM) {
 132  0
             yo = dim.height - jEuclidView.getDescentHeight();
 133  0
         } else {
 134  0
             yo = (dim.height + jEuclidView.getAscentHeight() - jEuclidView
 135  0
                     .getDescentHeight()) / 2.0f;
 136  
         }
 137  0
         return new Point2D.Float(xo, yo);
 138  
     }
 139  
 
 140  0
     private void paintBackground(final Graphics g, JComponent c,
 141  0
             final Dimension dim, final Point start) {
 142  0
         final Color back = this.getRealBackgroundColor(c);
 143  0
         if (back != null) {
 144  0
             g.setColor(back);
 145  0
             g.fillRect(start.x, start.y, dim.width, dim.height);
 146  0
         }
 147  0
     }
 148  
 
 149  
     private Point getStartPointWithBordersAndAdjustDimension(JComponent c,
 150  0
             final Dimension dim) {
 151  0
         Point start = new Point(0, 0);
 152  0
         final Border border = c.getBorder();
 153  0
         if (border != null) {
 154  0
             final Insets insets = border.getBorderInsets(c);
 155  0
             if (insets != null) {
 156  0
                 dim.width -= insets.left + insets.right;
 157  0
                 dim.height -= insets.top + insets.bottom;
 158  0
                 start = new Point(insets.left, insets.top);
 159  
             }
 160  0
         }
 161  0
         return start;
 162  0
     }
 163  
 
 164  
     private Color getRealBackgroundColor(JComponent c) {
 165  0
         Color back = c.getBackground();
 166  0
         if (c.isOpaque()) {
 167  0
             if (back == null) {
 168  0
                 back = Color.WHITE;
 169  0
             }
 170  0
             // Remove Alpha
 171  0
             back = new Color(back.getRGB());
 172  
         }
 173  0
         return back;
 174  
     }
 175  
 
 176  0
     /** {@inheritDoc} */
 177  
     @Override
 178  
     public void installUI(final JComponent c) {
 179  4
         if (c instanceof JMathComponent) {
 180  4
             c.addPropertyChangeListener(this);
 181  4
             this.installDefaults(c);
 182  
         } else {
 183  0
             throw new IllegalArgumentException(
 184  
                     "This UI can only be installed on a JMathComponent");
 185  
         }
 186  4
     }
 187  0
 
 188  0
     /**
 189  
      * Configures the default properties from L&F.
 190  
      * 
 191  
      * @param c
 192  
      *            the component
 193  0
      */
 194  0
     protected void installDefaults(final JComponent c) {
 195  0
         // LookAndFeel.installColorsAndFont(c, "Label.background",
 196  
         // "Label.foreground", "Label.font");
 197  4
         LookAndFeel.installProperty(c, "opaque", Boolean.FALSE);
 198  4
     }
 199  0
 
 200  0
     /** {@inheritDoc} */
 201  0
     @Override
 202  0
     public void uninstallUI(final JComponent c) {
 203  0
         c.removePropertyChangeListener(this);
 204  0
         this.contextCache.remove(c);
 205  0
     }
 206  
 
 207  0
     /** {@inheritDoc} */
 208  0
     public void propertyChange(final PropertyChangeEvent evt) {
 209  16
         this.contextCache.remove(evt.getSource());
 210  16
     }
 211  0
 
 212  
     /** {@inheritDoc} */
 213  0
     @Override
 214  
     public Dimension getPreferredSize(final JComponent c) {
 215  12
         return this.getMathComponentSize(c);
 216  
     }
 217  0
 
 218  0
     /**
 219  
      * Retrieve the preferred size of the math component.
 220  0
      * 
 221  
      * @param c
 222  0
      *            the math component to measure
 223  
      * @return the preferred size.
 224  
      */
 225  
     private Dimension getMathComponentSize(final JComponent c) {
 226  12
         JEuclidView jEuclidView = this.getJeuclidView(c.getGraphics(), c);
 227  12
         return this.calculatePreferredSize(c, jEuclidView);
 228  
     }
 229  
 
 230  
     private Dimension calculatePreferredSize(final JComponent c,
 231  
             JEuclidView jEuclidView) {
 232  
         Dimension retVal;
 233  12
         retVal = new Dimension((int) Math.ceil(jEuclidView.getWidth()),
 234  
                 (int) Math.ceil(jEuclidView.getAscentHeight()
 235  
                         + jEuclidView.getDescentHeight()));
 236  
 
 237  12
         final Border border = c.getBorder();
 238  12
         if (border != null) {
 239  0
             final Insets insets = border.getBorderInsets(c);
 240  0
             if (insets != null) {
 241  0
                 retVal.width += insets.left + insets.right;
 242  0
                 retVal.height += insets.top + insets.bottom;
 243  0
             }
 244  
         }
 245  12
         return retVal;
 246  
     }
 247  
 
 248  
     /** {@inheritDoc} */
 249  0
     @Override
 250  
     public Dimension getMaximumSize(final JComponent c) {
 251  0
         return this.getMathComponentSize(c);
 252  
     }
 253  
 
 254  0
     /** {@inheritDoc} */
 255  0
     @Override
 256  0
     public Dimension getMinimumSize(final JComponent c) {
 257  0
         return this.getMathComponentSize(c);
 258  0
     }
 259  0
 
 260  
     /**
 261  
      * Get vector of {@link JEuclidView.NodeRect} at a particular mouse
 262  0
      * position.
 263  
      * 
 264  
      * @param mathComponent
 265  
      *            MathComponent to look in.
 266  
      * @param x
 267  0
      *            x-coord
 268  
      * @param y
 269  
      *            y-coord
 270  
      * @return list of nodes with rendering information
 271  
      */
 272  
     public List<JEuclidView.NodeRect> getNodesAt(JMathComponent mathComponent,
 273  0
             final float x, final float y) {
 274  0
         JEuclidView jEuclidView = getJeuclidView(mathComponent.getGraphics(),
 275  
                 mathComponent);
 276  0
         final Point2D point = this.calculateAlignmentOffset(mathComponent,
 277  
                 jEuclidView, mathComponent.getSize());
 278  0
         return jEuclidView.getNodesAt(x, y, (float) point.getX(),
 279  
                 (float) point.getY());
 280  
     }
 281  
 
 282  
     private static class ViewContext {
 283  
         final Node document;
 284  
         final LayoutContext layoutContext;
 285  8
         final Map<Graphics2D, JEuclidView> jeuclidViews = new HashMap<Graphics2D, JEuclidView>();
 286  
 
 287  0
         public ViewContext(JMathComponent jMathComponent) {
 288  8
             this.document = jMathComponent.getDocument();
 289  0
             this.layoutContext = jMathComponent.getParameters();
 290  8
         }
 291  
 
 292  
         public JEuclidView getJeculidView(Graphics2D g2d) {
 293  12
             JEuclidView jeuclidView = jeuclidViews.get(g2d);
 294  12
             if (jeuclidView == null) {
 295  8
                 jeuclidView = new JEuclidView(document, layoutContext, g2d);
 296  8
                 jeuclidViews.put(g2d, jeuclidView);
 297  
             }
 298  12
             return jeuclidView;
 299  
         }
 300  
     }
 301  
 
 302  
 }