/**
 * @(#)ColorPane.java    2.6.0   31 August 2007
 *
 * Copyright 2007
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.gui;

import edu.neu.ccs.*;
import edu.neu.ccs.util.*;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;

/**
 * <p>Class <code>ColorPane</code> provides a pane that may be
 * used to interactively set a color by setting the red, green,
 * blue, and alpha components using 4 <code>SliderView</code>
 * sliders.  The sliders are set to a width of 256 pixels so
 * that there is a 1-to-1 correspondance between pixels and
 * color component values.  The slider tracks use gradient
 * paints to reinforce which color component is being chosen.</p>
 * 
 * <p>The pane presents 2 color swatches.  The upper swatch
 * shows the initial color in the pane and the lower swatch
 * shows the current color as it evolves under the movements
 * of the 4 sliders or of the dropdown view.</p>
 * 
 * <p>The pane provides a <code>ColorView</code> with only its
 * dropdown list visible.  This enables a user to start with a
 * specific named color or a specific color given by its color
 * components and to then make changes from there.</p>
 * 
 * <p>Buttons are provided to set the selected (lower) color
 * from the original (upper) color and vice versa.</p>
 * 
 * <p>The caller may supply press, sliding, and release actions.
 * Each such action will be installed in all 4 sliders.  This
 * allows the caller to listen to the changing color behavior
 * in this pane and react accordingly.</p>
 * 
 * @author  Richard Rasala
 * @version 2.6.0
 * @since   2.6.0
 */
public class ColorPane
    extends BasePane
{
    /**
     * The size of the square that contains the upper and lower
     * swatches: 200.
     */
    public static final int SIZE = 200;
    
    
    /** The swatch border thickness: 2. */
    public static final int THICK = 2;
    
    
    /** The upper paint swatch for the initial color. */
    protected PaintSwatch upperSwatch =
        new PaintSwatch(null, SIZE, SIZE/2, null);
    
    
    /** The lower paint swatch for the current color. */
    protected PaintSwatch lowerSwatch =
        new PaintSwatch(null, SIZE, SIZE/2, null);
    
    
    /** The array for the pair of swatches. */
    protected Object[] swatchStuff =
        { upperSwatch, lowerSwatch };
    
    
    /** The panel with the pair of swatches. */
    protected VTable swatchPanel =
        new VTable(swatchStuff, 0, 0, CENTER);
    
    
    /** The labels for the panel with the pair of swatches. */
    protected PaintableComponent swatchLabelComponent =
        makeSwatchPanelLabels();
    
    
    /** The array for the labels and swatch panel. */
    protected Object[] labelSwatchStuff =
        { swatchLabelComponent, swatchPanel };
    
    
    /** The panel for the labels and swatch panel. */
    protected HTable labelSwatchPanel =
        new HTable(labelSwatchStuff, 2*gap, 2*gap, CENTER);
    
    
    /** The gradient paint for the red slider track. */
    protected Paint R_Paint =
        new GradientPaint(0, 0, Color.black, 255, 0, Colors.red);
    
    
    /** The gradient paint for the green slider track. */
    protected Paint G_Paint =
        new GradientPaint(0, 0, Color.black, 255, 0, Colors.lime);
    
    
    /** The gradient paint for the blue slider track. */
    protected Paint B_Paint =
        new GradientPaint(0, 0, Color.black, 255, 0, Colors.blue);
    
    
    /** The gradient paint for the alpha slider track. */
    protected Paint A_Paint =
        new GradientPaint(0, 0, Color.black, 255, 0, Colors.white);
    
    
    /** The common color for the red slider. */
    protected Color R_Color = Colors.red;
    
    
    /** The common color for the green slider. */
    protected Color G_Color = Colors.lime;
    
    
    /** The common color for the blue slider. */
    protected Color B_Color = Colors.blue;
    
    
    /** The common color for the alpha slider. */
    protected Color A_Color = Colors.black;
    
    
    /** The red slider. */
    protected SliderView R_Slider =
        new SliderView(HORIZONTAL, 0, 255, 255, 256, 32, 8,
            null, R_Color, R_Paint, R_Color, true);
    
    
    /** The green slider. */
    protected SliderView G_Slider =
        new SliderView(HORIZONTAL, 0, 255, 255, 256, 32, 8,
            null, G_Color, G_Paint, G_Color, true);
    
    
    /** The blue slider. */
    protected SliderView B_Slider =
        new SliderView(HORIZONTAL, 0, 255, 255, 256, 32, 8,
            null, B_Color, B_Paint, B_Color, true);
    
    
    /** The alpha slider. */
    protected SliderView A_Slider =
        new SliderView(HORIZONTAL, 0, 255, 255, 256, 32, 8,
            null, A_Color, A_Paint, A_Color, true);
    
    
    /** The label for the red slider. */
    protected Annotation R_Label =
        new Annotation("Red",   labelFont);
    
    
    /** The label for the green slider. */
    protected Annotation G_Label =
        new Annotation("Green", labelFont);
    
    
    /** The label for the blue slider. */
    protected Annotation B_Label =
        new Annotation("Blue",  labelFont);
    
    
    /** The label for the alpha slider. */
    protected Annotation A_Label =
        new Annotation("Alpha", labelFont);
    
    
    /** The array with sliders and their labels. */
    protected Object[][] sliderStuff = {
        { R_Label, R_Slider },
        { G_Label, G_Slider },
        { B_Label, B_Slider },
        { A_Label, A_Slider },
    };
    
    
    /** The panel with sliders and their labels. */
    protected TablePanel sliderPanel =
        new TablePanel(sliderStuff, gap, gap, WEST);
    
    
    /** The color view with only the dropdown color name list. */
    protected ColorView colorview =
        new ColorView(null, 0, 0);
    
    
    /** The label for the colorview. */
    protected Annotation colorviewLabel =
        new Annotation("Color Dropdown List", labelFont);
    
    
    /** The array with the color view and its label. */
    protected Object[] colorviewStuff = { colorviewLabel, colorview };
    
    
    /** The panel with the color view and its label. */
    protected HTable colorviewPanel =
        new HTable(colorviewStuff, gap, gap, CENTER);
    
    
    /** The action to reset the current color to the initial color. */
    protected SimpleAction initialToCurrentAction =
        new SimpleAction("Reset Selected Color From Original") {
            public void perform() {
                setCurrentFromInitial();
            }
    };
    
    
    /** The button to reset the current color to the initial color. */
    protected JButton initialToCurrentButton =
        new JButton(initialToCurrentAction);
    
    
    /** The action to reset the current color to the initial color. */
    protected SimpleAction currentToInitialAction =
        new SimpleAction("Reset Original Color From Selected") {
            public void perform() {
                setInitialFromCurrent();
            }
    };
    
    
    /** The button to reset the current color to the initial color. */
    protected JButton currentToInitialButton =
        new JButton(currentToInitialAction);
    
    
    /** The array with the main GUI components. */
    protected Object[] mainStuff =
        { labelSwatchPanel, sliderPanel, colorviewPanel,
          initialToCurrentButton, currentToInitialButton };
    
    
    /** The panel with the main GUI components. */
    protected VTable mainPanel =
        new VTable(mainStuff, 2*gap, 2*gap, CENTER);
    
    
    /** The action to set the color using the slider settings. */
    protected SimpleAction setColorFromSlidersAction =
        new SimpleAction() {
            public void perform() {
                setColorFromSliders();
            }
    };
    
    
    /** The action to set the color using the color view. */
    protected SimpleAction setColorFromColorViewAction =
        new SimpleAction() {
            public void perform() {
                setColorFromColorView();
            }
    };
    
    
    /** 
     * The caller-defined actions performed 
     * when the slider thumb is pressed
     * in any of the 4 sliders. 
     */
    protected ActionSequence pressActions = new ActionSequence();
    
    
    /** 
     * The caller-defined actions performed 
     * when the slider thumb is sliding
     * in any of the 4 sliders. 
     */
    protected ActionSequence slidingActions = new ActionSequence();
    
    
    /** 
     * The caller-defined actions performed 
     * when the slider thumb is released
     * in any of the 4 sliders. 
     */
    protected ActionSequence releaseActions = new ActionSequence();
    
    
    /**
     * The default <code>ColorPane</code> constructor that sets
     * the initial color to white.
     */
    public ColorPane() {
        this(Colors.white);
    }
    
    
    /**
     * The <code>ColorPane</code> constructor that sets
     * the initial color to the given color.
     * 
     * @param color the initial color of the color pane
     */
    public ColorPane(Color color) {
        if (color == null)
            color = Colors.white;
        
        setInitialColor(color);
        setColor(color);
        
        R_Slider.addSlidingAction(setColorFromSlidersAction);
        G_Slider.addSlidingAction(setColorFromSlidersAction);
        B_Slider.addSlidingAction(setColorFromSlidersAction);
        A_Slider.addSlidingAction(setColorFromSlidersAction);
        
        R_Slider.addPressAction(pressActions);
        G_Slider.addPressAction(pressActions);
        B_Slider.addPressAction(pressActions);
        A_Slider.addPressAction(pressActions);
        
        R_Slider.addSlidingAction(slidingActions);
        G_Slider.addSlidingAction(slidingActions);
        B_Slider.addSlidingAction(slidingActions);
        A_Slider.addSlidingAction(slidingActions);
        
        R_Slider.addReleaseAction(releaseActions);
        G_Slider.addReleaseAction(releaseActions);
        B_Slider.addReleaseAction(releaseActions);
        A_Slider.addReleaseAction(releaseActions);
        
        R_Label.setForeground(R_Color);
        G_Label.setForeground(G_Color);
        B_Label.setForeground(B_Color);
        A_Label.setForeground(A_Color);
        
        colorview.addAction(setColorFromColorViewAction);

        initialToCurrentButton.setFont(buttonFont);
        currentToInitialButton.setFont(buttonFont);
        
        swatchPanel.lineBorder(THICK);
        
        mainPanel.setDeepBackground(Colors.white);
        mainPanel.emptyBorder(gap);
        
        addObject(mainPanel);
    }
    
    
    /** Returns the current color. */
    public Color getColor() {
        return (Color) lowerSwatch.getPaint();
    }
    
    
    /**
     * <p>Algorithmically sets the current color and
     * adjusts the 4 sliders, the lower swatch, and
     * the color view appropriately.</p>
     * 
     * @param color the color to set as the current color
     */
    public void setColor(Color color) {
        if (color == null)
            return;
        
        int r = color.getRed();
        int g = color.getGreen();
        int b = color.getBlue();
        int a = color.getAlpha();
        
        R_Slider.setValue(r);
        G_Slider.setValue(g);
        B_Slider.setValue(b);
        A_Slider.setValue(a);
        
        colorview.setColorHelper(color);
        
        lowerSwatch.setPaint(color);
    }
    
    
    /**
     * <p>Returns the initial color which is the color
     * of the upper swatch.</p>
     * 
     * <p>The call <code>resetColor()</code> is equivalent to
     * <code>setColor(getInitialColor())</code>.</p>
     */
    public Color getInitialColor() {
        return (Color) upperSwatch.getPaint();
    }
    
    
    /**
     * <p>Sets the initial color which is the color
     * of the upper swatch.</p>
     * 
     * <p>This method does not change the 4 sliders or
     * the lower swatch.  No actions are executed.</p>
     * 
     * @param color
     */
    public void setInitialColor(Color color) {
        if (color == null)
            return;
        
        upperSwatch.setPaint(color);
    }
    
    
    /** Set the current color from the initial color. */
    public void setCurrentFromInitial() {
        setColor(getInitialColor());
    }
    
    
    /** Set the initial color from the current color. */
    public void setInitialFromCurrent() {
        setInitialColor(getColor());
    }
    
    
    /** Sets the current color from the settings in the 4 sliders. */
    protected void setColorFromSliders() {
        int r = R_Slider.getValue();
        int g = G_Slider.getValue();
        int b = B_Slider.getValue();
        int a = A_Slider.getValue();
        
        Color color = new Color(r, g, b, a);
        
        colorview.setColorHelper(color);
        
        lowerSwatch.setPaint(color);
    }
    
    
    /** Sets the current color from the color view. */
    protected void setColorFromColorView() {
        setColor(colorview.getColor());
    }
    
    
    /**
     * <p>Adds the given action listener to the sequence of actions
     * that is performed when slider is pressed in its active area
     * (thumb or track) in any of the 4 sliders.</p>
     * 
     * @param a the press action listener to add
     */
    public void addPressAction(ActionListener a) {
        pressActions.add(a);
    }
    
    
    /**
     * <p>Adds the given action listener to the sequence of actions
     * that is performed when slider is sliding after being pressed
     * in its active area (thumb or track) in any of the 4 sliders.</p>
     *
     * @param a the sliding action listener to add
     */
    public void addSlidingAction(ActionListener a) {
        slidingActions.add(a);
    }
    
    
    /**
     * <p>Adds the given action listener to the sequence of actions
     * that is performed when slider is released after being pressed
     * in its active area (thumb or track) in any of the 4 sliders.</p>
     *
     * @param a the release action listener to add
     */
    public void addReleaseAction(ActionListener a) {
        releaseActions.add(a);
    }
    
    
    /**
     * Removes the given action listener from the press actions.
     *
     * @param a the press action listener to remove
     */
    public void removePressAction(ActionListener a) {
        pressActions.remove(a);
    }
    
    
    /**
     * Removes the given action listener from the sliding actions.
     *
     * @param a the sliding action listener to remove
     */
    public void removeSlidingAction(ActionListener a) {
        slidingActions.remove(a);
    }
    
    
    /**
     * Removes the given action listener from the release actions.
     *
     * @param a the release action listener to remove
     */
    public void removeReleaseAction(ActionListener a) {
        releaseActions.remove(a);
    }
    
    
    /**
     * Make the labels for the swatch panel.
     */
    protected PaintableComponent makeSwatchPanelLabels() {
        String label1 = "Original\nColor";
        String label2 = "Selected\nColor";
        
        MultiLineTextPaintable tp1 =
            new MultiLineTextPaintable
                (label1, labelFont, TextAnchor.CENTER_ASCENTLINE, 0, 0);
        
        MultiLineTextPaintable tp2 =
            new MultiLineTextPaintable
                (label2, labelFont, TextAnchor.CENTER_ASCENTLINE, 0, 0);
        
        XRect bounds = tp1.getActualBounds2D();
        
        int w = (int) bounds.width;
        int h = (int) bounds.height;
        
        if ((w % 2) == 0)
            w += 2 * gap;
        else
            w += 2 * gap + 1;
        
        int half_w = w/2;
        int half_h = h/2;
        
        int jump = SIZE/4;
        
        tp1.moveCenterTo(half_w,     jump + THICK - half_h);
        tp2.moveCenterTo(half_w, 3 * jump + THICK - half_h);
        
        bounds = new XRect(0, 0, w, 200 + 2 * THICK);
        
        PaintableSequence sequence =
            new PaintableSequence();
        
        sequence.setDefaultOriginalBounds2D(bounds);
        sequence.addPaintable(tp1);
        sequence.addPaintable(tp2);
        
        return new PaintableComponent(sequence);
    }
    
    
    /**
     * For test purposes, this main program creates a
     * <code>ColorPane</code> whose initial color has
     * random RGB components and has alpha=255.  The
     * pane is then installed in a frame.
     *
     * @param args ignored
     */
    public static void main(String[] args) {
        int r = MathUtilities.randomInt(0, 255);
        int g = MathUtilities.randomInt(0, 255);
        int b = MathUtilities.randomInt(0, 255);
        
        Color color = new Color(r, g, b);
        
        new ColorPane(color).frame();
    }
    
}

