/*
* Copyright 2003-2011 Tufts University Licensed under the
* Educational Community License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.osedu.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package tufts.vue.gui;
import tufts.vue.DEBUG;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Color;
import java.awt.Insets;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GradientPaint;
import java.awt.RenderingHints;
import java.awt.AlphaComposite;
import javax.swing.Icon;
import javax.swing.AbstractButton;
import javax.swing.border.*;
/**
* VueButtonIcon
*
* Icon class to support the various states of VUE buttons (rollover, pressed, disabled, etc).
* Takes a raw icon which is used for painting the body of all the button states, but adjust borders,
* transparency, etc, based on button state. Can also handle a null raw icon for just drawing borders.
* Can install, via installGenerated, a set of icons into any AbstractButton using our default VUE GUI scheme.
*
* @version $Revision: 1.16 $ / $Date: 2010-02-03 19:15:46 $ / $Author: mike $
* @author Scott Fraize
*/
public class VueButtonIcon implements Icon
{
public static final int UP = 0; // unselected/default
public static final int PRESSED = 1; // only while being held down by a mouse press
public static final int SELECTED = 2; // selected (for toggle buttons)
public static final int DISABLED = 3; // disabled
public static final int ROLLOVER = 4; // rollover
public static final int MENU = 5; // sub-menus: default (palette menu)
public static final int MENU_SELECTED = 6; // sub-menus: rollover (palette menu)
public static void installGenerated(AbstractButton b, Icon raw, Dimension s) {
installGenerated(b,raw,raw,s);
}
public static void installGenerated(AbstractButton b, Icon raw, Icon selected,Dimension s) {
installGenerated(b,raw,selected,raw,s);
}
public static void installGenerated(AbstractButton b, Icon raw, Icon selected,Icon rollover,Dimension s) {
if (DEBUG.INIT||DEBUG.TOOL) System.out.println(b + " generating button states from " + raw);
if (s == null)
s = new Dimension(0,0);
if (GUI.isMacAqua() && b instanceof MenuButton) {
b.setIcon(raw);
} else {
b.setIcon(new VueButtonIcon(raw, UP, s));
b.setPressedIcon(new VueButtonIcon(raw, PRESSED, s));
b.setSelectedIcon(new VueButtonIcon(selected, SELECTED, s));
b.setDisabledIcon(new VueButtonIcon(raw, DISABLED, s));
b.setRolloverIcon(new VueButtonIcon(rollover, ROLLOVER, s));
}
}
private static final Color sButtonColor = new Color(222,222,222);
private static final Color sOverColor = Color.gray;
private static final Color sDownColor = sOverColor;
private static final AbstractBorder sRolloverBorder = new EtchedBorder();
//private static final AbstractBorder sRolloverBorder = new EtchedBorder(EtchedBorder.RAISED);
//private static final AbstractBorder sRolloverBorder = new BevelBorder(BevelBorder.LOWERED);
//private static final AbstractBorder sRolloverBorder = new SoftBevelBorder(BevelBorder.LOWERED);
private static final AlphaComposite DisabledAlpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f);
protected final int width;
protected final int height;
private static Insets insets = new Insets(0,0,0,0);
private Color mColor = sButtonColor;
private final int mType;
private final Icon mRawIcon;
private final boolean isPressIcon;
protected boolean isRadioButton = false; // if in an exclusive button-group
// OffsetWhenDown: nudge the icon when in the down state.
// Set to true of "up" state appears as a button -- can
// turn on otherwise but will need to adjust whole button so
// icon stays centered.
private final static boolean OffsetWhenDown = false;
private final static boolean debug = false;
public VueButtonIcon(Icon rawIcon, int t, int width, int height)
{
this.mRawIcon = rawIcon;
this.mType = t;
if (rawIcon != null) {
if (tufts.Util.isMacLeopard()) {
// TODO: not sure why we need this -- may have to do with clipping
this.width = width <= 0 ? rawIcon.getIconWidth() + 6 : width;
this.height = height <= 0 ? rawIcon.getIconHeight() + 6 : height;
} else {
this.width = width <= 0 ? rawIcon.getIconWidth() + 4 : width;
this.height = height <= 0 ? rawIcon.getIconHeight() + 4 : height;
}
} else {
this.width = width;
this.height = height;
}
this.isPressIcon = (t == PRESSED || t == SELECTED || t == MENU_SELECTED);
if (isPressIcon)
mColor = Color.lightGray;
if (debug) {
if (t == MENU) mColor = Color.pink;
if (t == MENU_SELECTED) mColor = Color.magenta;
if (t == ROLLOVER) mColor = Color.green;
}
//if (t >= MENU) insets = new Insets(-1,-1,1,1);
}
public VueButtonIcon(Icon rawIcon, int t, Dimension size) {
this(rawIcon, t, size.width, size.height);
}
public VueButtonIcon(Icon rawIcon, int t) {
this(rawIcon, t, 0, 0);
}
public int getIconWidth() { return width; }
public int getIconHeight() { return height; }
private final static String _types[] = {"Up","Pressed","Selected","Disabled","Rollover","Menu","Menu Selected"};
/**
* paint the entire button as an icon plus it's visible icon graphic
*/
public void paintIcon(Component c, Graphics g, int x, int y) {
if (debug||DEBUG.PAINT) System.out.println("painting "
+ mRawIcon + " type " + mType + "(" + _types[mType]
+ ") on " + c + " bg=" + c.getBackground());
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// if (mType >= MENU) {
// the drop-down menus have GC bugs on the PC such
// that we need to be sure to paint something in
// the entire region, or we appear to get another
// version of the icon painted *under* us.
g2.setColor(c.getBackground());
g2.fillRect(0,0, width,height);
// }
if (debug) {
g2.setColor(Color.red);
g2.fillRect(0,0, 99,99);
}
x += insets.top;
y += insets.left;
//if (VueUtil.isMacPlatform()) { x += 2; y += 2; }// try now that attend to x/y above
g2.translate(x,y);
int w = width - (insets.left + insets.right);
int h = height - (insets.top + insets.bottom);
float gw = width;
//GradientPaint gradient = new GradientPaint(gw/2,0,Color.white,gw/2,h/2,mColor,true);
//GradientPaint gradient = new GradientPaint(gw/6,0,Color.white,gw/2,h/2,mColor,true);
GradientPaint gradient = new GradientPaint(gw/6,0,Color.white,gw*.33f,h/2,mColor,true);
// Set gradient for the whole button.
if (isPressIcon) {
// Draw the 3d button border -- raised/lowered depending on down state
g2.setColor(c.getBackground());
g2.draw3DRect(0,0, w-1,h-1, !isPressIcon);
} else if (mType == ROLLOVER) {
// Draw an etched rollover border:
if (DEBUG.BOXES) {
g.setColor(Color.green);
g.fillRect(0,0, w, h);
}
if (true) {
if (tufts.Util.isMacLeopard()) {
// TODO: not sure why we need this -- may have to do with clipping
sRolloverBorder.paintBorder(c, g, 0, 2, w, h-2);
} else {
sRolloverBorder.paintBorder(c, g, 0, 0, w, h);
}
} else {
// experiment in separate border for drop-down portions
sRolloverBorder.paintBorder(c, g, 0, 0, w-8, h);
sRolloverBorder.paintBorder(c, g, w-7, 0, 7, h);
}
// this make it look like button-pressed:
//g2.draw3DRect(0,0, w-1,h-1, false);
}
// now fill the icon, but don't fill if we're just holding the mouse down on a grouped toggle button
// (so the group won't ever show two buttons in the fully selected state at once)
if (isPressIcon && (!isRadioButton || mType != PRESSED)) {
g2.setPaint(gradient);
g2.fillRect(1,1, w-2,h-2);
}
else
g2.setColor(mColor);
//g2.fillRect(1,1, w-2,h-2);
// skipping the fill here creates the flush-look
// if we're down, nudge icon
if (OffsetWhenDown && isPressIcon)
g2.translate(1,1);
// now draw the actual graphic in the center
if (mRawIcon != null) {
int ix = (w - mRawIcon.getIconWidth()) / 2;
int iy = (h - mRawIcon.getIconHeight()) / 2;
if (mType == DISABLED)
g2.setComposite(DisabledAlpha);
if (DEBUG.BOXES) {
g2.setColor(Color.red);
g2.fillRect(ix, iy, mRawIcon.getIconWidth(), mRawIcon.getIconHeight());
}
drawGraphic(c, g2, ix, iy);
}
}
// can be overriden to do anything really fancy
void drawGraphic(Component c, Graphics2D g, int x, int y)
{
mRawIcon.paintIcon(c, g, x, y);
}
public String toString()
{
return "VueButtonIcon[" + mType + " " + mRawIcon + "]";
}
}