/* ==============================================
* Simtools : The tools library used in JSynoptic
* ==============================================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* 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.
*
* (C) Copyright 2003, by :
* Corporate:
* Astrium SAS
* EADS CRC
* Individual:
* Nicolas Brodu
*
* $Id: ColorMapper.java,v 1.18 2008/10/23 15:53:14 ogor Exp $
*
* Changes
* -------
* 25-Sep-2003 : Initial public release (NB);
* 07-Oct-03: Implemented all edit dialogs (NB);
* 03-Nov-03 : Separated generic and color part (NB)
*
*/
package simtools.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Insets;
import java.awt.Paint;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Iterator;
import java.util.ResourceBundle;
import java.util.Vector;
import javax.swing.BorderFactory;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.border.Border;
import javax.swing.table.TableCellRenderer;
import simtools.data.DataSource;
/**
* This class maps data sources and index to colors.
* The mapping is user defined, and based for example on the value of the data source
* For example, green value < 10, red above. Useful for making plots with "alarm" colors
*
* The data source shall not be saved into the object. Rather, this class goal is to implement
* a mapping independent of the data source given.
*
*/
public class ColorMapper extends GenericMapper {
static final long serialVersionUID = 3583538958279328171L;
public static ResourceBundle resources = ResourceFinder.get(ColorMapper.class);
public ColorMapper() {
super();
}
public ColorMapper(String name) {
super(name);
}
/**
* If the mapper already exists in the list, update its values
* Otherwise add it to the list of existing mappers
* @param m, the mapper to update
* @return
*/
public static ColorMapper updateColorMapper(ColorMapper m) {
ColorMapper ret = ColorMapper.getColorMapper(m.toString());
if (ret ==null){
colorMappers.add(m);
ret = m;
}
return ret;
}
/**
* @param name
* @return returns mapper related to name, or null if no mapper were found
*/
public static ColorMapper getColorMapper(String id) {
if (colorMappers != null) {
for (Iterator it = colorMappers.iterator(); it.hasNext(); ) {
ColorMapper sm = (ColorMapper)it.next();
if ((sm.toString()!=null) &&(sm.toString().equals(id)))
return sm;
}
}
return null;
}
/**
* The default colors cycle between COLOR_CYCLE different hues, and
* SATURATION_CYCLE different SATURATION levels.
*/
public static final int COLOR_CYCLE = 6;
public static final int SATURATION_CYCLE = 2;
public static final int BRIGHTNESS_CYCLE = 3;
protected static int defaultColorIndex = 0;
/** The color mappers are application global */
public static Vector colorMappers;
static{
colorMappers = new Vector();
}
/**
* Realizes the mapping, and returns a Paint object for this data source and index
* @param ds The data source to map
* @param index The index in the data source (may be used, or not, and defaults to the last Index if not specified).
* @return a Paint object
*/
public Paint getPaint(DataSource ds, long index) {
return (Paint)getMapping(ds,index);
}
public void setDefaultPaint(Paint defaultValue) {
super.setDefaultValue(defaultValue);
}
public Paint getDefaultPaint() {
return (Paint)super.getDefaultValue();
}
/**
* Realizes the mapping, and returns a Paint object for this data source and index
* @param ds The data source to map
* @return a Paint object
*/
public Paint getPaint(DataSource ds) {
return (Paint)getMapping(ds);
}
public Paint getPaint(Object value) {
return (Paint)getMapping(value);
}
/** Associate a paint with a value */
public void setPaint(Object value, Paint paint) {
setMapping(value,paint);
}
/** Associate a paint with a double value interval (bounds included) */
public void setPaint(double minvalue, double maxvalue, Paint paint) {
setMapping(minvalue, true, maxvalue, true, paint);
}
/** Associate a paint with a double value interval. Interval bounds are specified */
public void setPaint(double minvalue, boolean boundedMin, double maxvalue, boolean boundedMax, Paint paint) {
setMapping(minvalue,boundedMin,maxvalue,boundedMax,paint);
}
/** Associate a paint with a double value infinite range (bound included).
* @param isMore if true, the interval is x >= value, else it is x <= value
*/
public void setPaint(boolean isMore, double value, Paint paint) {
setMapping(isMore, value, true, paint);
}
/** Associate a paint with a double value infinite range.
* @param isMore if true, the interval is x >= value, else it is x <= value
*/
public void setPaint(boolean isMore, double value, boolean boundIncluded, Paint paint) {
setMapping(isMore, value, boundIncluded, paint);
}
/** Associate a paint with a long value interval (bounds included) */
public void setPaint(long minvalue, long maxvalue, Paint paint) {
setMapping(minvalue, true, maxvalue, true, paint);
}
/** Associate a paint with a long value interval. Interval bounds are specified */
public void setPaint(long minvalue, boolean boundedMin, long maxvalue, boolean boundedMax, Paint paint) {
setMapping(minvalue,boundedMin,maxvalue,boundedMax,paint);
}
/** Associate a paint with a long value infinite range (bound included).
* @param isMore if true, the interval is x >= value, else it is x <= value
*/
public void setPaint(boolean isMore, long value, Paint paint) {
setMapping(isMore, value, true, paint);
}
/** Associate a paint with a long value infinite range.
* @param isMore if true, the interval is x >= value, else it is x <= value
*/
public void setPaint(boolean isMore, long value, boolean boundIncluded, Paint paint) {
setMapping(isMore, value, boundIncluded, paint);
}
/** Associate a paint with a String value interval (bounds included) */
public void setPaint(String minvalue, String maxvalue, Paint paint) {
setMapping(minvalue, true, maxvalue, true, paint);
}
/** Associate a paint with a String value interval. Interval bounds are specified */
public void setPaint(String minvalue, boolean boundedMin, String maxvalue, boolean boundedMax, Paint paint) {
setMapping(minvalue,boundedMin,maxvalue,boundedMax,paint);
}
/** Associate a paint with a String value infinite range (bound included).
* @param isMore if true, the interval is x >= value, else it is x <= value
*/
public void setPaint(boolean isMore, String value, Paint paint) {
setMapping(isMore, value, true, paint);
}
/** Associate a paint with a String value infinite range.
* @param isMore if true, the interval is x >= value, else it is x <= value
*/
public void setPaint(boolean isMore, String value, boolean boundIncluded, Paint paint) {
setMapping(isMore, value, boundIncluded, paint);
}
/*
* The editor button that brings up the dialog.
* We extend DefaultCellEditor for convenience,
* even though it mean we have to create a dummy
* check box. Another approach would be to copy
* the implementation of TableCellEditor methods
* from the source code for DefaultCellEditor.
*/
protected static class ColorEditor extends DefaultCellEditor {
Color currentColor = null;
public ColorEditor(JButton b) {
super(new JCheckBox()); //Unfortunately, the constructor
//expects a check box, combo box,
//or text field.
editorComponent = b;
setClickCountToStart(2); //This is usually 1 or 2.
//Must do this so that editing stops when appropriate.
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
fireEditingStopped();
}
});
}
protected void fireEditingStopped() {
super.fireEditingStopped();
}
public Object getCellEditorValue() {
return currentColor;
}
public Component getTableCellEditorComponent(JTable table,
Object value,
boolean isSelected,
int row,
int column) {
((JButton)editorComponent).setText(value.toString());
currentColor = (Color)value;
return editorComponent;
}
}
protected static class ColorRenderer extends JLabel
implements TableCellRenderer {
Border unselectedBorder = null;
Border selectedBorder = null;
boolean isBordered = true;
public ColorRenderer(boolean isBordered) {
super();
this.isBordered = isBordered;
setOpaque(true); //MUST do this for background to show up.
}
public Component getTableCellRendererComponent(
JTable table, Object color,
boolean isSelected, boolean hasFocus,
int row, int column) {
setBackground((Color)color);
if (isBordered) {
if (isSelected) {
if (selectedBorder == null) {
selectedBorder = BorderFactory.createMatteBorder(2,5,2,5,
table.getSelectionBackground());
}
setBorder(selectedBorder);
} else {
if (unselectedBorder == null) {
unselectedBorder = BorderFactory.createMatteBorder(2,5,2,5,
table.getBackground());
}
setBorder(unselectedBorder);
}
}
return this;
}
}
public MapperTableModel createModel() {
return new ColorMapperTableModel();
}
protected class ColorMappingTable extends ExpressionMappingTable {
/**
* @param dialog
*/
public ColorMappingTable(JDialog owner) {
super(owner);
}
/** Sets up the renderer to a color renderer
* @see simtools.ui.GenericMapper.ExpressionMappingTable#setUpRenderer()
*/
protected void setUpRenderer() {
setDefaultRenderer(Color.class,
new ColorRenderer(true));
}
/** Sets up the editor to a color editor
* @see simtools.ui.GenericMapper.ExpressionMappingTable#setUpEditor()
*/
protected void setUpEditor() {
//First, set up the button that brings up the dialog.
final JButton button = new JButton("") {
public void setText(String s) {
//Button never shows text -- only color.
}
};
button.setBackground(Color.white);
button.setBorderPainted(false);
button.setMargin(new Insets(0,0,0,0));
//Now create an editor to encapsulate the button, and
//set it up as the editor for all Color cells.
final ColorEditor colorEditor = new ColorEditor(button);
setDefaultEditor(Color.class, colorEditor);
//Set up the dialog that the button brings up.
final JColorChooser colorChooser = new JColorChooser();
ActionListener okListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
colorEditor.currentColor = colorChooser.getColor();
}
};
final JDialog dialog = JColorChooser.createDialog(button,
resources.getString("PickAColor"),
true,
colorChooser,
okListener,
null); //XXXDoublecheck this is OK
//Here's the code that brings up the dialog.
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
button.setBackground(colorEditor.currentColor);
colorChooser.setColor(colorEditor.currentColor);
//Without the following line, the dialog comes up
//in the middle of the screen.
dialog.setLocationRelativeTo(button);
dialog.show();
}
});
}
}
protected class ColorMapperTableModel extends MapperTableModel {
public Class getColumnClass(int c) {
if (c==1) return Color.class;
return super.getColumnClass(c);
}
public String getColumnName(int col) {
if (col==1) return resources.getString("Color");
return super.getColumnName(col);
}
public boolean isCellEditable(int row, int col) {
if (col==1) return true;
return super.isCellEditable(row, col);
}
}
protected ExpressionMappingTable createTable(JDialog parent) {
return new ColorMappingTable(parent);
}
protected Object createNewValue() {
float s = 1.0f - ((defaultColorIndex / COLOR_CYCLE) % SATURATION_CYCLE) * 1.0f / (float)SATURATION_CYCLE;
float b = 1.0f - 0.5f * (((defaultColorIndex / (COLOR_CYCLE * SATURATION_CYCLE)) % BRIGHTNESS_CYCLE) * 1.0f / (float)BRIGHTNESS_CYCLE);
float h = 4.0f/6.0f+(float)defaultColorIndex/COLOR_CYCLE+(1-s)*(1-b);
defaultColorIndex++;
return Color.getHSBColor(h, s, b);
}
public static ColorMapper createColorMapperDialog(JDialog owner) {
ColorMapper cm = new ColorMapper();
cm.editDialog(owner);
return cm;
}
public static void main(String[] args) {
// GenericMapper cm = new GenericMapper();
ColorMapper cm = new ColorMapper();
cm.setMapping(new Long(3),Color.red);
cm.setMapping(new Long(4),Color.blue);
cm.setMapping(5,10,Color.magenta);
cm.setMapping(8,20,Color.green);
cm.setMapping(-18,7,Color.black);
cm.editDialog(null);
System.exit(0);
}
}