/*
* DiscreteColourScaleDialog.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.*;
import figtree.ui.components.RangeSlider;
import jam.panels.OptionsPanel;
import javax.activation.DataHandler;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DragSource;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
/**
* @author Andrew Rambaut
* @version $Id$
*
* $HeadURL$
*
* $LastChangedBy$
* $LastChangedDate$
* $LastChangedRevision$
*/
public class DiscreteColourScaleDialog {
private static final int SLIDER_RANGE = 1000;
private static final String HSB_SPECTRUM = "HSB Spectrum";
private static final String FIXED_COLOURS = "Fixed Colours";
private JFrame frame;
private JComboBox colourSchemeCombo = new JComboBox(new String[] { HSB_SPECTRUM, FIXED_COLOURS} );
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 DiscreteColourDecorator decorator;
private java.util.List<Object> discreteValues = null;
private JTable table;
private ColourTableModel tableModel;
private JDialog dialog;
public DiscreteColourScaleDialog(final JFrame frame) {
this.frame = frame;
colourSchemeNamePanelMap.put(HSB_SPECTRUM, new HSBColourSchemePanel());
colourSchemeNamePanelMap.put(FIXED_COLOURS, new FixedColourSchemePanel());
colourSchemeClassNameMap.put(HSBDiscreteColourDecorator.class, HSB_SPECTRUM);
colourSchemeClassNameMap.put(FixedDiscreteColourDecorator.class, FIXED_COLOURS);
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();
tableModel.fireTableDataChanged();
dialog.pack();
}
});
tableModel = new ColourTableModel();
table = new JTable(tableModel);
table.setDefaultRenderer(Color.class, new ColorRenderer(true));
table.setDefaultRenderer(Paint.class, new ColorRenderer(true));
table.setDragEnabled(true);
table.setDropMode(DropMode.INSERT_ROWS);
table.setTransferHandler(new TableRowTransferHandler(table));
table.setRowSelectionAllowed(true);
table.setColumnSelectionAllowed(false);
table.getColumnModel().getColumn(1).setWidth(80);
table.getColumnModel().getColumn(1).setMinWidth(80);
table.getColumnModel().getColumn(1).setMaxWidth(80);
table.getColumnModel().getColumn(1).setResizable(false);
}
public int showDialog() {
DiscreteColourDecorator currentDecorator = getDecorator();
final OptionsPanel options = new OptionsPanel(6, 6);
// options.addSpanningComponent(new JLabel("Editing colour range for attribute: " + decorator.getAttributeName()));
JScrollPane scrollPane = new JScrollPane(table);
scrollPane.setMinimumSize(new Dimension(120, 120));
options.addSpanningComponent(scrollPane);
options.addComponentWithLabel("Scheme: ", colourSchemeCombo);
colourSchemePanel.setBorder(BorderFactory.createBevelBorder(1));
options.addSpanningComponent(colourSchemePanel);
// setDecorator(currentDecorator);
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(currentDecorator.getClass()));
dialog.pack();
dialog.setResizable(true);
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(DiscreteColourDecorator decorator) {
// this.decorator = decorator;
discreteValues = new ArrayList<Object>(decorator.getValues());
for (String key : colourSchemeNamePanelMap.keySet()) {
colourSchemeNamePanelMap.get(key).setDecorator(decorator);
}
}
public DiscreteColourDecorator getDecorator() {
String name = colourSchemeCombo.getSelectedItem().toString();
DiscreteColourDecorator decorator = colourSchemeNamePanelMap.get(name).getDecorator();
decorator.setValuesOrder(discreteValues);
return decorator;
}
interface Reorderable {
public void reorder(java.util.List<Integer> sourceIndices, int destinationIndex);
};
class ColourTableModel extends DefaultTableModel implements Reorderable {
private final String[] COLUMN_NAMES = { "Value", "Colour" };
@Override
public int getRowCount() {
if (discreteValues == null) return 0;
return discreteValues.size();
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public String getColumnName(int column) {
return COLUMN_NAMES[column];
}
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
@Override
public Object getValueAt(int row, int column) {
switch (column) {
case 0:
return discreteValues.get(row);
case 1:
DiscreteColourDecorator decorator = getDecorator();
// until the OK button is pressed the trait values are not actually
// reordered so use the default colour order.
return decorator.getColor(decorator.getValues().get(row));
}
return null;
}
@Override
public Class<?> getColumnClass(int column) {
if (column == 1) {
return Color.class;
}
return String.class;
}
@Override
public void setValueAt(Object o, int row, int column) {
}
@Override
public void reorder(java.util.List<Integer> sourceIndices, int destinationIndex) {
java.util.List<Object> itemsToMove = new ArrayList<Object>();
for (int src : sourceIndices) {
itemsToMove.add(discreteValues.get(src));
}
for (int i = sourceIndices.size() - 1; i >= 0; i--) {
int src = sourceIndices.get(i);
discreteValues.remove(src);
if (destinationIndex > src) {
destinationIndex -= 1;
}
}
discreteValues.addAll(destinationIndex, itemsToMove);
fireTableDataChanged();
}
}
public class ColorRenderer extends JLabel
implements TableCellRenderer {
Border unselectedBorder = null;
Border selectedBorder = null;
boolean isBordered = true;
public ColorRenderer(boolean isBordered) {
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) {
Color newColor = (Color)color;
setBackground(newColor);
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);
}
}
setToolTipText("RGB value: " + newColor.getRed() + ", "
+ newColor.getGreen() + ", "
+ newColor.getBlue());
return this;
}
}
/**
* Handles drag & drop row reordering
*/
public class TableRowTransferHandler extends TransferHandler {
// private final DataFlavor localObjectFlavor = new ActivationDataFlavor(Integer.class, DataFlavor.javaJVMLocalObjectMimeType, "Integer Row Index");
private final DataFlavor localObjectFlavor = new DataFlavor(ArrayList.class, "Integer Row Index");
private JTable table = null;
public TableRowTransferHandler(JTable table) {
this.table = table;
}
@Override
protected Transferable createTransferable(JComponent c) {
assert (c == table);
java.util.List<Integer> selectedRows = new ArrayList<Integer>();
for (int row : table.getSelectedRows()) {
selectedRows.add(row);
}
return new DataHandler(selectedRows, localObjectFlavor.getMimeType());
}
@Override
public boolean canImport(TransferHandler.TransferSupport info) {
boolean b = info.getComponent() == table && info.isDrop() && info.isDataFlavorSupported(localObjectFlavor);
table.setCursor(b ? DragSource.DefaultMoveDrop : DragSource.DefaultMoveNoDrop);
return b;
}
@Override
public int getSourceActions(JComponent c) {
return TransferHandler.MOVE;
}
@Override
public boolean importData(TransferHandler.TransferSupport info) {
JTable target = (JTable) info.getComponent();
JTable.DropLocation dl = (JTable.DropLocation) info.getDropLocation();
int index = dl.getRow();
int max = table.getModel().getRowCount();
if (index < 0 || index > max)
index = max;
target.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
try {
java.util.List<Integer> selectedRows = (java.util.List)info.getTransferable().getTransferData(localObjectFlavor);
if (selectedRows != null && selectedRows.size() > 0) {
((Reorderable)table.getModel()).reorder(selectedRows, index);
for (int row : selectedRows) {
if (index > row)
index--;
}
target.getSelectionModel().addSelectionInterval(index, index + selectedRows.size() - 1);
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@Override
protected void exportDone(JComponent c, Transferable t, int act) {
if (act == TransferHandler.MOVE) {
table.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
}
private interface ColourSchemePanel {
void setDecorator(DiscreteColourDecorator decorator);
DiscreteColourDecorator 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);
}
public void setDecorator(DiscreteColourDecorator decorator) {
if (decorator instanceof HSBDiscreteColourDecorator) {
hsbDecorator = (HSBDiscreteColourDecorator)decorator;
} else {
if (hsbDecorator == null) {
hsbDecorator = new HSBDiscreteColourDecorator(decorator.getAttributeName());
}
}
settingValues = true;
secondaryCountSpinnerModel.setValue(hsbDecorator.getSecondaryCount());
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));
settingValues = false;
getDecorator();
tableModel.fireTableDataChanged();
}
@Override
public DiscreteColourDecorator getDecorator() {
hsbDecorator.setSecondaryCount(secondaryCountSpinnerModel.getNumber().intValue());
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);
return hsbDecorator;
}
private boolean settingValues = false;
@Override
public JPanel getPanel() {
if (panel == null) {
final OptionsPanel options = new OptionsPanel(6, 6);
options.addComponentWithLabel("Primary: ", primaryAxisCombo);
options.addComponentWithLabel("Secondary count: ", secondaryCountSpinner);
options.addComponentWithLabel("Hue: ", hueSlider);
options.addComponentWithLabel("Saturation: ", saturationSlider);
options.addComponentWithLabel("Brightness: ", brightnessSlider);
primaryAxisCombo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
getDecorator();
tableModel.fireTableDataChanged();
}
});
ChangeListener listener = new ChangeListener() {
public void stateChanged(ChangeEvent e) {
if (!settingValues) {
getDecorator();
tableModel.fireTableDataChanged();
}
}
};
secondaryCountSpinner.addChangeListener(listener);
hueSlider.addChangeListener(listener);
saturationSlider.addChangeListener(listener);
brightnessSlider.addChangeListener(listener);
panel = options;
}
return panel;
}
@Override
public String getName() {
return HSB_SPECTRUM;
}
private JComboBox primaryAxisCombo = new JComboBox(HSBDiscreteColourDecorator.Axis.values());
private SpinnerNumberModel secondaryCountSpinnerModel = new SpinnerNumberModel(2, 1, 100, 1);
private JSpinner secondaryCountSpinner = new JSpinner(secondaryCountSpinnerModel);
private RangeSlider hueSlider;
private RangeSlider saturationSlider;
private RangeSlider brightnessSlider;
private HSBDiscreteColourDecorator hsbDecorator = null;
private JPanel panel = null;
}
private class FixedColourSchemePanel implements ColourSchemePanel {
public FixedColourSchemePanel() {
}
public void setDecorator(DiscreteColourDecorator decorator) {
if (decorator instanceof FixedDiscreteColourDecorator) {
fixedDecorator = (FixedDiscreteColourDecorator)decorator;
} else {
if (fixedDecorator == null) {
fixedDecorator = new FixedDiscreteColourDecorator(decorator.getAttributeName());
}
}
fixedDecorator.setValues(discreteValues);
}
@Override
public DiscreteColourDecorator getDecorator() {
return fixedDecorator;
}
@Override
public JPanel getPanel() {
if (panel == null) {
final OptionsPanel options = new OptionsPanel(6, 6);
panel = options;
}
return panel;
}
@Override
public String getName() {
return FIXED_COLOURS;
}
private FixedDiscreteColourDecorator fixedDecorator = null;
private JPanel panel = null;
}
}