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