001/*
002 * $Id: JXColorSelectionButton.java 4082 2011-11-15 18:39:43Z kschaefe $
003 *
004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 * 
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015 * Lesser General Public License for more details.
016 * 
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020 */
021
022package org.jdesktop.swingx;
023
024import static java.awt.RenderingHints.KEY_ANTIALIASING;
025import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON;
026
027import java.awt.Color;
028import java.awt.Dimension;
029import java.awt.Graphics;
030import java.awt.Graphics2D;
031import java.awt.Insets;
032import java.awt.event.ActionEvent;
033import java.awt.event.ActionListener;
034import java.awt.geom.Ellipse2D;
035import java.awt.image.BufferedImage;
036import java.beans.PropertyChangeEvent;
037import java.beans.PropertyChangeListener;
038
039import javax.imageio.ImageIO;
040import javax.swing.JButton;
041import javax.swing.JColorChooser;
042import javax.swing.JDialog;
043import javax.swing.event.ChangeEvent;
044import javax.swing.event.ChangeListener;
045
046import org.jdesktop.swingx.color.EyeDropperColorChooserPanel;
047import org.jdesktop.swingx.plaf.UIManagerExt;
048import org.jdesktop.swingx.util.GraphicsUtilities;
049import org.jdesktop.swingx.util.OS;
050import org.jdesktop.swingx.util.PaintUtils;
051
052/**
053 * A button which allows the user to select a single color. The button has a platform
054 * specific look. Ex: on Mac OS X it will mimic an NSColorWell. When the user
055 * clicks the button it will open a color chooser set to the current background
056 * color of the button. The new selected color will be stored in the background
057 * property and can be retrieved using the getBackground() method. As the user is
058 * choosing colors within the color chooser the background property will be updated.
059 * By listening to this property developers can make other parts of their programs
060 * update.
061 *
062 * @author joshua@marinacci.org
063 */
064public class JXColorSelectionButton extends JButton {
065    private BufferedImage colorwell;
066    private JDialog dialog = null;
067    private JColorChooser chooser = null;
068    private Color initialColor = null;
069    
070    /**
071     * Creates a new instance of JXColorSelectionButton
072     */
073    public JXColorSelectionButton() {
074        this(Color.red);
075    }
076    
077    /**
078     * Creates a new instance of JXColorSelectionButton set to the specified color.
079     * @param col The default color
080     */
081    public JXColorSelectionButton(Color col) {
082        setBackground(col);
083        this.addActionListener(new ActionHandler());
084        this.setContentAreaFilled(false);
085        this.setOpaque(false);
086        
087        try {
088            colorwell = ImageIO.read(JXColorSelectionButton.class.getResourceAsStream("color/colorwell.png"));
089        } catch (Exception ex) {
090            ex.printStackTrace();
091        }
092        
093        this.addPropertyChangeListener("background",new PropertyChangeListener() {
094            public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
095                getChooser().setColor(getBackground());
096            }
097        });
098    }
099    
100    
101    /**
102     * A listener class to update the button's background when the selected
103     * color changes.
104     */
105    private class ColorChangeListener implements ChangeListener {
106        public JXColorSelectionButton button;
107        public ColorChangeListener(JXColorSelectionButton button) {
108            this.button = button;
109        }
110        public void stateChanged(ChangeEvent changeEvent) {
111            button.setBackground(button.getChooser().getColor());
112        }
113    }
114
115    /**
116     * {@inheritDoc}
117     */
118    @Override
119    protected void paintComponent(Graphics g) {
120        // want disabledForeground when disabled, current colour otherwise
121        final Color FILL_COLOR = isEnabled() ? PaintUtils.removeAlpha(getBackground())
122                : UIManagerExt.getSafeColor("Button.disabledForeground", Color.LIGHT_GRAY);
123        
124        // draw the colorwell image (should only be on OSX)
125        if(OS.isMacOSX() && colorwell != null) {
126            Insets ins = new Insets(5,5,5,5);
127            GraphicsUtilities.tileStretchPaint(g, this, colorwell, ins);
128            
129            // fill in the color area
130            g.setColor(FILL_COLOR);
131            g.fillRect(ins.left, ins.top,
132                    getWidth()  - ins.left - ins.right,
133                    getHeight() - ins.top - ins.bottom);
134            // draw the borders
135            g.setColor(PaintUtils.setBrightness(FILL_COLOR,0.85f));
136            g.drawRect(ins.left, ins.top,
137                    getWidth() - ins.left - ins.right - 1,
138                    getHeight() - ins.top - ins.bottom - 1);
139            g.drawRect(ins.left + 1, ins.top + 1,
140                    getWidth() - ins.left - ins.right - 3,
141                    getHeight() - ins.top - ins.bottom - 3);
142        }else{
143            Graphics2D g2 = (Graphics2D) g.create();
144            
145            try {
146                g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
147                g2.setColor(Color.LIGHT_GRAY);
148                final int DIAM = Math.min(getWidth(), getHeight());
149                final int inset = 3;
150                g2.fill(new Ellipse2D.Float(inset, inset, DIAM-2*inset, DIAM-2*inset));
151                g2.setColor(FILL_COLOR);
152                final int border = 1;
153                g2.fill(new Ellipse2D.Float(inset+border, inset+border, DIAM-2*inset-2*border, DIAM-2*inset-2*border));
154            } finally {
155                g2.dispose();
156            }
157            
158        }
159    }
160
161//    /**
162//     * Sample usage of JXColorSelectionButton
163//     * @param args not used
164//     */
165//    public static void main(String[] args) {
166//        javax.swing.JFrame frame = new javax.swing.JFrame("Color Button Test");
167//        frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
168//        javax.swing.JPanel panel = new javax.swing.JPanel();
169//        javax.swing.JComponent btn = new JXColorSelectionButton();
170//        btn.setEnabled(true);
171//        panel.add(btn);
172//        panel.add(new javax.swing.JLabel("ColorSelectionButton test"));
173//        
174//        frame.add(panel);
175//        frame.pack();
176//        frame.setVisible(true);
177//    }
178
179    /**
180     * Conditionally create and show the color chooser dialog.
181     */
182    private void showDialog() {
183        if (dialog == null) {
184            dialog = JColorChooser.createDialog(JXColorSelectionButton.this,
185                    "Choose a color", true, getChooser(),
186                    new ActionListener() {
187                public void actionPerformed(ActionEvent actionEvent) {
188                    Color color = getChooser().getColor();
189                    if (color != null) {
190                        setBackground(color);
191                    }
192                }
193            },
194            new ActionListener() {
195                public void actionPerformed(ActionEvent actionEvent) {
196                    setBackground(initialColor);
197                }
198            });
199            dialog.getContentPane().add(getChooser());
200            getChooser().getSelectionModel().addChangeListener(
201                    new ColorChangeListener(JXColorSelectionButton.this));
202        }
203        
204        initialColor = getBackground();
205        dialog.setVisible(true);
206
207    }
208    
209    /**
210     * Get the JColorChooser that is used by this JXColorSelectionButton. This
211     * chooser instance is shared between all invocations of the chooser, but is unique to
212     * this instance of JXColorSelectionButton.
213     * @return the JColorChooser used by this JXColorSelectionButton
214     */
215    public JColorChooser getChooser() {
216        if(chooser == null) {
217            chooser = new JColorChooser();
218            // add the eyedropper color chooser panel
219            chooser.addChooserPanel(new EyeDropperColorChooserPanel());
220        }
221        return chooser;
222    }
223    
224    /**
225     * Set the JColorChooser that is used by this JXColorSelectionButton.
226     * chooser instance is shared between all invocations of the chooser,
227     * but is unique to
228     * this instance of JXColorSelectionButton.
229     * @param chooser The new JColorChooser to use.
230     */
231    public void setChooser(JColorChooser chooser) {
232        JColorChooser oldChooser = getChooser();
233        this.chooser = chooser;
234        firePropertyChange("chooser",oldChooser,chooser);
235    }
236    
237    /**
238     * {@inheritDoc}
239     */
240    @Override
241    public Dimension getPreferredSize() {
242        if (isPreferredSizeSet() || colorwell == null) {
243            return super.getPreferredSize();
244        }
245        
246        return new Dimension(colorwell.getWidth(), colorwell.getHeight());
247    }
248
249    /**
250     * A private class to conditionally create and show the color chooser
251     * dialog.
252     */
253    private class ActionHandler implements ActionListener {
254        
255        public void actionPerformed(ActionEvent actionEvent) {
256            showDialog();
257        }
258    }
259}