KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > java > swing > plaf > gtk > GTKColorChooserPanel


1 /*
2  * @(#)GTKColorChooserPanel.java 1.10 05/03/03
3  *
4  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package com.sun.java.swing.plaf.gtk;
8
9 import java.awt.*;
10 import java.awt.event.*;
11 import java.awt.image.*;
12 import javax.swing.*;
13 import javax.swing.colorchooser.*;
14 import javax.swing.event.*;
15 import javax.swing.plaf.*;
16
17 /**
18  * A color chooser panel mimicking that of GTK's: a color wheel showing
19  * hue and a triangle that varies saturation and brightness.
20  *
21  * @version 1.10, 03/03/05
22  * @author Scott Violet
23  */

24 class GTKColorChooserPanel extends AbstractColorChooserPanel implements
25               ChangeListener {
26     private static final float PI_3 = (float)(Math.PI / 3);
27
28     private ColorTriangle triangle;
29     private JLabel lastLabel;
30     private JLabel label;
31
32     private JSpinner hueSpinner;
33     private JSpinner saturationSpinner;
34     private JSpinner valueSpinner;
35
36     private JSpinner redSpinner;
37     private JSpinner greenSpinner;
38     private JSpinner blueSpinner;
39
40     private JTextField colorNameTF;
41
42     private boolean settingColor;
43
44     // The colors are mirrored to avoid creep in adjusting an individual
45
// value.
46
private float hue;
47     private float saturation;
48     private float brightness;
49
50
51
52     /**
53      * Convenience method to transfer focus to the next child of component.
54      */

55     // PENDING: remove this when a variant of this is added to awt.
56
static void compositeRequestFocus(Component component, boolean direction) {
57     if (component instanceof Container) {
58         Container container = (Container)component;
59         if (container.isFocusCycleRoot()) {
60         FocusTraversalPolicy policy = container.
61                                               getFocusTraversalPolicy();
62         Component comp = policy.getDefaultComponent(container);
63         if (comp!=null) {
64             comp.requestFocus();
65             return;
66         }
67         }
68         Container rootAncestor = container.getFocusCycleRootAncestor();
69         if (rootAncestor!=null) {
70         FocusTraversalPolicy policy = rootAncestor.
71                                                   getFocusTraversalPolicy();
72         Component comp;
73
74                 if (direction) {
75                     comp = policy.getComponentAfter(rootAncestor, container);
76                 }
77                 else {
78                     comp = policy.getComponentBefore(rootAncestor, container);
79                 }
80                 if (comp != null) {
81             comp.requestFocus();
82             return;
83         }
84         }
85     }
86     component.requestFocus();
87     }
88
89
90     /**
91      * Returns a user presentable description of this GTKColorChooserPane.
92      */

93     public String JavaDoc getDisplayName() {
94         return (String JavaDoc)UIManager.get("GTKColorChooserPanel.nameText");
95     }
96
97     /**
98      * Returns the mnemonic to use with <code>getDisplayName</code>.
99      */

100     public int getMnemonic() {
101         String JavaDoc m = (String JavaDoc)UIManager.get("GTKColorChooserPanel.mnemonic");
102
103         if (m != null) {
104             try {
105                 int value = Integer.parseInt(m);
106
107                 return value;
108             } catch (NumberFormatException JavaDoc nfe) {}
109         }
110         return -1;
111     }
112
113     /**
114      * Character to underline that represents the mnemonic.
115      */

116     public int getDisplayedMnemonicIndex() {
117         String JavaDoc m = (String JavaDoc)UIManager.get(
118                            "GTKColorChooserPanel.displayedMnemonicIndex");
119
120         if (m != null) {
121             try {
122                 int value = Integer.parseInt(m);
123
124                 return value;
125             } catch (NumberFormatException JavaDoc nfe) {}
126         }
127         return -1;
128     }
129
130     public Icon getSmallDisplayIcon() {
131         return null;
132     }
133
134     public Icon getLargeDisplayIcon() {
135         return null;
136     }
137
138     public void uninstallChooserPanel(JColorChooser enclosingChooser) {
139         super.uninstallChooserPanel(enclosingChooser);
140         removeAll();
141     }
142
143     /**
144      * Builds and configures the widgets for the GTKColorChooserPanel.
145      */

146     protected void buildChooser() {
147         triangle = new ColorTriangle();
148         triangle.setName("GTKColorChooserPanel.triangle");
149
150         // PENDING: when we straighten out user setting opacity, this should
151
// be changed.
152
label = new OpaqueLabel();
153         label.setName("GTKColorChooserPanel.colorWell");
154         label.setOpaque(true);
155         label.setMinimumSize(new Dimension(67, 32));
156         label.setPreferredSize(new Dimension(67, 32));
157         label.setMaximumSize(new Dimension(67, 32));
158
159         // PENDING: when we straighten out user setting opacity, this should
160
// be changed.
161
lastLabel = new OpaqueLabel();
162         lastLabel.setName("GTKColorChooserPanel.lastColorWell");
163         lastLabel.setOpaque(true);
164         lastLabel.setMinimumSize(new Dimension(67, 32));
165         lastLabel.setPreferredSize(new Dimension(67, 32));
166         lastLabel.setMaximumSize(new Dimension(67, 32));
167
168         hueSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 360, 1));
169         configureSpinner(hueSpinner, "GTKColorChooserPanel.hueSpinner");
170         saturationSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 255, 1));
171         configureSpinner(saturationSpinner,
172                          "GTKColorChooserPanel.saturationSpinner");
173         valueSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 255, 1));
174         configureSpinner(valueSpinner, "GTKColorChooserPanel.valueSpinner");
175         redSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 255, 1));
176         configureSpinner(redSpinner, "GTKColorChooserPanel.redSpinner");
177         greenSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 255, 1));
178         configureSpinner(greenSpinner, "GTKColorChooserPanel.greenSpinner");
179         blueSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 255, 1));
180         configureSpinner(blueSpinner, "GTKColorChooserPanel.blueSpinner");
181
182         colorNameTF = new JTextField(8);
183
184         setLayout(new GridBagLayout());
185
186         add(this, "GTKColorChooserPanel.hue", hueSpinner, -1, -1);
187         add(this, "GTKColorChooserPanel.red", redSpinner, -1, -1);
188         add(this, "GTKColorChooserPanel.saturation", saturationSpinner, -1,-1);
189         add(this, "GTKColorChooserPanel.green", greenSpinner, -1, -1);
190         add(this, "GTKColorChooserPanel.value", valueSpinner, -1, -1);
191         add(this, "GTKColorChooserPanel.blue", blueSpinner, -1, -1);
192
193         add(new JSeparator(SwingConstants.HORIZONTAL), new
194                   GridBagConstraints(1, 3, 4, 1, 1, 0,
195                   GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL,
196                   new Insets(14, 0, 0, 0), 0, 0));
197
198         add(this, "GTKColorChooserPanel.colorName", colorNameTF, 0, 4);
199
200         add(triangle, new GridBagConstraints(0, 0, 1, 5, 0, 0,
201                       GridBagConstraints.LINE_START, GridBagConstraints.NONE,
202                       new Insets(14, 20, 2, 9), 0, 0));
203
204         Box hBox = Box.createHorizontalBox();
205         hBox.add(lastLabel);
206         hBox.add(label);
207         add(hBox, new GridBagConstraints(0, 5, 1, 1, 0, 0,
208                       GridBagConstraints.CENTER, GridBagConstraints.NONE,
209                       new Insets(0, 0, 0, 0), 0, 0));
210
211         add(new JSeparator(SwingConstants.HORIZONTAL), new
212                   GridBagConstraints(0, 6, 5, 1, 1, 0,
213                   GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL,
214                   new Insets(12, 0, 0, 0), 0, 0));
215     }
216
217     /**
218      * Configures the spinner.
219      */

220     private void configureSpinner(JSpinner spinner, String JavaDoc name) {
221         spinner.addChangeListener(this);
222         spinner.setName(name);
223         JComponent editor = spinner.getEditor();
224         if (editor instanceof JSpinner.DefaultEditor) {
225             JFormattedTextField ftf = ((JSpinner.DefaultEditor)editor).
226                                                  getTextField();
227
228             ftf.setFocusLostBehavior(JFormattedTextField.COMMIT_OR_REVERT);
229         }
230     }
231
232     /**
233      * Adds the widget creating a JLabel with the specified name.
234      */

235     private void add(Container parent, String JavaDoc key, JComponent widget,
236                      int x, int y) {
237         JLabel label = new JLabel(UIManager.getString(key + "Text",
238                                                       getLocale()));
239         String JavaDoc mnemonic = (String JavaDoc)UIManager.get(key + "Mnemonic", getLocale());
240
241         if (mnemonic != null) {
242             try {
243                 label.setDisplayedMnemonic(Integer.parseInt(mnemonic));
244             } catch (NumberFormatException JavaDoc nfe) {
245             }
246             String JavaDoc mnemonicIndex = (String JavaDoc)UIManager.get(key + "MnemonicIndex",
247                                                     getLocale());
248
249             if (mnemonicIndex != null) {
250                 try {
251                     label.setDisplayedMnemonicIndex(Integer.parseInt(
252                                                         mnemonicIndex));
253                 } catch (NumberFormatException JavaDoc nfe) {
254                 }
255             }
256         }
257         label.setLabelFor(widget);
258         if (x < 0) {
259             x = parent.getComponentCount() % 4;
260         }
261         if (y < 0) {
262             y = parent.getComponentCount() / 4;
263         }
264         GridBagConstraints con = new GridBagConstraints(x + 1, y, 1, 1, 0, 0,
265                    GridBagConstraints.FIRST_LINE_END, GridBagConstraints.NONE,
266                    new Insets(4, 0, 0, 4), 0, 0);
267         if (y == 0) {
268             con.insets.top = 14;
269         }
270         parent.add(label, con);
271         con.gridx++;
272         parent.add(widget, con);
273     }
274
275     /**
276      * Refreshes the display from the model.
277      */

278     public void updateChooser() {
279         if (!settingColor) {
280             lastLabel.setBackground(getColorFromModel());
281             setColor(getColorFromModel(), true, true, false);
282         }
283     }
284
285     /**
286      * Resets the red component of the selected color.
287      */

288     private void setRed(int red) {
289         setRGB(red << 16 | getColor().getGreen() << 8 | getColor().getBlue());
290     }
291
292     /**
293      * Resets the green component of the selected color.
294      */

295     private void setGreen(int green) {
296         setRGB(getColor().getRed() << 16 | green << 8 | getColor().getBlue());
297     }
298
299     /**
300      * Resets the blue component of the selected color.
301      */

302     private void setBlue(int blue) {
303         setRGB(getColor().getRed() << 16 | getColor().getGreen() << 8 | blue);
304     }
305
306     /**
307      * Sets the hue of the selected color and updates the display if
308      * necessary.
309      */

310     private void setHue(float hue, boolean update) {
311         setHSB(hue, saturation, brightness);
312         if (update) {
313             settingColor = true;
314             hueSpinner.setValue(new Integer JavaDoc((int)(hue * 360)));
315             settingColor = false;
316         }
317     }
318
319     /**
320      * Returns the current amount of hue.
321      */

322     private float getHue() {
323         return hue;
324     }
325
326     /**
327      * Resets the saturation.
328      */

329     private void setSaturation(float saturation) {
330         setHSB(hue, saturation, brightness);
331     }
332
333     /**
334      * Returns the saturation.
335      */

336     private float getSaturation() {
337         return saturation;
338     }
339
340     /**
341      * Sets the brightness.
342      */

343     private void setBrightness(float brightness) {
344         setHSB(hue, saturation, brightness);
345     }
346
347     /**
348      * Returns the brightness.
349      */

350     private float getBrightness() {
351         return brightness;
352     }
353
354     /**
355      * Sets the saturation and brightness and updates the display if
356      * necessary.
357      */

358     private void setSaturationAndBrightness(float s, float b, boolean update) {
359         setHSB(hue, s, b);
360         if (update) {
361             settingColor = true;
362             saturationSpinner.setValue(new Integer JavaDoc((int)(s * 255)));
363             valueSpinner.setValue(new Integer JavaDoc((int)(b * 255)));
364             settingColor = false;
365         }
366     }
367
368     /**
369      * Resets the rgb values.
370      */

371     private void setRGB(int rgb) {
372         Color color = new Color(rgb);
373
374         setColor(color, false, true, true);
375
376         settingColor = true;
377         hueSpinner.setValue(new Integer JavaDoc((int)(hue * 360)));
378         saturationSpinner.setValue(new Integer JavaDoc((int)(saturation * 255)));
379         valueSpinner.setValue(new Integer JavaDoc((int)(brightness * 255)));
380         settingColor = false;
381     }
382
383     /**
384      * Resets the hsb values.
385      */

386     private void setHSB(float h, float s, float b) {
387         Color color = Color.getHSBColor(h, s, b);
388
389         this.hue = h;
390         this.saturation = s;
391         this.brightness = b;
392         setColor(color, false, false, true);
393
394         settingColor = true;
395         redSpinner.setValue(new Integer JavaDoc(color.getRed()));
396         greenSpinner.setValue(new Integer JavaDoc(color.getGreen()));
397         blueSpinner.setValue(new Integer JavaDoc(color.getBlue()));
398         settingColor = false;
399     }
400
401     
402     /**
403      * Rests the color.
404      *
405      * @param color new Color
406      * @param updateSpinners whether or not to update the spinners.
407      * @param updateHSB if true, the hsb fields are updated based on the
408      * new color
409      * @param updateModel if true, the model is set.
410      */

411     private void setColor(Color color, boolean updateSpinners,
412                           boolean updateHSB, boolean updateModel) {
413         if (color == null) {
414             color = Color.BLACK;
415         }
416
417         settingColor = true;
418
419         if (updateHSB) {
420             float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(),
421                                          color.getBlue(), null);
422             hue = hsb[0];
423             saturation = hsb[1];
424             brightness = hsb[2];
425         }
426
427         if (updateModel) {
428             getColorSelectionModel().setSelectedColor(color);
429         }
430
431         triangle.setColor(hue, saturation, brightness);
432         label.setBackground(color);
433         // Force Integer to pad the string with 0's by adding 0x1000000 and
434
// then removing the first character.
435
String JavaDoc hexString = Integer.toHexString(
436                   (color.getRGB() & 0xFFFFFF) | 0x1000000);
437         colorNameTF.setText("#" + hexString.substring(1));
438
439         if (updateSpinners) {
440             redSpinner.setValue(new Integer JavaDoc(color.getRed()));
441             greenSpinner.setValue(new Integer JavaDoc(color.getGreen()));
442             blueSpinner.setValue(new Integer JavaDoc(color.getBlue()));
443
444             hueSpinner.setValue(new Integer JavaDoc((int)(hue * 360)));
445             saturationSpinner.setValue(new Integer JavaDoc((int)(saturation * 255)));
446             valueSpinner.setValue(new Integer JavaDoc((int)(brightness * 255)));
447         }
448         settingColor = false;
449     }
450
451     public Color getColor() {
452         return label.getBackground();
453     }
454
455     /**
456      * ChangeListener method, updates the necessary display widgets.
457      */

458     public void stateChanged(ChangeEvent e) {
459         if (settingColor) {
460             return;
461         }
462         Color color = getColor();
463
464         if (e.getSource() == hueSpinner) {
465             setHue(((Number JavaDoc)hueSpinner.getValue()).floatValue() / 360, false);
466         }
467         else if (e.getSource() == saturationSpinner) {
468             setSaturation(((Number JavaDoc)saturationSpinner.getValue()).
469                           floatValue() / 255);
470         }
471         else if (e.getSource() == valueSpinner) {
472             setBrightness(((Number JavaDoc)valueSpinner.getValue()).
473                           floatValue() / 255);
474         }
475         else if (e.getSource() == redSpinner) {
476             setRed(((Number JavaDoc)redSpinner.getValue()).intValue());
477         }
478         else if (e.getSource() == greenSpinner) {
479             setGreen(((Number JavaDoc)greenSpinner.getValue()).intValue());
480         }
481         else if (e.getSource() == blueSpinner) {
482             setBlue(((Number JavaDoc)blueSpinner.getValue()).intValue());
483         }
484     }
485
486
487
488     /**
489      * Flag indicating the angle, or hue, has changed and the triangle
490      * needs to be recreated.
491      */

492     private static final int FLAGS_CHANGED_ANGLE = 1 << 0;
493     /**
494      * Indicates the wheel is being dragged.
495      */

496     private static final int FLAGS_DRAGGING = 1 << 1;
497     /**
498      * Indicates the triangle is being dragged.
499      */

500     private static final int FLAGS_DRAGGING_TRIANGLE = 1 << 2;
501     /**
502      * Indicates a color is being set and we should ignore setColor
503      */

504     private static final int FLAGS_SETTING_COLOR = 1 << 3;
505     /**
506      * Indicates the wheel has focus.
507      */

508     private static final int FLAGS_FOCUSED_WHEEL = 1 << 4;
509     /**
510      * Indicates the triangle has focus.
511      */

512     private static final int FLAGS_FOCUSED_TRIANGLE = 1 << 5;
513
514
515     /**
516      * Class responsible for rendering a color wheel and color triangle.
517      */

518     private class ColorTriangle extends JPanel {
519         /**
520          * Cached image of the wheel.
521          */

522         private Image wheelImage;
523
524         /**
525          * Cached image of the triangle.
526          */

527         private Image triangleImage;
528
529         /**
530          * Angle triangle is rotated by.
531          */

532         private double angle;
533
534         /**
535          * Boolean bitmask.
536          */

537         private int flags;
538
539         /**
540          * X location of selected color indicator.
541          */

542         private int circleX;
543         /**
544          * Y location of selected color indicator.
545          */

546         private int circleY;
547
548
549         public ColorTriangle() {
550             enableEvents(AWTEvent.FOCUS_EVENT_MASK);
551             enableEvents(AWTEvent.MOUSE_EVENT_MASK);
552             enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK);
553
554             setMinimumSize(new Dimension(getWheelRadius() * 2 + 2,
555                                          getWheelRadius() * 2 + 2));
556             setPreferredSize(new Dimension(getWheelRadius() * 2 + 2,
557                                            getWheelRadius() * 2 + 2));
558
559             // We want to handle tab ourself.
560
setFocusTraversalKeysEnabled(false);
561
562             // PENDING: this should come from the style.
563
getInputMap().put(KeyStroke.getKeyStroke("UP"), "up");
564             getInputMap().put(KeyStroke.getKeyStroke("DOWN"), "down");
565             getInputMap().put(KeyStroke.getKeyStroke("LEFT"), "left");
566             getInputMap().put(KeyStroke.getKeyStroke("RIGHT"), "right");
567
568             getInputMap().put(KeyStroke.getKeyStroke("KP_UP"), "up");
569             getInputMap().put(KeyStroke.getKeyStroke("KP_DOWN"), "down");
570             getInputMap().put(KeyStroke.getKeyStroke("KP_LEFT"), "left");
571             getInputMap().put(KeyStroke.getKeyStroke("KP_RIGHT"), "right");
572
573             getInputMap().put(KeyStroke.getKeyStroke("TAB"), "focusNext");
574             getInputMap().put(KeyStroke.getKeyStroke("shift TAB"),"focusLast");
575
576             ActionMap map = (ActionMap)UIManager.get(
577                                        "GTKColorChooserPanel.actionMap");
578
579             if (map == null) {
580                 map = new ActionMapUIResource();
581                 map.put("left", new ColorAction("left", 2));
582                 map.put("right", new ColorAction("right", 3));
583                 map.put("up", new ColorAction("up", 0));
584                 map.put("down", new ColorAction("down", 1));
585                 map.put("focusNext", new ColorAction("focusNext", 4));
586                 map.put("focusLast", new ColorAction("focusLast", 5));
587         UIManager.getLookAndFeelDefaults().put(
588                              "GTKColorChooserPanel.actionMap", map);
589             }
590             SwingUtilities.replaceUIActionMap(this, map);
591         }
592
593         /**
594          * Returns the GTKColorChooserPanel.
595          */

596         GTKColorChooserPanel getGTKColorChooserPanel() {
597             return GTKColorChooserPanel.this;
598         }
599
600         /**
601          * Gives focus to the wheel.
602          */

603         void focusWheel() {
604             setFocusType(1);
605         }
606
607         /**
608          * Gives focus to the triangle.
609          */

610         void focusTriangle() {
611             setFocusType(2);
612         }
613
614         /**
615          * Returns true if the wheel currently has focus.
616          */

617         boolean isWheelFocused() {
618             return isSet(FLAGS_FOCUSED_WHEEL);
619         }
620
621         /**
622          * Resets the selected color.
623          */

624         public void setColor(float h, float s, float b) {
625             if (isSet(FLAGS_SETTING_COLOR)) {
626                 return;
627             }
628
629             setAngleFromHue(h);
630             setSaturationAndBrightness(s, b);
631         }
632
633         /**
634          * Returns the selected color.
635          */

636         public Color getColor() {
637             return GTKColorChooserPanel.this.getColor();
638         }
639
640         /**
641          * Returns the x location of the selected color indicator.
642          */

643         int getColorX() {
644             return circleX + getIndicatorSize() / 2 - getWheelXOrigin();
645         }
646
647         /**
648          * Returns the y location of the selected color indicator.
649          */

650         int getColorY() {
651             return circleY + getIndicatorSize() / 2 - getWheelYOrigin();
652         }
653
654         protected void processEvent(AWTEvent e) {
655             if (e.getID() == MouseEvent.MOUSE_PRESSED ||
656                    ((isSet(FLAGS_DRAGGING) ||isSet(FLAGS_DRAGGING_TRIANGLE)) &&
657                    e.getID() == MouseEvent.MOUSE_DRAGGED)) {
658                 // Assign focus to either the wheel or triangle and attempt
659
// to drag either the wheel or triangle.
660
int size = getWheelRadius();
661                 int x = ((MouseEvent)e).getX() - size;
662                 int y = ((MouseEvent)e).getY() - size;
663
664                 if (!hasFocus()) {
665                     requestFocus();
666                 }
667                 if (!isSet(FLAGS_DRAGGING_TRIANGLE) &&
668                       adjustHue(x, y, e.getID() == MouseEvent.MOUSE_PRESSED)) {
669                     setFlag(FLAGS_DRAGGING, true);
670                     setFocusType(1);
671                 }
672                 else if (adjustSB(x, y, e.getID() ==
673                                         MouseEvent.MOUSE_PRESSED)) {
674                     setFlag(FLAGS_DRAGGING_TRIANGLE, true);
675                     setFocusType(2);
676                 }
677                 else {
678                     setFocusType(2);
679                 }
680             }
681             else if (e.getID() == MouseEvent.MOUSE_RELEASED) {
682                 // Stopped dragging
683
setFlag(FLAGS_DRAGGING_TRIANGLE, false);
684                 setFlag(FLAGS_DRAGGING, false);
685             }
686             else if (e.getID() == FocusEvent.FOCUS_LOST) {
687                 // Reset the flags to indicate no one has focus
688
setFocusType(0);
689             }
690             else if (e.getID() == FocusEvent.FOCUS_GAINED) {
691                 // Gained focus, reassign focus to the wheel if no one
692
// currently has focus.
693
if (!isSet(FLAGS_FOCUSED_TRIANGLE) &&
694                           !isSet(FLAGS_FOCUSED_WHEEL)) {
695                     setFlag(FLAGS_FOCUSED_WHEEL, true);
696                     setFocusType(1);
697                 }
698                 repaint();
699             }
700             super.processEvent(e);
701         }
702
703         public void paintComponent(Graphics g) {
704             super.paintComponent(g);
705
706             // Draw the wheel and triangle
707
int size = getWheelRadius();
708             int width = getWheelWidth();
709             Image image = getImage(size);
710             g.drawImage(image, getWheelXOrigin() - size,
711                         getWheelYOrigin() - size, null);
712
713             // Draw the focus indicator for the wheel
714
if (hasFocus() && isSet(FLAGS_FOCUSED_WHEEL)) {
715                 g.setColor(Color.BLACK);
716                 g.drawOval(getWheelXOrigin() - size, getWheelYOrigin() - size,
717                            2 * size, 2 * size);
718                 g.drawOval(getWheelXOrigin() - size + width, getWheelYOrigin()-
719                            size + width, 2 * (size - width), 2 *
720                            (size - width));
721             }
722
723             // Draw a line on the wheel indicating the selected hue.
724
if (Math.toDegrees(Math.PI * 2 - angle) <= 20 ||
725                      Math.toDegrees(Math.PI * 2 - angle) >= 201) {
726                 g.setColor(Color.WHITE);
727             }
728             else {
729                 g.setColor(Color.BLACK);
730             }
731             int lineX0 = (int)(Math.cos(angle) * size);
732             int lineY0 = (int)(Math.sin(angle) * size);
733             int lineX1 = (int)(Math.cos(angle) * (size - width));
734             int lineY1 = (int)(Math.sin(angle) * (size - width));
735             g.drawLine(lineX0 + size, lineY0 + size, lineX1 + size,
736                        lineY1 + size);
737
738             // Draw the focus indicator on the triangle
739
if (hasFocus() && isSet(FLAGS_FOCUSED_TRIANGLE)) {
740                 Graphics g2 = g.create();
741                 int innerR = getTriangleCircumscribedRadius();
742                 int a = (int)(3 * innerR / Math.sqrt(3));
743                 g2.translate(getWheelXOrigin(), getWheelYOrigin());
744                 ((Graphics2D)g2).rotate(angle + Math.PI / 2);
745                 g2.setColor(Color.BLACK);
746                 g2.drawLine(0, -innerR, a / 2, innerR / 2);
747                 g2.drawLine(a / 2, innerR / 2, -a / 2, innerR / 2);
748                 g2.drawLine(-a / 2, innerR / 2, 0, -innerR);
749                 g2.dispose();
750             }
751
752             // Draw the selected color indicator.
753
g.setColor(Color.BLACK);
754             g.drawOval(circleX, circleY, getIndicatorSize() - 1,
755                        getIndicatorSize() - 1);
756             g.setColor(Color.WHITE);
757             g.drawOval(circleX + 1, circleY + 1, getIndicatorSize() - 3,
758                        getIndicatorSize() - 3);
759         }
760
761         /**
762          * Returns an image representing the triangle and wheel.
763          */

764         private Image getImage(int size) {
765             if (!isSet(FLAGS_CHANGED_ANGLE) && wheelImage != null &&
766                         wheelImage.getWidth(null) == size * 2) {
767                 return wheelImage;
768             }
769             if (wheelImage == null || wheelImage.getWidth(null) != size) {
770                 wheelImage = getWheelImage(size);
771             }
772             int innerR = getTriangleCircumscribedRadius();
773             int triangleSize = (int)(innerR * 3.0 / 2.0);
774             int a = (int)(2 * triangleSize / Math.sqrt(3));
775             if (triangleImage == null || triangleImage.getWidth(null) != a) {
776                 triangleImage = new BufferedImage(a, a,
777                                                   BufferedImage.TYPE_INT_ARGB);
778             }
779             Graphics g = triangleImage.getGraphics();
780             g.setColor(new Color(0, 0, 0, 0));
781             g.fillRect(0, 0, a, a);
782             g.translate((int)(a / 2), 0);
783             paintTriangle(g, triangleSize, getColor());
784             g.translate((int)(-a / 2), 0);
785             g.dispose();
786
787             g = wheelImage.getGraphics();
788             g.setColor(new Color(0, 0, 0, 0));
789             g.fillOval(getWheelWidth(), getWheelWidth(),
790                        2 * (size - getWheelWidth()),
791                        2 * (size - getWheelWidth()));
792
793             double rotate = Math.toRadians(-30.0) + angle;
794             g.translate(size, size);
795             ((Graphics2D)g).rotate(rotate);
796             g.drawImage(triangleImage, -a / 2,
797                         getWheelWidth() - size, null);
798             ((Graphics2D)g).rotate(-rotate);
799             g.translate(a / 2, size - getWheelWidth());
800
801             setFlag(FLAGS_CHANGED_ANGLE, false);
802
803             return wheelImage;
804         }
805
806         private void paintTriangle(Graphics g, int size, Color color) {
807             float[] colors = Color.RGBtoHSB(color.getRed(),
808                                             color.getGreen(),
809                                             color.getBlue(), null);
810             float hue = colors[0];
811             double dSize = (double)size;
812             for (int y = 0; y < size; y++) {
813                 int maxX = (int)(y * Math.tan(Math.toRadians(30.0)));
814                 float factor = maxX * 2;
815                 if (maxX > 0) {
816                     float value = (float)(y / dSize);
817                     for (int x = -maxX; x <= maxX; x++) {
818                         float saturation = (float)x / factor + .5f;
819                         g.setColor(Color.getHSBColor(hue, saturation, value));
820                         g.fillRect(x, y, 1, 1);
821                     }
822                 }
823                 else {
824                     g.setColor(color);
825                     g.fillRect(0, y, 1, 1);
826                 }
827             }
828         }
829
830         /**
831          * Returns a color wheel image for the specified size.
832          *
833          * @param size Integer giving size of color wheel.
834          * @return Color wheel image
835          */

836         private Image getWheelImage(int size) {
837             int minSize = size - getWheelWidth();
838             int doubleSize = size * 2;
839             BufferedImage image = new BufferedImage(doubleSize, doubleSize,
840                                               BufferedImage.TYPE_INT_ARGB);
841
842             for (int y = -size; y < size; y++) {
843                 int ySquared = y * y;
844                 for (int x = -size; x < size; x++) {
845                     double rad = Math.sqrt(ySquared + x * x);
846
847                     if (rad < size && rad > minSize) {
848                         int rgb = colorWheelLocationToRGB(x, y, rad) |
849                               0xFF000000;
850                         image.setRGB(x + size, y + size, rgb);
851                     }
852                 }
853             }
854             wheelImage = image;
855             return wheelImage;
856         }
857
858         /**
859          * Adjusts the saturation and brightness. <code>x</code> and
860          * <code>y</code> give the location to adjust to and are relative
861          * to the origin of the wheel/triangle.
862          *
863          * @param x X coordinate on the triangle to adjust to
864          * @param y Y coordinate on the triangle to adjust to
865          * @param checkLoc if true the location is checked to make sure
866          * it is contained in the triangle, if false the location is
867          * constrained to fit in the triangle.
868          * @return true if the location is valid
869          */

870         boolean adjustSB(int x, int y, boolean checkLoc) {
871             int innerR = getWheelRadius() - getWheelWidth();
872             boolean resetXY = false;
873             // Invert the axis.
874
y = -y;
875             if (checkLoc && (x < -innerR || x > innerR || y < -innerR ||
876                              y > innerR)) {
877                 return false;
878             }
879             // Rotate to origin and and verify x is valid.
880
int triangleSize = (int)innerR * 3 / 2;
881             double x1 = Math.cos(angle) * x - Math.sin(angle) * y;
882             double y1 = Math.sin(angle) * x + Math.cos(angle) * y;
883             if (x1 < -(innerR / 2)) {
884                 if (checkLoc) {
885                     return false;
886                 }
887                 x1 = -innerR / 2;
888                 resetXY = true;
889             }
890             else if ((int)x1 > innerR) {
891                 if (checkLoc) {
892                     return false;
893                 }
894                 x1 = innerR;
895                 resetXY = true;
896             }
897             // Verify y location is valid.
898
int maxY = (int)((triangleSize - x1 - innerR / 2.0) *
899                              Math.tan(Math.toRadians(30.0)));
900             if (y1 <= -maxY) {
901                 if (checkLoc) {
902                     return false;
903                 }
904                 y1 = -maxY;
905                 resetXY = true;
906             }
907             else if (y1 > maxY) {
908                 if (checkLoc) {
909                     return false;
910                 }
911                 y1 = maxY;
912                 resetXY = true;
913             }
914             // Rotate again to determine value and scale
915
double x2 = Math.cos(Math.toRadians(-30.0)) * x1 -
916                  Math.sin(Math.toRadians(-30.0)) * y1;
917             double y2 = Math.sin(Math.toRadians(-30.0)) * x1 +
918                  Math.cos(Math.toRadians(-30.0)) * y1;
919             float value = Math.min(1.0f, (float)((innerR - y2) /
920                                                 (double)triangleSize));
921             float maxX = (float)(Math.tan(Math.toRadians(30)) * (innerR - y2));
922             float saturation = Math.min(1.0f, (float)(x2 / maxX / 2 + .5));
923
924             setFlag(FLAGS_SETTING_COLOR, true);
925             if (resetXY) {
926                 setSaturationAndBrightness(saturation, value);
927             }
928             else {
929                 setSaturationAndBrightness(saturation, value, x +
930                                       getWheelXOrigin(),getWheelYOrigin() - y);
931             }
932             GTKColorChooserPanel.this.setSaturationAndBrightness(saturation,
933                                                                  value, true);
934             setFlag(FLAGS_SETTING_COLOR, false);
935             return true;
936         }
937
938         /**
939          * Sets the saturation and brightness.
940          */

941         private void setSaturationAndBrightness(float s, float b) {
942             int innerR = getTriangleCircumscribedRadius();
943             int triangleSize = (int)innerR * 3 / 2;
944             double x = b * triangleSize;
945             double maxY = x * Math.tan(Math.toRadians(30.0));
946             double y = 2 * maxY * s - maxY;
947             x = x - innerR;
948             double x1 = Math.cos(Math.toRadians(-60.0) - angle) *
949                         x - Math.sin(Math.toRadians(-60.0) - angle) * y;
950             double y1 = Math.sin(Math.toRadians(-60.0) - angle) * x +
951                         Math.cos(Math.toRadians(-60.0) - angle) * y;
952             int newCircleX = (int)x1 + getWheelXOrigin();
953             int newCircleY = getWheelYOrigin() - (int)y1;
954
955             setSaturationAndBrightness(s, b, newCircleX, newCircleY);
956         }
957
958             
959         /**
960          * Sets the saturation and brightness.
961          */

962         private void setSaturationAndBrightness(float s, float b,
963                                              int newCircleX, int newCircleY) {
964             newCircleX -= getIndicatorSize() / 2;
965             newCircleY -= getIndicatorSize() / 2;
966
967             int minX = Math.min(newCircleX, circleX);
968             int minY = Math.min(newCircleY, circleY);
969
970             repaint(minX, minY, Math.max(circleX, newCircleX) - minX +
971                     getIndicatorSize() + 1, Math.max(circleY, newCircleY) -
972                     minY + getIndicatorSize() + 1);
973             circleX = newCircleX;
974             circleY = newCircleY;
975         }
976
977         /**
978          * Adjusts the hue based on the passed in location.
979          *
980          * @param x X location to adjust to, relative to the origin of the
981          * wheel
982          * @param y Y location to adjust to, relative to the origin of the
983          * wheel
984          * @param check if true the location is checked to make sure
985          * it is contained in the wheel, if false the location is
986          * constrained to fit in the wheel
987          * @return true if the location is valid.
988          */

989         private boolean adjustHue(int x, int y, boolean check) {
990             double rad = Math.sqrt(x * x + y * y);
991             int size = getWheelRadius();
992
993             if (!check || (rad >= size - getWheelWidth() && rad < size)) {
994                 // Map the location to an angle and reset hue
995
double angle;
996                 if (x == 0) {
997                     if (y > 0) {
998                         angle = Math.PI / 2.0;
999                     }
1000                    else {
1001                        angle = Math.PI + Math.PI / 2.0;
1002                    }
1003                }
1004                else {
1005                    angle = Math.atan((double)y / (double)x);
1006                    if (x < 0) {
1007                        angle += Math.PI;
1008                    }
1009                    else if (angle < 0) {
1010                        angle += 2 * Math.PI;
1011                    }
1012                }
1013                setFlag(FLAGS_SETTING_COLOR, true);
1014                setHue((float)(1.0 - angle / Math.PI / 2), true);
1015                setFlag(FLAGS_SETTING_COLOR, false);
1016                setHueAngle(angle);
1017                setSaturationAndBrightness(getSaturation(), getBrightness());
1018                return true;
1019            }
1020            return false;
1021        }
1022
1023        /**
1024         * Rotates the triangle to accomodate the passed in hue.
1025         */

1026        private void setAngleFromHue(float hue) {
1027            setHueAngle((1.0 - hue) * Math.PI * 2);
1028        }
1029
1030        /**
1031         * Sets the angle representing the hue.
1032         */

1033        private void setHueAngle(double angle) {
1034            double oldAngle = this.angle;
1035
1036            this.angle = angle;
1037            if (angle != oldAngle) {
1038                setFlag(FLAGS_CHANGED_ANGLE, true);
1039                repaint();
1040            }
1041        }
1042
1043        /**
1044         * Returns the size of the color indicator.
1045         */

1046        private int getIndicatorSize() {
1047            return 8;
1048        }
1049
1050        /**
1051         * Returns the circumscribed radius of the triangle.
1052         */

1053        private int getTriangleCircumscribedRadius() {
1054            return 72;
1055        }
1056
1057        /**
1058         * Returns the x origin of the wheel and triangle.
1059         */

1060        private int getWheelXOrigin() {
1061            return 85;
1062        }
1063
1064        /**
1065         * Returns y origin of the wheel and triangle.
1066         */

1067        private int getWheelYOrigin() {
1068            return 85;
1069        }
1070
1071        /**
1072         * Returns the width of the wheel.
1073         */

1074        private int getWheelWidth() {
1075            return 13;
1076        }
1077
1078        /**
1079         * Sets the focus to one of: 0 no one, 1 the wheel or 2 the triangle.
1080         */

1081        private void setFocusType(int type) {
1082            if (type == 0) {
1083                setFlag(FLAGS_FOCUSED_WHEEL, false);
1084                setFlag(FLAGS_FOCUSED_TRIANGLE, false);
1085                repaint();
1086            }
1087            else {
1088                int toSet = FLAGS_FOCUSED_WHEEL;
1089                int toUnset = FLAGS_FOCUSED_TRIANGLE;
1090
1091                if (type == 2) {
1092                    toSet = FLAGS_FOCUSED_TRIANGLE;
1093                    toUnset = FLAGS_FOCUSED_WHEEL;
1094                }
1095                if (!isSet(toSet)) {
1096                    setFlag(toSet, true);
1097                    repaint();
1098                    setFlag(toUnset, false);
1099                }
1100            }
1101        }
1102
1103        /**
1104         * Returns the radius of the wheel.
1105         */

1106        private int getWheelRadius() {
1107            // As far as I can tell, GTK doesn't allow stretching this
1108
// widget
1109
return 85;
1110        }
1111
1112        /**
1113         * Updates the flags bitmask.
1114         */

1115        private void setFlag(int flag, boolean value) {
1116            if (value) {
1117                flags |= flag;
1118            }
1119            else {
1120                flags &= ~flag;
1121            }
1122        }
1123
1124        /**
1125         * Returns true if a particular flag has been set.
1126         */

1127        private boolean isSet(int flag) {
1128            return ((flags & flag) == flag);
1129        }
1130
1131        /**
1132         * Returns the RGB color to use for the specified location. The
1133         * passed in point must be on the color wheel and be relative to the
1134         * origin of the color wheel.
1135         *
1136         * @param x X location to get color for
1137         * @param y Y location to get color for
1138         * @param rad Radius from center of color wheel
1139         * @param integer with red, green and blue components
1140         */

1141        private int colorWheelLocationToRGB(int x, int y, double rad) {
1142            double angle = Math.acos((double)x / rad);
1143            int rgb;
1144
1145            if (angle < PI_3) {
1146                if (y < 0) {
1147                    // FFFF00 - FF0000
1148
rgb = 0xFF0000 | (int)Math.min(255,
1149                                           (int)(255 * angle / PI_3)) << 8;
1150                }
1151                else {
1152                    // FF0000 - FF00FF
1153
rgb = 0xFF0000 | (int)Math.min(255,
1154                                           (int)(255 * angle / PI_3));
1155                }
1156            }
1157            else if (angle < 2 * PI_3) {
1158                angle -= PI_3;
1159                if (y < 0) {
1160                    // 00FF00 - FFFF00
1161
rgb = 0x00FF00 | (int)Math.max(0, 255 -
1162                                           (int)(255 * angle / PI_3)) << 16;
1163                }
1164                else {
1165                    // FF00FF - 0000FF
1166
rgb = 0x0000FF | (int)Math.max(0, 255 -
1167                                           (int)(255 * angle / PI_3)) << 16;
1168                }
1169            }
1170            else {
1171                angle -= 2 * PI_3;
1172                if (y < 0) {
1173                    // 00FFFF - 00FF00
1174
rgb = 0x00FF00 | (int)Math.min(255,
1175                                           (int)(255 * angle / PI_3));
1176                }
1177                else {
1178                    // 0000FF - 00FFFF
1179
rgb = 0x0000FF | (int)Math.min(255,
1180                                           (int)(255 * angle / PI_3)) << 8;
1181                }
1182            }
1183            return rgb;
1184        }
1185
1186        /**
1187         * Increments the hue.
1188         */

1189        void incrementHue(boolean positive) {
1190            float hue = triangle.getGTKColorChooserPanel().getHue();
1191
1192            if (positive) {
1193                hue += 1.0f / 360.0f;
1194            }
1195            else {
1196                hue -= 1.0f / 360.0f;
1197            }
1198            if (hue > 1) {
1199                hue -= 1;
1200            }
1201            else if (hue < 0) {
1202                hue += 1;
1203            }
1204            getGTKColorChooserPanel().setHue(hue, true);
1205        }
1206    }
1207
1208
1209    /**
1210     * Action class used for colors.
1211     */

1212    private static class ColorAction extends AbstractAction {
1213        private int type;
1214
1215        ColorAction(String JavaDoc name, int type) {
1216            super(name);
1217            this.type = type;
1218        }
1219
1220        public void actionPerformed(ActionEvent e) {
1221            ColorTriangle triangle = (ColorTriangle)e.getSource();
1222
1223            if (triangle.isWheelFocused()) {
1224                float hue = triangle.getGTKColorChooserPanel().getHue();
1225
1226                switch (type) {
1227                case 0:
1228                case 2:
1229                    triangle.incrementHue(true);
1230                    break;
1231                case 1:
1232                case 3:
1233                    triangle.incrementHue(false);
1234                    break;
1235                case 4:
1236                    triangle.focusTriangle();
1237                    break;
1238                case 5:
1239                    compositeRequestFocus(triangle, false);
1240                    break;
1241                }
1242            }
1243            else {
1244                int xDelta = 0;
1245                int yDelta = 0;
1246
1247                switch (type) {
1248                case 0:
1249                    // up
1250
yDelta--;
1251                    break;
1252                case 1:
1253                    // down
1254
yDelta++;
1255                    break;
1256                case 2:
1257                    // left
1258
xDelta--;
1259                    break;
1260                case 3:
1261                    // right
1262
xDelta++;
1263                    break;
1264                case 4:
1265                    compositeRequestFocus(triangle, true);
1266                    return;
1267                case 5:
1268                    triangle.focusWheel();
1269                    return;
1270                }
1271                triangle.adjustSB(triangle.getColorX() + xDelta,
1272                                  triangle.getColorY() + yDelta, true);
1273            }
1274        }
1275    }
1276
1277
1278    private class OpaqueLabel extends JLabel {
1279        public boolean isOpaque() {
1280            return true;
1281        }
1282    }
1283}
1284
Popular Tags