package org.geogebra.desktop.gui.color;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.colorchooser.AbstractColorChooserPanel;
import org.geogebra.common.awt.GColor;
import org.geogebra.common.main.GeoGebraColorConstants;
import org.geogebra.common.main.Localization;
import org.geogebra.desktop.awt.GColorD;
import org.geogebra.desktop.gui.util.LayoutUtil;
import org.geogebra.desktop.main.AppD;
import org.geogebra.desktop.util.GuiResourcesD;
/**
* A color swatch chooser panel for GeoGebra.
*
* @author G. Sturr
*/
public class GeoGebraColorChooserPanel extends AbstractColorChooserPanel {
private static final long serialVersionUID = 1L;
protected AppD app;
protected GeoGebraColorChooser enclosingChooser;
protected GeoGebraColorChooserPanel myChooser;
protected MainSwatchPanel mainSwatchPanel;
protected RecentSwatchPanel recentSwatchPanel;
protected PrimarySwatchPanel primarySwatchPanel;
protected CustomSwatchPanel customSwatchPanel;
protected SwatchListener swatchListener;
protected ArrayList<SwatchPanel> swatchPanelList;
private JButton btnCustomColor;
private JLabel lblRecent, lblCustom;
private JPanel recentPanel, customPanel;
protected static final int largeSwatchSize = 16;
protected static final int smallSwatchSize = 14;
/**********************************************************
* Constructs a color chooser panel
*
* @param app
*/
public GeoGebraColorChooserPanel(AppD app) {
super();
this.app = app;
this.myChooser = this;
}
@Override
public String getDisplayName() {
return UIManager.getString("ColorChooser.swatchesNameText");
}
@Override
public int getMnemonic() {
// return getInt("ColorChooser.swatchesMnemonic", -1);
return -1;
}
@Override
public int getDisplayedMnemonicIndex() {
// return getInt("ColorChooser.swatchesDisplayedMnemonicIndex", -1);
return -1;
}
@Override
public Icon getSmallDisplayIcon() {
return null;
}
@Override
public Icon getLargeDisplayIcon() {
return null;
}
@Override
public void installChooserPanel(JColorChooser enclosingChooser) {
super.installChooserPanel(enclosingChooser);
this.enclosingChooser = (GeoGebraColorChooser) enclosingChooser;
}
@Override
protected void buildChooser() {
// create the swatch panels and other GUI elements
createGUIElements();
// create a GridBagLayout
GridBagLayout gb = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.weightx = 1.0;
gbc.insets = new Insets(0, 0, 0, 10);
// create a panel to hold all of our GUI
JPanel mainPanel = new JPanel(gb);
// add the primary swatch panel on the far left
gbc.gridheight = 2;
gbc.gridx = 0;
gbc.gridy = 0;
mainPanel.add(primarySwatchPanel, gbc);
// add the main swatch panel in the middle
gbc.gridheight = 2;
gbc.gridx = 1;
gbc.gridy = 0;
mainPanel.add(mainSwatchPanel, gbc);
// stack the recent panel, custom panel and RGB button on the far right
gbc.insets = new Insets(0, 0, 0, 0);
gbc.weighty = 0;
gbc.gridheight = 1;
gbc.gridx = 2;
gbc.gridy = 0;
mainPanel.add(recentPanel, gbc);
gbc.insets = new Insets(10, 0, 0, 0);
gbc.weighty = 1;
gbc.gridx = 2;
gbc.gridy = 1;
mainPanel.add(customPanel, gbc);
add(mainPanel);
}
private void createGUIElements() {
// create the swatch panels
mainSwatchPanel = new MainSwatchPanel();
recentSwatchPanel = new RecentSwatchPanel();
primarySwatchPanel = new PrimarySwatchPanel();
customSwatchPanel = new CustomSwatchPanel();
swatchPanelList = new ArrayList<SwatchPanel>();
swatchPanelList.add(mainSwatchPanel);
swatchPanelList.add(primarySwatchPanel);
swatchPanelList.add(recentSwatchPanel);
swatchPanelList.add(customSwatchPanel);
// create a mouse listener and register it with each swatch panel
swatchListener = new SwatchListener();
for (SwatchPanel sp : swatchPanelList) {
sp.addMouseListener(swatchListener);
}
// add borders to the swatch panels
Border border = BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(Color.gray, 1),
BorderFactory.createLineBorder(Color.white, 1));
for (SwatchPanel sp : swatchPanelList) {
sp.setBorder(border);
}
// create a panel with label to contain the recent swatch panel
recentPanel = new JPanel(new BorderLayout());
lblRecent = new JLabel();
lblRecent.setLabelFor(recentSwatchPanel);
recentPanel.add(lblRecent, BorderLayout.NORTH);
recentPanel.add(recentSwatchPanel, BorderLayout.CENTER);
recentPanel.setMaximumSize(getPreferredSize());
// create a button to open a RGB color chooser for custom colors
btnCustomColor = new JButton(app.getImageIcon(GuiResourcesD.LIST_ADD));
btnCustomColor.addActionListener(new CustomButtonActionListener());
btnCustomColor.setPreferredSize(new Dimension(24, 18));
btnCustomColor.setFocusPainted(false);
// create a panel with label to contain the custom swatch panel
lblCustom = new JLabel();
lblCustom.setLabelFor(customSwatchPanel);
customPanel = new JPanel(new BorderLayout());
customPanel.add(lblCustom, BorderLayout.NORTH);
customPanel.add(customSwatchPanel, BorderLayout.CENTER);
customPanel.add(LayoutUtil.flowPanel(0, 2, 0, btnCustomColor),
BorderLayout.SOUTH);
customPanel.setMaximumSize(getPreferredSize());
// set the labels
setLabels();
}
@Override
public void uninstallChooserPanel(JColorChooser enclosingChooser) {
super.uninstallChooserPanel(enclosingChooser);
for (SwatchPanel sp : swatchPanelList) {
sp.removeMouseListener(swatchListener);
sp = null;
}
swatchListener = null;
removeAll(); // strip out all the sub-components
}
@Override
public void updateChooser() {
setSwatchPanelSelection(getColorSelectionModel().getSelectedColor());
}
/**
* Sets the visual feedback for the swatch panels so that the appropriate
* panel shows the current selection.
*
* @param color
* @return
*/
public boolean setSwatchPanelSelection(Color color) {
// clear visual feedback for swatch selection in the swatch panels
for (SwatchPanel panel : swatchPanelList) {
panel.setSelectionFromLocation(-1, -1);
}
// exit if the chooser null selection flag is set
// (JColorChooser doesn't handle null color ... see class
// GeoGebraColorChooser for workaround)
if (enclosingChooser != null && enclosingChooser.isNullSelection()) {
return true;
}
// set the selected swatch visual feedback in the appropriate panel
if (primarySwatchPanel.setSelectionFromColor(color)) {
return true;
} else if (mainSwatchPanel.setSelectionFromColor(color)) {
return true;
} else {
return customSwatchPanel.setSelectionFromColor(color);
}
}
/**
* Set localized strings.
*/
public void setLabels() {
Localization loc = app.getLocalization();
btnCustomColor.setToolTipText(loc.getMenu("AddCustomColor"));
lblCustom.setText(loc.getMenu("Other") + ":");
lblRecent.setText(loc.getMenu("RecentColor") + ":");
}
public void updateFonts() {
Font font = app.getPlainFont();
btnCustomColor.setFont(font);
lblCustom.setFont(font);
lblRecent.setFont(font);
}
/**
* MouseListener for the swatch panels.
*
*/
class SwatchListener extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {
if (e.getSource() instanceof SwatchPanel) {
SwatchPanel mySwatchPanel = (SwatchPanel) e.getSource();
// exit if the mouse is not over a color swatch
if (!mySwatchPanel.isSwatchLocation(e.getX(), e.getY())) {
return;
}
// set the color selection to the color of the cell the mouse is
// above
Color color = mySwatchPanel.getColorForLocation(e.getX(),
e.getY());
getColorSelectionModel().setSelectedColor(color);
// update the the recent swatch panel
if (mySwatchPanel != recentSwatchPanel) {
recentSwatchPanel.setMostRecentColor(color);
}
}
}
}
/**
* Action listener for the custom color button. Creates and shows a color
* chooser dialog with a RGB color chooser panel.
*
*/
class CustomButtonActionListener implements ActionListener {
JColorChooser chooser;
@Override
public void actionPerformed(ActionEvent arg0) {
chooser = new JColorChooser();
chooser.setColor(myChooser.getColorFromModel());
DefaultRGBChooserPanel rgb = new DefaultRGBChooserPanel(app);
AbstractColorChooserPanel panels[] = { rgb };
chooser.setChooserPanels(panels);
chooser.setPreviewPanel(rgb.getPreview());
// show the chooser dialog
JDialog dialog = JColorChooser.createDialog(app.getMainComponent(),
app.getLocalization().getMenu("ChooseColor"), true, chooser,
okActionListener, null);
dialog.setVisible(true);
}
ActionListener okActionListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
customSwatchPanel.addCustomColor(chooser.getColor());
}
};
}
/**********************************************************
*
* Base class for all swatch panels.
*
**********************************************************/
class SwatchPanel extends JPanel {
private static final long serialVersionUID = 1L;
protected GColor[] colors;
protected Dimension swatchSize = new Dimension(largeSwatchSize,
largeSwatchSize);
protected Dimension gap = new Dimension(1, 1);
protected Dimension numSwatches;
protected Dimension selectedSwatch = new Dimension(-1, -1);
protected Dimension hoverSwatch = new Dimension(-1, -1);
protected Dimension prevHoverSwatch = new Dimension(-1, -1);
protected int swatchCount = 0;
public SwatchPanel() {
initValues();
initColors();
initSwatchCount();
setToolTipText(""); // register for events
setOpaque(true);
setBackground(Color.white);
setRequestFocusEnabled(false);
addMouseListener(new SwatchMouseListener());
addMouseMotionListener(new SwatchMouseMotionListener());
setMaximumSize(getPreferredSize());
}
protected void initValues() {
// overridden
}
protected void initColors() {
// overridden
}
protected void initSwatchCount() {
swatchCount = colors.length;
}
class SwatchMouseMotionListener extends MouseMotionAdapter {
@Override
public void mouseMoved(MouseEvent e) {
updateHoverSwatch(e.getX(), e.getY());
}
}
class SwatchMouseListener extends MouseAdapter {
@Override
public void mouseExited(MouseEvent e) {
updateHoverSwatch(-1, -1);
}
}
protected void updateHoverSwatch(int x, int y) {
prevHoverSwatch.width = hoverSwatch.width;
prevHoverSwatch.height = hoverSwatch.height;
setCellFromLocation(x, y, hoverSwatch);
if (prevHoverSwatch.width != hoverSwatch.width
|| prevHoverSwatch.height != hoverSwatch.height) {
repaint();
}
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
// g2d.setStroke(new BasicStroke(2));
Insets insets = getInsets();
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
for (int row = 0; row < numSwatches.height; row++) {
for (int column = 0; column < numSwatches.width; column++) {
g2d.setColor(getColorForCell(column, row));
int x;
if ((!this.getComponentOrientation().isLeftToRight())
&& (this instanceof RecentSwatchPanel)) {
x = (numSwatches.width - column - 1)
* (swatchSize.width + gap.width) + insets.left;
} else {
x = column * (swatchSize.width + gap.width)
+ insets.left;
}
int y = row * (swatchSize.height + gap.height) + insets.top;
g2d.fill3DRect(x + 1, y + 1, swatchSize.width - 1,
swatchSize.height - 1, true);
if (isSwatchCell(column, row)) {
if (selectedSwatch != null
&& row == selectedSwatch.height
&& column == selectedSwatch.width) {
g2d.setColor(Color.DARK_GRAY);
g2d.drawRect(x, y, swatchSize.width,
swatchSize.height);
if (app != null) {
g2d.drawImage(
app.getImageIcon(
GuiResourcesD.COLOR_CHOOSER_CHECK)
.getImage(),
x + 3, y + 3, null);
}
}
if (hoverSwatch != null && row == hoverSwatch.height
&& column == hoverSwatch.width) {
g2d.setColor(Color.DARK_GRAY);
g2d.drawRect(x, y, swatchSize.width,
swatchSize.height);
}
}
}
}
}
@Override
public Dimension getPreferredSize() {
Insets insets = getInsets();
int x = numSwatches.width * (swatchSize.width + gap.width)
+ insets.left + insets.right;
int y = numSwatches.height * (swatchSize.height + gap.height)
+ insets.top + insets.bottom;
return new Dimension(x, y);
}
@Override
public String getToolTipText(MouseEvent e) {
if (!isSwatchLocation(e.getX(), e.getY())) {
return "";
}
Color color = getColorForLocation(e.getX(), e.getY());
String name = GeoGebraColorConstants.getGeogebraColorName(app,
GColor.newColor(color.getRed(), color.getGreen(),
color.getBlue(), color.getAlpha()));
String rgbStr = color.getRed() + ", " + color.getGreen() + ", "
+ color.getBlue();
if (name != null) {
return name + " " + rgbStr;
}
return rgbStr;
}
public Color getColorForLocation(int x, int y) {
int column;
if ((!this.getComponentOrientation().isLeftToRight())
&& (this instanceof RecentSwatchPanel)) {
column = numSwatches.width - x / (swatchSize.width + gap.width)
- 1;
} else {
column = x / (swatchSize.width + gap.width);
}
int row = y / (swatchSize.height + gap.height);
return getColorForCell(column, row);
}
public void setCellFromLocation(int x, int y, Dimension p) {
if (x == -1 || y == -1) {
p.width = -1;
p.height = -1;
return;
}
int column;
if ((!this.getComponentOrientation().isLeftToRight())
&& (this instanceof RecentSwatchPanel)) {
column = numSwatches.width - x / (swatchSize.width + gap.width)
- 1;
} else {
column = x / (swatchSize.width + gap.width);
}
int row = y / (swatchSize.height + gap.height);
p.width = column;
p.height = row;
}
private Color getColorForCell(int column, int row) {
if ((row * numSwatches.width) + column < colors.length) {
return GColorD.getAwtColor(
colors[(row * numSwatches.width) + column]);
}
return Color.WHITE;
}
private boolean getCellForColor(Color color, Dimension cell) {
for (int i = 0; i < colors.length; i++) {
if (color.getRed() == colors[i].getRed()
&& color.getGreen() == colors[i].getGreen()
&& color.getBlue() == colors[i].getBlue()) {
cell.height = i / numSwatches.width;
cell.width = i % numSwatches.width;
return true;
}
}
cell.height = -1;
cell.width = -1;
return false;
}
protected boolean setSelectionFromColor(Color color) {
if (selectedSwatch == null) {
selectedSwatch = new Dimension();
}
boolean success = false;
if (color == null) {
selectedSwatch.width = -1;
selectedSwatch.height = -1;
success = true;
} else {
success = getCellForColor(color, selectedSwatch);
}
repaint();
return success;
}
protected void setSelectionFromLocation(int xLoc, int yLoc) {
if (xLoc < 0 || yLoc < 0) {
selectedSwatch.width = -1;
selectedSwatch.height = -1;
} else {
setCellFromLocation(xLoc, yLoc, selectedSwatch);
}
this.repaint();
}
Dimension cell = new Dimension();
protected boolean isSwatchLocation(int xLoc, int yLoc) {
setCellFromLocation(xLoc, yLoc, cell);
return isSwatchCell(cell.width, cell.height);
}
protected boolean isSwatchCell(int column, int row) {
int count = row * numSwatches.width + column + 1;
return count <= swatchCount;
}
}
/*******************************************************
* Recent swatch panel. This holds recently selected colors.
*
*/
class RecentSwatchPanel extends SwatchPanel {
private static final long serialVersionUID = 1L;
@Override
protected void initValues() {
numSwatches = new Dimension(6, 4);
swatchSize = new Dimension(smallSwatchSize, smallSwatchSize);
}
@Override
protected void initColors() {
Color defaultRecentColor = UIManager
.getColor("ColorChooser.swatchesDefaultRecentColor");
int numColors = numSwatches.width * numSwatches.height;
colors = new GColor[numColors];
for (int i = 0; i < numColors; i++) {
colors[i] = GColorD.newColor(defaultRecentColor);
}
}
@Override
protected void initSwatchCount() {
swatchCount = 0;
}
public void setMostRecentColor(Color c) {
System.arraycopy(colors, 0, colors, 1, colors.length - 1);
colors[0] = GColorD.newColor(c);
if (swatchCount < swatchSize.width * swatchSize.height) {
swatchCount++;
}
repaint();
}
}
/*******************************************************
* Custom swatch panel. This holds user defined RGB colors.
*
*/
class CustomSwatchPanel extends SwatchPanel {
private static final long serialVersionUID = 1L;
@Override
protected void initValues() {
numSwatches = new Dimension(5, 2);
// swatchSize = new Dimension(smallSwatchSize,smallSwatchSize);
}
@Override
protected void initSwatchCount() {
swatchCount = 0;
}
@Override
protected void initColors() {
Color defaultRecentColor = UIManager
.getColor("ColorChooser.swatchesDefaultRecentColor");
int numColors = numSwatches.width * numSwatches.height;
colors = new GColor[numColors];
for (int i = 0; i < numColors; i++) {
colors[i] = GColorD.newColor(defaultRecentColor);
}
}
public void addCustomColor(Color color) {
if (color == null) {
selectedSwatch.width = -1;
selectedSwatch.height = -1;
repaint();
} else {
System.arraycopy(colors, 0, colors, 1, colors.length - 1);
colors[0] = GColorD.newColor(color);
myChooser.setSwatchPanelSelection(color);
repaint();
if (swatchCount < swatchSize.width * swatchSize.height) {
swatchCount++;
}
getColorSelectionModel().setSelectedColor(color);
primarySwatchPanel.setSelectionFromLocation(-1, -1);
mainSwatchPanel.setSelectionFromLocation(-1, -1);
recentSwatchPanel.setMostRecentColor(color);
}
}
@Override
protected boolean setSelectionFromColor(Color color) {
if (!super.setSelectionFromColor(color)) {
addCustomColor(color);
}
return true;
}
}
/*******************************************************
* Primary swatch panel. This holds primary (and near primary) colors and
* grays.
*
*/
class PrimarySwatchPanel extends SwatchPanel {
private static final long serialVersionUID = 1L;
@Override
protected void initValues() {
numSwatches = new Dimension(2, 9);
}
@Override
protected void initColors() {
colors = GeoGebraColorConstants.getPrimarySwatchColors();
}
}
/********************************************************
* Main swatch panel. This panel sits in the middle and allows a set of
* colors to be picked which will move to the recent swatch panel.
*/
class MainSwatchPanel extends SwatchPanel {
private static final long serialVersionUID = 1L;
@Override
protected void initValues() {
numSwatches = new Dimension(8, 9);
}
@Override
protected void initColors() {
colors = GeoGebraColorConstants.mainColorSwatchColors;
}
}
}