/*
* Copyright (c) 2009 Kathryn Huxtable and Kenneth Orr.
*
* This file is part of the SeaGlass Pluggable Look and Feel.
*
* Licensed under the Apache 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.apache.org/licenses/LICENSE-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.
*
* $Id$
*/
package com.seaglasslookandfeel.painter;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.Path2D;
import javax.swing.JComponent;
import com.seaglasslookandfeel.painter.AbstractRegionPainter.PaintContext.CacheMode;
/**
* Title pane close button implementation.
*
* @author Kathryn Huxtable
*/
public final class TitlePaneCloseButtonPainter extends TitlePaneButtonPainter {
/**
* Control state.
*/
public static enum Which {
BACKGROUND_DISABLED, BACKGROUND_ENABLED, BACKGROUND_MOUSEOVER, BACKGROUND_PRESSED, BACKGROUND_ENABLED_WINDOWNOTFOCUSED,
BACKGROUND_PRESSED_WINDOWNOTFOCUSED,
}
private ButtonColors enabled = new ButtonColors(white16, white4c, black66, white33, transparentColor, transparentColor, black99,
white99);
private ButtonColors hover = new ButtonColors(closeButtonTopHover, closeButtonLeftHover, closeButtonEdgeHover, white33,
closeButtonInteriorTopHover, closeButtonInteriorBottomHover, closeButtonMarkBorderHover,
white);
private ButtonColors pressed = new ButtonColors(closeButtonTopPressed, closeButtonLeftPressed, black66, white33,
closeButtonInteriorTopPressed, closeButtonInteriorTopPressed,
closeButtonMarkBorderPressed,
closeButtonMarkInteriorPressed);
private MyPath2D path = new MyPath2D();
private Which state;
private PaintContext ctx;
/**
* Creates a new TitlePaneCloseButtonPainter object.
*
* @param state the button state.
*/
public TitlePaneCloseButtonPainter(Which state) {
super();
this.state = state;
this.ctx = new PaintContext(CacheMode.FIXED_SIZES);
}
/**
* {@inheritDoc}
*/
@Override
protected void doPaint(Graphics2D g, JComponent c, int width, int height, Object[] extendedCacheKeys) {
switch (state) {
case BACKGROUND_DISABLED:
case BACKGROUND_ENABLED:
case BACKGROUND_ENABLED_WINDOWNOTFOCUSED:
paintCloseEnabled(g, c, width, height);
break;
case BACKGROUND_MOUSEOVER:
paintCloseHover(g, c, width, height);
break;
case BACKGROUND_PRESSED:
case BACKGROUND_PRESSED_WINDOWNOTFOCUSED:
paintClosePressed(g, c, width, height);
break;
}
}
/**
* {@inheritDoc}
*/
@Override
protected PaintContext getPaintContext() {
return ctx;
}
/**
* Paint the background enabled state.
*
* @param g the Graphics2D context to paint with.
* @param c the component.
* @param width the width of the component.
* @param height the height of the component.
*/
private void paintCloseEnabled(Graphics2D g, JComponent c, int width, int height) {
paintClose(g, c, width, height, enabled);
}
/**
* Paint the background mouse-over state.
*
* @param g the Graphics2D context to paint with.
* @param c the component.
* @param width the width of the component.
* @param height the height of the component.
*/
private void paintCloseHover(Graphics2D g, JComponent c, int width, int height) {
paintClose(g, c, width, height, hover);
}
/**
* Paint the background pressed state.
*
* @param g the Graphics2D context to paint with.
* @param c the component.
* @param width the width of the component.
* @param height the height of the component.
*/
private void paintClosePressed(Graphics2D g, JComponent c, int width, int height) {
paintClose(g, c, width, height, pressed);
}
/**
* Paint the background of the button using the specified colors.
*
* @param g the Graphics2D context to paint with.
* @param c the component.
* @param width the width of the component.
* @param height the height of the component.
* @param colors the color set to use to paint the button.
*/
private void paintClose(Graphics2D g, JComponent c, int width, int height, ButtonColors colors) {
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Shape s = decodeInterior(width, height);
g.setPaint(decodeCloseGradient(s, colors.interiorTop, colors.interiorBottom));
g.fill(s);
s = decodeEdge(width, height);
g.setColor(colors.edge);
g.draw(s);
s = decodeShadow(width, height);
g.setColor(colors.shadow);
g.draw(s);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
g.setColor(colors.top);
g.drawLine(0, 0, width - 2, 0);
g.setColor(colors.left);
g.drawLine(0, 1, 0, height - 3);
s = decodeMarkInterior(width, height);
g.setColor(colors.markInterior);
g.fill(s);
s = decodeMarkBorder(width, height);
g.setColor(colors.markBorder);
g.draw(s);
}
/**
* Create the gradient for the close button.
*
* @param s the shape to fill.
* @param top the top color.
* @param bottom the bottom color.
*
* @return the gradient.
*/
private Paint decodeCloseGradient(Shape s, Color top, Color bottom) {
Rectangle r = s.getBounds();
int width = r.width;
int height = r.height;
return createGradient(r.x + width / 2, r.y, r.x + width / 2, r.y + height - 1, new float[] { 0f, 1f }, new Color[] { top, bottom });
}
/**
* Create the interior of the button.
*
* @param width the width.
* @param height the height.
*
* @return the shape.
*/
private Shape decodeInterior(int width, int height) {
path.reset();
path.moveTo(1, 1);
path.lineTo(width - 2, 1);
path.lineTo(width - 2, height - 3);
path.lineTo(width - 3, height - 2);
path.lineTo(1, height - 2);
path.closePath();
return path;
}
/**
* Create the edge of the button.
*
* @param width the width.
* @param height the height.
*
* @return the shape of the edge.
*/
private Shape decodeEdge(int width, int height) {
path.reset();
path.moveTo(width - 2, 0);
path.lineTo(width - 2, height - 4);
path.lineTo(width - 4, height - 2);
path.lineTo(0, height - 2);
return path;
}
/**
* Create the shadow for the button.
*
* @param width the width.
* @param height the height.
*
* @return the shape of the shadow.
*/
private Shape decodeShadow(int width, int height) {
path.reset();
path.moveTo(width - 1, 0);
path.lineTo(width - 1, height - 4);
path.lineTo(width - 4, height - 1);
path.lineTo(0, height - 1);
return path;
}
/**
* Create the shape for the mark border.
*
* @param width the width.
* @param height the height.
*
* @return the shape of the mark border.
*/
private Shape decodeMarkBorder(int width, int height) {
int left = (width - 3) / 2 - 5;
int top = (height - 2) / 2 - 5;
path.reset();
path.moveTo(left + 1, top + 0);
path.lineTo(left + 3, top + 0);
path.pointAt(left + 4, top + 1);
path.pointAt(left + 5, top + 2);
path.pointAt(left + 6, top + 1);
path.moveTo(left + 7, top + 0);
path.lineTo(left + 9, top + 0);
path.pointAt(left + 10, top + 1);
path.pointAt(left + 9, top + 2);
path.pointAt(left + 8, top + 3);
path.moveTo(left + 7, top + 4);
path.lineTo(left + 7, top + 5);
path.pointAt(left + 8, top + 6);
path.pointAt(left + 9, top + 7);
path.pointAt(left + 10, top + 8);
path.moveTo(left + 9, top + 9);
path.lineTo(left + 7, top + 9);
path.pointAt(left + 6, top + 8);
path.pointAt(left + 5, top + 7);
path.pointAt(left + 4, top + 8);
path.moveTo(left + 3, top + 9);
path.lineTo(left + 1, top + 9);
path.pointAt(left + 0, top + 8);
path.pointAt(left + 1, top + 7);
path.pointAt(left + 2, top + 6);
path.moveTo(left + 3, top + 5);
path.lineTo(left + 3, top + 4);
path.pointAt(left + 2, top + 3);
path.pointAt(left + 1, top + 2);
path.pointAt(left + 0, top + 1);
return path;
}
/**
* Create the shape for the mark interior.
*
* @param width the width.
* @param height the height.
*
* @return the shape of the mark interior.
*/
private Shape decodeMarkInterior(int width, int height) {
int left = (width - 3) / 2 - 5;
int top = (height - 2) / 2 - 5;
path.reset();
path.moveTo(left + 1, top + 1);
path.lineTo(left + 4, top + 1);
path.lineTo(left + 5, top + 3);
path.lineTo(left + 7, top + 1);
path.lineTo(left + 10, top + 1);
path.lineTo(left + 7, top + 4);
path.lineTo(left + 7, top + 5);
path.lineTo(left + 10, top + 9);
path.lineTo(left + 6, top + 8);
path.lineTo(left + 5, top + 6);
path.lineTo(left + 4, top + 9);
path.lineTo(left + 0, top + 9);
path.lineTo(left + 4, top + 5);
path.lineTo(left + 4, top + 4);
path.closePath();
return path;
}
/**
* Extend Path2D to have a pointAt method to draw a single point.
*/
private static class MyPath2D extends Path2D.Double {
private static final long serialVersionUID = -3577694636085901945L;
/**
* Draw a single point at the (x,y) coordinate specified.
*
* @param x the x coordinate to draw the point at.
* @param y the y coordinate to draw the point at.
*/
public void pointAt(int x, int y) {
moveTo(x, y);
lineTo(x, y);
}
}
/**
* A class encapsulating the colors used for a button.
*/
private static class ButtonColors {
/** Top line color. */
public Color top;
/** Left edge color. */
public Color left;
/** Main edge color. */
public Color edge;
/** Main shadow color. */
public Color shadow;
/** Interior top gradient color. */
public Color interiorTop;
/** Interior bottom gradient color. */
public Color interiorBottom;
/** Mark border color. */
public Color markBorder;
/** Mark interior color. */
public Color markInterior;
/**
* Creates a new ButtonColors object.
*
* @param top the top line color.
* @param left the left edge color.
* @param edge the main edge color.
* @param shadow the Main shadow color.
* @param interiorTop the interior top gradient color.
* @param interiorBottom the interior bottom gradient color.
* @param markBorder the mark border color.
* @param markInterior the mark interior color.
*/
public ButtonColors(Color top, Color left, Color edge, Color shadow, Color interiorTop, Color interiorBottom, Color markBorder,
Color markInterior) {
this.top = top;
this.left = left;
this.edge = edge;
this.shadow = shadow;
this.interiorTop = interiorTop;
this.interiorBottom = interiorBottom;
this.markBorder = markBorder;
this.markInterior = markInterior;
}
}
}