- /*
- * @(#)GTKColorChooserPanel.java 1.5 03/01/23
- *
- * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
- package com.sun.java.swing.plaf.gtk;
-
- import java.awt.*;
- import java.awt.event.*;
- import java.awt.image.*;
- import javax.swing.*;
- import javax.swing.colorchooser.*;
- import javax.swing.event.*;
- import javax.swing.plaf.*;
-
- /**
- * A color chooser panel mimicking that of GTK's: a color wheel showing
- * hue and a triangle that varies saturation and brightness.
- *
- * @version 1.5, 01/23/03
- * @author Scott Violet
- */
- class GTKColorChooserPanel extends AbstractColorChooserPanel implements
- ChangeListener {
- private static final float PI_3 = (float)(Math.PI / 3);
-
- private ColorTriangle triangle;
- private JLabel lastLabel;
- private JLabel label;
-
- private JSpinner hueSpinner;
- private JSpinner saturationSpinner;
- private JSpinner valueSpinner;
-
- private JSpinner redSpinner;
- private JSpinner greenSpinner;
- private JSpinner blueSpinner;
-
- private JTextField colorNameTF;
-
- private boolean settingColor;
-
- // The colors are mirrored to avoid creep in adjusting an individual
- // value.
- private float hue;
- private float saturation;
- private float brightness;
-
-
-
- /**
- * Convenience method to transfer focus to the next child of component.
- */
- // PENDING: remove this when a variant of this is added to awt.
- static void compositeRequestFocus(Component component, boolean direction) {
- if (component instanceof Container) {
- Container container = (Container)component;
- if (container.isFocusCycleRoot()) {
- FocusTraversalPolicy policy = container.
- getFocusTraversalPolicy();
- Component comp = policy.getDefaultComponent(container);
- if (comp!=null) {
- comp.requestFocus();
- return;
- }
- }
- Container rootAncestor = container.getFocusCycleRootAncestor();
- if (rootAncestor!=null) {
- FocusTraversalPolicy policy = rootAncestor.
- getFocusTraversalPolicy();
- Component comp;
-
- if (direction) {
- comp = policy.getComponentAfter(rootAncestor, container);
- }
- else {
- comp = policy.getComponentBefore(rootAncestor, container);
- }
- if (comp != null) {
- comp.requestFocus();
- return;
- }
- }
- }
- component.requestFocus();
- }
-
-
- /**
- * Returns a user presentable description of this GTKColorChooserPane.
- */
- public String getDisplayName() {
- return (String)UIManager.get("GTKColorChooserPanel.nameText");
- }
-
- /**
- * Returns the mnemonic to use with <code>getDisplayName</code>.
- */
- public int getMnemonic() {
- String m = (String)UIManager.get("GTKColorChooserPanel.mnemonic");
-
- if (m != null) {
- try {
- int value = Integer.parseInt(m);
-
- return value;
- } catch (NumberFormatException nfe) {}
- }
- return -1;
- }
-
- /**
- * Character to underline that represents the mnemonic.
- */
- public int getDisplayedMnemonicIndex() {
- String m = (String)UIManager.get(
- "GTKColorChooserPanel.dispalyedMnemonicIndex");
-
- if (m != null) {
- try {
- int value = Integer.parseInt(m);
-
- return value;
- } catch (NumberFormatException nfe) {}
- }
- return -1;
- }
-
- public Icon getSmallDisplayIcon() {
- return null;
- }
-
- public Icon getLargeDisplayIcon() {
- return null;
- }
-
- /**
- * Builds and configures the widgets for the GTKColorChooserPanel.
- */
- protected void buildChooser() {
- triangle = new ColorTriangle();
- triangle.setName("GTKColorChooserPanel.triangle");
-
- // PENDING: when we straighten out user setting opacity, this should
- // be changed.
- label = new OpaqueLabel();
- label.setName("GTKColorChooserPanel.colorWell");
- label.setOpaque(true);
- label.setMinimumSize(new Dimension(67, 32));
- label.setPreferredSize(new Dimension(67, 32));
- label.setMaximumSize(new Dimension(67, 32));
-
- // PENDING: when we straighten out user setting opacity, this should
- // be changed.
- lastLabel = new OpaqueLabel();
- lastLabel.setName("GTKColorChooserPanel.lastColorWell");
- lastLabel.setOpaque(true);
- lastLabel.setMinimumSize(new Dimension(67, 32));
- lastLabel.setPreferredSize(new Dimension(67, 32));
- lastLabel.setMaximumSize(new Dimension(67, 32));
-
- hueSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 360, 1));
- // configureSpinner(hueSpinner, "GTKColorChooserPanel.hueSpinner");
- saturationSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 255, 1));
- configureSpinner(saturationSpinner,
- "GTKColorChooserPanel.saturationSpinner");
- valueSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 255, 1));
- configureSpinner(valueSpinner, "GTKColorChooserPanel.valueSpinner");
- redSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 255, 1));
- configureSpinner(redSpinner, "GTKColorChooserPanel.redSpinner");
- greenSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 255, 1));
- configureSpinner(greenSpinner, "GTKColorChooserPanel.greenSpinner");
- blueSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 255, 1));
- configureSpinner(blueSpinner, "GTKColorChooserPanel.blueSpinner");
-
- colorNameTF = new JTextField(8);
-
- setLayout(new GridBagLayout());
-
- add(this, "GTKColorChooserPanel.hue", hueSpinner, -1, -1);
- add(this, "GTKColorChooserPanel.red", redSpinner, -1, -1);
- add(this, "GTKColorChooserPanel.saturation", saturationSpinner, -1,-1);
- add(this, "GTKColorChooserPanel.green", greenSpinner, -1, -1);
- add(this, "GTKColorChooserPanel.value", valueSpinner, -1, -1);
- add(this, "GTKColorChooserPanel.blue", blueSpinner, -1, -1);
-
- add(new JSeparator(SwingConstants.HORIZONTAL), new
- GridBagConstraints(1, 3, 4, 1, 1, 0,
- GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL,
- new Insets(14, 0, 0, 0), 0, 0));
-
- add(this, "GTKColorChooserPanel.colorName", colorNameTF, 0, 4);
-
- add(triangle, new GridBagConstraints(0, 0, 1, 5, 0, 0,
- GridBagConstraints.LINE_START, GridBagConstraints.NONE,
- new Insets(14, 20, 2, 9), 0, 0));
-
- Box hBox = Box.createHorizontalBox();
- hBox.add(lastLabel);
- hBox.add(label);
- add(hBox, new GridBagConstraints(0, 5, 1, 1, 0, 0,
- GridBagConstraints.CENTER, GridBagConstraints.NONE,
- new Insets(0, 0, 0, 0), 0, 0));
-
- add(new JSeparator(SwingConstants.HORIZONTAL), new
- GridBagConstraints(0, 6, 5, 1, 1, 0,
- GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL,
- new Insets(12, 0, 0, 0), 0, 0));
- }
-
- /**
- * Configures the spinner.
- */
- private void configureSpinner(JSpinner spinner, String name) {
- spinner.addChangeListener(this);
- spinner.setName(name);
- JComponent editor = spinner.getEditor();
- if (editor instanceof JSpinner.DefaultEditor) {
- JFormattedTextField ftf = ((JSpinner.DefaultEditor)editor).
- getTextField();
-
- ftf.setFocusLostBehavior(JFormattedTextField.COMMIT_OR_REVERT);
- }
- }
-
- /**
- * Adds the widget creating a JLabel with the specified name.
- */
- private void add(Container parent, String key, JComponent widget,
- int x, int y) {
- JLabel label = new JLabel(UIManager.getString(key + "Text",
- getLocale()));
- String mnemonic = (String)UIManager.get(key + "Mnemonic", getLocale());
-
- if (mnemonic != null) {
- try {
- label.setDisplayedMnemonic(Integer.parseInt(mnemonic));
- } catch (NumberFormatException nfe) {
- }
- String mnemonicIndex = (String)UIManager.get(key + "MnemonicIndex",
- getLocale());
-
- if (mnemonicIndex != null) {
- try {
- label.setDisplayedMnemonicIndex(Integer.parseInt(
- mnemonicIndex));
- } catch (NumberFormatException nfe) {
- }
- }
- }
- label.setLabelFor(widget);
- if (x < 0) {
- x = parent.getComponentCount() % 4;
- }
- if (y < 0) {
- y = parent.getComponentCount() / 4;
- }
- GridBagConstraints con = new GridBagConstraints(x + 1, y, 1, 1, 0, 0,
- GridBagConstraints.FIRST_LINE_END, GridBagConstraints.NONE,
- new Insets(4, 0, 0, 4), 0, 0);
- if (y == 0) {
- con.insets.top = 14;
- }
- parent.add(label, con);
- con.gridx++;
- parent.add(widget, con);
- }
-
- /**
- * Refreshes the display from the model.
- */
- public void updateChooser() {
- if (!settingColor) {
- lastLabel.setBackground(getColorFromModel());
- setColor(getColorFromModel(), true, true, false);
- }
- }
-
- /**
- * Resets the red component of the selected color.
- */
- private void setRed(int red) {
- setRGB(red << 16 | getColor().getGreen() << 8 | getColor().getBlue());
- }
-
- /**
- * Resets the green component of the selected color.
- */
- private void setGreen(int green) {
- setRGB(getColor().getRed() << 16 | green << 8 | getColor().getBlue());
- }
-
- /**
- * Resets the blue component of the selected color.
- */
- private void setBlue(int blue) {
- setRGB(getColor().getRed() << 16 | getColor().getGreen() << 8 | blue);
- }
-
- /**
- * Sets the hue of the selected color and updates the display if
- * necessary.
- */
- private void setHue(float hue, boolean update) {
- setHSB(hue, saturation, brightness);
- if (update) {
- settingColor = true;
- hueSpinner.setValue(new Integer((int)(hue * 360)));
- settingColor = false;
- }
- }
-
- /**
- * Returns the current amount of hue.
- */
- private float getHue() {
- return hue;
- }
-
- /**
- * Resets the saturation.
- */
- private void setSaturation(float saturation) {
- setHSB(hue, saturation, brightness);
- }
-
- /**
- * Returns the saturation.
- */
- private float getSaturation() {
- return saturation;
- }
-
- /**
- * Sets the brightness.
- */
- private void setBrightness(float brightness) {
- setHSB(hue, saturation, brightness);
- }
-
- /**
- * Returns the brightness.
- */
- private float getBrightness() {
- return brightness;
- }
-
- /**
- * Sets the saturation and brightness and updates the display if
- * necessary.
- */
- private void setSaturationAndBrightness(float s, float b, boolean update) {
- setHSB(hue, s, b);
- if (update) {
- settingColor = true;
- saturationSpinner.setValue(new Integer((int)(s * 255)));
- valueSpinner.setValue(new Integer((int)(b * 255)));
- settingColor = false;
- }
- }
-
- /**
- * Resets the rgb values.
- */
- private void setRGB(int rgb) {
- Color color = new Color(rgb);
-
- setColor(color, false, true, true);
-
- settingColor = true;
- hueSpinner.setValue(new Integer((int)(hue * 360)));
- saturationSpinner.setValue(new Integer((int)(saturation * 255)));
- valueSpinner.setValue(new Integer((int)(brightness * 255)));
- settingColor = false;
- }
-
- /**
- * Resets the hsb values.
- */
- private void setHSB(float h, float s, float b) {
- Color color = Color.getHSBColor(h, s, b);
-
- this.hue = h;
- this.saturation = s;
- this.brightness = b;
- setColor(color, false, false, true);
-
- settingColor = true;
- redSpinner.setValue(new Integer(color.getRed()));
- greenSpinner.setValue(new Integer(color.getGreen()));
- blueSpinner.setValue(new Integer(color.getBlue()));
- settingColor = false;
- }
-
-
- /**
- * Rests the color.
- *
- * @param color new Color
- * @param updateSpinners whether or not to update the spinners.
- * @param updateHSB if true, the hsb fields are updated based on the
- * new color
- * @param updateModel if true, the model is set.
- */
- private void setColor(Color color, boolean updateSpinners,
- boolean updateHSB, boolean updateModel) {
- if (color == null) {
- color = Color.BLACK;
- }
-
- settingColor = true;
-
- if (updateHSB) {
- float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(),
- color.getBlue(), null);
- hue = hsb[0];
- saturation = hsb[1];
- brightness = hsb[2];
- }
-
- if (updateModel) {
- getColorSelectionModel().setSelectedColor(color);
- }
-
- triangle.setColor(hue, saturation, brightness);
- label.setBackground(color);
- // Force Integer to pad the string with 0's by adding 0x1000000 and
- // then removing the first character.
- String hexString = Integer.toHexString(
- (color.getRGB() & 0xFFFFFF) | 0x1000000);
- colorNameTF.setText("#" + hexString.substring(1));
-
- if (updateSpinners) {
- redSpinner.setValue(new Integer(color.getRed()));
- greenSpinner.setValue(new Integer(color.getGreen()));
- blueSpinner.setValue(new Integer(color.getBlue()));
-
- hueSpinner.setValue(new Integer((int)(hue * 360)));
- saturationSpinner.setValue(new Integer((int)(saturation * 255)));
- valueSpinner.setValue(new Integer((int)(brightness * 255)));
- }
- settingColor = false;
- }
-
- public Color getColor() {
- return label.getBackground();
- }
-
- /**
- * ChangeListener method, updates the necessary display widgets.
- */
- public void stateChanged(ChangeEvent e) {
- if (settingColor) {
- return;
- }
- Color color = getColor();
-
- if (e.getSource() == hueSpinner) {
- setHue(((Number)hueSpinner.getValue()).floatValue() / 360, false);
- }
- else if (e.getSource() == saturationSpinner) {
- setSaturation(((Number)saturationSpinner.getValue()).
- floatValue() / 255);
- }
- else if (e.getSource() == valueSpinner) {
- setBrightness(((Number)valueSpinner.getValue()).
- floatValue() / 255);
- }
- else if (e.getSource() == redSpinner) {
- setRed(((Number)redSpinner.getValue()).intValue());
- }
- else if (e.getSource() == greenSpinner) {
- setGreen(((Number)greenSpinner.getValue()).intValue());
- }
- else if (e.getSource() == blueSpinner) {
- setBlue(((Number)blueSpinner.getValue()).intValue());
- }
- }
-
-
-
- /**
- * Flag indicating the angle, or hue, has changed and the triangle
- * needs to be recreated.
- */
- private static final int FLAGS_CHANGED_ANGLE = 1 << 0;
- /**
- * Indicates the wheel is being dragged.
- */
- private static final int FLAGS_DRAGGING = 1 << 1;
- /**
- * Indicates the triangle is being dragged.
- */
- private static final int FLAGS_DRAGGING_TRIANGLE = 1 << 2;
- /**
- * Indicates a color is being set and we should ignore setColor
- */
- private static final int FLAGS_SETTING_COLOR = 1 << 3;
- /**
- * Indicates the wheel has focus.
- */
- private static final int FLAGS_FOCUSED_WHEEL = 1 << 4;
- /**
- * Indicates the triangle has focus.
- */
- private static final int FLAGS_FOCUSED_TRIANGLE = 1 << 5;
-
-
- /**
- * Class responsible for rendering a color wheel and color triangle.
- */
- private class ColorTriangle extends JPanel {
- /**
- * Cached image of the wheel.
- */
- private Image wheelImage;
-
- /**
- * Cached image of the triangle.
- */
- private Image triangleImage;
-
- /**
- * Angle triangle is rotated by.
- */
- private double angle;
-
- /**
- * Boolean bitmask.
- */
- private int flags;
-
- /**
- * X location of selected color indicator.
- */
- private int circleX;
- /**
- * Y location of selected color indicator.
- */
- private int circleY;
-
-
- public ColorTriangle() {
- enableEvents(AWTEvent.FOCUS_EVENT_MASK);
- enableEvents(AWTEvent.MOUSE_EVENT_MASK);
- enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK);
-
- setMinimumSize(new Dimension(getWheelRadius() * 2 + 2,
- getWheelRadius() * 2 + 2));
- setPreferredSize(new Dimension(getWheelRadius() * 2 + 2,
- getWheelRadius() * 2 + 2));
-
- // We want to handle tab ourself.
- setFocusTraversalKeysEnabled(false);
-
- // PENDING: this should come from the style.
- getInputMap().put(KeyStroke.getKeyStroke("UP"), "up");
- getInputMap().put(KeyStroke.getKeyStroke("DOWN"), "down");
- getInputMap().put(KeyStroke.getKeyStroke("LEFT"), "left");
- getInputMap().put(KeyStroke.getKeyStroke("RIGHT"), "right");
-
- getInputMap().put(KeyStroke.getKeyStroke("KP_UP"), "up");
- getInputMap().put(KeyStroke.getKeyStroke("KP_DOWN"), "down");
- getInputMap().put(KeyStroke.getKeyStroke("KP_LEFT"), "left");
- getInputMap().put(KeyStroke.getKeyStroke("KP_RIGHT"), "right");
-
- getInputMap().put(KeyStroke.getKeyStroke("TAB"), "focusNext");
- getInputMap().put(KeyStroke.getKeyStroke("shift TAB"),"focusLast");
-
- ActionMap map = (ActionMap)UIManager.get(
- "GTKColorChooserPanel.actionMap");
-
- if (map == null) {
- map = new ActionMapUIResource();
- map.put("left", new ColorAction("left", 2));
- map.put("right", new ColorAction("right", 3));
- map.put("up", new ColorAction("up", 0));
- map.put("down", new ColorAction("down", 1));
- map.put("focusNext", new ColorAction("focusNext", 4));
- map.put("focusLast", new ColorAction("focusLast", 5));
- UIManager.getLookAndFeelDefaults().put(
- "GTKColorChooserPanel.actionMap", map);
- }
- SwingUtilities.replaceUIActionMap(this, map);
- }
-
- /**
- * Returns the GTKColorChooserPanel.
- */
- GTKColorChooserPanel getGTKColorChooserPanel() {
- return GTKColorChooserPanel.this;
- }
-
- /**
- * Gives focus to the wheel.
- */
- void focusWheel() {
- setFocusType(1);
- }
-
- /**
- * Gives focus to the triangle.
- */
- void focusTriangle() {
- setFocusType(2);
- }
-
- /**
- * Returns true if the wheel currently has focus.
- */
- boolean isWheelFocused() {
- return isSet(FLAGS_FOCUSED_WHEEL);
- }
-
- /**
- * Resets the selected color.
- */
- public void setColor(float h, float s, float b) {
- if (isSet(FLAGS_SETTING_COLOR)) {
- return;
- }
-
- setAngleFromHue(h);
- setSaturationAndBrightness(s, b);
- }
-
- /**
- * Returns the selected color.
- */
- public Color getColor() {
- return GTKColorChooserPanel.this.getColor();
- }
-
- /**
- * Returns the x location of the selected color indicator.
- */
- int getColorX() {
- return circleX + getIndicatorSize() / 2 - getWheelXOrigin();
- }
-
- /**
- * Returns the y location of the selected color indicator.
- */
- int getColorY() {
- return circleY + getIndicatorSize() / 2 - getWheelYOrigin();
- }
-
- protected void processEvent(AWTEvent e) {
- if (e.getID() == MouseEvent.MOUSE_PRESSED ||
- ((isSet(FLAGS_DRAGGING) ||isSet(FLAGS_DRAGGING_TRIANGLE)) &&
- e.getID() == MouseEvent.MOUSE_DRAGGED)) {
- // Assign focus to either the wheel or triangle and attempt
- // to drag either the wheel or triangle.
- int size = getWheelRadius();
- int x = ((MouseEvent)e).getX() - size;
- int y = ((MouseEvent)e).getY() - size;
-
- if (!hasFocus()) {
- requestFocus();
- }
- if (!isSet(FLAGS_DRAGGING_TRIANGLE) &&
- adjustHue(x, y, e.getID() == MouseEvent.MOUSE_PRESSED)) {
- setFlag(FLAGS_DRAGGING, true);
- setFocusType(1);
- }
- else if (adjustSB(x, y, e.getID() ==
- MouseEvent.MOUSE_PRESSED)) {
- setFlag(FLAGS_DRAGGING_TRIANGLE, true);
- setFocusType(2);
- }
- else {
- setFocusType(2);
- }
- }
- else if (e.getID() == MouseEvent.MOUSE_RELEASED) {
- // Stopped dragging
- setFlag(FLAGS_DRAGGING_TRIANGLE, false);
- setFlag(FLAGS_DRAGGING, false);
- }
- else if (e.getID() == FocusEvent.FOCUS_LOST) {
- // Reset the flags to indicate no one has focus
- setFocusType(0);
- }
- else if (e.getID() == FocusEvent.FOCUS_GAINED) {
- // Gained focus, reassign focus to the wheel if no one
- // currently has focus.
- if (!isSet(FLAGS_FOCUSED_TRIANGLE) &&
- !isSet(FLAGS_FOCUSED_WHEEL)) {
- setFlag(FLAGS_FOCUSED_WHEEL, true);
- setFocusType(1);
- }
- repaint();
- }
- super.processEvent(e);
- }
-
- public void paintComponent(Graphics g) {
- super.paintComponent(g);
-
- // Draw the wheel and triangle
- int size = getWheelRadius();
- int width = getWheelWidth();
- Image image = getImage(size);
- g.drawImage(image, getWheelXOrigin() - size,
- getWheelYOrigin() - size, null);
-
- // Draw the focus indicator for the wheel
- if (hasFocus() && isSet(FLAGS_FOCUSED_WHEEL)) {
- g.setColor(Color.BLACK);
- g.drawOval(getWheelXOrigin() - size, getWheelYOrigin() - size,
- 2 * size, 2 * size);
- g.drawOval(getWheelXOrigin() - size + width, getWheelYOrigin()-
- size + width, 2 * (size - width), 2 *
- (size - width));
- }
-
- // Draw a line on the wheel indicating the selected hue.
- if (Math.toDegrees(Math.PI * 2 - angle) <= 20 ||
- Math.toDegrees(Math.PI * 2 - angle) >= 201) {
- g.setColor(Color.WHITE);
- }
- else {
- g.setColor(Color.BLACK);
- }
- int lineX0 = (int)(Math.cos(angle) * size);
- int lineY0 = (int)(Math.sin(angle) * size);
- int lineX1 = (int)(Math.cos(angle) * (size - width));
- int lineY1 = (int)(Math.sin(angle) * (size - width));
- g.drawLine(lineX0 + size, lineY0 + size, lineX1 + size,
- lineY1 + size);
-
- // Draw the focus indicator on the triangle
- if (hasFocus() && isSet(FLAGS_FOCUSED_TRIANGLE)) {
- Graphics g2 = g.create();
- int innerR = getTriangleCircumscribedRadius();
- int a = (int)(3 * innerR / Math.sqrt(3));
- g2.translate(getWheelXOrigin(), getWheelYOrigin());
- ((Graphics2D)g2).rotate(angle + Math.PI / 2);
- g2.setColor(Color.BLACK);
- g2.drawLine(0, -innerR, a / 2, innerR / 2);
- g2.drawLine(a / 2, innerR / 2, -a / 2, innerR / 2);
- g2.drawLine(-a / 2, innerR / 2, 0, -innerR);
- g2.dispose();
- }
-
- // Draw the selected color indicator.
- g.setColor(Color.BLACK);
- g.drawOval(circleX, circleY, getIndicatorSize() - 1,
- getIndicatorSize() - 1);
- g.setColor(Color.WHITE);
- g.drawOval(circleX + 1, circleY + 1, getIndicatorSize() - 3,
- getIndicatorSize() - 3);
- }
-
- /**
- * Returns an image representing the triangle and wheel.
- */
- private Image getImage(int size) {
- if (!isSet(FLAGS_CHANGED_ANGLE) && wheelImage != null &&
- wheelImage.getWidth(null) == size * 2) {
- return wheelImage;
- }
- if (wheelImage == null || wheelImage.getWidth(null) != size) {
- wheelImage = getWheelImage(size);
- }
- int innerR = getTriangleCircumscribedRadius();
- int triangleSize = (int)(innerR * 3.0 / 2.0);
- int a = (int)(2 * triangleSize / Math.sqrt(3));
- if (triangleImage == null || triangleImage.getWidth(null) != a) {
- triangleImage = new BufferedImage(a, a,
- BufferedImage.TYPE_INT_ARGB);
- }
- Graphics g = triangleImage.getGraphics();
- g.setColor(new Color(0, 0, 0, 0));
- g.fillRect(0, 0, a, a);
- g.translate((int)(a / 2), 0);
- paintTriangle(g, triangleSize, getColor());
- g.translate((int)(-a / 2), 0);
- g.dispose();
-
- g = wheelImage.getGraphics();
- g.setColor(new Color(0, 0, 0, 0));
- g.fillOval(getWheelWidth(), getWheelWidth(),
- 2 * (size - getWheelWidth()),
- 2 * (size - getWheelWidth()));
-
- double rotate = Math.toRadians(-30.0) + angle;
- g.translate(size, size);
- ((Graphics2D)g).rotate(rotate);
- g.drawImage(triangleImage, -a / 2,
- getWheelWidth() - size, null);
- ((Graphics2D)g).rotate(-rotate);
- g.translate(a / 2, size - getWheelWidth());
-
- setFlag(FLAGS_CHANGED_ANGLE, false);
-
- return wheelImage;
- }
-
- private void paintTriangle(Graphics g, int size, Color color) {
- float[] colors = Color.RGBtoHSB(color.getRed(),
- color.getGreen(),
- color.getBlue(), null);
- float hue = colors[0];
- double dSize = (double)size;
- for (int y = 0; y < size; y++) {
- int maxX = (int)(y * Math.tan(Math.toRadians(30.0)));
- float factor = maxX * 2;
- if (maxX > 0) {
- float value = (float)(y / dSize);
- for (int x = -maxX; x <= maxX; x++) {
- float saturation = (float)x / factor + .5f;
- g.setColor(Color.getHSBColor(hue, saturation, value));
- g.fillRect(x, y, 1, 1);
- }
- }
- else {
- g.setColor(color);
- g.fillRect(0, y, 1, 1);
- }
- }
- }
-
- /**
- * Returns a color wheel image for the specified size.
- *
- * @param size Integer giving size of color wheel.
- * @return Color wheel image
- */
- private Image getWheelImage(int size) {
- int minSize = size - getWheelWidth();
- int doubleSize = size * 2;
- BufferedImage image = new BufferedImage(doubleSize, doubleSize,
- BufferedImage.TYPE_INT_ARGB);
-
- for (int y = -size; y < size; y++) {
- int ySquared = y * y;
- for (int x = -size; x < size; x++) {
- double rad = Math.sqrt(ySquared + x * x);
-
- if (rad < size && rad > minSize) {
- int rgb = colorWheelLocationToRGB(x, y, rad) |
- 0xFF000000;
- image.setRGB(x + size, y + size, rgb);
- }
- }
- }
- wheelImage = image;
- return wheelImage;
- }
-
- /**
- * Adjusts the saturation and brightness. <code>x</code> and
- * <code>y</code> give the location to adjust to and are relative
- * to the origin of the wheel/triangle.
- *
- * @param x X coordinate on the triangle to adjust to
- * @param y Y coordinate on the triangle to adjust to
- * @param checkLoc if true the location is checked to make sure
- * it is contained in the triangle, if false the location is
- * constrained to fit in the triangle.
- * @return true if the location is valid
- */
- boolean adjustSB(int x, int y, boolean checkLoc) {
- int innerR = getWheelRadius() - getWheelWidth();
- boolean resetXY = false;
- // Invert the axis.
- y = -y;
- if (checkLoc && (x < -innerR || x > innerR || y < -innerR ||
- y > innerR)) {
- return false;
- }
- // Rotate to origin and and verify x is valid.
- int triangleSize = (int)innerR * 3 / 2;
- double x1 = Math.cos(angle) * x - Math.sin(angle) * y;
- double y1 = Math.sin(angle) * x + Math.cos(angle) * y;
- if (x1 < -(innerR / 2)) {
- if (checkLoc) {
- return false;
- }
- x1 = -innerR / 2;
- resetXY = true;
- }
- else if ((int)x1 > innerR) {
- if (checkLoc) {
- return false;
- }
- x1 = innerR;
- resetXY = true;
- }
- // Verify y location is valid.
- int maxY = (int)((triangleSize - x1 - innerR / 2.0) *
- Math.tan(Math.toRadians(30.0)));
- if (y1 <= -maxY) {
- if (checkLoc) {
- return false;
- }
- y1 = -maxY;
- resetXY = true;
- }
- else if (y1 > maxY) {
- if (checkLoc) {
- return false;
- }
- y1 = maxY;
- resetXY = true;
- }
- // Rotate again to determine value and scale
- double x2 = Math.cos(Math.toRadians(-30.0)) * x1 -
- Math.sin(Math.toRadians(-30.0)) * y1;
- double y2 = Math.sin(Math.toRadians(-30.0)) * x1 +
- Math.cos(Math.toRadians(-30.0)) * y1;
- float value = Math.min(1.0f, (float)((innerR - y2) /
- (double)triangleSize));
- float maxX = (float)(Math.tan(Math.toRadians(30)) * (innerR - y2));
- float saturation = Math.min(1.0f, (float)(x2 / maxX / 2 + .5));
-
- setFlag(FLAGS_SETTING_COLOR, true);
- if (resetXY) {
- setSaturationAndBrightness(saturation, value);
- }
- else {
- setSaturationAndBrightness(saturation, value, x +
- getWheelXOrigin(),getWheelYOrigin() - y);
- }
- GTKColorChooserPanel.this.setSaturationAndBrightness(saturation,
- value, true);
- setFlag(FLAGS_SETTING_COLOR, false);
- return true;
- }
-
- /**
- * Sets the saturation and brightness.
- */
- private void setSaturationAndBrightness(float s, float b) {
- int innerR = getTriangleCircumscribedRadius();
- int triangleSize = (int)innerR * 3 / 2;
- double x = b * triangleSize;
- double maxY = x * Math.tan(Math.toRadians(30.0));
- double y = 2 * maxY * s - maxY;
- x = x - innerR;
- double x1 = Math.cos(Math.toRadians(-60.0) - angle) *
- x - Math.sin(Math.toRadians(-60.0) - angle) * y;
- double y1 = Math.sin(Math.toRadians(-60.0) - angle) * x +
- Math.cos(Math.toRadians(-60.0) - angle) * y;
- int newCircleX = (int)x1 + getWheelXOrigin();
- int newCircleY = getWheelYOrigin() - (int)y1;
-
- setSaturationAndBrightness(s, b, newCircleX, newCircleY);
- }
-
-
- /**
- * Sets the saturation and brightness.
- */
- private void setSaturationAndBrightness(float s, float b,
- int newCircleX, int newCircleY) {
- newCircleX -= getIndicatorSize() / 2;
- newCircleY -= getIndicatorSize() / 2;
-
- int minX = Math.min(newCircleX, circleX);
- int minY = Math.min(newCircleY, circleY);
-
- repaint(minX, minY, Math.max(circleX, newCircleX) - minX +
- getIndicatorSize() + 1, Math.max(circleY, newCircleY) -
- minY + getIndicatorSize() + 1);
- circleX = newCircleX;
- circleY = newCircleY;
- }
-
- /**
- * Adjusts the hue based on the passed in location.
- *
- * @param x X location to adjust to, relative to the origin of the
- * wheel
- * @param y Y location to adjust to, relative to the origin of the
- * wheel
- * @param check if true the location is checked to make sure
- * it is contained in the wheel, if false the location is
- * constrained to fit in the wheel
- * @return true if the location is valid.
- */
- private boolean adjustHue(int x, int y, boolean check) {
- double rad = Math.sqrt(x * x + y * y);
- int size = getWheelRadius();
-
- if (!check || (rad >= size - getWheelWidth() && rad < size)) {
- // Map the location to an angle and reset hue
- double angle;
- if (x == 0) {
- if (y > 0) {
- angle = Math.PI / 2.0;
- }
- else {
- angle = Math.PI + Math.PI / 2.0;
- }
- }
- else {
- angle = Math.atan((double)y / (double)x);
- if (x < 0) {
- angle += Math.PI;
- }
- else if (angle < 0) {
- angle += 2 * Math.PI;
- }
- }
- setFlag(FLAGS_SETTING_COLOR, true);
- setHue((float)(1.0 - angle / Math.PI / 2), true);
- setFlag(FLAGS_SETTING_COLOR, false);
- setHueAngle(angle);
- setSaturationAndBrightness(getSaturation(), getBrightness());
- return true;
- }
- return false;
- }
-
- /**
- * Rotates the triangle to accomodate the passed in hue.
- */
- private void setAngleFromHue(float hue) {
- setHueAngle((1.0 - hue) * Math.PI * 2);
- }
-
- /**
- * Sets the angle representing the hue.
- */
- private void setHueAngle(double angle) {
- double oldAngle = this.angle;
-
- this.angle = angle;
- if (angle != oldAngle) {
- setFlag(FLAGS_CHANGED_ANGLE, true);
- repaint();
- }
- }
-
- /**
- * Returns the size of the color indicator.
- */
- private int getIndicatorSize() {
- return 8;
- }
-
- /**
- * Returns the circumscribed radius of the triangle.
- */
- private int getTriangleCircumscribedRadius() {
- return 72;
- }
-
- /**
- * Returns the x origin of the wheel and triangle.
- */
- private int getWheelXOrigin() {
- return 85;
- }
-
- /**
- * Returns y origin of the wheel and triangle.
- */
- private int getWheelYOrigin() {
- return 85;
- }
-
- /**
- * Returns the width of the wheel.
- */
- private int getWheelWidth() {
- return 13;
- }
-
- /**
- * Sets the focus to one of: 0 no one, 1 the wheel or 2 the triangle.
- */
- private void setFocusType(int type) {
- if (type == 0) {
- setFlag(FLAGS_FOCUSED_WHEEL, false);
- setFlag(FLAGS_FOCUSED_TRIANGLE, false);
- repaint();
- }
- else {
- int toSet = FLAGS_FOCUSED_WHEEL;
- int toUnset = FLAGS_FOCUSED_TRIANGLE;
-
- if (type == 2) {
- toSet = FLAGS_FOCUSED_TRIANGLE;
- toUnset = FLAGS_FOCUSED_WHEEL;
- }
- if (!isSet(toSet)) {
- setFlag(toSet, true);
- repaint();
- setFlag(toUnset, false);
- }
- }
- }
-
- /**
- * Returns the radius of the wheel.
- */
- private int getWheelRadius() {
- // As far as I can tell, GTK doesn't allow stretching this
- // widget
- return 85;
- }
-
- /**
- * Updates the flags bitmask.
- */
- private void setFlag(int flag, boolean value) {
- if (value) {
- flags |= flag;
- }
- else {
- flags &= ~flag;
- }
- }
-
- /**
- * Returns true if a particular flag has been set.
- */
- private boolean isSet(int flag) {
- return ((flags & flag) == flag);
- }
-
- /**
- * Returns the RGB color to use for the specified location. The
- * passed in point must be on the color wheel and be relative to the
- * origin of the color wheel.
- *
- * @param x X location to get color for
- * @param y Y location to get color for
- * @param rad Radius from center of color wheel
- * @param integer with red, green and blue components
- */
- private int colorWheelLocationToRGB(int x, int y, double rad) {
- double angle = Math.acos((double)x / rad);
- int rgb;
-
- if (angle < PI_3) {
- if (y < 0) {
- // FFFF00 - FF0000
- rgb = 0xFF0000 | (int)Math.min(255,
- (int)(255 * angle / PI_3)) << 8;
- }
- else {
- // FF0000 - FF00FF
- rgb = 0xFF0000 | (int)Math.min(255,
- (int)(255 * angle / PI_3));
- }
- }
- else if (angle < 2 * PI_3) {
- angle -= PI_3;
- if (y < 0) {
- // 00FF00 - FFFF00
- rgb = 0x00FF00 | (int)Math.max(0, 255 -
- (int)(255 * angle / PI_3)) << 16;
- }
- else {
- // FF00FF - 0000FF
- rgb = 0x0000FF | (int)Math.max(0, 255 -
- (int)(255 * angle / PI_3)) << 16;
- }
- }
- else {
- angle -= 2 * PI_3;
- if (y < 0) {
- // 00FFFF - 00FF00
- rgb = 0x00FF00 | (int)Math.min(255,
- (int)(255 * angle / PI_3));
- }
- else {
- // 0000FF - 00FFFF
- rgb = 0x0000FF | (int)Math.min(255,
- (int)(255 * angle / PI_3)) << 8;
- }
- }
- return rgb;
- }
-
- /**
- * Increments the hue.
- */
- void incrementHue(boolean positive) {
- float hue = triangle.getGTKColorChooserPanel().getHue();
-
- if (positive) {
- hue += 1.0f / 360.0f;
- }
- else {
- hue -= 1.0f / 360.0f;
- }
- if (hue > 1) {
- hue -= 1;
- }
- else if (hue < 0) {
- hue += 1;
- }
- getGTKColorChooserPanel().setHue(hue, true);
- }
- }
-
-
- /**
- * Action class used for colors.
- */
- private static class ColorAction extends AbstractAction {
- private int type;
-
- ColorAction(String name, int type) {
- super(name);
- this.type = type;
- }
-
- public void actionPerformed(ActionEvent e) {
- ColorTriangle triangle = (ColorTriangle)e.getSource();
-
- if (triangle.isWheelFocused()) {
- float hue = triangle.getGTKColorChooserPanel().getHue();
-
- switch (type) {
- case 0:
- case 2:
- triangle.incrementHue(true);
- break;
- case 1:
- case 3:
- triangle.incrementHue(false);
- break;
- case 4:
- triangle.focusTriangle();
- break;
- case 5:
- compositeRequestFocus(triangle, false);
- break;
- }
- }
- else {
- int xDelta = 0;
- int yDelta = 0;
-
- switch (type) {
- case 0:
- // up
- yDelta--;
- break;
- case 1:
- // down
- yDelta++;
- break;
- case 2:
- // left
- xDelta--;
- break;
- case 3:
- // right
- xDelta++;
- break;
- case 4:
- compositeRequestFocus(triangle, true);
- return;
- case 5:
- triangle.focusWheel();
- return;
- }
- triangle.adjustSB(triangle.getColorX() + xDelta,
- triangle.getColorY() + yDelta, true);
- }
- }
- }
-
-
- private class OpaqueLabel extends JLabel {
- public boolean isOpaque() {
- return true;
- }
- }
- }