001    /*
002     * $Id: JXGradientChooser.java 3360 2009-06-14 20:29:45Z 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    
022    package org.jdesktop.swingx;
023    
024    import java.awt.BorderLayout;
025    import java.awt.Color;
026    import java.awt.Component;
027    import java.awt.Dimension;
028    import java.awt.GridBagConstraints;
029    import java.awt.GridBagLayout;
030    import java.awt.GridLayout;
031    import java.awt.MultipleGradientPaint;
032    import java.awt.RadialGradientPaint;
033    import java.awt.event.ActionEvent;
034    import java.awt.event.ActionListener;
035    import java.awt.event.ItemEvent;
036    import java.awt.event.ItemListener;
037    import java.awt.event.MouseEvent;
038    import java.beans.PropertyChangeEvent;
039    import java.beans.PropertyChangeListener;
040    import java.util.logging.Logger;
041    
042    import javax.swing.ActionMap;
043    import javax.swing.ButtonGroup;
044    import javax.swing.DefaultComboBoxModel;
045    import javax.swing.JButton;
046    import javax.swing.JCheckBox;
047    import javax.swing.JComboBox;
048    import javax.swing.JDialog;
049    import javax.swing.JFrame;
050    import javax.swing.JLabel;
051    import javax.swing.JPanel;
052    import javax.swing.JRadioButton;
053    import javax.swing.JSlider;
054    import javax.swing.JSpinner;
055    import javax.swing.JTextField;
056    import javax.swing.SpinnerNumberModel;
057    import javax.swing.SwingUtilities;
058    import javax.swing.event.ChangeEvent;
059    import javax.swing.event.ChangeListener;
060    
061    import org.jdesktop.swingx.action.AbstractActionExt;
062    import org.jdesktop.swingx.color.ColorUtil;
063    import org.jdesktop.swingx.color.GradientPreviewPanel;
064    import org.jdesktop.swingx.color.GradientThumbRenderer;
065    import org.jdesktop.swingx.color.GradientTrackRenderer;
066    import org.jdesktop.swingx.multislider.Thumb;
067    import org.jdesktop.swingx.multislider.ThumbListener;
068    
069    /**
070     * <p>A specialized JXPanel that allows the user to construct and choose a Gradient.
071     * The returned values will be one of: LinearGradientPaint or RadialGradientPaint.</p>
072     *
073     * <p><b>Dependency</b>: Because this class relies on LinearGradientPaint and
074     * RadialGradientPaint, it requires the optional MultipleGradientPaint.jar</p>
075     * 
076     * @author joshy
077     */
078    public class JXGradientChooser extends JXPanel {
079        private enum GradientStyle { Linear, Radial }
080        
081        /**
082         * The multi-thumb slider to use for the gradient stops
083         */
084        private JXMultiThumbSlider<Color> slider;
085        private JButton deleteThumbButton;
086        private JButton addThumbButton;
087        
088        private JTextField colorField;
089        private JXColorSelectionButton changeColorButton;
090        private JSpinner colorLocationSpinner;
091        private JSpinner alphaSpinner;
092        private JSlider alphaSlider;
093        
094        private JComboBox styleCombo;
095        private GradientPreviewPanel gradientPreview;
096        
097        private JRadioButton noCycleRadio;
098        private JRadioButton reflectedRadio;
099        private JRadioButton repeatedRadio;
100        private JCheckBox reversedCheck;
101        private MultipleGradientPaint gradient;
102    
103        /**
104         * Creates new JXGradientChooser
105         */
106        public JXGradientChooser() {
107            initComponents2();
108        }
109    
110        /**
111         * Returns the MultipleGradientPaint currently choosen by the user.
112         * @return the currently selected gradient
113         */
114        public MultipleGradientPaint getGradient() {
115            return gradient;
116        }
117    
118        private boolean thumbsMoving = false;
119        private Logger log = Logger.getLogger(JXGradientChooser.class.getName());
120    
121        /**
122         * Sets the gradient within this panel to the new gradient. This will delete
123         * the old gradient all of it's settings, resetting the slider, gradient
124         * type selection, and other gradient configuration options to match the
125         * new gradient.
126         *
127         * @param mgrad The desired gradient.
128         */
129        public void setGradient(MultipleGradientPaint mgrad) {
130            if(gradient == mgrad) {
131                return;
132            }
133            float[] fracts = mgrad.getFractions();
134            Color[] colors = mgrad.getColors();
135    
136            if(!thumbsMoving) {
137                // update the slider properly
138                if(slider.getModel().getThumbCount() !=
139                        mgrad.getColors().length) {
140                    // removing all thumbs;
141                    while(slider.getModel().getThumbCount() > 0) {
142                        slider.getModel().removeThumb(0);
143                    }
144                    // add them back
145                    for(int i=0; i<fracts.length; i++) {
146                        slider.getModel().addThumb(fracts[i],colors[i]);
147                    }
148                } else {
149                    for(int i=0; i<fracts.length; i++) {
150                        slider.getModel().getThumbAt(i).setObject(colors[i]);
151                        slider.getModel().getThumbAt(i).setPosition(fracts[i]);
152                    }
153                }
154            } else {
155                log.fine("not updating because it's moving");
156            }
157            if(mgrad instanceof RadialGradientPaint) {
158                if(styleCombo.getSelectedItem() != GradientStyle.Radial) {
159                    styleCombo.setSelectedItem(GradientStyle.Radial);
160                }
161            } else {
162                if(styleCombo.getSelectedItem() != GradientStyle.Linear) {
163                    styleCombo.setSelectedItem(GradientStyle.Linear);
164                }
165            }
166            
167            if(mgrad.getCycleMethod() == MultipleGradientPaint.CycleMethod.REFLECT) {
168                this.reflectedRadio.setSelected(true);
169                gradientPreview.setReflected(true);
170            }
171            if(mgrad.getCycleMethod() == MultipleGradientPaint.CycleMethod.REPEAT) {
172                this.repeatedRadio.setSelected(true);
173                gradientPreview.setRepeated(true);
174            }
175            gradientPreview.setGradient(mgrad);
176            //reflectedRadio.setSelected()
177            MultipleGradientPaint old = this.getGradient();
178            gradient = mgrad;
179            firePropertyChange("gradient",old,getGradient());
180            repaint();
181        }
182    
183        private void recalcGradientFromStops() {
184            setGradient(gradientPreview.getGradient());
185        }
186        
187        private void updateFromStop(Thumb<Color> thumb) {
188            if(thumb == null) {
189                updateFromStop(-1,-1,Color.black);
190            } else {
191                updateFromStop(1,thumb.getPosition(),thumb.getObject());
192            }
193        }
194        
195        private void updateFromStop(int thumb, float position, Color color) {
196            log.fine("updating: " + thumb + " " + position + " " + color);
197            if(thumb == -1) {
198                colorLocationSpinner.setEnabled(false);
199                alphaSpinner.setEnabled(false);
200                alphaSlider.setEnabled(false);
201                colorField.setEnabled(false);
202                changeColorButton.setEnabled(false);
203                changeColorButton.setBackground(Color.black);
204                deleteThumbButton.setEnabled(false);
205            } else {
206                colorLocationSpinner.setEnabled(true);
207                alphaSpinner.setEnabled(true);
208                alphaSlider.setEnabled(true);
209                colorField.setEnabled(true);
210                changeColorButton.setEnabled(true);
211                colorLocationSpinner.setValue((int)(100*position));
212                colorField.setText(Integer.toHexString(color.getRGB()).substring(2));
213                alphaSpinner.setValue(color.getAlpha()*100/255);
214                alphaSlider.setValue(color.getAlpha()*100/255);
215                changeColorButton.setBackground(color);
216                deleteThumbButton.setEnabled(true);
217            }
218            updateDeleteButtons();
219            recalcGradientFromStops();
220        }
221        
222        private void updateDeleteButtons() {
223            if(slider.getModel().getThumbCount() <= 2) {
224                deleteThumbButton.setEnabled(false);
225            }
226        }
227        
228        private void updateGradientProperty() {
229            firePropertyChange("gradient",null,getGradient());
230            gradientPreview.repaint();
231        }
232        
233        
234        /** This method is called from within the constructor to
235         * initialize the form.
236         */
237        
238        private JPanel topPanel, previewPanel;
239        private void initComponents() {
240            // declarations for anonymous components
241            JPanel jPanel1, jPanel2, jPanel3, jPanel4;
242            JLabel jLabel1, jLabel5, jLabel2, jLabel6, jLabel4, jLabel7, jLabel8, jLabel9;
243            ButtonGroup typeGroup;
244            // pre-init stuff
245            slider = new JXMultiThumbSlider<Color>();
246            gradientPreview = new GradientPreviewPanel();
247            gradientPreview.setMultiThumbModel(slider.getModel());
248            
249            java.awt.GridBagConstraints gridBagConstraints;
250            
251            typeGroup = new javax.swing.ButtonGroup();
252            jPanel1 = new javax.swing.JPanel();
253            topPanel = new javax.swing.JPanel();
254            jPanel2 = new javax.swing.JPanel();
255            jLabel1 = new javax.swing.JLabel();
256            jLabel5 = new javax.swing.JLabel();
257            colorField = new javax.swing.JTextField();
258            jLabel2 = new javax.swing.JLabel();
259            jLabel6 = new javax.swing.JLabel();
260            colorLocationSpinner = new javax.swing.JSpinner();
261            jLabel4 = new javax.swing.JLabel();
262            jLabel7 = new javax.swing.JLabel();
263            alphaSpinner = new javax.swing.JSpinner();
264            changeColorButton = new JXColorSelectionButton();
265            alphaSlider = new javax.swing.JSlider();
266            //slider = new javax.swing.JSlider();
267            jPanel4 = new javax.swing.JPanel();
268            addThumbButton = new javax.swing.JButton();
269            deleteThumbButton = new javax.swing.JButton();
270            previewPanel = new javax.swing.JPanel();
271            jPanel3 = new javax.swing.JPanel();
272            jLabel8 = new javax.swing.JLabel();
273            styleCombo = new javax.swing.JComboBox();
274            jLabel9 = new javax.swing.JLabel();
275            noCycleRadio = new javax.swing.JRadioButton();
276            reflectedRadio = new javax.swing.JRadioButton();
277            repeatedRadio = new javax.swing.JRadioButton();
278            reversedCheck = new javax.swing.JCheckBox();
279            //gradientPreview = new javax.swing.JPanel();
280            
281            //setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
282            jPanel1.setLayout(new java.awt.GridBagLayout());
283            
284            topPanel.setLayout(new java.awt.GridBagLayout());
285            
286            topPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Gradient"));
287            jPanel2.setLayout(new java.awt.GridBagLayout());
288            
289            jLabel1.setText("Color:");
290            gridBagConstraints = new java.awt.GridBagConstraints();
291            gridBagConstraints.gridx = 0;
292            gridBagConstraints.gridy = 0;
293            gridBagConstraints.ipadx = 2;
294            gridBagConstraints.ipady = 2;
295            gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
296            gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
297            jPanel2.add(jLabel1, gridBagConstraints);
298            
299            jLabel5.setText("#");
300            gridBagConstraints = new java.awt.GridBagConstraints();
301            gridBagConstraints.gridx = 1;
302            gridBagConstraints.gridy = 0;
303            gridBagConstraints.insets = new java.awt.Insets(4, 0, 4, 4);
304            jPanel2.add(jLabel5, gridBagConstraints);
305            
306            colorField.setColumns(6);
307            colorField.setEnabled(false);
308            colorField.setPreferredSize(null);
309            gridBagConstraints = new java.awt.GridBagConstraints();
310            gridBagConstraints.gridy = 0;
311            gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
312            jPanel2.add(colorField, gridBagConstraints);
313            
314            jLabel2.setText("Location:");
315            gridBagConstraints = new java.awt.GridBagConstraints();
316            gridBagConstraints.gridy = 1;
317            gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
318            gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
319            jPanel2.add(jLabel2, gridBagConstraints);
320            
321            jLabel6.setText("%");
322            gridBagConstraints = new java.awt.GridBagConstraints();
323            gridBagConstraints.gridy = 1;
324            jPanel2.add(jLabel6, gridBagConstraints);
325            
326            colorLocationSpinner.setEnabled(false);
327            colorLocationSpinner.setPreferredSize(null);
328            gridBagConstraints = new java.awt.GridBagConstraints();
329            gridBagConstraints.gridy = 1;
330            gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
331            jPanel2.add(colorLocationSpinner, gridBagConstraints);
332            
333            jLabel4.setText("Opacity:");
334            gridBagConstraints = new java.awt.GridBagConstraints();
335            gridBagConstraints.gridy = 2;
336            gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
337            gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
338            jPanel2.add(jLabel4, gridBagConstraints);
339            
340            jLabel7.setText("%");
341            gridBagConstraints = new java.awt.GridBagConstraints();
342            gridBagConstraints.gridy = 2;
343            jPanel2.add(jLabel7, gridBagConstraints);
344            
345            alphaSpinner.setEnabled(false);
346            alphaSpinner.setPreferredSize(null);
347            gridBagConstraints = new java.awt.GridBagConstraints();
348            gridBagConstraints.gridy = 2;
349            gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
350            jPanel2.add(alphaSpinner, gridBagConstraints);
351            
352            changeColorButton.setText("00");
353            changeColorButton.setEnabled(false);
354            gridBagConstraints = new java.awt.GridBagConstraints();
355            gridBagConstraints.fill = java.awt.GridBagConstraints.NONE;
356            gridBagConstraints.weightx = 1.0;
357            gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
358            gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
359            jPanel2.add(changeColorButton, gridBagConstraints);
360            
361            alphaSlider.setEnabled(false);
362            alphaSlider.setPreferredSize(new java.awt.Dimension(20, 25));
363            gridBagConstraints = new java.awt.GridBagConstraints();
364            gridBagConstraints.gridy = 2;
365            gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
366            gridBagConstraints.weightx = 1.0;
367            jPanel2.add(alphaSlider, gridBagConstraints);
368            
369            gridBagConstraints = new java.awt.GridBagConstraints();
370            gridBagConstraints.gridx = 0;
371            gridBagConstraints.gridy = 2;
372            gridBagConstraints.gridwidth = 2;
373            gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
374            gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
375            gridBagConstraints.weightx = 1.0;
376            gridBagConstraints.weighty = 1.0;
377            gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
378            topPanel.add(jPanel2, gridBagConstraints);
379            
380            gridBagConstraints = new java.awt.GridBagConstraints();
381            gridBagConstraints.gridy = 0;
382            gridBagConstraints.gridwidth = 2;
383            gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
384            topPanel.add(slider, gridBagConstraints);
385            
386            jPanel4.setLayout(new java.awt.GridLayout(1, 0, 2, 0));
387            
388            addThumbButton.setText("Add");
389            jPanel4.add(addThumbButton);
390            
391            deleteThumbButton.setText("Delete");
392            jPanel4.add(deleteThumbButton);
393            
394            gridBagConstraints = new java.awt.GridBagConstraints();
395            gridBagConstraints.gridy = 1;
396            gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
397            gridBagConstraints.weightx = 1.0;
398            gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
399            topPanel.add(jPanel4, gridBagConstraints);
400            
401            gridBagConstraints = new java.awt.GridBagConstraints();
402            gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
403            gridBagConstraints.weightx = 1.0;
404            jPanel1.add(topPanel, gridBagConstraints);
405            
406            previewPanel.setLayout(new java.awt.GridBagLayout());
407            
408            previewPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Preview"));
409            jPanel3.setLayout(new java.awt.GridBagLayout());
410            
411            jLabel8.setText("Style:");
412            gridBagConstraints = new java.awt.GridBagConstraints();
413            gridBagConstraints.gridy = 0;
414            gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
415            gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
416            jPanel3.add(jLabel8, gridBagConstraints);
417            
418            styleCombo.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Linear", "Radial" }));
419            gridBagConstraints = new java.awt.GridBagConstraints();
420            gridBagConstraints.gridy = 0;
421            gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
422            gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
423            jPanel3.add(styleCombo, gridBagConstraints);
424            
425            jLabel9.setText("Type:");
426            gridBagConstraints = new java.awt.GridBagConstraints();
427            gridBagConstraints.gridy = 1;
428            gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
429            gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
430            jPanel3.add(jLabel9, gridBagConstraints);
431            
432            typeGroup.add(noCycleRadio);
433            noCycleRadio.setSelected(true);
434            noCycleRadio.setText("None");
435            noCycleRadio.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
436            noCycleRadio.setMargin(new java.awt.Insets(0, 0, 0, 0));
437            gridBagConstraints = new java.awt.GridBagConstraints();
438            gridBagConstraints.gridy = 1;
439            gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
440            gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
441            jPanel3.add(noCycleRadio, gridBagConstraints);
442            
443            typeGroup.add(reflectedRadio);
444            reflectedRadio.setText("Reflect");
445            reflectedRadio.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
446            reflectedRadio.setMargin(new java.awt.Insets(0, 0, 0, 0));
447            gridBagConstraints = new java.awt.GridBagConstraints();
448            gridBagConstraints.gridx = 1;
449            gridBagConstraints.gridy = 2;
450            gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
451            gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
452            jPanel3.add(reflectedRadio, gridBagConstraints);
453            
454            typeGroup.add(repeatedRadio);
455            repeatedRadio.setText("Repeat");
456            repeatedRadio.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
457            repeatedRadio.setMargin(new java.awt.Insets(0, 0, 0, 0));
458            gridBagConstraints = new java.awt.GridBagConstraints();
459            gridBagConstraints.gridx = 1;
460            gridBagConstraints.gridy = 3;
461            gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
462            gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
463            jPanel3.add(repeatedRadio, gridBagConstraints);
464            
465            reversedCheck.setText("Reverse");
466            reversedCheck.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
467            reversedCheck.setMargin(new java.awt.Insets(0, 0, 0, 0));
468            gridBagConstraints = new java.awt.GridBagConstraints();
469            gridBagConstraints.gridx = 1;
470            gridBagConstraints.gridy = 4;
471            gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
472            gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
473            jPanel3.add(reversedCheck, gridBagConstraints);
474            
475            gridBagConstraints = new java.awt.GridBagConstraints();
476            gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
477            previewPanel.add(jPanel3, gridBagConstraints);
478            
479            gradientPreview.setBorder(javax.swing.BorderFactory.createEtchedBorder());
480            gradientPreview.setPreferredSize(new java.awt.Dimension(130, 130));
481            gridBagConstraints = new java.awt.GridBagConstraints();
482            gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
483            gridBagConstraints.weightx = 10.0;
484            gridBagConstraints.weighty = 10.0;
485            previewPanel.add(gradientPreview, gridBagConstraints);
486            
487            gridBagConstraints = new java.awt.GridBagConstraints();
488            gridBagConstraints.gridy = 1;
489            gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
490            gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
491            gridBagConstraints.weightx = 1.0;
492            gridBagConstraints.weighty = 1.0;
493            jPanel1.add(previewPanel, gridBagConstraints);
494            
495        }// </editor-fold>
496        private void initComponents2() {
497            this.initComponents();
498            setLayout(new BorderLayout());
499            add(topPanel, BorderLayout.NORTH);
500            add(previewPanel, BorderLayout.CENTER);
501            
502            
503            // do event handling stuff
504            //create the actions and load them in the action map
505            AddThumbAction addThumbAction = new AddThumbAction();
506            DeleteThumbAction deleteThumbAction = new DeleteThumbAction();
507            deleteThumbAction.setEnabled(false); //disabled to begin with
508            //TODO Add to the action map with proper keys, etc
509            ActionMap actions = getActionMap();
510            actions.put("add-thumb", addThumbAction);
511            actions.put("delete-thumb", deleteThumbAction);
512            //actions.put("change-color", changeColorAction);
513            addThumbButton.setAction(addThumbAction);
514            deleteThumbButton.setAction(deleteThumbAction);
515            changeColorButton.addPropertyChangeListener("background", new PropertyChangeListener() {
516                public void propertyChange(PropertyChangeEvent evt) {
517                    selectColorForThumb();
518                    updateGradientProperty();
519                }
520            });
521            colorLocationSpinner.addChangeListener(new ChangeLocationListener());
522            ChangeAlphaListener changeAlphaListener = new ChangeAlphaListener();
523            alphaSpinner.addChangeListener(changeAlphaListener);
524            alphaSlider.addChangeListener(changeAlphaListener);
525            RepaintOnEventListener repaintListener = new RepaintOnEventListener();
526            styleCombo.addItemListener(repaintListener);
527            styleCombo.setModel(new DefaultComboBoxModel(GradientStyle.values()));
528            noCycleRadio.addActionListener(repaintListener);
529            reflectedRadio.addActionListener(repaintListener);
530            repeatedRadio.addActionListener(repaintListener);
531            reversedCheck.addActionListener(repaintListener);
532            gradientPreview.picker = this; //wow, nasty
533            
534            
535            ///To still refactor below::
536            SpinnerNumberModel alpha_model = new SpinnerNumberModel(100,0,100,1);
537            alphaSpinner.setModel(alpha_model);
538            SpinnerNumberModel location_model = new SpinnerNumberModel(100,0,100,1);
539            colorLocationSpinner.setModel(location_model);
540    
541            slider.setOpaque(false);
542            slider.setPreferredSize(new Dimension(100,35));
543            slider.getModel().setMinimumValue(0f);
544            slider.getModel().setMaximumValue(1.0f);
545            
546            slider.getModel().addThumb(0,Color.black);
547            slider.getModel().addThumb(0.5f,Color.red);
548            slider.getModel().addThumb(1.0f,Color.white);
549            
550            slider.setThumbRenderer(new GradientThumbRenderer());
551            slider.setTrackRenderer(new GradientTrackRenderer());
552            slider.addMultiThumbListener(new StopListener());
553            
554            // called when the gradient property of the preview pane changes
555            gradientPreview.addPropertyChangeListener("gradient", new PropertyChangeListener() {
556                public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
557                    recalcGradientFromStops();
558                }
559            });
560            
561            recalcGradientFromStops();
562            
563        }
564        
565        // called whenever the color location spinner is changed
566        private final class ChangeLocationListener implements ChangeListener {
567            public void stateChanged(ChangeEvent evt) {
568                if(slider.getSelectedIndex() >= 0) {
569                    Thumb<Color> thumb = slider.getModel().getThumbAt(slider.getSelectedIndex());
570                    thumb.setPosition((Integer)colorLocationSpinner.getValue()/100f);
571                    updateFromStop(thumb);
572                    updateGradientProperty();
573                }
574            }
575        }
576        
577        // called when the alpha slider moves
578        private final class ChangeAlphaListener implements ChangeListener {
579            public void stateChanged(ChangeEvent changeEvent) {
580                if(slider.getSelectedIndex() >= 0 && !thumbsMoving) {
581                    // get the selected thumb
582                    Thumb<Color> thumb = slider.getModel().getThumbAt(slider.getSelectedIndex());
583                    // get the new alpha value
584                    int alpha = changeEvent.getSource() == alphaSpinner ?
585                        (Integer)alphaSpinner.getValue()
586                        : alphaSlider.getValue();
587                    
588                    
589                    // calc new color and set it on thumb
590                    Color col = thumb.getObject();
591                    col = ColorUtil.setAlpha(col, alpha*255/100);
592                    thumb.setObject(col);
593                    
594                    // set the new alpha value on the other alpha control
595                    if (changeEvent.getSource() == alphaSpinner) {
596                        alphaSlider.setValue(alpha);
597                    } else {
598                        alphaSpinner.setValue(alpha);
599                    }
600                    
601                    recalcGradientFromStops();
602                }
603            }
604        }
605        
606        
607        private final class AddThumbAction extends AbstractActionExt {
608            public AddThumbAction() {
609                super("Add");
610            }
611            
612            public void actionPerformed(ActionEvent actionEvent) {
613                float pos = 0.2f;
614                Color color = Color.black;
615                int num = slider.getModel().addThumb(pos,color);
616                log.fine("new number = " + num);
617                /*
618                for (int i = 0; i < slider.getModel().getThumbCount(); i++) {
619                    float pos2 = slider.getModel().getThumbAt(i).getPosition();
620                    if (pos2 < pos) {
621                        continue;
622                    }
623                    slider.getModel().insertThumb(pos, color, i);
624                    updateFromStop(i,pos,color);
625                    break;
626                }
627                 */
628                
629            }
630        }
631        
632        private final class DeleteThumbAction extends AbstractActionExt {
633            public DeleteThumbAction() {
634                super("Delete");
635            }
636            
637            public void actionPerformed(ActionEvent actionEvent) {
638                int index = slider.getSelectedIndex();
639                if (index >= 0) {
640                    slider.getModel().removeThumb(index);
641                    updateFromStop(-1,-1,null);
642                }
643            }
644        }
645        
646        private class StopListener implements ThumbListener {
647    
648            public StopListener() {
649                super();
650            }
651            
652            public void thumbMoved(int thumb, float pos) {
653                log.fine("moved: " + thumb + " " + pos);
654                Color color = slider.getModel().getThumbAt(thumb).getObject();
655                thumbsMoving = true;
656                updateFromStop(thumb,pos,color);
657                updateDeleteButtons();
658                thumbsMoving = false;
659                
660            }
661            
662            public void thumbSelected(int thumb) {
663                
664                if(thumb == -1) {
665                    updateFromStop(-1,-1,Color.black);
666                    return;
667                }
668                thumbsMoving = true;
669                float pos = slider.getModel().getThumbAt(thumb).getPosition();
670                Color color = slider.getModel().getThumbAt(thumb).getObject();
671                log.fine("selected = " + thumb + " " + pos + " " + color);
672                updateFromStop(thumb,pos,color);
673                updateDeleteButtons();
674                slider.repaint();
675                thumbsMoving = false;
676                 
677            }
678            
679            public void mousePressed(MouseEvent e) {
680                if(e.getClickCount() > 1) {
681                    selectColorForThumb();
682                }
683            }
684        }
685        
686        private final class RepaintOnEventListener implements ActionListener, ItemListener {
687            public void actionPerformed(ActionEvent e) {
688                gradientPreview.setReflected(reflectedRadio.isSelected());
689                gradientPreview.setReversed(reversedCheck.isSelected());
690                gradientPreview.setRepeated(repeatedRadio.isSelected());
691                //updateGradientProperty();
692                recalcGradientFromStops();
693                gradientPreview.repaint();
694            }
695            
696            public void itemStateChanged(ItemEvent e) {
697                if(styleCombo.getSelectedItem() == GradientStyle.Radial) {
698                    gradientPreview.setRadial(true);
699                } else {
700                    gradientPreview.setRadial(false);
701                }
702                recalcGradientFromStops();
703            }
704        }
705        
706        private void selectColorForThumb() {
707            int index = slider.getSelectedIndex();
708            if (index >= 0) {
709                Color color = changeColorButton.getBackground();
710                slider.getModel().getThumbAt(index).setObject(color);
711                updateFromStop(index, slider.getModel().getThumbAt(index).getPosition(), color);
712            }
713        }
714        
715        /**
716         * This static utility method <b>cannot</b> be called from the
717         * ETD, or your application will lock up. Call it from a separate
718         * thread or create a new Thread with a Runnable.
719         * @param comp The component to use when finding a top level window or frame for
720         * the dialog.
721         * @param title The desired title of the gradient chooser dialog.
722         * @param mgrad The gradient to initialize the chooser too.
723         * @return The gradient the user chose.
724         */
725        public static MultipleGradientPaint showDialog(Component comp, String title, MultipleGradientPaint mgrad) {
726            Component root = SwingUtilities.getRoot(comp);
727            final JDialog dialog = new JDialog((JFrame)root,title,true);
728            final JXGradientChooser picker = new JXGradientChooser();
729            if(mgrad != null) {
730                picker.setGradient(mgrad);
731            }
732            dialog.add(picker);
733            
734            
735            JPanel panel = new JPanel();
736            JButton cancel = new JButton("Cancel");
737            cancel.addActionListener(new ActionListener() {
738                public void actionPerformed(ActionEvent actionEvent) {
739                    dialog.setVisible(false);
740                }
741            });
742            JButton okay = new JButton("Ok");
743            okay.addActionListener(new ActionListener() {
744                public void actionPerformed(ActionEvent actionEvent) {
745                    dialog.setVisible(false);
746                }
747            });
748            okay.setDefaultCapable(true);
749            
750            
751            GridLayout gl = new GridLayout();
752            gl.setHgap(2);
753            panel.setLayout(gl);
754            panel.add(cancel);
755            panel.add(okay);
756            
757            JPanel p2 = new JPanel();
758            p2.setLayout(new GridBagLayout());
759            GridBagConstraints gbc = new GridBagConstraints();
760            gbc.anchor = GridBagConstraints.EAST;
761            gbc.weightx = 1.0;
762            p2.add(panel,gbc);
763            dialog.add(p2,"South");
764            
765            dialog.getRootPane().setDefaultButton(okay);
766            dialog.pack();
767            dialog.setResizable(false);
768            dialog.setVisible(true);
769            
770            return picker.getGradient();
771        }
772        
773        /**
774         * Creates a string representation of a {@code MultipleGradientPaint}. This
775         * string is used for debugging purposes. Its contents cannot be guaranteed
776         * between releases.
777         * 
778         * @param paint
779         *                the {@code paint} to create a string for
780         * @return a string representing the supplied {@code paint}
781         */
782        public static String toString(MultipleGradientPaint paint) {
783            StringBuffer buffer = new StringBuffer();
784            buffer.append(paint.getClass().getName());
785            Color[] colors = paint.getColors();
786            float[] values = paint.getFractions();
787            buffer.append("[");
788            for(int i=0; i<colors.length; i++) {
789                buffer.append("#").append(Integer.toHexString(colors[i].getRGB()));
790                buffer.append(":");
791                buffer.append(values[i]);
792                buffer.append(", ");
793            }
794            buffer.append("]");
795            return buffer.toString();
796        }
797    
798    }
799    
800