/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2009-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, Geomatys
*
* 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;
* version 2.1 of the License.
*
* 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.
*/
package org.geotoolkit.gui.swing.image;
import java.util.Locale;
import java.text.ParseException;
import java.awt.Font;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.IndexColorModel;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JSplitPane;
import javax.swing.JScrollPane;
import javax.swing.JColorChooser;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.colorchooser.ColorSelectionModel;
import org.geotoolkit.gui.swing.Dialog;
import org.geotoolkit.gui.swing.ListTableModel;
import org.geotoolkit.internal.swing.SwingUtilities;
import org.geotoolkit.internal.swing.table.LabeledRenderer;
import org.apache.sis.util.collection.IntegerList;
import org.geotoolkit.resources.Vocabulary;
/**
* A chooser for an arbitrary amount of colors. The list of selected colors is displayed on the
* left side and a {@linkplain JColorChooser color chooser} is displayed on the right side.
* Only opaque colors can be selected in the current implementation.
* <p>
* This widget is typically used for creating an {@link IndexColorModel}, or (which is the same
* in a more indirect way) the colors to be given to a {@link org.geotoolkit.coverage.Category}
* object.
*
* @author Martin Desruisseaux (Geomatys)
* @version 3.12
*
* @since 3.00
* @module
*/
@SuppressWarnings("serial")
public class MultiColorChooser extends JComponent implements Dialog {
/**
* The table of colors.
*/
private final Colors colors;
/**
* Creates a new, initially empty, list of colors.
*/
public MultiColorChooser() {
setLayout(new BorderLayout());
final Locale locale = getLocale();
final Vocabulary resources = Vocabulary.getResources(locale);
/*
* Create the color chooser.
*/
final JColorChooser chooser = new JColorChooser();
/*
* Create the list of selected colors.
*/
final Colors colors = new Colors(resources);
this.colors = colors;
final JTable colorsTable = new JTable(colors);
colorsTable.setDefaultRenderer(Color.class, new CellRenderer());
colorsTable.setDefaultRenderer(Integer.class, new LabeledRenderer.Numeric(locale, true));
colorsTable.getColumnModel().getColumn(0).setPreferredWidth(30);
final JScrollPane scroll = new JScrollPane(colorsTable);
final JLabel title = new JLabel (resources.getString(Vocabulary.Keys.SelectedColors), JLabel.CENTER);
final JButton add = new JButton(resources.getString(Vocabulary.Keys.Add));
final JButton remove = new JButton(resources.getString(Vocabulary.Keys.Remove));
final JPanel buttons = new JPanel(new GridLayout(1,2));
buttons.add(add);
buttons.add(remove);
final JPanel colorsPanel = new JPanel(new BorderLayout());
scroll.setPreferredSize(new Dimension(160, 200));
colorsPanel.add(title, BorderLayout.NORTH);
colorsPanel.add(scroll, BorderLayout.CENTER);
colorsPanel.add(buttons, BorderLayout.SOUTH);
/*
* Create the split pane which will contains the above pane.
*/
final JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, colorsPanel, chooser);
split.setContinuousLayout(true);
add(split, BorderLayout.CENTER);
/*
* Register the listeners.
*/
final ColorSelectionModel select = chooser.getSelectionModel();
final class Listeners implements ActionListener {
@Override public void actionPerformed(final ActionEvent event) {
if (event.getSource() == remove) {
colors.remove(colorsTable.getSelectedRows());
} else {
colors.add(select.getSelectedColor());
}
}
}
final Listeners listeners = new Listeners();
add.addActionListener(listeners);
remove.addActionListener(listeners);
}
/**
* Returns the selected colors, or an empty array if none.
*
* @return The selected colors.
*/
public Color[] getSelectedColors() {
return colors.getColors();
}
/**
* The table model for the colors table. This table have two columns; the first one is the row
* number and the second one is the RGB code in hexadecimal. The type of the second color is
* {@link Color}, consequently the table needs the custom {@link CellRenderer} in order to
* display the expected text in the cells.
*
* @author Martin Desruisseaux (Geomatys)
* @version 3.00
*
* @since 3.00
* @module
*/
private static final class Colors extends ListTableModel<Integer> {
/**
* For cross-version compatibility.
*/
private static final long serialVersionUID = 9063526691205174844L;
/**
* Localized column titles.
*/
private final String[] titles;
/**
* Creates a default set of subsampling values.
*/
Colors(final Vocabulary resources) {
super(Integer.class, new IntegerList(8, 0xFFFFFF));
titles = new String[] {
resources.getString(Vocabulary.Keys.Index),
resources.getString(Vocabulary.Keys.Colors) + " (RGB)"
};
}
/**
* Adds the given color at the end of the current list.
*/
final void add(final Color color) {
final IntegerList elements = (IntegerList) this.elements;
final int n = elements.size();
elements.addInt(color.getRGB() & 0xFFFFFF);
fireTableRowsInserted(n, n);
}
/**
* Returns the colors in this table.
*/
public Color[] getColors() {
final IntegerList elements = (IntegerList) this.elements;
final Color[] colors = new Color[elements.size()];
for (int i=0; i<colors.length; i++) {
colors[i] = new Color(elements.getInt(i));
}
return colors;
}
/**
* Returns the number of row.
*/
@Override
public int getRowCount() {
return elements.size();
}
/**
* Returns the number of columns.
*/
@Override
public int getColumnCount() {
return titles.length;
}
/**
* Returns the name of the given column.
*/
@Override
public String getColumnName(final int column) {
return titles[column];
}
/**
* Returns the type of data in the given column.
*/
@Override
public Class<?> getColumnClass(final int columnIndex) {
return (columnIndex == 0) ? Integer.class : Color.class;
}
/**
* Returns the value in the given cell.
*/
@Override
public Object getValueAt(final int rowIndex, final int columnIndex) {
if (columnIndex == 0) {
return rowIndex + 1;
}
final IntegerList elements = (IntegerList) this.elements;
return new Color(elements.getInt(rowIndex));
}
}
/**
* The renderer to use for columns of type {@link Color} in the the table of colors.
*
* @author Martin Desruisseaux (Geomatys)
* @version 3.00
*
* @since 3.00
* @module
*/
private static final class CellRenderer extends DefaultTableCellRenderer {
/**
* For cross-version compatibility.
*/
private static final long serialVersionUID = -5368741385192560217L;
/**
* The font to use for the cells to be rendered.
*/
private final Font font;
/**
* Creates a cell renderer for the color codes.
*/
public CellRenderer() {
setHorizontalAlignment(CENTER);
font = Font.decode("Monospaced");
}
/**
* Returns the renderer to use for rendering a cell.
*/
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column)
{
final Color color = (Color) value;
int ARGB = color.getRGB();
final Color background, foreground;
if (!isSelected && !hasFocus) {
final int m = (ARGB & 0xFF) + ((ARGB >>> 8) & 0xFF) + ((ARGB >>> 16) & 0xFF);
background = color;
foreground = (m >= 0x180) ? Color.BLACK : Color.WHITE;
} else {
background = null;
foreground = null;
}
setBackground(background);
setForeground(foreground);
/*
* Formats the color as a hexadecimal string. At the difference of
* Integer.toHexString(ARGB), we format exactly 6 digits no matter
* the amount of leading zeros. We also insert spaces between R, G
* and B components.
*/
final StringBuilder buffer = new StringBuilder(9);
for (int i=0; i<6; i++) {
if (i == 2 || i == 4) {
buffer.insert(0, ' ');
}
char c = (char) (ARGB & 0xF);
c += (c >= 10 ? ('A'-10) : '0');
buffer.insert(0, c);
ARGB >>>= 4;
}
value = buffer.toString();
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
setFont(font); // Must be invoked here because super.getTable... overwrites the font.
return this;
}
}
/**
* {@inheritDoc}
*
* @since 3.12
*/
@Override
public void commitEdit() throws ParseException {
}
/**
* {@inheritDoc}
*/
@Override
public boolean showDialog(final Component owner, final String title) {
return SwingUtilities.showDialog(owner, this, title);
}
}