package tim.prune.gui.colour;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EtchedBorder;
import tim.prune.I18nManager;
import tim.prune.gui.GuiGridLayout;
import tim.prune.gui.colour.ColourerFactory.ColourerId;
/**
* Class to provide a gui panel for selecting a colourer
* including which colourer, the start/end colours and
* optionally the max number of colours
*/
public class ColourerSelectorPanel extends JPanel
{
/** Combo box for selecting the type of colourer */
private JComboBox<String> _typeCombo = null;
/** Array of type ids as stored in combo box */
private ColourerId[] _typeIds = null;
/** Panel object holding the colour patches */
private JPanel _patchPanel = null;
/** Array of colour patches for start and end */
private ColourPatch[] _startEndPatches = null;
/** Panel holding the max colours selection */
JPanel _maxColoursPanel = null;
private JComboBox<String> _maxColoursCombo = null;
/** Array of label keys for the 2 patches */
private static final String[] LABEL_KEYS = {"start", "end"};
/**
* Constructor
* @param inColourChooser colour chooser to use (needs reference to parent dialog)
*/
public ColourerSelectorPanel(ColourChooser inColourChooser)
{
_typeIds = new ColourerId[] {ColourerId.NONE, ColourerId.BY_FILE,
ColourerId.BY_SEGMENT, ColourerId.BY_DATE, ColourerId.BY_ALTITUDE,
ColourerId.BY_SPEED, ColourerId.BY_VSPEED, ColourerId.BY_GRADIENT};
makeGuiComponents(inColourChooser);
}
/**
* Create all the gui components and lay them out in the panel
* @param inColourChooser colour chooser to use
*/
private void makeGuiComponents(ColourChooser inColourChooser)
{
// Etched border and vertical layout
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(4, 4, 4, 4))
);
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
// Label at the top
JLabel introLabel = new JLabel(I18nManager.getText("dialog.colourer.intro"));
introLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
add(introLabel);
// Combo box for selecting which colourer to use
JPanel typePanel = new JPanel();
GuiGridLayout grid = new GuiGridLayout(typePanel);
final String keyPrefix = "dialog.colourer.type.";
String[] colourerTypes = new String[_typeIds.length];
for (int i=0; i<colourerTypes.length; i++)
{
colourerTypes[i] = I18nManager.getText(keyPrefix +
ColourerFactory.getDescriptionKey(_typeIds[i]));
}
_typeCombo = new JComboBox<String>(colourerTypes);
_typeCombo.setAlignmentX(Component.LEFT_ALIGNMENT);
_typeCombo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
onColourerTypeChanged();
}
});
// Add to the panel
grid.add(new JLabel(I18nManager.getText("dialog.colourer.type")));
grid.add(_typeCombo);
typePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
add(typePanel);
// Make panel for colour patches
_patchPanel = new JPanel();
_patchPanel.setLayout(new GridLayout());
_startEndPatches = new ColourPatch[2];
// Blank column
JPanel blankColumn = new JPanel();
ColourPatch blankPatch = new ColourPatch(Color.BLACK);
blankPatch.setVisible(false);
blankColumn.add(blankPatch);
_patchPanel.add(blankColumn);
// Loop over two columns of patches
for (int i=0; i<2; i++)
{
JPanel colPanel = new JPanel();
colPanel.setLayout(new BoxLayout(colPanel, BoxLayout.Y_AXIS));
// Top label and patch
colPanel.add(new JLabel(I18nManager.getText("dialog.colourer." + LABEL_KEYS[i])));
ColourPatch patch = new ColourPatch(Color.BLUE); // will be set by init() method shortly
patch.addMouseListener(new PatchListener(patch, inColourChooser));
colPanel.add(patch);
_startEndPatches[i] = patch;
// Add column to panel
colPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
_patchPanel.add(colPanel);
}
// Blank column
blankColumn = new JPanel();
blankPatch = new ColourPatch(Color.BLACK);
blankPatch.setVisible(false);
blankColumn.add(blankPatch);
_patchPanel.add(blankColumn);
_patchPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
add(_patchPanel);
// Combo box for selecting max colours
_maxColoursPanel = new JPanel();
grid = new GuiGridLayout(_maxColoursPanel);
grid.add(new JLabel(I18nManager.getText("dialog.colourer.maxcolours")));
String[] colourOptions = new String[] {"2", "3", "5", "10", "15"};
_maxColoursCombo = new JComboBox<String>(colourOptions);
grid.add(_maxColoursCombo);
_maxColoursPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
add(_maxColoursPanel);
}
/**
* Init the colours from the colourer (if possible) or the default colour
* @param inColourer current colourer object, or null
* @param inDefaultColour current colour for points
*/
public void init(PointColourer inColourer, Color inDefaultColour)
{
Color startColour = null, endColour = null;
if (inColourer != null)
{
selectColourerType(ColourerFactory.getId(inColourer));
startColour = inColourer.getStartColour();
endColour = inColourer.getEndColour();
_maxColoursCombo.setSelectedItem("" + inColourer.getMaxColours());
}
else
{
// no colourer, so default to 5 colours maximum
_maxColoursCombo.setSelectedIndex(2);
}
if (startColour == null) {startColour = inDefaultColour;}
if (endColour == null) {endColour = makeDefaultEndColour(inDefaultColour);}
if (startColour != null) {_startEndPatches[0].setBackground(startColour);}
if (endColour != null) {_startEndPatches[1].setBackground(endColour);}
onColourerTypeChanged(); // make sure gui is updated
}
/**
* Make a default end colour if there isn't one already defined
* @param inStartColour start colour
* @return end colour, with the hue shifted by a third from the start
*/
private static Color makeDefaultEndColour(Color inStartColour)
{
float[] defaultHSB = Color.RGBtoHSB(inStartColour.getRed(), inStartColour.getGreen(), inStartColour.getBlue(), null);
// add 120 degrees to the hue
defaultHSB[0] += (1.0f/3f);
return Color.getHSBColor(defaultHSB[0], defaultHSB[1], defaultHSB[2]);
}
/**
* React to the colourer type being changed
* by showing / hiding gui elements
*/
private void onColourerTypeChanged()
{
final ColourerId id = _typeIds[_typeCombo.getSelectedIndex()];
// Set visibility of controls according to whether they're needed for the selected type
_patchPanel.setVisible(ColourerFactory.areColoursRequired(id));
_maxColoursPanel.setVisible(ColourerFactory.isMaxColoursRequired(id));
}
/**
* @return the selected colourer object, or null
*/
public PointColourer getSelectedColourer()
{
final ColourerId id = _typeIds[_typeCombo.getSelectedIndex()];
return ColourerFactory.createColourer(id, _startEndPatches[0].getBackground(),
_startEndPatches[1].getBackground(), _maxColoursCombo.getSelectedItem().toString());
}
/**
* Select the appropriate item in the dropdown
* @param inId id of colourer to choose
*/
private void selectColourerType(ColourerId inId)
{
int selIndex = -1;
for (int i=0; i<_typeIds.length; i++)
{
if (_typeIds[i] == inId) {
selIndex = i;
break;
}
}
if (selIndex < 0) {
System.err.println("Id " + inId + " not found in _typeIds!");
}
else {
_typeCombo.setSelectedIndex(selIndex);
}
}
}