/* * org.openmicroscopy.shoola.util.ui.ColouredButtonUI * *------------------------------------------------------------------------------ * Copyright (C) 2006 University of Dundee. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ */ package org.openmicroscopy.shoola.util.ui; //Java imports import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.Rectangle; import java.awt.Stroke; import java.awt.geom.Point2D; import javax.swing.JComponent; import javax.swing.plaf.basic.BasicButtonUI; //Third-party libraries //Application-internal dependencies import org.jdesktop.swingx.JXButton; import org.jdesktop.swingx.painter.CompoundPainter; import org.jdesktop.swingx.painter.MattePainter; import org.jdesktop.swingx.painter.Painter; import org.openmicroscopy.shoola.util.ui.colour.HSV; /** * Basic UI for coloured button. * * @author Jean-Marie Burel      * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author Donald MacDonald      * <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a> * @version 3.0 * <small> * (<b>Internal version:</b> $Revision: $ $Date: $) * </small> * @since OME2.2 */ class ColouredButtonUI extends BasicButtonUI { /** Identifies the matte painter. */ private static final int MATTE = 0; /** Identifies the specular painter. */ private static final int SPEC = 1; /** The stroke of the graphics context. */ private static final Stroke STROKE = new BasicStroke(1.0f); /** The default value for width or height. */ private static final int DEFAULT_SIZE = 32; /** The default insets for the painter. */ private static final Insets INSETS = new Insets(3, 3, 3, 3); /** Current Colour of the button. */ private Color colour; /** Reference to parent button. */ private final ColouredButton button; /** The button's size, used by paint to draw onto component. */ private Rectangle buttonRect; /** * HSV colour user to determine the start(top) of the gradient in the * button. */ private HSV gradientStartHSV; /** The Color conversion from HSV used in the paintGradient command.*/ private Color gradientStartRGB; /** * HSV colour user to determine the End(bottom) of the gradient in the * button. */ private HSV gradientEndHSV; /** The Color conversion from HSV used in the paintGradient command.*/ private Color gradientEndRGB; /** * If true the buttons will have a grey mask painted on top of their * button face. */ private boolean greyedOut; /** The index of the derived font. */ private int fontIndex; /** Show the button borders? */ private boolean paintBorderInsets; /** The button face painter. */ private Painter buttonFacePainter; /** The button face painter. */ private Painter selectedButtonFacePainter; /** The grey-mask face painter. */ private Painter greyMaskPainter; /** The grey-mask face painter. */ private Painter selectedGreyMaskPainter; /** * This method calculates the start and end colours for the gradient on * the face of the button. */ private void setGradientColours() { final HSV col = new HSV(colour); // top gradient value from HSV model. float topGradientValue = col.getValue(); // bottom gradient value from HSV model. float bottomGradientValue = col.getValue(); // top and bottom gradient saturation from HSV model. float topGradientSaturation, bottomGradientSaturation; // if colour greyscale(achromatic) don't touch saturation if (col.getSaturation() == 0) { topGradientSaturation = bottomGradientSaturation = col.getSaturation(); // A check to see what gives greatest increase, +0.3 or *1.3 // and set topGradientValue to that. topGradientValue = col.getValue()+0.3f; if (col.getValue()*1.3f > topGradientValue) topGradientValue = col.getValue()*1.3f; if (topGradientValue>1) topGradientValue = 1; // Set bottomGradientValue to 75% of colour value. bottomGradientValue = col.getValue()*0.75f; } else { // We're in a colour space. // Increase topGradientValue to 1.5 * value of colour face. topGradientValue = col.getValue()*1.5f; if (topGradientValue>1) topGradientValue = 1; topGradientSaturation = col.getSaturation()*0.6f; bottomGradientSaturation = col.getSaturation(); } gradientStartHSV = new HSV(col.getHue(), topGradientSaturation, topGradientValue, 1.0f); gradientStartRGB = gradientStartHSV.toColorA(); gradientEndHSV = new HSV(col.getHue(), bottomGradientSaturation, bottomGradientValue, 1.0f); gradientEndRGB = gradientEndHSV.toColorA(); } /** * This method calculates the size of the button's text. It then * renders the text in the centre of the button, it also changes the * colour of the text to maximize the contrast between it and the * background. * * @param g Graphics2D drawing context. */ private void drawText(final Graphics2D g) { final HSV col = new HSV(colour); //Font fnt = button.getFont(); //fnt = fnt.deriveFont(fontIndex, 10); //g.setFont(fnt); final FontMetrics fm = g.getFontMetrics(); // Using the font metrics, centre the text in the button face. final int x = (int) ((buttonRect.width/2.0f)- fm.stringWidth(button.getText())/2.0f); final int y = (int) ((buttonRect.height/2.0f) + (fm.getHeight()-fm.getDescent())/2.0f); // If the button face is dark or does not contrast well with the // black text turn the text white. if (col.getValue() < 0.6 || (col.getHue() > 0.6 && col.getSaturation() > 0.7) || greyedOut) g.setPaint(Color.white); else g.setPaint(Color.black); g.drawString(button.getText(), x, y); } /** * Draws a beveled border for an unselected Button, the top and left * bevels will be lighter than the bottom and Right. * * @param g Graphics2D render context. */ private void drawBorder(Graphics2D g) { Color borderColour; borderColour = gradientStartRGB.brighter(); // Set the colour of the top, left bevels to be a lighter colour // than the gradient at that same corner. g.setPaint(borderColour); // Draw the bevel, it is drawn as four line from: topleft to // topright, and topleft to bottom left. g.drawLine(0, 0, 0, (int) buttonRect.getHeight()); g.drawLine(0, 0, (int) buttonRect.getWidth(), 0); g.drawLine(1, 1, 1, (int) buttonRect.getHeight()-1); g.drawLine(1, 1, (int) buttonRect.getWidth()-1, 1); borderColour = gradientEndRGB.darker(); // Set the colour of the top, left bevels to be a lighter colour // than the gradient at that same corner. g.setPaint(borderColour); // Draw the bevel, it is drawn as four line from: bottomleft to // bottom right, and bottomright to top left. g.drawLine((int) buttonRect.getWidth()-1, 0, (int) buttonRect.getWidth()-1, (int) buttonRect.getHeight()-1); g.drawLine(0, (int) buttonRect.getHeight()-1, (int) buttonRect.getWidth()-1, (int) buttonRect.getHeight()-1); g.drawLine((int) buttonRect.getWidth()-2, 2, (int) buttonRect.getWidth()-2, (int) buttonRect.getHeight()-2); g.drawLine(1, (int) buttonRect.getHeight()-2, (int) buttonRect.getWidth()-1, (int) buttonRect.getHeight()-2); } /** * Draws a bevelled border for a Grey unselected Button, the top and left * bevels will be lighter than the bottom and Right. The Bevels will * be the greyscale equivalent of the face colour. * * @param g Graphics2D render context. */ private void drawGreyBorder(Graphics2D g) { Color borderColour; HSV borderColourHSV = new HSV(0, 0, gradientStartHSV.getValue(), 0.8f); borderColour = borderColourHSV.toColorA(); // Set the colour of the top, left bevels to be a lighter colour // than the grey mask of the gradient at that same corner. g.setPaint(borderColour); g.setStroke(STROKE); // Draw the bevel, it is drawn as four line from: topleft to // topright, and topleft to bottom left. final int height = (int) buttonRect.getHeight(); final int width = (int) buttonRect.getWidth(); g.drawLine(0, 0, 0, height); g.drawLine(0, 0, width, 0); g.drawLine(1, 1, 1, height-1); g.drawLine(1, 1, width-1, 1); borderColourHSV = new HSV(0, 0, gradientEndHSV.getValue(), 0.8f); borderColour = borderColourHSV.toColorA(); // Set the colour of the bottom, right bevels to be a darker colour // than the grey mask of the gradient at that same corner. g.setPaint(borderColour.darker().darker().darker()); // Draw the bevel, it is drawn as four line from: bottomleft to // bottom right, and bottomright to top left. g.drawLine(width-1, 0, width-1, height-1); g.drawLine(0, height-1, width-1, height-1); g.drawLine(width-2, 2, width-2, height-2); g.drawLine(1, height-2, width-1, height-2); } /** * Draws a bevelled border for a Grey selected Button, the top and left * bevels will be darker than the bottom and Right. The border will be * drawn using greyscale equivalent of the face colour. * * @param g Graphics2D render context. */ private void drawGreySelectedBorder(Graphics2D g) { Color borderColour; HSV borderColourHSV = new HSV(0, 0, gradientEndHSV.getValue(), 0.8f); borderColour = borderColourHSV.toColorA(); // Set the colour of the top, left bevels to be a lighter colour // than the grey mask of the gradient at that same corner. g.setPaint(borderColour.darker().darker().darker()); g.setStroke(STROKE); // Draw the bevel, it is drawn as four line from: topleft to // topright, and topleft to bottom left. int height = (int) buttonRect.getHeight(); int width = (int) buttonRect.getWidth(); g.drawLine(0, 0, 0, height); g.drawLine(0, 0, width, 0); g.drawLine(1, 1, 1, height-1); g.drawLine(1, 1, width-1, 1); borderColourHSV = new HSV(0, 0, gradientStartHSV.getValue(), 0.8f); borderColour = borderColourHSV.toColorA(); // Set the colour of the bottom, right bevels to be a darker colour // than the grey mask of the gradient at that same corner. g.setPaint(borderColour.darker()); // Draw the bevel, it is drawn as four line from: bottomleft to // bottom right, and bottomright to top left. g.drawLine(width-1, 0, width-1, height-1); g.drawLine(0, height-1, width-1, height-1); g.drawLine(width-2, 2, width-2, height-2); g.drawLine(1, height-2, width-1, height-2); } /** * Draws a bevelled border for a selected Button, the top and left * bevels will be darker than the bottom and Right. * * @param g Graphics2D render context. */ private void drawSelectedBorder(Graphics2D g) { Color borderColour; borderColour = gradientEndRGB.darker(); HSV col = new HSV(borderColour); g.setPaint(col.toColorA()); g.setStroke(STROKE); // Draw the bevel, it is drawn as four line from: topleft to // topright, and topleft to bottom left. int height = (int) buttonRect.getHeight(); int width = (int) buttonRect.getWidth(); g.drawLine(0, 0, 0, height); g.drawLine(0, 0, width, 0); g.drawLine(1, 1, 1, height-1); g.drawLine(1, 1, width-1, 1); borderColour = gradientStartRGB; col = new HSV(borderColour); col.setSaturation(col.getSaturation()*0.8f); borderColour = col.toColorA(); // Set the colour of the bottom, right bevels to be a darker colour // than the grey mask of the gradient at that same corner. g.setPaint(borderColour); // Draw the bevel, it is drawn as four line from: bottomleft to // bottom right, and bottomright to top left. g.drawLine(width-1, 0, width-1, (int) buttonRect.getHeight()-1); g.drawLine(0, height-1, width-1, height-1); g.drawLine(width-2, 2, width-2, height-2); g.drawLine(1, width-2, width-1, height-2); } /** * Paints the square, beveled button with gradient fill on to the * Graphics context. * * @param g Graphics context. */ private void paintSquareButton(Graphics2D g) { // If the button is selected draw selected button face. // Check to see if it's greyed out, if not draw border else // draw mask and draw the grey mask selected border. if (button.isSelected()) { if (button.isEnabled()) { if (!greyedOut) { invokePainter(g, selectedButtonFacePainter); drawSelectedBorder(g); } else { invokePainter(g, selectedGreyMaskPainter); drawGreySelectedBorder(g); } } else { invokePainter(g, selectedGreyMaskPainter); drawGreySelectedBorder(g); } } else { // If the button is not selected draw unselected button face. // Check to see if it's greyed out, if not draw border else // draw mask and draw the grey mask unselected border. if (button.isEnabled()) { if (greyedOut) { invokePainter(g, greyMaskPainter); drawGreyBorder(g); } else { invokePainter(g, buttonFacePainter); drawBorder(g); } } else { invokePainter(g, greyMaskPainter); drawGreyBorder(g); } } // Draw text in centre of button. drawText(g); } /** Creates painters for the different button options. */ private void createPainters() { buttonFacePainter = getPainter(colour, SPEC); selectedButtonFacePainter = getPainter(colour, MATTE); greyMaskPainter = getPainter(Color.gray, SPEC); selectedGreyMaskPainter = getPainter(Color.gray, MATTE); } /** * Creates the painters for the colour, and adds specular highlight if the * passed parameter is <code>true</code>. * * @param colour see above. * @param spec see above. * @return see above. */ private Painter<JXButton> getPainter(Color colour, int spec) { int startX = (int) (getWidth()*0.2); int startY = 6; int colourStartX = (int) (getWidth()*0.3); int colourStartY = 18; int matteEndX = 10; int matteEndY = 18; Color c = colour.brighter(); MattePainter gradientWhite = new MattePainter( new GradientPaint(new Point2D.Double(0.0, 0.0), c, new Point2D.Double(matteEndX, matteEndY), Color.white)); /* MattePainter gradientBrighterColour = new MattePainter( new GradientPaint(new Point2D.Double(0.0, 0.0), c, new Point2D.Double(matteEndX, matteEndY), colour)); */ MattePainter gradientBrighterDarker = new MattePainter( new GradientPaint(new Point2D.Double(0.0, 0.0), c, new Point2D.Double(matteEndX, matteEndY), colour.darker())); //We cannot use this /*org.apache.batik.ext.awt.RadialGradientPaint rp = new org.apache.batik.ext.awt.RadialGradientPaint(new Point2D.Double(startX,startY), radius, new Point2D.Double(colourStartX, colourStartY), new float[] { 0.0f, 0.5f }, new Color[] { new Color(1.0f, 1.0f, 1.0f, 0.4f), new Color(1.0f, 1.0f, 1.0f, 0.0f) } );*/ HSV newHSV = new HSV(colour); float colourAlpha = 0.5f; if (newHSV.getHue()>(100.0/360.0) && newHSV.getHue()<(150.0/360.0)) colourAlpha = 0.7f; MattePainter gradientLight = new MattePainter( new GradientPaint(new Point2D.Double(startX, startY), new Color(1.0f, 1.0f, 1.0f, colourAlpha), new Point2D.Double(colourStartX, colourStartY), new Color(1.0f, 1.0f, 1.0f, 0.0f) )); switch (spec) { case SPEC: return new CompoundPainter<JXButton>(gradientWhite, gradientBrighterDarker, gradientLight); case MATTE: return new CompoundPainter<JXButton>( gradientWhite, gradientBrighterDarker); } return null; } /** * Draws the painter on the graphics context. * * @param g The graphics context. * @param painter The painter to draw. */ private void invokePainter(Graphics g, Painter painter) { if (painter == null) return; Graphics2D g2d = (Graphics2D) g.create(); if (!isPaintBorderInsets()) painter.paint(g2d, this, getWidth(), getHeight()); else { g2d.translate(INSETS.left, INSETS.top); painter.paint(g2d, this, getWidth()-INSETS.left-INSETS.right, getHeight()-INSETS.top-INSETS.bottom); } } /** * Creates a new instance. * * @param b Reference to parent Button. Mustn't be <code>null</code>. * @param c Colour of the button. Mustn't be <code>null</code>. */ ColouredButtonUI(ColouredButton b, Color c) { if (b == null) throw new IllegalArgumentException("No button."); if (c == null) throw new IllegalArgumentException("No color."); button = b; greyedOut = false; fontIndex = Font.PLAIN; setColor(c); uninstallListeners(b); } /** * Buttons can be greyed out, representing that the current model is * in greyscale, not RGB, HSV. This is done by adding an alpha blended * grey mask over the button. This method sets, unsets buttons to be * greyed out. * * @param greyedOut Pass <code>true</code> to gray out the button, * <code>false</code> otherwise. */ void setGrayedOut(boolean greyedOut) { this.greyedOut = greyedOut; } /** * Sets the colour of the button. * * @param c Color to set. Mustn't be <code>null</code>. */ void setColor(Color c) { if (c == null) throw new IllegalArgumentException("No color."); this.colour = c; setGradientColours(); createPainters(); } /** * Sets the index of the derived font used to paint the text. * * @param fontIndex The font index. */ void setDeriveFont(int fontIndex) { this.fontIndex = fontIndex; } /** * Overridden to paint the button and to renders the text in the centre of * the button. * @see BasicButtonUI#paint(Graphics, JComponent) */ public void paint(Graphics og, JComponent comp) { Graphics2D g = (Graphics2D) og; buttonRect = new Rectangle(comp.getWidth(), comp.getHeight()); paintSquareButton(g); } /** * Returns <code>true</code> if the button paints borders, * <code>false</code> otherwise. * * @return see above. */ boolean isPaintBorderInsets() { return paintBorderInsets; } /** * Sets to <code>true</code> if the button paints borders, to * <code>false</code> otherwise. * * @param pb The value to set. */ void setPaintBorderInsets(boolean pb) { paintBorderInsets = pb; } /** * Returns the width of the components. * * @return See above. */ protected int getWidth() { return Math.max(button.getWidth(), DEFAULT_SIZE); } /** * Returns the height of the components. * * @return See above. */ protected int getHeight() { return Math.max(button.getHeight(), DEFAULT_SIZE); } }