/*
* ContinuousColourScaleDialog.java
*
* Copyright (C) 2006-2014 Andrew Rambaut
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package figtree.treeviewer;
import figtree.treeviewer.decorators.ContinuousColourDecorator;
import figtree.treeviewer.decorators.ContinuousScale;
import figtree.treeviewer.decorators.HSBContinuousColourDecorator;
import figtree.treeviewer.decorators.InterpolatingColourDecorator;
import figtree.ui.components.ColorWellButton;
import figtree.ui.components.RangeSlider;
import figtree.ui.components.RealNumberField;
import jam.panels.OptionsPanel;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;
/**
* @author Andrew Rambaut
* @version $Id$
*
* $HeadURL$
*
* $LastChangedBy$
* $LastChangedDate$
* $LastChangedRevision$
*/
public class ContinuousColourScaleDialog {
private static final int SLIDER_RANGE = 1000;
private static final String HSB_SPECTRUM = "HSB Spectrum";
private static final String COLOUR_GRADIENT = "Colour Gradient";
private JFrame frame;
private JComboBox colourSchemeCombo = new JComboBox(new String[] { HSB_SPECTRUM, COLOUR_GRADIENT} );
CardLayout cardLayout = new CardLayout();
private final JPanel colourSchemePanel;
private Map<String, ColourSchemePanel> colourSchemeNamePanelMap = new HashMap<String, ColourSchemePanel>();
private Map<Class, String> colourSchemeClassNameMap = new HashMap<Class, String>();
private ContinuousColourDecorator decorator;
private JComponent colourDisplay;
private final JCheckBox autoScaleCheck;
private final JLabel fromLabel;
private final RealNumberField fromNumberField;
private final JLabel toLabel;
private final RealNumberField toNumberField;
private JDialog dialog;
public ContinuousColourScaleDialog(final JFrame frame) {
this.frame = frame;
colourSchemeNamePanelMap.put(HSB_SPECTRUM, new HSBColourSchemePanel());
colourSchemeNamePanelMap.put(COLOUR_GRADIENT, new InterpolatingColourSchemePanel());
colourSchemeClassNameMap.put(HSBContinuousColourDecorator.class, HSB_SPECTRUM);
colourSchemeClassNameMap.put(InterpolatingColourDecorator.class, COLOUR_GRADIENT);
autoScaleCheck = new JCheckBox("Auto-scale range between min and max values");
fromLabel = new JLabel("Range from:");
fromNumberField = new RealNumberField();
fromNumberField.setColumns(10);
toLabel = new JLabel("to:");
toNumberField = new RealNumberField();
toNumberField.setColumns(10);
autoScaleCheck.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent changeEvent) {
boolean enabled = !autoScaleCheck.isSelected();
fromLabel.setEnabled(enabled);
fromNumberField.setEnabled(enabled);
toLabel.setEnabled(enabled);
toNumberField.setEnabled(enabled);
}
});
colourSchemePanel = new JPanel(cardLayout);
for (String name : colourSchemeNamePanelMap.keySet()) {
ColourSchemePanel panel = colourSchemeNamePanelMap.get(name);
colourSchemePanel.add(panel.getPanel(), name);
}
colourSchemeCombo.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
cardLayout.show(colourSchemePanel, colourSchemeCombo.getSelectedItem().toString());
decorator = colourSchemeNamePanelMap.get(colourSchemeCombo.getSelectedItem()).getDecorator();
colourDisplay.repaint();
dialog.pack();
}
});
colourDisplay = new JComponent() {
private final static int MAX_HEIGHT = 20;
@Override
public void paint(Graphics graphics) {
ContinuousColourDecorator decorator = getDecorator();
Graphics2D g = (Graphics2D)graphics;
Rectangle r = getVisibleRect();
int width = r.width;
r.width = 1;
ContinuousScale scale = decorator.getContinuousScale();
double v = scale.getMinValue();
double d = (scale.getMaxValue() - v) / width;
for (int i = 0; i < width; i ++) {
g.setColor(decorator.getColourForValue(v));
g.fill(r);
r.x ++;
v += d;
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(super.getPreferredSize().width, MAX_HEIGHT);
}
};
}
public int showDialog() {
ContinuousColourDecorator currentDecorator = getDecorator();
final OptionsPanel options = new OptionsPanel(6, 6);
JPanel panel5 = new JPanel(new BorderLayout());
panel5.add(colourDisplay, BorderLayout.CENTER);
panel5.setBorder(BorderFactory.createLineBorder(Color.black, 1));
options.addSpanningComponent(panel5);
options.addComponent(autoScaleCheck);
JPanel panel4 = new JPanel();
panel4.setLayout(new FlowLayout());
panel4.add(fromLabel);
panel4.add(fromNumberField);
panel4.add(toLabel);
panel4.add(toNumberField);
options.addComponent(panel4);
options.addComponentWithLabel("Scheme: ", colourSchemeCombo);
colourSchemePanel.setBorder(BorderFactory.createBevelBorder(1));
options.addSpanningComponent(colourSchemePanel);
JOptionPane optionPane = new JOptionPane(options,
JOptionPane.QUESTION_MESSAGE,
JOptionPane.OK_CANCEL_OPTION,
null,
null,
null);
optionPane.setBorder(new EmptyBorder(12, 12, 12, 12));
dialog = optionPane.createDialog(frame, "Setup colour range: " + currentDecorator.getAttributeName());
colourSchemeCombo.setSelectedItem(colourSchemeClassNameMap.get(decorator.getClass()));
dialog.pack();
dialog.setResizable(false);
dialog.setVisible(true);
int result = JOptionPane.CANCEL_OPTION;
Integer value = (Integer)optionPane.getValue();
if (value != null && value.intValue() != -1) {
result = value.intValue();
}
return result;
}
public void setDecorator(ContinuousColourDecorator decorator) {
this.decorator = decorator;
autoScaleCheck.setSelected(!decorator.getContinuousScale().isNormalize());
fromNumberField.setValue(decorator.getContinuousScale().getLowerRange());
toNumberField.setValue(decorator.getContinuousScale().getUpperRange());
for (String key : colourSchemeNamePanelMap.keySet()) {
colourSchemeNamePanelMap.get(key).setDecorator(decorator);
}
}
public ContinuousColourDecorator getDecorator() {
String name = colourSchemeCombo.getSelectedItem().toString();
decorator = colourSchemeNamePanelMap.get(name).getDecorator();
decorator.getContinuousScale().setNormalize(!autoScaleCheck.isSelected());
decorator.getContinuousScale().setLowerRange(fromNumberField.getValue());
decorator.getContinuousScale().setUpperRange(toNumberField.getValue());
return decorator;
}
private interface ColourSchemePanel {
void setDecorator(ContinuousColourDecorator decorator);
ContinuousColourDecorator getDecorator();
JPanel getPanel();
String getName();
}
private class HSBColourSchemePanel implements ColourSchemePanel {
public HSBColourSchemePanel() {
hueSlider = new RangeSlider(0, SLIDER_RANGE);
saturationSlider = new RangeSlider(0, SLIDER_RANGE);
brightnessSlider = new RangeSlider(0, SLIDER_RANGE);
reverseHueCheck = new JCheckBox("Reverse hue spectrum");
}
public void setDecorator(ContinuousColourDecorator decorator) {
if (decorator instanceof HSBContinuousColourDecorator) {
hsbDecorator = (HSBContinuousColourDecorator)decorator;
} else {
if (hsbDecorator == null) {
hsbDecorator = new HSBContinuousColourDecorator(decorator.getContinuousScale());
}
}
hueSlider.setValue((int)(hsbDecorator.getHueLower() * SLIDER_RANGE));
hueSlider.setUpperValue((int) (hsbDecorator.getHueUpper() * SLIDER_RANGE));
saturationSlider.setValue((int)(hsbDecorator.getSaturationLower() * SLIDER_RANGE));
saturationSlider.setUpperValue((int)(hsbDecorator.getSaturationUpper() * SLIDER_RANGE));
brightnessSlider.setValue((int)(hsbDecorator.getBrightnessLower() * SLIDER_RANGE));
brightnessSlider.setUpperValue((int)(hsbDecorator.getBrightnessUpper() * SLIDER_RANGE));
reverseHueCheck.setSelected(hsbDecorator.isReverseHue());
}
@Override
public ContinuousColourDecorator getDecorator() {
hsbDecorator.setHueLower(((float) hueSlider.getValue()) / SLIDER_RANGE);
hsbDecorator.setHueUpper(((float) hueSlider.getUpperValue()) / SLIDER_RANGE);
hsbDecorator.setSaturationLower(((float) saturationSlider.getValue()) / SLIDER_RANGE);
hsbDecorator.setSaturationUpper(((float) saturationSlider.getUpperValue()) / SLIDER_RANGE);
hsbDecorator.setBrightnessLower(((float) brightnessSlider.getValue()) / SLIDER_RANGE);
hsbDecorator.setBrightnessUpper(((float) brightnessSlider.getUpperValue()) / SLIDER_RANGE);
hsbDecorator.setReverseHue(reverseHueCheck.isSelected());
return hsbDecorator;
}
@Override
public JPanel getPanel() {
if (panel == null) {
final OptionsPanel options = new OptionsPanel(6, 6);
options.addComponentWithLabel("Hue: ", hueSlider);
options.addComponentWithLabel("Saturation: ", saturationSlider);
options.addComponentWithLabel("Brightness: ", brightnessSlider);
options.addComponent(reverseHueCheck);
ChangeListener listener = new ChangeListener() {
public void stateChanged(ChangeEvent e) {
getDecorator();
colourDisplay.repaint();
}
};
hueSlider.addChangeListener(listener);
saturationSlider.addChangeListener(listener);
brightnessSlider.addChangeListener(listener);
reverseHueCheck.addChangeListener(listener);
panel = options;
}
return panel;
}
@Override
public String getName() {
return HSB_SPECTRUM;
}
private RangeSlider hueSlider;
private RangeSlider saturationSlider;
private RangeSlider brightnessSlider;
private JCheckBox reverseHueCheck;
private HSBContinuousColourDecorator hsbDecorator = null;
private JPanel panel = null;
}
private class InterpolatingColourSchemePanel implements ColourSchemePanel {
public InterpolatingColourSchemePanel() {
middleColourCheck = new JCheckBox("through:");
fromColourButton = new ColorWellButton(InterpolatingColourDecorator.DEFAULT_COLOR_1, "Choose Start Colour");
toColourButton = new ColorWellButton(InterpolatingColourDecorator.DEFAULT_COLOR_2, "Choose End Colour");
middleColourButton = new ColorWellButton(InterpolatingColourDecorator.DEFAULT_COLOR_3, "Choose Middle Colour");
middleColourCheck.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent changeEvent) {
middleColourButton.setEnabled(middleColourCheck.isSelected());
}
});
middleColourCheck.setSelected(false);
middleColourButton.setEnabled(false);
}
public void setDecorator(ContinuousColourDecorator decorator) {
if (decorator instanceof InterpolatingColourDecorator) {
this.interpolatingDecorator = (InterpolatingColourDecorator)decorator;
} else {
if (interpolatingDecorator == null) {
interpolatingDecorator = new InterpolatingColourDecorator(decorator.getContinuousScale());
}
}
fromColourButton.setSelectedColor(interpolatingDecorator.getColor1());
if (interpolatingDecorator.getColor3() != null) {
middleColourCheck.setSelected(true);
middleColourButton.setSelectedColor(interpolatingDecorator.getColor2());
toColourButton.setSelectedColor(interpolatingDecorator.getColor3());
} else {
middleColourCheck.setSelected(false);
toColourButton.setSelectedColor(interpolatingDecorator.getColor2());
}
}
@Override
public ContinuousColourDecorator getDecorator() {
if (middleColourCheck.isSelected()) {
interpolatingDecorator.setColours(
fromColourButton.getSelectedColor(),
middleColourButton.getSelectedColor(),
toColourButton.getSelectedColor());
} else {
interpolatingDecorator.setColours(
fromColourButton.getSelectedColor(),
toColourButton.getSelectedColor());
}
return interpolatingDecorator;
}
@Override
public JPanel getPanel() {
if (panel == null) {
final OptionsPanel options = new OptionsPanel(6, 6);
JPanel panel1 = new JPanel();
panel1.setLayout(new FlowLayout());
panel1.add(new JLabel("Colour gradient from:"));
panel1.add(fromColourButton);
panel1.add(new JLabel("to:"));
panel1.add(toColourButton);
options.addComponent(panel1);
JPanel panel2 = new JPanel();
panel1.setLayout(new FlowLayout());
panel1.add(middleColourCheck);
panel1.add(middleColourButton);
options.addComponent(panel2);
this.panel = options;
ChangeListener listener = new ChangeListener() {
public void stateChanged(ChangeEvent e) {
getDecorator();
colourDisplay.repaint();
}
};
fromColourButton.addChangeListener(listener);
toColourButton.addChangeListener(listener);
middleColourCheck.addChangeListener(listener);
middleColourButton.addChangeListener(listener);
}
return panel;
}
@Override
public String getName() {
return COLOUR_GRADIENT;
}
private ColorWellButton fromColourButton;
private ColorWellButton toColourButton;
private JCheckBox middleColourCheck;
private ColorWellButton middleColourButton;
private InterpolatingColourDecorator interpolatingDecorator = null;
private JPanel panel = null;
}
}