Coverage report

  %line %branch
org.cb.jset.client.ui.JSetCardComponent$1
100% 
?% 

 1  
 // START LICENSE
 2  
 // JSet - a Java JSet card board game implementation
 3  
 // Copyright (C) 2004 Jerome Lacoste
 4  
 //
 5  
 // This program is free software; you can redistribute it and/or modify
 6  
 // it under the terms of the GNU General Public License as published by
 7  
 // the Free Software Foundation; either version 2 of the License, or (at
 8  
 // your option) any later version.
 9  
 //
 10  
 // This program is distributed in the hope that it will be useful, but
 11  
 // WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 // General Public License for more details.
 14  
 //
 15  
 // You should have received a copy of the GNU General Public License
 16  
 // along with this program; if not, write to the Free Software
 17  
 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 18  
 // END LICENSE
 19  
 
 20  
 package org.cb.jset.client.ui;
 21  
 
 22  
 import org.cb.jset.CardProperties;
 23  
 import org.cb.jset.JSetCard;
 24  
 import org.cb.jset.client.model.SetGameClientCardListener;
 25  
 import org.cb.jset.client.model.CardEvent;
 26  
 import org.apache.log4j.Logger;
 27  
 
 28  
 import javax.swing.JPanel;
 29  
 import javax.swing.JComponent;
 30  
 import javax.swing.BorderFactory;
 31  
 import javax.swing.border.Border;
 32  
 import java.awt.Rectangle;
 33  
 import java.awt.Color;
 34  
 import java.awt.Paint;
 35  
 import java.awt.Graphics;
 36  
 import java.awt.Graphics2D;
 37  
 import java.awt.Shape;
 38  
 import java.awt.TexturePaint;
 39  
 import java.awt.BasicStroke;
 40  
 import java.awt.Stroke;
 41  
 import java.awt.geom.RectangularShape;
 42  
 import java.awt.geom.Area;
 43  
 import java.awt.geom.Ellipse2D;
 44  
 import java.awt.geom.GeneralPath;
 45  
 import java.awt.event.MouseAdapter;
 46  
 import java.awt.event.MouseEvent;
 47  
 import java.awt.image.BufferedImage;
 48  
 import java.util.HashMap;
 49  
 import java.util.Map;
 50  
 import java.io.Serializable;
 51  
 
 52  
 /**
 53  
  * The visual component representing a card on the board.
 54  
  *
 55  
  * @author jerome@coffeebreaks.org - last modified by $LastChangedBy: jerome $
 56  
  * @version $Id: JSetCardComponent.java 130 2004-04-15 05:18:07Z jerome $
 57  
  */
 58  
 public class JSetCardComponent extends JPanel
 59  
 {
 60  
   private final transient Logger _logger;
 61  
   private ShapeComponent[] _shapes;
 62  
 
 63  
   private boolean _isSelected = false;
 64  
   // private Border _selectedBorder = BorderFactory.createLoweredBevelBorder();
 65  
   private Border _selectedBorder = BorderFactory.createLineBorder(Color.black, 2);
 66  
   private Border _unselectedBorder = BorderFactory.createRaisedBevelBorder();
 67  
   private EventHandler _eventHandler = new EventHandler();
 68  
   private JSetCard _card;
 69  
 
 70  
   /**
 71  
    * A class representing the third type of shape.
 72  
    * @todo make it a subclass of RectangularShape ?
 73  
    */
 74  
   class OddShape
 75  
   {
 76  
     // int xa, ya, x1, y1, x2, y2, x3, y3;
 77  
     private final GeneralPath _oddShape;
 78  
 //    private boolean _firstTime;
 79  
 //    private final Rectangle _area;
 80  
     /**
 81  
      * Constructs an instance of <code>OddShape</code>.
 82  
      */
 83  
     public OddShape()
 84  
     {
 85  
       _oddShape = new GeneralPath();
 86  
 //      _firstTime = true;
 87  
 //      _area = new Rectangle();
 88  
     }
 89  
 
 90  
     /**
 91  
      * Return a general path that walks throught the contour of the odd shape, given the specified bounds.
 92  
      * @param bounds
 93  
      * @return the general path of the odd shape
 94  
      */
 95  
     GeneralPath constructOddShape(final Rectangle bounds)
 96  
     {
 97  
       _logger.debug("building odd shape " + bounds);
 98  
       //
 99  
       //   x0,y0 --- x1,y1
 100  
       //     \         \
 101  
       //      \         \
 102  
       //      |         |
 103  
       //     /         /
 104  
       //    /         /
 105  
       //   |         |
 106  
       //    \         \
 107  
       //   x2,y2 ---- x3,y3
 108  
 
 109  
       final int margin = 8;
 110  
 
 111  
       final int x0 = (class="keyword">int) bounds.getX() + margin;
 112  
       final int y0 = (class="keyword">int) bounds.getY();
 113  
       _oddShape.moveTo(x0, y0);
 114  
       final int x1 = x0 + (class="keyword">int) bounds.getWidth() - 2 * margin;
 115  
       final int y1 = y0;
 116  
       _oddShape.lineTo(x1, y1);
 117  
 
 118  
       final int x3 = x1;
 119  
       final int y3 = y1 + (class="keyword">int) bounds.getHeight();
 120  
       final int bez1x1 = x1 + 15;
 121  
       final int bez1y1 = y1 + 10;
 122  
       final int bez1x2 = x1 - 15;
 123  
       final int bez1y2 = y3 - 10;
 124  
       _oddShape.curveTo(bez1x1, bez1y1, bez1x2, bez1y2, x3, y3);
 125  
 
 126  
       final int x2 = x0;
 127  
       final int y2 = y3;
 128  
       _oddShape.lineTo(x2, y2);
 129  
 
 130  
       // temp
 131  
       final int bez2x1 = x2 - 15;
 132  
       final int bez2y1 = y2 - 10;
 133  
       final int bez2x2 = x2 + 15;
 134  
       final int bez2y2 = y0 + 10;
 135  
       _oddShape.curveTo(bez2x1, bez2y1, bez2x2, bez2y2, x0, y0);
 136  
 //      _oddShape.lineTo(x0, y0);
 137  
 
 138  
       return _oddShape;
 139  
     }
 140  
   }
 141  
 
 142  
   /**
 143  
    * A graphical component representing the shapes on the card.
 144  
    */
 145  
   final class ShapeComponent extends JComponent
 146  
   {
 147  
     private final Color _color;
 148  
     private final CardProperties _cardProperties;
 149  
     private final transient Logger _logger;
 150  
     private Map _shapes;
 151  
 
 152  
     /**
 153  
      * Constructs an instance of <code>ShapeComponent</code> given the specified card properties, and index
 154  
      * on the card.
 155  
      * @param cardProperties the properties used to determine the shape
 156  
      * @param index the index used to determine the position.
 157  
      */
 158  
     private ShapeComponent(final CardProperties cardProperties, class="keyword">final int index)
 159  
     {
 160  
       _cardProperties = cardProperties;
 161  
       _color = getColor(_cardProperties.getColor());
 162  
       _logger = Logger.getLogger("Shape " + index + " " + cardProperties);
 163  
       setBackground(Color.white);
 164  
     }
 165  
 
 166  
     private Color getColor(final byte pColor)
 167  
     {
 168  
       switch(pColor)
 169  
       {
 170  
         case CardProperties.COLOR_RED:
 171  
           return Color.RED;
 172  
         case CardProperties.COLOR_GREEN:
 173  
           return new Color(0, 102, 0);
 174  
         case CardProperties.COLOR_BLUE:
 175  
           return Color.BLUE;
 176  
         default:
 177  
           throw new IllegalStateException("Invalid color " + pColor);
 178  
       }
 179  
     }
 180  
 
 181  
     /**
 182  
      * @inheritDoc
 183  
      */
 184  
     protected void paintComponent(final Graphics g)
 185  
     {
 186  
       _logger.debug("painting shape component: " +  getBounds());
 187  
       super.paintComponent(g);    //To change body of overridden methods use File | Settings | File Templates.
 188  
 
 189  
       final Graphics2D g2 = (Graphics2D) g;
 190  
 
 191  
       _logger.debug("this bounds: " +  getBounds());
 192  
       _logger.debug("container bounds: " +  getParent().getBounds());
 193  
 
 194  
       final Paint oldPaint = g2.getPaint();
 195  
 
 196  
       switch (_cardProperties.getFill())
 197  
       {
 198  
         case CardProperties.FILL_EMPTY :
 199  
         case CardProperties.FILL_FULL :
 200  
           g2.setPaint(_color);
 201  
           break;
 202  
         case CardProperties.FILL_DOTTED:
 203  
           final BufferedImage bi = new BufferedImage(5, 5, BufferedImage.TYPE_INT_RGB);
 204  
           final Graphics2D big = bi.createGraphics();
 205  
           big.setColor(_color);
 206  
           big.fillRect(0, 0, 5, 5);
 207  
           big.setColor(getParent().getBackground());
 208  
           big.fillOval(0, 0, 5, 5);
 209  
           final Rectangle r = new Rectangle(0,0,5,5);
 210  
           g2.setPaint(new TexturePaint(bi, r));
 211  
           break;
 212  
       }
 213  
 
 214  
       // set the stroke
 215  
       switch (_cardProperties.getFill())
 216  
       {
 217  
         case CardProperties.FILL_EMPTY:
 218  
           final Stroke biggerStroke = new BasicStroke(4.0f);
 219  
           g2.setStroke(biggerStroke);
 220  
           break;
 221  
         case CardProperties.FILL_FULL :
 222  
           break;
 223  
         case CardProperties.FILL_DOTTED:
 224  
           break;
 225  
       }
 226  
 
 227  
       final Shape shape = getShape(_cardProperties.getShape());
 228  
       _logger.debug("%%% shape: " + shape + " bounds " + shape.getBounds());
 229  
 
 230  
       // Determines whether to fill, stroke, or fill and stroke.
 231  
       switch (_cardProperties.getFill())
 232  
       {
 233  
         case CardProperties.FILL_FULL :
 234  
           g2.fill(shape);
 235  
           break;
 236  
         case CardProperties.FILL_DOTTED:
 237  
           final Graphics2D tempg2 = g2;
 238  
           g2.fill(shape);
 239  
           g2.setColor(Color.darkGray);
 240  
           g2.draw(shape);
 241  
           g2.setPaint(tempg2.getPaint());
 242  
           break;
 243  
         case CardProperties.FILL_EMPTY:
 244  
           g2.draw(shape);
 245  
           break;
 246  
       }
 247  
 
 248  
       _logger.debug("painting circle: " +  getSize());
 249  
 
 250  
       g2.setPaint(oldPaint);
 251  
     }
 252  
 
 253  
     private Map initShapes()
 254  
     {
 255  
       final Map shapes = new HashMap(3);
 256  
 
 257  
       final Rectangle rectangle = new Rectangle();
 258  
       final Rectangle internRectangle = new Rectangle();
 259  
       shapes.put(new Integer(CardProperties.SHAPE_RECTANGLE), createShape(rectangle, internRectangle));
 260  
 
 261  
       final Ellipse2D.Double oval = new Ellipse2D.Double();
 262  
       final Ellipse2D.Double internOval = new Ellipse2D.Double();
 263  
       shapes.put(new Integer(CardProperties.SHAPE_OVALE), createShape(oval, internOval));
 264  
 
 265  
       final Rectangle lShapeBounds =
 266  
           new Rectangle(0, 0, (int) getBounds().getWidth(), (class="keyword">int) getBounds().getHeight());
 267  
       final Shape odd = new OddShape().constructOddShape(lShapeBounds);
 268  
       shapes.put(new Integer(CardProperties.SHAPE_ODD), odd);
 269  
 
 270  
       return shapes;
 271  
     }
 272  
 
 273  
     /**
 274  
      * Create a shape by {@link Area#subtract(java.awt.geom.Area) substracting} the specified
 275  
      * internal shape to the specified external one.
 276  
      * @param externalShape
 277  
      * @param internShape
 278  
      * @return
 279  
      */
 280  
     private Area createShape(final RectangularShape externalShape, class="keyword">final RectangularShape internShape)
 281  
     {
 282  
       externalShape.setFrame(0, 0, getBounds().getWidth(), getBounds().getHeight());
 283  
       // FIXME rename
 284  
       final int xxxX = (class="keyword">int) (getBounds().getWidth() / 4);
 285  
       final int xxxY = (class="keyword">int) (getBounds().getHeight() / 4);
 286  
       internShape.setFrame(xxxX, xxxY, getBounds().getWidth() - 2 * xxxX, getBounds().getHeight() - 2 * xxxY);
 287  
       final Area area2 = new Area(externalShape);
 288  
       final Area internOv = new Area(internShape);
 289  
       area2.subtract(internOv);
 290  
       return area2;
 291  
     }
 292  
 
 293  
     private Shape getShape(final byte shapeID)
 294  
     {
 295  
       // lazily construct the shapes so that they are built at a time when surrouding size information is known.
 296  
       if (_shapes == null)
 297  
       {
 298  
         _shapes = this.initShapes();
 299  
       }
 300  
       return (Shape) _shapes.get(new Integer(shapeID));
 301  
     }
 302  
   }
 303  
 
 304  
   /**
 305  
    * Constructs an instance of <code>JSetCardComponent</code> given the specified card.
 306  
    * @param card
 307  
    */
 308  
   public JSetCardComponent(final JSetCard card)
 309  
   {
 310  
     _logger = Logger.getLogger("Shape " + card);
 311  
     _card = card;
 312  
     _shapes = initShapes(card.getProperties());
 313  
     for (int i = 0; i < _shapes.length; i++)
 314  
     {
 315  
       final ShapeComponent mShape = _shapes[i];
 316  
       this.add(mShape);
 317  
     }
 318  
     init();
 319  
   }
 320  
 
 321  
   private void init()
 322  
   {
 323  
     this.setBackground(Color.white);
 324  
     this.addMouseListener(_eventHandler);
 325  
     this.setBorder(_unselectedBorder);
 326  
     final int width = 80;
 327  
     final int height = (class="keyword">int) (1.5 * width);
 328  
     setSize(width, height);
 329  
     setPreferredSize(getSize());
 330  
     _logger.debug("initialized: " +  getSize());
 331  
   }
 332  
 
 333  
   private ShapeComponent[] initShapes(final CardProperties pProperties)
 334  
   {
 335  
     final ShapeComponent[] lResult = new ShapeComponent[pProperties.getNumber()];
 336  
     for (int i = 0; i < lResult.length; i++)
 337  
     {
 338  
       lResult[i] = new ShapeComponent(pProperties, i);
 339  
     }
 340  
     return lResult;
 341  
   }
 342  
 
 343  
 
 344  
   private void resetShapeBounds()
 345  
   {
 346  
     final int width = (class="keyword">int) this.getBounds().getWidth();
 347  
     final int height = (class="keyword">int) this.getBounds().getHeight();
 348  
     // margins when displaying 3 shapes
 349  
     final int sideMargin = 5;
 350  
     final int middleMargin = 5;
 351  
     final int shapeWidth = (width - 2 * sideMargin - middleMargin) / 2;
 352  
     final int shapeHeight = (height - 2 * sideMargin - middleMargin) / 2;
 353  
     _logger.debug("** setting new shapes bounds: " + this.getBounds());
 354  
     _logger.debug("** shapeWidth: " + shapeWidth + " shapeHeight: " + shapeHeight);
 355  
 
 356  
     switch (_shapes.length)
 357  
     {
 358  
       case 1:
 359  
         _shapes[0].setBounds((width - shapeWidth) / 2, (height - shapeHeight) / 2, shapeWidth, shapeHeight);
 360  
         break;
 361  
       case 2:
 362  
         // upper _area
 363  
         _shapes[0].setBounds((width - shapeWidth) / 2, sideMargin, shapeWidth, shapeHeight);
 364  
         // lower _area
 365  
         _shapes[1].setBounds((width - shapeWidth) / 2, sideMargin + shapeHeight + middleMargin, shapeWidth, shapeHeight);
 366  
         break;
 367  
       case 3:
 368  
         // upper _area
 369  
         _shapes[0].setBounds(sideMargin, sideMargin, shapeWidth, shapeHeight);
 370  
         _shapes[1].setBounds(sideMargin + shapeWidth + middleMargin, sideMargin, shapeWidth, shapeHeight);
 371  
         // lower _area
 372  
         _shapes[2].setBounds((width - shapeWidth) / 2, sideMargin + shapeHeight + middleMargin, shapeWidth, shapeHeight);
 373  
         break;
 374  
       default:
 375  
         throw new IllegalStateException("Invalid number of shapes " + _shapes.length);
 376  
     }
 377  
     for (int i = 0; i < _shapes.length; i++)
 378  
     {
 379  
       final ShapeComponent mShape = _shapes[i];
 380  
       _logger.debug("shape[" + i + "]s bounds: " + mShape.getBounds());
 381  
     }
 382  
   }
 383  
 
 384  
   // debugging code
 385  
   /*
 386  
   public void paint(final Graphics g)
 387  
   {
 388  
     super.paint(g);    //To change body of overridden methods use File | Settings | File Templates.
 389  
     _logger.debug("painting card: " +  getBounds());
 390  
   }
 391  
 
 392  
   // debugging code
 393  
   public void setSize(int width, int height)
 394  
   {
 395  
     _logger.debug("setting size, " + width + " " + height);
 396  
     super.setSize(width, height);    //To change body of overridden methods use File | Settings | File Templates.
 397  
   }
 398  
 
 399  
   public void setBounds(final int x, final int y, final int width, final int height)
 400  
   {
 401  
     _logger.debug("setting bounds, " + x + " " + y + " " + width + " " + height);
 402  
     super.setBounds(x, y, width, height);
 403  
     _logger.debug("this bounds: " +  getBounds());
 404  
     if (getParent() != null)
 405  
     {
 406  
       _logger.debug("container bounds: " +  getParent().getBounds());
 407  
     }
 408  
   }
 409  
   */
 410  
 
 411  
   /**
 412  
    * Update the border depending on the internal selection status.
 413  
    * @see #isSelected
 414  
    */
 415  
   public void updateBorder()
 416  
   {
 417  
     if (_isSelected)
 418  
     {
 419  
       setBorder(_selectedBorder);
 420  
     }
 421  
     else
 422  
     {
 423  
       setBorder(_unselectedBorder);
 424  
     }
 425  
     invalidate();
 426  
   }
 427  
 
 428  
   /**
 429  
    * Modifies the internal selection status
 430  
    */
 431  
   private void changeSelection()
 432  
   {
 433  
     setSelection(! _isSelected);
 434  
   }
 435  
 
 436  
   /**
 437  
    * Set the new internal selection status for the card.
 438  
    * If the selection has changed, fire an new {@link CardEvent} with the appropriate event type.
 439  
    * @param newSelection
 440  
    * @see #isSelected()
 441  
    */
 442  
   public void setSelection(final boolean newSelection)
 443  
   {
 444  
     final boolean oldSelection = _isSelected;
 445  
     if (oldSelection != newSelection)
 446  
     {
 447  
       _isSelected = newSelection;
 448  
       final int selected = _isSelected ? CardEvent.SELECTED : CardEvent.DESELECTED;
 449  
       fireCardSelectionEvent(new CardEvent(getCard(), selected));
 450  
     }
 451  
   }
 452  
 
 453  
   /**
 454  
    * @return <code>true</code> if the card is selected, <code>false</code> otherwise.
 455  
    */
 456  
   public boolean isSelected()
 457  
   {
 458  
     return _isSelected;
 459  
   }
 460  
 
 461  
   /**
 462  
    * @return the card
 463  
    */
 464  
   public JSetCard getCard()
 465  
   {
 466  
     return _card;
 467  
   }
 468  
 
 469  
   /**
 470  
    * Paint this card component given the specified graphics.
 471  
    * @param g
 472  
    */
 473  
   protected void paintComponent(final Graphics g)
 474  
   {
 475  
     _logger.debug("painting card component: " +  getBounds());
 476  
     super.paintComponent(g);    //To change body of overridden methods use File | Settings | File Templates.
 477  
     resetShapeBounds();
 478  
     updateBorder();
 479  
   }
 480  
 
 481  
   /**
 482  
    * @param i
 483  
    * @return the ith shape from that card.
 484  
    */
 485  
   ShapeComponent getShape(int i)
 486  
   {
 487  
     return _shapes[i];
 488  
   }
 489  
 
 490  
   /**
 491  
    * A mouse listener that {@link JSetCardComponent#changeSelection changes the card's selection} upon a mouse click.
 492  
    */
 493  
   private class EventHandler extends MouseAdapter implements Serializable
 494  
   {
 495  
     /**
 496  
      * @inheritDoc
 497  
      */
 498  
     public void mouseClicked(final MouseEvent e)
 499  
     {
 500  
       // Checks whether or not the cursor is inside of the rectangle when the
 501  
       // user releases the mouse button.
 502  
 			if(JSetCardComponent.this.contains(e.getX(), e.getY()))
 503  
       {
 504  
         changeSelection();
 505  
       }
 506  
     }
 507  
   }
 508  
 
 509  
   /**
 510  
    * Adds the specified cardlistener to the component listener list.
 511  
    * @param cardListener
 512  
    */
 513  
   void addCardEventListener(final SetGameClientCardListener cardListener)
 514  
   {
 515  
     super.listenerList.add(SetGameClientCardListener.class, cardListener);
 516  
   }
 517  
 
 518  
   /**
 519  
    * Removes the specified cardlistener to the component listener list.
 520  
    * @param cardListener
 521  
    */
 522  
   void removeCardEventListener(final SetGameClientCardListener cardListener)
 523  
   {
 524  
     super.listenerList.remove(SetGameClientCardListener.class, cardListener);
 525  
   }
 526  
 
 527  
   /** fire the specified card selection event
 528  
    * @param e the event to fire
 529  
    * @see SetGameClientCardListener#notifyCardEvent(org.cb.jset.client.model.CardEvent)
 530  
    */
 531  
   private void fireCardSelectionEvent(final CardEvent e)
 532  
   {
 533  
     // Guaranteed to return a non-null array
 534  
     final Object[] listeners = super.listenerList.getListenerList();
 535  
     _logger.debug("" + listeners.length);
 536  
     // Process the listeners last to first, notifying
 537  
     // those that are interested in this event
 538  
     for (int i = listeners.length - 2; i >= 0; i -= 2)
 539  
     {
 540  
       if (listeners[i] == SetGameClientCardListener.class)
 541  
       {
 542  
         ((SetGameClientCardListener) listeners[i + 1]).notifyCardEvent(e);
 543  
       }
 544  
     }
 545  
   }
 546  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.