swingx-master/swingx-core/src/main/java/org/jdesktop/swingx/JXColorSelectionButton.java
/*
 * $Id$
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package org.jdesktop.swingx;

import static java.awt.RenderingHints.KEY_ANTIALIASING;
import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JDialog;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.jdesktop.swingx.color.EyeDropperColorChooserPanel;
import org.jdesktop.swingx.plaf.UIManagerExt;
import org.jdesktop.swingx.util.GraphicsUtilities;
import org.jdesktop.swingx.util.OS;
import org.jdesktop.swingx.util.PaintUtils;

/**
 * A button which allows the user to select a single color. The button has a platform
 * specific look. Ex: on Mac OS X it will mimic an NSColorWell. When the user
 * clicks the button it will open a color chooser set to the current background
 * color of the button. The new selected color will be stored in the background
 * property and can be retrieved using the getBackground() method. As the user is
 * choosing colors within the color chooser the background property will be updated.
 * By listening to this property developers can make other parts of their programs
 * update.
 *
 * @author joshua@marinacci.org
 */
public class JXColorSelectionButton extends JButton {
    private BufferedImage colorwell;
    private JDialog dialog = null;
    private JColorChooser chooser = null;
    private Color initialColor = null;
    
    /**
     * Creates a new instance of JXColorSelectionButton
     */
    public JXColorSelectionButton() {
        this(Color.red);
    }
    
    /**
     * Creates a new instance of JXColorSelectionButton set to the specified color.
     * @param col The default color
     */
    public JXColorSelectionButton(Color col) {
        setBackground(col);
        this.addActionListener(new ActionHandler());
        this.setContentAreaFilled(false);
        this.setOpaque(false);
        
        try {
            colorwell = ImageIO.read(JXColorSelectionButton.class.getResourceAsStream("color/colorwell.png"));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        
        this.addPropertyChangeListener("background",new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
                getChooser().setColor(getBackground());
            }
        });
    }
    
    
    /**
     * A listener class to update the button's background when the selected
     * color changes.
     */
    private class ColorChangeListener implements ChangeListener {
        public JXColorSelectionButton button;
        public ColorChangeListener(JXColorSelectionButton button) {
            this.button = button;
        }
        public void stateChanged(ChangeEvent changeEvent) {
            button.setBackground(button.getChooser().getColor());
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void paintComponent(Graphics g) {
        // want disabledForeground when disabled, current colour otherwise
        final Color FILL_COLOR = isEnabled() ? PaintUtils.removeAlpha(getBackground())
                : UIManagerExt.getSafeColor("Button.disabledForeground", Color.LIGHT_GRAY);
        
        // draw the colorwell image (should only be on OSX)
        if(OS.isMacOSX() && colorwell != null) {
            Insets ins = new Insets(5,5,5,5);
            GraphicsUtilities.tileStretchPaint(g, this, colorwell, ins);
            
            // fill in the color area
            g.setColor(FILL_COLOR);
            g.fillRect(ins.left, ins.top,
                    getWidth()  - ins.left - ins.right,
                    getHeight() - ins.top - ins.bottom);
            // draw the borders
            g.setColor(PaintUtils.setBrightness(FILL_COLOR,0.85f));
            g.drawRect(ins.left, ins.top,
                    getWidth() - ins.left - ins.right - 1,
                    getHeight() - ins.top - ins.bottom - 1);
            g.drawRect(ins.left + 1, ins.top + 1,
                    getWidth() - ins.left - ins.right - 3,
                    getHeight() - ins.top - ins.bottom - 3);
        }else{
            Graphics2D g2 = (Graphics2D) g.create();
            
            try {
                g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
                g2.setColor(Color.LIGHT_GRAY);
                final int DIAM = Math.min(getWidth(), getHeight());
                final int inset = 3;
                g2.fill(new Ellipse2D.Float(inset, inset, DIAM-2*inset, DIAM-2*inset));
                g2.setColor(FILL_COLOR);
                final int border = 1;
                g2.fill(new Ellipse2D.Float(inset+border, inset+border, DIAM-2*inset-2*border, DIAM-2*inset-2*border));
            } finally {
                g2.dispose();
            }
            
        }
    }

//    /**
//     * Sample usage of JXColorSelectionButton
//     * @param args not used
//     */
//    public static void main(String[] args) {
//        javax.swing.JFrame frame = new javax.swing.JFrame("Color Button Test");
//        frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
//        javax.swing.JPanel panel = new javax.swing.JPanel();
//        javax.swing.JComponent btn = new JXColorSelectionButton();
//        btn.setEnabled(true);
//        panel.add(btn);
//        panel.add(new javax.swing.JLabel("ColorSelectionButton test"));
//        
//        frame.add(panel);
//        frame.pack();
//        frame.setVisible(true);
//    }

    /**
     * Conditionally create and show the color chooser dialog.
     */
    private void showDialog() {
        if (dialog == null) {
            dialog = JColorChooser.createDialog(JXColorSelectionButton.this,
                    "Choose a color", true, getChooser(),
                    new ActionListener() {
                public void actionPerformed(ActionEvent actionEvent) {
                    Color color = getChooser().getColor();
                    if (color != null) {
                        setBackground(color);
                    }
                }
            },
            new ActionListener() {
                public void actionPerformed(ActionEvent actionEvent) {
                    setBackground(initialColor);
                }
            });
            dialog.getContentPane().add(getChooser());
            getChooser().getSelectionModel().addChangeListener(
                    new ColorChangeListener(JXColorSelectionButton.this));
        }
        
        initialColor = getBackground();
        dialog.setVisible(true);

    }
    
    /**
     * Get the JColorChooser that is used by this JXColorSelectionButton. This
     * chooser instance is shared between all invocations of the chooser, but is unique to
     * this instance of JXColorSelectionButton.
     * @return the JColorChooser used by this JXColorSelectionButton
     */
    public JColorChooser getChooser() {
        if(chooser == null) {
            chooser = new JColorChooser();
            // add the eyedropper color chooser panel
            chooser.addChooserPanel(new EyeDropperColorChooserPanel());
        }
        return chooser;
    }
    
    /**
     * Set the JColorChooser that is used by this JXColorSelectionButton.
     * chooser instance is shared between all invocations of the chooser,
     * but is unique to
     * this instance of JXColorSelectionButton.
     * @param chooser The new JColorChooser to use.
     */
    public void setChooser(JColorChooser chooser) {
        JColorChooser oldChooser = getChooser();
        this.chooser = chooser;
        firePropertyChange("chooser",oldChooser,chooser);
    }
    
    /**
     * {@inheritDoc}
     */
    @Override
    public Dimension getPreferredSize() {
        if (isPreferredSizeSet() || colorwell == null) {
            return super.getPreferredSize();
        }
        
        return new Dimension(colorwell.getWidth(), colorwell.getHeight());
    }

    /**
     * A private class to conditionally create and show the color chooser
     * dialog.
     */
    private class ActionHandler implements ActionListener {
        
        public void actionPerformed(ActionEvent actionEvent) {
            showDialog();
        }
    }
}
