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