/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2010-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2010-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.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.ComboBoxModel;
import javax.swing.JList;
import javax.swing.JTable;
import javax.swing.JComponent;
import javax.swing.DefaultListCellRenderer;
import javax.swing.table.TableCellRenderer;
import org.geotoolkit.image.palette.PaletteFactory;
import org.geotoolkit.internal.swing.ColorRamp;
import org.geotoolkit.internal.coverage.ColorPalette;
/**
* The cell renderer for palette names in a table or a combo box.
* This renderer can paint any of the following:
* <p>
* <ul>
* <li>A {@link String} as an ordinary label.</li>
* <li>A {@link ColorPalette} as the name of a color palette. The colors are fetched
* using the {@link PaletteFactory} given at construction time.</li>
* <li>A uniform (usually opaque) {@link Color}.</li>
* </ul>
*
* @author Martin Desruisseaux (Geomatys)
* @version 3.16
*
* @since 3.14 (derived from 3.13)
* @module
*/
@SuppressWarnings("serial")
final class PaletteCellRenderer extends DefaultListCellRenderer implements TableCellRenderer {
/**
* The factory to use for fetching the palette colors.
*/
private final PaletteFactory factory;
/**
* The object on which to delegate the paint operations.
*/
private final ColorRamp painter;
/**
* The currently selected item. Used in order to avoid setting the color palette twice.
*/
private transient Object currentSelection;
/**
* {@code true} if this renderer should paint as a color ramp, or {@code false}
* to paint as a label.
*/
private transient boolean paintColors;
/**
* The available choices of colors. Shall contain instances of {@link ColorPalette}
* or {@link Color}. Other types (especially {@link String}) will be ignored.
*/
private final ComboBoxModel<?> choices;
/**
* Creates a new list for the given factory.
*
* @param choices The available choices of colors.
* @param factory The factory to use for fetching color palettes from their name.
* @param locale The locale to use for formatting error messages and graduation labels.
*/
public PaletteCellRenderer(final ComboBoxModel<?> choices, final PaletteFactory factory, final Locale locale) {
this.choices = choices;
this.factory = factory;
painter = new ColorRamp();
painter.setLocale(locale);
painter.labelVisibles = false;
setPreferredSize(new Dimension(100, 20));
setHorizontalAlignment(CENTER);
}
/**
* Configures a table cell for rendering the given value. This method first converts the
* given value (usually a {@link String}) to a {@link Color} or {@link ColorPalette}
* value.
*
* @return Always {@code this}.
*/
@Override
public Component getTableCellRendererComponent(final JTable table, Object value,
final boolean isSelected, final boolean hasFocus, final int row, final int column)
{
if (value != null) {
final String name = value.toString().trim();
if (name.startsWith("#")) try {
value = Color.decode(name);
} catch (NumberFormatException e) {
// Ignore: we will format the name as a string.
} else {
int i = choices.getSize();
while (--i != 0) {
final Object candidate = choices.getElementAt(i);
if (candidate instanceof ColorPalette && name.equals(((ColorPalette) candidate).paletteName)) {
value = candidate;
break;
}
}
}
}
configure(table, value, isSelected);
if (!paintColors) {
// Render a text label.
setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
setForeground(isSelected ? table.getSelectionForeground() : table.getForeground());
setFont(table.getFont());
setText((value == null) ? "" : value.toString());
setEnabled(table.isEnabled());
setBorder(noFocusBorder);
}
return this;
}
/**
* Configures a list cell for rendering the given value.
*
* @return Always {@code this}.
*/
@Override
public Component getListCellRendererComponent(final JList<?> list, final Object value,
final int index, final boolean isSelected, final boolean cellHasFocus)
{
configure(list, value, isSelected);
if (paintColors) {
return this;
}
return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
}
/**
* Configures this component for rendering the given value. If the value is an instance of
* {@link ColorPalette}, then this method will loads the colors using the palette factory when
* first needed. If the colors can not be loaded, then a warning message is logged and a
* gray scale color palette is used.
* <p>
* This method sets {@link #paintColors} to {@code true} if the value can be rendered as a
* color ramp.
*
* @param component The {@link JList} or {@link JTable} instance.
* @param value The item to be rendered.
* @param isSelected {@code true} if the item is on the selected row.
*/
private void configure(final JComponent component, final Object value, final boolean isSelected) {
final Object oldValue = currentSelection;
currentSelection = value;
final boolean isColorPalette = (value instanceof ColorPalette);
paintColors = isColorPalette || (value instanceof Color);
if (paintColors) {
final Color background, foreground;
if (!isSelected) {
background = component.getBackground();
foreground = component.getForeground();
} else if (component instanceof JList) {
final JList<?> list = (JList<?>) component;
background = list.getSelectionBackground();
foreground = list.getSelectionForeground();
} else {
final JTable table = (JTable) component;
background = table.getSelectionBackground();
foreground = table.getSelectionForeground();
}
setBackground(background);
setForeground(foreground);
if (!value.equals(oldValue)) {
setText(null);
final Color[] colors;
if (isColorPalette) {
colors = ((ColorPalette) value).getColors(factory);
} else {
colors = new Color[] {(Color) value};
}
painter.setColors(colors);
}
setBorder(noFocusBorder);
setEnabled(component.isEnabled());
}
}
/**
* Paints the color ramp.
*
* @param graphics The graphics to use for painting the color palette.
*/
@Override
public void paintComponent(final Graphics graphics) {
super.paintComponent(graphics);
if (paintColors && painter.hasColors()) {
final Rectangle bounds = getBounds();
bounds.x = 6;
bounds.y = 3;
bounds.width -= 12;
bounds.height -= 6;
painter.paint((Graphics2D) graphics, bounds, getFont(), getForeground());
}
}
}