/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2007 by:
Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/deegree/
lat/lon GmbH
http://www.lat-lon.de
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact:
Andreas Poth
lat/lon GmbH
Aennchenstr. 19
53177 Bonn
Germany
E-Mail: poth@lat-lon.de
Prof. Dr. Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: greve@giub.uni-bonn.de
This file is a modified version of a file that was originally developed as part
of the SwingLabs SwingX project (https://swingx.dev.java.net/). The original
file JXGradientChooser is dating from 2009-02-01 and was published under
LGPL 3.0. It can be obtained from http://swinglabs.org/.
---------------------------------------------------------------------------*/
package org.deegree.igeo.views.swing.style.component.classification;
import static org.deegree.igeo.i18n.Messages.get;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.logging.Logger;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.apache.batik.ext.awt.LinearGradientPaint;
import org.jdesktop.swingx.JXColorSelectionButton;
import org.jdesktop.swingx.JXMultiThumbSlider;
import org.jdesktop.swingx.JXPanel;
import org.jdesktop.swingx.action.AbstractActionExt;
import org.jdesktop.swingx.color.GradientThumbRenderer;
import org.jdesktop.swingx.color.GradientTrackRenderer;
import org.jdesktop.swingx.multislider.Thumb;
import org.jdesktop.swingx.multislider.ThumbListener;
/**
* <code>GradientChooser</code>
*
* @author <a href="mailto:buesching@lat-lon.de">Lyn Buesching</a>
* @author last edited by: $Author$
*
* @version $Revision$, $Date$
*
*/
public class GradientChooser extends JXPanel {
private static final long serialVersionUID = -7208689345569214731L;
/**
* The multi-thumb slider to use for the gradient stops
*/
private JXMultiThumbSlider<Color> slider;
private JButton deleteThumbButton;
private JButton addThumbButton;
private JTextField colorField;
private JXColorSelectionButton changeColorButton;
private JSpinner colorLocationSpinner;
private LinearGradientPaint gradient;
public GradientChooser() {
initComponents2();
}
private LinearGradientPaint createGradientFromSlider() {
int thumbsCount = slider.getModel().getThumbCount();
float[] positions = new float[thumbsCount];
Color[] colors = new Color[thumbsCount];
float min = 0;
float max = 1;
if ( thumbsCount > 0 ) {
min = slider.getModel().getThumbAt( 0 ).getPosition();
max = slider.getModel().getThumbAt( 0 ).getPosition();
for ( int i = 0; i < thumbsCount; i++ ) {
Thumb<Color> thumb = slider.getModel().getThumbAt( i );
positions[i] = thumb.getPosition();
colors[i] = thumb.getObject();
min = Math.min( min, positions[i] );
max = Math.max( max, positions[i] );
}
}
Point2D start = new Point2D.Float( min, min );
Point2D end = new Point2D.Float( max, max );
return new LinearGradientPaint( start, end, positions, colors );
}
/**
* Returns the MultipleGradientPaint currently choosen by the user.
*
* @return the currently selected gradient
*/
public LinearGradientPaint getGradient() {
return gradient;
}
private boolean thumbsMoving = false;
private Logger log = Logger.getLogger( GradientChooser.class.getName() );
/**
* Sets the gradient within this panel to the new gradient. This will delete the old gradient all of it's settings,
* resetting the slider, gradient type selection, and other gradient configuration options to match the new
* gradient.
*
* @param mgrad
* The desired gradient.
*/
public void setGradient( LinearGradientPaint mgrad ) {
if ( gradient == mgrad ) {
return;
}
float[] fracts = mgrad.getFractions();
Color[] colors = mgrad.getColors();
if ( !thumbsMoving ) {
// update the slider properly
if ( slider.getModel().getThumbCount() != mgrad.getColors().length ) {
// removing all thumbs;
while ( slider.getModel().getThumbCount() > 0 ) {
slider.getModel().removeThumb( 0 );
}
// add them back
for ( int i = 0; i < fracts.length; i++ ) {
slider.getModel().addThumb( fracts[i], colors[i] );
}
} else {
for ( int i = 0; i < fracts.length; i++ ) {
slider.getModel().getThumbAt( i ).setObject( colors[i] );
slider.getModel().getThumbAt( i ).setPosition( fracts[i] );
}
}
} else {
log.fine( "not updating because it's moving" );
}
LinearGradientPaint old = this.getGradient();
gradient = mgrad;
firePropertyChange( "gradient", old, getGradient() );
repaint();
}
private void recalcGradientFromStops() {
setGradient( createGradientFromSlider() );
}
private void updateFromStop( Thumb<Color> thumb ) {
if ( thumb == null ) {
updateFromStop( -1, -1, Color.black );
} else {
updateFromStop( 1, thumb.getPosition(), thumb.getObject() );
}
}
private void updateFromStop( int thumb, float position, Color color ) {
log.fine( "updating: " + thumb + " " + position + " " + color );
if ( thumb == -1 ) {
colorLocationSpinner.setEnabled( false );
colorField.setEnabled( false );
changeColorButton.setEnabled( false );
changeColorButton.setBackground( Color.black );
deleteThumbButton.setEnabled( false );
} else {
colorLocationSpinner.setEnabled( true );
colorField.setEnabled( true );
changeColorButton.setEnabled( true );
colorLocationSpinner.setValue( (int) ( 100 * position ) );
colorField.setText( Integer.toHexString( color.getRGB() ).substring( 2 ) );
changeColorButton.setBackground( color );
deleteThumbButton.setEnabled( true );
}
updateDeleteButtons();
recalcGradientFromStops();
}
private void updateDeleteButtons() {
if ( slider.getModel().getThumbCount() <= 2 ) {
deleteThumbButton.setEnabled( false );
}
}
private void updateGradientProperty() {
firePropertyChange( "gradient", null, getGradient() );
}
/**
* This method is called from within the constructor to initialize the form.
*/
private JPanel topPanel;
private void initComponents() {
// declarations for anonymous components
JPanel jPanel1, jPanel2, jPanel4;
JLabel jLabel1, jLabel5, jLabel2, jLabel6;
slider = new JXMultiThumbSlider<Color>();
java.awt.GridBagConstraints gridBagConstraints;
jPanel1 = new javax.swing.JPanel();
topPanel = new javax.swing.JPanel();
jPanel2 = new javax.swing.JPanel();
jLabel1 = new javax.swing.JLabel();
jLabel5 = new javax.swing.JLabel();
colorField = new javax.swing.JTextField();
jLabel2 = new javax.swing.JLabel();
jLabel6 = new javax.swing.JLabel();
colorLocationSpinner = new javax.swing.JSpinner();
changeColorButton = new JXColorSelectionButton();
jPanel4 = new javax.swing.JPanel();
addThumbButton = new javax.swing.JButton();
deleteThumbButton = new javax.swing.JButton();
// setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jPanel1.setLayout( new java.awt.GridBagLayout() );
topPanel.setLayout( new java.awt.GridBagLayout() );
Border outer = BorderFactory.createEmptyBorder( 5, 5, 5, 5 );
Border inner = javax.swing.BorderFactory.createTitledBorder( get( "$MD11034" ) );
topPanel.setBorder( BorderFactory.createCompoundBorder( outer, inner ) );
jPanel2.setLayout( new java.awt.GridBagLayout() );
jLabel1.setText( get( "$MD11037" ) );
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.ipadx = 2;
gridBagConstraints.ipady = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
gridBagConstraints.insets = new java.awt.Insets( 4, 4, 4, 4 );
jPanel2.add( jLabel1, gridBagConstraints );
jLabel5.setText( "#" );
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.insets = new java.awt.Insets( 4, 0, 4, 4 );
jPanel2.add( jLabel5, gridBagConstraints );
colorField.setColumns( 6 );
colorField.setEnabled( false );
colorField.setPreferredSize( null );
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
jPanel2.add( colorField, gridBagConstraints );
jLabel2.setText( get( "$MD11038" ) );
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
gridBagConstraints.insets = new java.awt.Insets( 4, 4, 4, 4 );
jPanel2.add( jLabel2, gridBagConstraints );
jLabel6.setText( "%" );
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridy = 1;
jPanel2.add( jLabel6, gridBagConstraints );
colorLocationSpinner.setEnabled( false );
colorLocationSpinner.setPreferredSize( null );
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
jPanel2.add( colorLocationSpinner, gridBagConstraints );
changeColorButton.setText( "00" );
changeColorButton.setEnabled( false );
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.fill = java.awt.GridBagConstraints.NONE;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets( 0, 4, 0, 0 );
jPanel2.add( changeColorButton, gridBagConstraints );
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets( 5, 5, 5, 5 );
topPanel.add( jPanel2, gridBagConstraints );
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridy = 0;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
topPanel.add( slider, gridBagConstraints );
jPanel4.setLayout( new java.awt.GridLayout( 1, 0, 2, 0 ) );
addThumbButton.setText( get( "$MD11035" ) );
jPanel4.add( addThumbButton );
deleteThumbButton.setText( get( "$MD11036" ) );
jPanel4.add( deleteThumbButton );
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets( 5, 5, 5, 5 );
topPanel.add( jPanel4, gridBagConstraints );
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.weightx = 1.0;
jPanel1.add( topPanel, gridBagConstraints );
}// </editor-fold>
private void initComponents2() {
this.initComponents();
setLayout( new BorderLayout() );
add( topPanel, BorderLayout.NORTH );
// do event handling stuff
// create the actions and load them in the action map
AddThumbAction addThumbAction = new AddThumbAction();
DeleteThumbAction deleteThumbAction = new DeleteThumbAction();
deleteThumbAction.setEnabled( false ); // disabled to begin with
// TODO Add to the action map with proper keys, etc
ActionMap actions = getActionMap();
actions.put( "add-thumb", addThumbAction );
actions.put( "delete-thumb", deleteThumbAction );
// actions.put("change-color", changeColorAction);
addThumbButton.setAction( addThumbAction );
deleteThumbButton.setAction( deleteThumbAction );
changeColorButton.addPropertyChangeListener( "background", new PropertyChangeListener() {
public void propertyChange( PropertyChangeEvent evt ) {
selectColorForThumb();
updateGradientProperty();
}
} );
colorLocationSpinner.addChangeListener( new ChangeLocationListener() );
SpinnerNumberModel location_model = new SpinnerNumberModel( 100, 0, 100, 1 );
colorLocationSpinner.setModel( location_model );
slider.setOpaque( false );
slider.setPreferredSize( new Dimension( 100, 35 ) );
slider.getModel().setMinimumValue( 0f );
slider.getModel().setMaximumValue( 1.0f );
slider.getModel().addThumb( 0, Color.black );
slider.getModel().addThumb( 0.5f, Color.red );
slider.getModel().addThumb( 1.0f, Color.white );
slider.setThumbRenderer( new GradientThumbRenderer() );
slider.setTrackRenderer( new GradientTrackRenderer() );
slider.addMultiThumbListener( new StopListener() );
recalcGradientFromStops();
}
/**
*
* called whenever the color location spinner is changed
*
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
*
* @author last edited by: $Author$
*
* @version $Revision$, $Date$
*
*/
private final class ChangeLocationListener implements ChangeListener {
public void stateChanged( ChangeEvent evt ) {
if ( slider.getSelectedIndex() >= 0 ) {
Thumb<Color> thumb = slider.getModel().getThumbAt( slider.getSelectedIndex() );
thumb.setPosition( (Integer) colorLocationSpinner.getValue() / 100f );
updateFromStop( thumb );
updateGradientProperty();
}
}
}
/**
*
* The <code>GradientChooser</code> class TODO add class documentation here.
*
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
*
* @author last edited by: $Author$
*
* @version $Revision$, $Date$
*
*/
private final class AddThumbAction extends AbstractActionExt {
private static final long serialVersionUID = 795030290741092424L;
public AddThumbAction() {
super( get( "$MD11035" ) );
}
public void actionPerformed( ActionEvent actionEvent ) {
float pos = 0.2f;
Color color = Color.black;
int num = slider.getModel().addThumb( pos, color );
recalcGradientFromStops();
log.fine( "new number = " + num );
/*
* for (int i = 0; i < slider.getModel().getThumbCount(); i++) { float pos2 =
* slider.getModel().getThumbAt(i).getPosition(); if (pos2 < pos) { continue; }
* slider.getModel().insertThumb(pos, color, i); updateFromStop(i,pos,color); break; }
*/
}
}
/**
*
* The <code>GradientChooser</code> class TODO add class documentation here.
*
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
*
* @author last edited by: $Author$
*
* @version $Revision$, $Date$
*
*/
private final class DeleteThumbAction extends AbstractActionExt {
private static final long serialVersionUID = 6416885875952344563L;
public DeleteThumbAction() {
super( get( "$MD11036" ) );
}
public void actionPerformed( ActionEvent actionEvent ) {
int index = slider.getSelectedIndex();
if ( index >= 0 ) {
slider.getModel().removeThumb( index );
updateFromStop( -1, -1, null );
}
}
}
/**
*
* The <code>GradientChooser</code> class TODO add class documentation here.
*
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
*
* @author last edited by: $Author$
*
* @version $Revision$, $Date$
*
*/
private class StopListener implements ThumbListener {
public StopListener() {
super();
}
public void thumbMoved( int thumb, float pos ) {
log.fine( "moved: " + thumb + " " + pos );
Color color = slider.getModel().getThumbAt( thumb ).getObject();
thumbsMoving = true;
updateFromStop( thumb, pos, color );
updateDeleteButtons();
thumbsMoving = false;
}
public void thumbSelected( int thumb ) {
if ( thumb == -1 ) {
updateFromStop( -1, -1, Color.black );
return;
}
thumbsMoving = true;
float pos = slider.getModel().getThumbAt( thumb ).getPosition();
Color color = slider.getModel().getThumbAt( thumb ).getObject();
log.fine( "selected = " + thumb + " " + pos + " " + color );
updateFromStop( thumb, pos, color );
updateDeleteButtons();
slider.repaint();
thumbsMoving = false;
}
public void mousePressed( MouseEvent e ) {
if ( e.getClickCount() > 1 ) {
selectColorForThumb();
}
}
}
private void selectColorForThumb() {
int index = slider.getSelectedIndex();
if ( index >= 0 ) {
Color color = changeColorButton.getBackground();
slider.getModel().getThumbAt( index ).setObject( color );
updateFromStop( index, slider.getModel().getThumbAt( index ).getPosition(), color );
}
}
/**
* This static utility method <b>cannot</b> be called from the ETD, or your application will lock up. Call it from a
* separate thread or create a new Thread with a Runnable.
*
* @param comp
* The component to use when finding a top level window or frame for the dialog.
* @param title
* The desired title of the gradient chooser dialog.
* @param mgrad
* The gradient to initialize the chooser too.
* @return The gradient the user chose.
*/
public static LinearGradientPaint showDialog( Component comp, String title, LinearGradientPaint mgrad ) {
Component root = SwingUtilities.getRoot( comp );
final JDialog dialog = new JDialog( (JFrame) root, title, true );
final GradientChooser picker = new GradientChooser();
if ( mgrad != null ) {
picker.setGradient( mgrad );
}
dialog.add( picker );
JPanel panel = new JPanel();
JButton cancel = new JButton( "Cancel" );
cancel.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent actionEvent ) {
dialog.setVisible( false );
}
} );
JButton okay = new JButton( "Ok" );
okay.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent actionEvent ) {
dialog.setVisible( false );
}
} );
okay.setDefaultCapable( true );
GridLayout gl = new GridLayout();
gl.setHgap( 2 );
panel.setLayout( gl );
panel.add( cancel );
panel.add( okay );
JPanel p2 = new JPanel();
p2.setLayout( new GridBagLayout() );
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.EAST;
gbc.weightx = 1.0;
p2.add( panel, gbc );
dialog.add( p2, "South" );
dialog.getRootPane().setDefaultButton( okay );
dialog.pack();
dialog.setResizable( false );
dialog.setVisible( true );
return picker.getGradient();
}
/**
* Creates a string representation of a {@code MultipleGradientPaint}. This string is used for debugging purposes.
* Its contents cannot be guaranteed between releases.
*
* @param paint
* the {@code paint} to create a string for
* @return a string representing the supplied {@code paint}
*/
public static String toString( LinearGradientPaint paint ) {
StringBuffer buffer = new StringBuffer();
buffer.append( "Start: " ).append( paint.getStartPoint() );
buffer.append( " End: " ).append( paint.getEndPoint() );
Color[] colors = paint.getColors();
float[] values = paint.getFractions();
buffer.append( " [" );
for ( int i = 0; i < colors.length; i++ ) {
buffer.append( "#" ).append( Integer.toHexString( colors[i].getRGB() ) );
buffer.append( ":" );
buffer.append( values[i] );
buffer.append( ", " );
}
buffer.append( "]" );
return buffer.toString();
}
}