package edu.mit.blocks.codeblockutil;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Transparency;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
/**
* A CIconButton is a swing-compatible widget that allows clients
* to display a circular button with an optional icon. Clients
* may choose from the the possible set of of icons: play, pause, step.
*
* To add a particular action to this widget, users should invoke
* this.addMouseListener( new CButtonListener());
*/
public class CIconButton extends CButton {
private static final long serialVersionUID = 328149080213L;
/** Icon choices */
public static enum Icon {
PLAY, PAUSE, STEP, STOP
};
/** icon inset of this button */
private static final int ICON_INSET = 12;
/** Button icon */
private Icon icon;
/**
* @effects constructs this with particular icon, a black background,
* and a blue selected coloring.
*/
public CIconButton(Icon icon) {
super(Color.black, Color.blue, null);
this.icon = icon;
super.setPreferredSize(new Dimension(40, 40));
super.setSize(new Dimension(40, 40));
//Check rep
if (!(ICON_INSET > 2 * INSET)) {
throw new RuntimeException("insets faulty");
}
}
/**
* constructs this with the given icon, background,
* and selected coloring.
* @param backColor the normal "unarmard" background color
* @param selectedColor the color of the background when the button is pressed
* @param icon the CIconButton.Icon for this button
*/
public CIconButton(Color backColor, Color selectedColor, Icon icon) {
super(backColor, selectedColor, null);
this.icon = icon;
super.setPreferredSize(new Dimension(40, 40));
super.setSize(new Dimension(40, 40));
//Check rep
if (!(ICON_INSET > 2 * INSET)) {
throw new RuntimeException("insets faulty");
}
}
/**
* @param icon
*
* @requires none
* @modifies this.icon
* @effects Dynamically changes the icon of this button.
* If the icon is null, no icon will be displayed
*/
public void setIcon(Icon icon) {
this.icon = icon;
}
/**
* @return selected fag
*/
@Override
public boolean isSelected() {
return this.selected;
}
/**
* This methods maps an icon Enum to an icon Shape
*
* @param icon - icon enum
*
* @requires none
* @return the cooresponding icon shape (to match the icon enum)
* or null if the icon enum does not match any known ones.
*/
private Shape getIconShape(Icon icon) {
int width = this.getWidth() - 2 * ICON_INSET;
if (icon == Icon.PLAY) {
GeneralPath shape = new GeneralPath();
shape.moveTo(ICON_INSET, ICON_INSET);
shape.lineTo(ICON_INSET + width, ICON_INSET + width / 2);
shape.lineTo(ICON_INSET, ICON_INSET + width);
shape.lineTo(ICON_INSET, ICON_INSET);
shape.closePath();
return shape;
} else if (icon == Icon.PAUSE) {
Rectangle2D.Float rect1 = new Rectangle2D.Float(ICON_INSET, ICON_INSET, width / 3, width);
Rectangle2D.Float rect2 = new Rectangle2D.Float(ICON_INSET + width * 2 / 3, ICON_INSET, width / 3, width);
Area shape1 = new Area(rect1);
Area shape2 = new Area(rect2);
shape1.add(shape2);
return shape1;
} else if (icon == Icon.STEP) {
Rectangle2D.Float rect = new Rectangle2D.Float(ICON_INSET, ICON_INSET, width / 5, width);
GeneralPath triangle = new GeneralPath();
triangle.moveTo(ICON_INSET + width * 2 / 5, ICON_INSET);
triangle.lineTo(ICON_INSET + width, ICON_INSET + width / 2);
triangle.lineTo(ICON_INSET + width * 2 / 5, ICON_INSET + width);
triangle.closePath();
Area area1 = new Area(rect);
Area area2 = new Area(triangle);
area1.add(area2);
return area1;
} else if (icon == Icon.STOP) {
Rectangle2D.Float rect1 = new Rectangle2D.Float(ICON_INSET, ICON_INSET, width, width);
Area shape1 = new Area(rect1);
return shape1;
}
return null;
}
/** Paints this */
@Override
public void paint(Graphics g) {
//selected color
Color backgroundColor;
if (this.pressed) {
backgroundColor = this.selectedColor;
} else {
backgroundColor = this.buttonColor;
}
// Set up graphics and buffer
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
BufferedImage buffer = GraphicsManager.gc.createCompatibleImage(this.getWidth(), this.getHeight(), Transparency.TRANSLUCENT);
Graphics2D gb = buffer.createGraphics();
gb.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Set up first layer
int buttonHeight = this.getHeight() - (INSET * 2);
int buttonWidth = this.getWidth() - (INSET * 2);
Color topColoring = backgroundColor.darker().darker().darker();
Color bottomColoring = backgroundColor.brighter().brighter().brighter();
gb.setPaint(new GradientPaint(0, INSET, topColoring, 0, buttonHeight, bottomColoring, false));
// Paint the first layer
gb.fillOval(INSET, INSET, buttonWidth, buttonHeight);
// set up paint data fields for second layer
int highlightHeight = buttonHeight - (HIGHLIGHT_INSET * 2);
int highlightWidth = buttonWidth - (HIGHLIGHT_INSET * 2);
topColoring = Color.WHITE;
bottomColoring = backgroundColor.brighter();
// Paint the second layer
gb.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .8f));
gb.setPaint(new GradientPaint(0, INSET + HIGHLIGHT_INSET, topColoring, 0, INSET + HIGHLIGHT_INSET + (highlightHeight / 2), backgroundColor.brighter(), false));
gb.setClip(new Ellipse2D.Float(INSET + HIGHLIGHT_INSET, INSET + HIGHLIGHT_INSET, highlightWidth, highlightHeight / 2));
gb.fillOval(INSET + HIGHLIGHT_INSET, INSET + HIGHLIGHT_INSET, highlightWidth, highlightHeight);
// Blur
ConvolveOp blurOp = new ConvolveOp(new Kernel(3, 3, BLUR));
BufferedImage blurredImage = blurOp.filter(buffer, null);
// Draw button
g2.drawImage(blurredImage, 0, 0, null);
//draw icon
if (this.icon != null) {
if (this.focus) {
g2.setColor(Color.white);
} else {
g2.setColor(Color.gray);
}
g2.fill(this.getIconShape(this.icon));
}
}
}