// Near Infinity - An Infinity Engine Browser and Editor
// Copyright (C) 2001 - 2005 Jon Olav Hauglid
// See LICENSE.txt for license information
package org.infinity.gui;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EventListener;
import java.util.EventObject;
import java.util.Iterator;
import java.util.List;
import javax.swing.JPanel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
* Implements a grid of selectable color indices.
*/
public class ColorGrid extends JPanel implements MouseListener, MouseMotionListener
{
/** Supported frame types for selected color entries. */
public enum Frame { SINGLE_LINE, DOUBLE_LINE }
/** Only one color entry can be selected at a time. */
public static final int SELECTION_SINGLE = 0;
/** One or more color entries can be selected at a time. */
public static final int SELECTION_MULTIPLE = 1;
private static final int DRAG_DISABLED = 0; // no drag&drop
private static final int DRAG_INITIALIZED = 1; // a potential drag&drop has been initiated
private static final int DRAG_ENABLED = 2; // drag&drop has been enabled
private static final int MaxColorCount = 65536;
private static final int DefaultColorCount = 16;
private static final int DefaultColorsPerRow = 16;
private static final int DefaultGap = 3;
private static final int DefaultSelectionMode = SELECTION_SINGLE;
private static final Dimension DefaultColorSize = new Dimension(16, 16);
private static final Color DefaultColor = Color.BLACK;
private static final Frame DefaultFrame = Frame.SINGLE_LINE;
private static final Color DefaultFrameColor = Color.BLUE;
private static final boolean DefaultDragDropEnabled = false;
// Defines the square of the minimum dragging distance before activating drag&drop mode
private static final int DragDropTriggerDistance2 = 16;
private final List<Color> listColors = new ArrayList<Color>();
private final List<ActionListener> listActionListeners = new ArrayList<ActionListener>();
private final List<MouseOverListener> listMouseOverListeners = new ArrayList<MouseOverListener>();
private final List<ChangeListener> listChangeListeners = new ArrayList<ChangeListener>();
// stores selected color entry indices
private final List<Integer> listSelection = new ArrayList<Integer>();
private Frame frame; // frame type
private Color frameColor; // frame color
private int colorsPerRow; // number of colors per row
private boolean readOnly; // indicates whether the user can interact with the color grid
private Dimension colorSize; // dimension of a single color entry
private int gapX, gapY; // horizontal and vertical gaps between color entries
private int currentMouseOverIndex; // current color index under the mouse cursor
private int selectionMode; // current selection mode
private boolean isDragDropEnabled;
private int DragDropMode; // current drag&drop mode
private Point pDragDropStart; // starting mouse position relative to color grid component
private int dragDropStart, dragDropCur; // initial and current color indices
public ColorGrid()
{
this(DefaultColorCount, null);
}
public ColorGrid(int colorCount)
{
this(colorCount, null);
}
public ColorGrid(int colorCount, Collection<Color> colors)
{
super(true);
init(colorCount, colors);
}
/**
* Adds an ActionListener to the component.
* ActionListener events will be triggered whenever a new color entry has been selected.
*/
public void addActionListener(ActionListener l)
{
if (l != null) {
if (listActionListeners.indexOf(l) < 0) {
listActionListeners.add(l);
}
}
}
/** Returns an array of all registered ActionListeners. */
public ActionListener[] getActionListeners()
{
ActionListener[] listeners = new ActionListener[listActionListeners.size()];
for (int i = 0; i < listActionListeners.size(); i++) {
listeners[i] = listActionListeners.get(i);
}
return listeners;
}
/** Removes an ActionListener from the component. */
public void removeActionListener(ActionListener l)
{
if (l != null) {
int idx = listActionListeners.indexOf(l);
if (idx >= 0) {
listActionListeners.remove(idx);
}
}
}
/**
* Adds a MouseOverListener to the component.
* MouseOverListener events will be triggered whenever the mouse cursor is over a new color entry.
*/
public void addMouseOverListener(MouseOverListener l)
{
if (l != null) {
if (listMouseOverListeners.indexOf(l) < 0) {
listMouseOverListeners.add(l);
}
}
}
/** Returns an array of all registered MouseOverListeners. */
public MouseOverListener[] getMouseOverListeners()
{
MouseOverListener[] listeners = new MouseOverListener[listMouseOverListeners.size()];
for (int i = 0; i < listMouseOverListeners.size(); i++) {
listeners[i] = listMouseOverListeners.get(i);
}
return listeners;
}
/** Removes a MouseOverListener from the component. */
public void removeMouseOverListener(MouseOverListener l)
{
if (l != null) {
int idx = listMouseOverListeners.indexOf(l);
if (idx >= 0) {
listMouseOverListeners.remove(idx);
}
}
}
/**
* Adds a ChangeListener to the component.
* ChangeListener events will be triggered whenever the palette definition of the grid has been
* modified by the user.
*/
public void addChangeListener(ChangeListener l)
{
if (l != null) {
if (listChangeListeners.indexOf(l) < 0) {
listChangeListeners.add(l);
}
}
}
/** Returns an array of all registered ChangeListeners. */
public ChangeListener[] getChangeListeners()
{
ChangeListener[] listeners = new ChangeListener[listChangeListeners.size()];
for (int i = 0; i < listChangeListeners.size(); i++) {
listeners[i] = listChangeListeners.get(i);
}
return listeners;
}
/** Removes a ChangeListener from the component. */
public void removeChangeListener(ChangeListener l)
{
if (l != null) {
int idx = listChangeListeners.indexOf(l);
if (idx >= 0) {
listChangeListeners.remove(idx);
}
}
}
/** Returns the currently active selection mode (one of SELECTION_SINGLE or SELECTION_MULTIPLE) */
public int getSelectionMode()
{
return selectionMode;
}
/**
* Sets the current color selection mode.
* @param mode Either one of {@link #SELECTION_SINGLE} or {@link #SELECTION_MULTIPLE}.
*/
public void setSelectionMode(int mode)
{
if (mode == SELECTION_SINGLE || mode == SELECTION_MULTIPLE && selectionMode != mode) {
selectionMode = mode;
if (selectionMode == SELECTION_SINGLE && !listSelection.isEmpty()) {
setSelectedIndex(listSelection.get(listSelection.size() - 1).intValue());
}
}
}
/** Returns whether the user can interact with the color grid (e.g. selecting color entries). */
public boolean isReadOnly()
{
return readOnly;
}
/** Specify whether the user can interact with the color grid (e.g. by selecting a color entry). */
public void setReadOnly(boolean set)
{
if (set != readOnly) {
readOnly = set;
if (getSelectedIndex() >= 0) {
setSelectedIndex(-1);
}
}
}
/** Returns whether drag&drop capability has been enabled. */
public boolean isDragDropEnabled()
{
return isDragDropEnabled;
}
/** Enables or disables the drag&drop capability of this color grid. */
public void setDragDropEnabled(boolean enable)
{
if (enable != isDragDropEnabled) {
isDragDropEnabled = enable;
}
}
/**
* Returns the number of colors displayed per row.
* @return the number of colors per row.
*/
public int getGridWidth()
{
return colorsPerRow;
}
/**
* Resizes the component to allow the specified number of color entries to be placed in a single row.
* @param width The number of color entries per row to display.
* @throws IllegalArgumentException if width is out of bounds.
*/
public void setGridWidth(int width)
{
if (width > 0 && width < getColorCount() && width != colorsPerRow) {
colorsPerRow = width;
updateSize();
} else {
throw new IllegalArgumentException("Invalid grid width: " + width);
}
}
/** Returns the dimension of a single color entry. */
public Dimension getColorEntrySize()
{
return new Dimension(colorSize);
}
/**
* Specify the size for each individual color entry displayed by the component.
* Specifying {@code null} restores the default size.
*/
public void setColorEntrySize(Dimension size)
{
if (size != null) {
if (size.width > 0 && size.height > 0) {
colorSize = new Dimension(size);
}
} else {
colorSize = new Dimension(DefaultColorSize);
}
updateSize();
}
/** Returns the horizontal gap between color entries. */
public int getColorEntryHorizontalGap()
{
return gapX;
}
/** Returns the vertical gap between color entries. */
public int getColorEntryVerticalGap()
{
return gapY;
}
/** Defines the horizintal gap between color entries. Default is 3 pixels. */
public void setColorEntryHorizontalGap(int gap)
{
if (gap >= 0 && gap != gapX) {
this.gapX = gap;
updateSize();
}
}
/** Defines the vertical gap between color entries. Default is 3 pixels. */
public void setColorEntryVerticalGap(int gap)
{
if (gap >= 0 && gap != gapY) {
this.gapY = gap;
updateSize();
}
}
/** Returns the number of defined colors. */
public int getColorCount()
{
return listColors.size();
}
/**
* Specify a new number of colors to be displayed by this component. New color entries are always
* filled with the default color "black".
* @param newCount The new number of colors to be displayed.
*/
public void setColorCount(int newCount)
{
if (newCount > 0 && newCount < MaxColorCount && newCount != getColorCount()) {
if (newCount < getColorCount()) {
// removing entries
while (listColors.size() > newCount) {
listColors.remove(listColors.size() - 1);
}
} else {
// adding entries
while (listColors.size() < newCount) {
listColors.add(DefaultColor);
}
}
updateSize();
}
}
/**
* Returns the color at the specified palette entry.
* @param index The index of the desired color entry.
* @return A Color object of the specified color entry.
* @throws IndexOutOfBoundsException if index is out of bounds.
*/
public Color getColor(int index)
{
if (index >= 0 && index < getColorCount()) {
return listColors.get(index);
} else {
throw new IndexOutOfBoundsException(String.format("%1$d out of bounds [0, $3$d]", index, getColorCount()));
}
}
/**
* Sets the value of a single color. (Note: alpha component will be ignored.)
* @param index The color entry to set.
* @param color The new color value.
*/
public void setColor(int index, Color color)
{
if (index >= 0 && index < getColorCount() && color != null) {
listColors.set(index, new Color(color.getRGB()));
repaint();
}
}
/**
* Sets the values of a number of colors, starting at a specified color index.
* @param index The start index for the color values to set
* @param colors An array of color values to set.
*/
public void setColor(int index, Color[] colors)
{
if (index >= 0 && index < getColorCount() && colors != null) {
int cnt = colors.length;
if (index + cnt > getColorCount()) {
cnt = getColorCount() - index;
}
for (int i = 0; i < cnt; i++) {
if (colors[i] != null) {
listColors.set(index, new Color(colors[i].getRGB()));
}
}
repaint();
}
}
/** Returns the frame type used for selected color entries. */
public Frame getSelectionFrame()
{
return frame;
}
/** Sets a new Frame type to show for selected color entries. */
public void setSelectionFrame(Frame frameType)
{
if (frameType != null && frameType != frame) {
this.frame = frameType;
repaint();
}
}
/** Returns the frame color */
public Color getSelectionFrameColor()
{
return frameColor;
}
/** Sets a new frame color. Specifying {@code null} sets the default frame color. */
public void setSelectionFrameColor(Color color)
{
if (color != null) {
frameColor = new Color(color.getRGB());
} else {
frameColor = DefaultFrameColor;
}
repaint();
}
/**
* Returns the color value at the selected color entry. In multiple selection mode only the last
* selected color value will be returned.
* Returns {@code null} if no color has been selected.
*/
public Color getSelectedColor()
{
if (!listSelection.isEmpty()) {
return listColors.get(listSelection.get(listSelection.size() - 1).intValue());
} else {
return null;
}
}
/**
* Returns a list of all selected color entries. Returns an empty array if no color has been
* selected.
*/
public Color[] getSelectedColors()
{
Color[] retVal = new Color[listSelection.size()];
for (int i = 0; i < listSelection.size(); i++) {
retVal[i] = listColors.get(listSelection.get(i).intValue());
}
return retVal;
}
/** Returns whether the specified color entry index is currently selected. */
public boolean isSelectedIndex(int index)
{
if (index >= 0 && index < getColorCount()) {
int idx = listSelection.indexOf(Integer.valueOf(index));
return (idx >= 0);
}
return false;
}
/** Returns {@code true} if nothing is selected, {@code false} otherwise. */
public boolean isSelectionEmpty()
{
return listSelection.isEmpty();
}
/**
* Returns the currently selected color in single selection mode or the last selected value
* in multiple selections mode..
* Returns -1 if no color has been selected.
*/
public int getSelectedIndex()
{
if (!listSelection.isEmpty()) {
return listSelection.get(listSelection.size() - 1).intValue();
} else {
return -1;
}
}
/**
* Returns the selected color entry indices as an array of integers.
* @return An array of all selected color entry indices.
*/
public int[] getSelectedIndices()
{
int[] retVal = new int[listSelection.size()];
for (int i = 0; i < listSelection.size(); i++) {
retVal[i] = listSelection.get(i).intValue();
}
return retVal;
}
/**
* Selects the color entry at the specified index. Previously selected entries will be
* unselected automatically.
*/
public void setSelectedIndex(int newIndex)
{
setSelectedIndices(new int[]{newIndex});
}
/** Selects the specified indices. Previously selected entries will be unselected automatically. */
public void setSelectedIndices(int[] indices)
{
listSelection.clear();
if (indices != null) {
for (int i = 0; i < indices.length; i++) {
if (indices[i] >= 0 && indices[i] < getColorCount()) {
int idx = listSelection.indexOf(Integer.valueOf(indices[i]));
if (idx < 0) {
listSelection.add(Integer.valueOf(indices[i]));
}
}
}
}
repaint();
}
/**
* Adds the specified index to the current selection in multiple selection mode.
* Behaves like {@link #setSelectedIndex(int)} in single selection.
*/
public void addSelectedIndex(int index)
{
addSelectedIndices(new int[]{index});
}
/**
* Adds the specified indices to the current selection in multiple selection mode.
* Behaves like {@link #setSelectedIndex(int)} in single selection, but selects only the last
* entry in the specified index array.
* @param indices Array of indices to select.
*/
public void addSelectedIndices(int[] indices)
{
if (getSelectionMode() == SELECTION_SINGLE) {
setSelectedIndex((indices != null && indices.length > 0) ? indices[indices.length - 1] : -1);
} else {
if (indices != null) {
for (int i = 0; i < indices.length; i++) {
if (indices[i] >= 0 && indices[i] < getColorCount()) {
int idx = listSelection.indexOf(Integer.valueOf(indices[i]));
if (idx < 0) {
listSelection.add(Integer.valueOf(indices[i]));
}
}
}
repaint();
}
}
}
/** Removes the specified index from the selection. */
public void removeSelectedIndex(int index)
{
removeSelectedIndices(new int[]{index});
}
/**
* Removes the specified list of indices from the selection.
* @param indices Array of indices to unselect.
*/
public void removeSelectedIndices(int[] indices)
{
if (indices != null) {
for (int i = 0; i < indices.length; i++) {
if (indices[i] >= 0 && indices[i] < getColorCount()) {
int idx = listSelection.indexOf(Integer.valueOf(indices[i]));
if (idx >= 0) {
listSelection.remove(idx);
}
}
}
repaint();
}
}
/** Clears all selected color entries. */
public void clearSelection()
{
if (!listSelection.isEmpty()) {
listSelection.clear();
repaint();
}
}
// Fires ActionListener events for all registered listeners
protected void fireActionListener()
{
ActionEvent event = new ActionEvent(this, 0, "", System.currentTimeMillis(), 0);
for (int i = 0; i < listActionListeners.size(); i++) {
listActionListeners.get(i).actionPerformed(event);
}
}
// Fires MouseOverListener events for all registered listeners
protected void fireMouseOverListener(int index)
{
MouseOverEvent event = new MouseOverEvent(this, index);
for (int i = 0; i < listMouseOverListeners.size(); i++) {
listMouseOverListeners.get(i).mouseOver(event);
}
}
// Fires ChangeListener events for all registered listeners
protected void fireChangeListener()
{
ChangeEvent event = new ChangeEvent(this);
for (int i = 0; i < listChangeListeners.size(); i++) {
listChangeListeners.get(i).stateChanged(event);
}
}
@Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
updateDisplay(g);
}
// First-time initializations
private void init(int count, Collection<Color> colors)
{
// setting default properties
frame = DefaultFrame;
frameColor = DefaultFrameColor;
colorsPerRow = DefaultColorsPerRow;
selectionMode = DefaultSelectionMode;
colorSize = (Dimension)DefaultColorSize.clone();
gapX = gapY = DefaultGap;
currentMouseOverIndex = -1;
isDragDropEnabled = DefaultDragDropEnabled;
// initializing color list
if (count < 1) count = 1; else if (count > MaxColorCount) count = MaxColorCount;
listColors.clear();
int idx = 0;
Iterator<Color> iter = null;
if (colors != null) {
iter = colors.iterator();
}
while (idx < count) {
if (iter != null && iter.hasNext()) {
Color c = iter.next();
if (c != null) {
listColors.add(new Color(c.getRGB()));
} else {
listColors.add(DefaultColor);
}
} else {
listColors.add(DefaultColor);
}
idx++;
}
updateSize();
addMouseListener(this);
addMouseMotionListener(this);
}
// Updates the dimension of the component
private void updateSize()
{
int colCount = (listColors.size()+colorsPerRow-1) / colorsPerRow;
int w = gapX + colorsPerRow*(gapX + colorSize.width);
int h = gapY + colCount*(gapY + colorSize.height);
setPreferredSize(new Dimension(w, h));
setMinimumSize(getPreferredSize());
validate();
repaint();
}
// Paints the component's content
private void updateDisplay(Graphics g)
{
if (g != null) {
// painting color boxes
for (int i = 0; i < getColorCount(); i++) {
int col = i % colorsPerRow;
int row = i / colorsPerRow;
int x = gapX + col*(gapX + colorSize.width);
int y = gapY + row*(gapY + colorSize.height);
g.setColor(listColors.get(i));
g.fillRect(x, y, colorSize.width, colorSize.height);
}
// painting frame around selected color entry
for (int i = 0; i < listSelection.size(); i++) {
int curIdx = listSelection.get(i).intValue();
int col = curIdx % colorsPerRow;
int row = curIdx / colorsPerRow;
int x = gapX + col*(gapX + colorSize.width);
int y = gapY + row*(gapY + colorSize.height);
g.setColor(frameColor);
switch (frame) {
case SINGLE_LINE:
{
g.drawRect(x - 2, y - 2, colorSize.width + 3, colorSize.height + 3);
break;
}
case DOUBLE_LINE:
{
g.drawRect(x - 1, y - 1, colorSize.width + 1, colorSize.height + 1);
g.drawRect(x - 3, y - 3, colorSize.width + 5, colorSize.height + 5);
break;
}
}
}
}
}
// Returns the color index at the specified coordinate
private int getColorIndexAt(Point coord)
{
if (coord != null && (coord.x - gapX) >= 0 && (coord.y - gapY) >= 0 &&
(coord.x - gapX) < getWidth() && (coord.y - gapY) < getHeight()) {
int col = (coord.x - gapX) / (gapX + colorSize.width);
int row = (coord.y - gapY) / (gapY + colorSize.height);
int x = gapX + col*(gapX + colorSize.width);
int y = gapY + row*(gapY + colorSize.height);
if (coord.x >= x && coord.x < x + colorSize.width && coord.y >= y && coord.y < y + colorSize.height) {
int index = row*colorsPerRow + col;
if (index >= 0 && index < listColors.size()) {
return index;
}
}
}
return -1;
}
// Returns whether a color drag is active
private boolean isColorDragEnabled()
{
return (DragDropMode != DRAG_DISABLED);
}
// Enables/disables the drag&drop mode
private void setColorDragEnabled(boolean enable, Point coords)
{
if (coords != null) {
if (isDragDropEnabled && DragDropMode == DRAG_DISABLED && enable) {
int idx = getColorIndexAt(coords);
if (idx >= 0) {
// enabling drag mode
DragDropMode = DRAG_INITIALIZED;
pDragDropStart = (Point)coords.clone();
dragDropStart = dragDropCur = idx;
}
} else if (!enable) {
// disabling drag mode and updating results
setCursor(Cursor.getDefaultCursor());
if (DragDropMode == DRAG_ENABLED) {
if (dragDropStart != dragDropCur) {
fireChangeListener();
}
}
DragDropMode = DRAG_DISABLED;
}
}
}
// Call whenever the mouse cursor moves
private void updateColorDrag(Point coords)
{
if (coords != null) {
if (DragDropMode == DRAG_INITIALIZED) {
int x = Math.abs(pDragDropStart.x - coords.x);
int y = Math.abs(pDragDropStart.y - coords.y);
if (x*x + y*y >= DragDropTriggerDistance2) {
DragDropMode = DRAG_ENABLED;
setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
}
}
if (DragDropMode == DRAG_ENABLED) {
if (coords.x >= 0 && coords.x < getWidth() &&
coords.y >= 0 && coords.y < getHeight()) {
updateColorLocation(getColorIndexAt(coords));
}
}
}
}
// Updates the locations of the colors in the grid
private void updateColorLocation(int newIndex)
{
if (DragDropMode == DRAG_ENABLED) {
if (newIndex >= 0 && dragDropCur != newIndex) {
// updating color grid
Color c = listColors.get(dragDropCur);
listColors.remove(dragDropCur);
listColors.add(newIndex, c);
boolean isSelected = isSelectedIndex(dragDropCur);
// updating selection list
for (int i = 0; i < listSelection.size(); i++) {
int idx = listSelection.get(i).intValue();
if (idx < dragDropCur && idx >= newIndex) {
listSelection.set(i, Integer.valueOf(idx+1));
} else if (idx > dragDropCur && idx <= newIndex) {
listSelection.set(i, Integer.valueOf(idx-1));
}
}
repaint();
if (isSelected) {
removeSelectedIndex(dragDropCur);
addSelectedIndex(newIndex);
}
dragDropCur = newIndex;
}
}
}
//--------------------- Begin Interface MouseListener ---------------------
@Override
public void mouseClicked(MouseEvent event)
{
}
@Override
public void mousePressed(MouseEvent event)
{
// selecting color entry
if (event.getSource() == this && event.getButton() == MouseEvent.BUTTON1) {
if (!isReadOnly()) {
int index = getColorIndexAt(event.getPoint());
if (getSelectionMode() == SELECTION_SINGLE) {
// single selection mode
if (!isSelectedIndex(index)) {
setSelectedIndex(index);
fireActionListener();
}
} else {
// multiple selection mode
if (isSelectedIndex(index)) {
removeSelectedIndex(index);
} else {
addSelectedIndex(index);
}
fireActionListener();
}
setColorDragEnabled(true, event.getPoint());
}
}
}
@Override
public void mouseReleased(MouseEvent event)
{
if (event.getSource() == this && event.getButton() == MouseEvent.BUTTON1) {
if (!isReadOnly()) {
setColorDragEnabled(false, event.getPoint());
}
}
}
@Override
public void mouseEntered(MouseEvent event)
{
}
@Override
public void mouseExited(MouseEvent event)
{
}
//--------------------- End Interface MouseListener ---------------------
//--------------------- Begin Interface MouseMotionListener ---------------------
@Override
public void mouseDragged(MouseEvent event)
{
if (event.getSource() == this) {
if (!isReadOnly() && isColorDragEnabled()) {
updateColorDrag(event.getPoint());
}
}
}
@Override
public void mouseMoved(MouseEvent event)
{
// detecting color entry under mouse cursor
if (event.getSource() == this) {
int index = getColorIndexAt(event.getPoint());
if (index != currentMouseOverIndex) {
currentMouseOverIndex = index;
fireMouseOverListener(currentMouseOverIndex);
}
}
}
//--------------------- End Interface MouseMotionListener ---------------------
//-------------------------- INNER CLASSES --------------------------
/** Defines an object which listens to MouseOverEvents. */
public interface MouseOverListener extends EventListener
{
public void mouseOver(MouseOverEvent event);
}
/** MouseOverEvent is used to notify listeners that the mouse has been placed over a specific color entry. */
public class MouseOverEvent extends EventObject
{
private int index;
/**
* Constructs a MouseOverEvent.
* @param source The source of the event.
* @param index The color index where the mouse cursor is currently located.
* Specify a negative value to indicate an invalid color entry.
*/
public MouseOverEvent(Object source, int index)
{
super(source);
if (index < 0) index = -1;
this.index = index;
}
/** Returns the color index. Or -1 when there is no color entry is under the mouse cursor. */
public int getColorIndex()
{
return index;
}
}
}