J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 1998-2004 Sun Microsystems, Inc. All Rights Reserved. |
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | * |
| 5 | * This code is free software; you can redistribute it and/or modify it |
| 6 | * under the terms of the GNU General Public License version 2 only, as |
| 7 | * published by the Free Software Foundation. Sun designates this |
| 8 | * particular file as subject to the "Classpath" exception as provided |
| 9 | * by Sun in the LICENSE file that accompanied this code. |
| 10 | * |
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 14 | * version 2 for more details (a copy is included in the LICENSE file that |
| 15 | * accompanied this code). |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public License version |
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 20 | * |
| 21 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 23 | * have any questions. |
| 24 | */ |
| 25 | |
| 26 | package javax.swing.colorchooser; |
| 27 | |
| 28 | import javax.swing.*; |
| 29 | import java.awt.*; |
| 30 | import java.awt.event.*; |
| 31 | import javax.swing.event.*; |
| 32 | import javax.swing.border.*; |
| 33 | import java.awt.image.*; |
| 34 | import java.util.Locale; |
| 35 | |
| 36 | /** |
| 37 | * Implements the default HSB Color chooser |
| 38 | * |
| 39 | * @author Tom Santos |
| 40 | * @author Steve Wilson |
| 41 | * @author Mark Davidson |
| 42 | * @author Shannon Hickey |
| 43 | */ |
| 44 | class DefaultHSBChooserPanel extends AbstractColorChooserPanel implements ChangeListener, HierarchyListener { |
| 45 | |
| 46 | private transient HSBImage palette; |
| 47 | private transient HSBImage sliderPalette; |
| 48 | |
| 49 | private transient Image paletteImage; |
| 50 | private transient Image sliderPaletteImage; |
| 51 | |
| 52 | private JSlider slider; |
| 53 | private JSpinner hField; |
| 54 | private JSpinner sField; |
| 55 | private JSpinner bField; |
| 56 | |
| 57 | private JTextField redField; |
| 58 | private JTextField greenField; |
| 59 | private JTextField blueField; |
| 60 | |
| 61 | private boolean isAdjusting = false; // Flag which indicates that values are set internally |
| 62 | private Point paletteSelection = new Point(); |
| 63 | private JLabel paletteLabel; |
| 64 | private JLabel sliderPaletteLabel; |
| 65 | |
| 66 | private JRadioButton hRadio; |
| 67 | private JRadioButton sRadio; |
| 68 | private JRadioButton bRadio; |
| 69 | |
| 70 | private static final int PALETTE_DIMENSION = 200; |
| 71 | private static final int MAX_HUE_VALUE = 359; |
| 72 | private static final int MAX_SATURATION_VALUE = 100; |
| 73 | private static final int MAX_BRIGHTNESS_VALUE = 100; |
| 74 | |
| 75 | private int currentMode = HUE_MODE; |
| 76 | |
| 77 | private static final int HUE_MODE = 0; |
| 78 | private static final int SATURATION_MODE = 1; |
| 79 | private static final int BRIGHTNESS_MODE = 2; |
| 80 | |
| 81 | public DefaultHSBChooserPanel() { |
| 82 | } |
| 83 | |
| 84 | private void addPaletteListeners() { |
| 85 | paletteLabel.addMouseListener(new MouseAdapter() { |
| 86 | public void mousePressed(MouseEvent e ) { |
| 87 | float[] hsb = new float[3]; |
| 88 | palette.getHSBForLocation( e.getX(), e.getY(), hsb ); |
| 89 | updateHSB( hsb[0], hsb[1], hsb[2] ); |
| 90 | } |
| 91 | }); |
| 92 | |
| 93 | paletteLabel.addMouseMotionListener(new MouseMotionAdapter() { |
| 94 | public void mouseDragged( MouseEvent e ){ |
| 95 | int labelWidth = paletteLabel.getWidth(); |
| 96 | |
| 97 | int labelHeight = paletteLabel.getHeight(); |
| 98 | int x = e.getX(); |
| 99 | int y = e.getY(); |
| 100 | |
| 101 | if ( x >= labelWidth ) { |
| 102 | x = labelWidth - 1; |
| 103 | } |
| 104 | |
| 105 | if ( y >= labelHeight ) { |
| 106 | y = labelHeight - 1; |
| 107 | } |
| 108 | |
| 109 | if ( x < 0 ) { |
| 110 | x = 0; |
| 111 | } |
| 112 | |
| 113 | if ( y < 0 ) { |
| 114 | y = 0; |
| 115 | } |
| 116 | |
| 117 | float[] hsb = new float[3]; |
| 118 | palette.getHSBForLocation( x, y, hsb ); |
| 119 | updateHSB( hsb[0], hsb[1], hsb[2] ); |
| 120 | } |
| 121 | }); |
| 122 | } |
| 123 | |
| 124 | private void updatePalette( float h, float s, float b ) { |
| 125 | int x = 0; |
| 126 | int y = 0; |
| 127 | |
| 128 | switch ( currentMode ) { |
| 129 | case HUE_MODE: |
| 130 | if ( h != palette.getHue() ) { |
| 131 | palette.setHue( h ); |
| 132 | palette.nextFrame(); |
| 133 | } |
| 134 | x = PALETTE_DIMENSION - (int)(s * PALETTE_DIMENSION); |
| 135 | y = PALETTE_DIMENSION - (int)(b * PALETTE_DIMENSION); |
| 136 | break; |
| 137 | case SATURATION_MODE: |
| 138 | if ( s != palette.getSaturation() ) { |
| 139 | palette.setSaturation( s ); |
| 140 | palette.nextFrame(); |
| 141 | } |
| 142 | x = (int)(h * PALETTE_DIMENSION); |
| 143 | y = PALETTE_DIMENSION - (int)(b * PALETTE_DIMENSION); |
| 144 | break; |
| 145 | case BRIGHTNESS_MODE: |
| 146 | if ( b != palette.getBrightness() ) { |
| 147 | palette.setBrightness( b ); |
| 148 | palette.nextFrame(); |
| 149 | } |
| 150 | x = (int)(h * PALETTE_DIMENSION); |
| 151 | y = PALETTE_DIMENSION - (int)(s * PALETTE_DIMENSION); |
| 152 | break; |
| 153 | } |
| 154 | |
| 155 | paletteSelection.setLocation( x, y ); |
| 156 | paletteLabel.repaint(); |
| 157 | } |
| 158 | |
| 159 | private void updateSlider( float h, float s, float b ) { |
| 160 | // Update the slider palette if necessary. |
| 161 | // When the slider is the hue slider or the hue hasn't changed, |
| 162 | // the hue of the palette will not need to be updated. |
| 163 | if (currentMode != HUE_MODE && h != sliderPalette.getHue() ) { |
| 164 | sliderPalette.setHue( h ); |
| 165 | sliderPalette.nextFrame(); |
| 166 | } |
| 167 | |
| 168 | float value = 0f; |
| 169 | |
| 170 | switch ( currentMode ) { |
| 171 | case HUE_MODE: |
| 172 | value = h; |
| 173 | break; |
| 174 | case SATURATION_MODE: |
| 175 | value = s; |
| 176 | break; |
| 177 | case BRIGHTNESS_MODE: |
| 178 | value = b; |
| 179 | break; |
| 180 | } |
| 181 | |
| 182 | slider.setValue( Math.round(value * (slider.getMaximum())) ); |
| 183 | } |
| 184 | |
| 185 | private void updateHSBTextFields( float hue, float saturation, float brightness ) { |
| 186 | int h = Math.round(hue * 359); |
| 187 | int s = Math.round(saturation * 100); |
| 188 | int b = Math.round(brightness * 100); |
| 189 | |
| 190 | if (((Integer)hField.getValue()).intValue() != h) { |
| 191 | hField.setValue(new Integer(h)); |
| 192 | } |
| 193 | if (((Integer)sField.getValue()).intValue() != s) { |
| 194 | sField.setValue(new Integer(s)); |
| 195 | } |
| 196 | if (((Integer)bField.getValue()).intValue() != b) { |
| 197 | bField.setValue(new Integer(b)); |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | /** |
| 202 | * Updates the values of the RGB fields to reflect the new color change |
| 203 | */ |
| 204 | private void updateRGBTextFields( Color color ) { |
| 205 | redField.setText(String.valueOf(color.getRed())); |
| 206 | greenField.setText(String.valueOf(color.getGreen())); |
| 207 | blueField.setText(String.valueOf(color.getBlue())); |
| 208 | } |
| 209 | |
| 210 | /** |
| 211 | * Main internal method of updating the ui controls and the color model. |
| 212 | */ |
| 213 | private void updateHSB( float h, float s, float b ) { |
| 214 | if ( !isAdjusting ) { |
| 215 | isAdjusting = true; |
| 216 | |
| 217 | updatePalette( h, s, b ); |
| 218 | updateSlider( h, s, b ); |
| 219 | updateHSBTextFields( h, s, b ); |
| 220 | |
| 221 | Color color = Color.getHSBColor(h, s, b); |
| 222 | updateRGBTextFields( color ); |
| 223 | |
| 224 | getColorSelectionModel().setSelectedColor( color ); |
| 225 | |
| 226 | isAdjusting = false; |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | /** |
| 231 | * Invoked automatically when the model's state changes. |
| 232 | * It is also called by <code>installChooserPanel</code> to allow |
| 233 | * you to set up the initial state of your chooser. |
| 234 | * Override this method to update your <code>ChooserPanel</code>. |
| 235 | */ |
| 236 | public void updateChooser() { |
| 237 | if ( !isAdjusting ) { |
| 238 | float[] hsb = getHSBColorFromModel(); |
| 239 | updateHSB( hsb[0], hsb[1], hsb[2] ); |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | public void installChooserPanel(JColorChooser enclosingChooser) { |
| 244 | super.installChooserPanel(enclosingChooser); |
| 245 | setInheritsPopupMenu(true); |
| 246 | addHierarchyListener(this); |
| 247 | } |
| 248 | |
| 249 | /** |
| 250 | * Invoked when the panel is removed from the chooser. |
| 251 | */ |
| 252 | public void uninstallChooserPanel(JColorChooser enclosingChooser) { |
| 253 | super.uninstallChooserPanel(enclosingChooser); |
| 254 | cleanupPalettesIfNecessary(); |
| 255 | removeAll(); |
| 256 | removeHierarchyListener(this); |
| 257 | } |
| 258 | |
| 259 | /** |
| 260 | * Returns an float array containing the HSB values of the selected color from |
| 261 | * the ColorSelectionModel |
| 262 | */ |
| 263 | private float[] getHSBColorFromModel() { |
| 264 | Color color = getColorFromModel(); |
| 265 | float[] hsb = new float[3]; |
| 266 | Color.RGBtoHSB( color.getRed(), color.getGreen(), color.getBlue(), hsb ); |
| 267 | |
| 268 | return hsb; |
| 269 | } |
| 270 | |
| 271 | /** |
| 272 | * Builds a new chooser panel. |
| 273 | */ |
| 274 | protected void buildChooser() { |
| 275 | setLayout(new BorderLayout()); |
| 276 | JComponent spp = buildSliderPalettePanel(); |
| 277 | spp.setInheritsPopupMenu(true); |
| 278 | add(spp, BorderLayout.BEFORE_LINE_BEGINS); |
| 279 | |
| 280 | JPanel controlHolder = new JPanel(new SmartGridLayout(1,3)); |
| 281 | JComponent hsbControls = buildHSBControls(); |
| 282 | hsbControls.setInheritsPopupMenu(true); |
| 283 | controlHolder.add(hsbControls); |
| 284 | |
| 285 | controlHolder.add(new JLabel(" ")); // spacer |
| 286 | |
| 287 | JComponent rgbControls = buildRGBControls(); |
| 288 | rgbControls.setInheritsPopupMenu(true); |
| 289 | controlHolder.add(rgbControls); |
| 290 | controlHolder.setInheritsPopupMenu(true); |
| 291 | |
| 292 | controlHolder.setBorder(new EmptyBorder( 10, 5, 10, 5)); |
| 293 | add( controlHolder, BorderLayout.CENTER); |
| 294 | } |
| 295 | |
| 296 | /** |
| 297 | * Creates the panel with the uneditable RGB field |
| 298 | */ |
| 299 | private JComponent buildRGBControls() { |
| 300 | JPanel panel = new JPanel(new SmartGridLayout(2,3)); |
| 301 | panel.setInheritsPopupMenu(true); |
| 302 | |
| 303 | Color color = getColorFromModel(); |
| 304 | redField = new JTextField( String.valueOf(color.getRed()), 3 ); |
| 305 | redField.setEditable(false); |
| 306 | redField.setHorizontalAlignment( JTextField.RIGHT ); |
| 307 | redField.setInheritsPopupMenu(true); |
| 308 | |
| 309 | greenField = new JTextField(String.valueOf(color.getGreen()), 3 ); |
| 310 | greenField.setEditable(false); |
| 311 | greenField.setHorizontalAlignment( JTextField.RIGHT ); |
| 312 | greenField.setInheritsPopupMenu(true); |
| 313 | |
| 314 | blueField = new JTextField( String.valueOf(color.getBlue()), 3 ); |
| 315 | blueField.setEditable(false); |
| 316 | blueField.setHorizontalAlignment( JTextField.RIGHT ); |
| 317 | blueField.setInheritsPopupMenu(true); |
| 318 | |
| 319 | Locale locale = getLocale(); |
| 320 | String redString = UIManager.getString("ColorChooser.hsbRedText", locale); |
| 321 | String greenString = UIManager.getString("ColorChooser.hsbGreenText", locale); |
| 322 | String blueString = UIManager.getString("ColorChooser.hsbBlueText", locale); |
| 323 | |
| 324 | panel.add( new JLabel(redString) ); |
| 325 | panel.add( redField ); |
| 326 | panel.add( new JLabel(greenString) ); |
| 327 | panel.add( greenField ); |
| 328 | panel.add( new JLabel(blueString) ); |
| 329 | panel.add( blueField ); |
| 330 | |
| 331 | return panel; |
| 332 | } |
| 333 | |
| 334 | /** |
| 335 | * Creates the panel with the editable HSB fields and the radio buttons. |
| 336 | */ |
| 337 | private JComponent buildHSBControls() { |
| 338 | |
| 339 | Locale locale = getLocale(); |
| 340 | String hueString = UIManager.getString("ColorChooser.hsbHueText", locale); |
| 341 | String saturationString = UIManager.getString("ColorChooser.hsbSaturationText", locale); |
| 342 | String brightnessString = UIManager.getString("ColorChooser.hsbBrightnessText", locale); |
| 343 | |
| 344 | RadioButtonHandler handler = new RadioButtonHandler(); |
| 345 | |
| 346 | hRadio = new JRadioButton(hueString); |
| 347 | hRadio.addActionListener(handler); |
| 348 | hRadio.setSelected(true); |
| 349 | hRadio.setInheritsPopupMenu(true); |
| 350 | |
| 351 | sRadio = new JRadioButton(saturationString); |
| 352 | sRadio.addActionListener(handler); |
| 353 | sRadio.setInheritsPopupMenu(true); |
| 354 | |
| 355 | bRadio = new JRadioButton(brightnessString); |
| 356 | bRadio.addActionListener(handler); |
| 357 | bRadio.setInheritsPopupMenu(true); |
| 358 | |
| 359 | ButtonGroup group = new ButtonGroup(); |
| 360 | group.add(hRadio); |
| 361 | group.add(sRadio); |
| 362 | group.add(bRadio); |
| 363 | |
| 364 | float[] hsb = getHSBColorFromModel(); |
| 365 | |
| 366 | hField = new JSpinner(new SpinnerNumberModel((int)(hsb[0] * 359), 0, 359, 1)); |
| 367 | sField = new JSpinner(new SpinnerNumberModel((int)(hsb[1] * 100), 0, 100, 1)); |
| 368 | bField = new JSpinner(new SpinnerNumberModel((int)(hsb[2] * 100), 0, 100, 1)); |
| 369 | |
| 370 | hField.addChangeListener(this); |
| 371 | sField.addChangeListener(this); |
| 372 | bField.addChangeListener(this); |
| 373 | |
| 374 | hField.setInheritsPopupMenu(true); |
| 375 | sField.setInheritsPopupMenu(true); |
| 376 | bField.setInheritsPopupMenu(true); |
| 377 | |
| 378 | JPanel panel = new JPanel( new SmartGridLayout(2, 3) ); |
| 379 | |
| 380 | panel.add(hRadio); |
| 381 | panel.add(hField); |
| 382 | panel.add(sRadio); |
| 383 | panel.add(sField); |
| 384 | panel.add(bRadio); |
| 385 | panel.add(bField); |
| 386 | panel.setInheritsPopupMenu(true); |
| 387 | |
| 388 | return panel; |
| 389 | } |
| 390 | |
| 391 | /** |
| 392 | * Handler for the radio button classes. |
| 393 | */ |
| 394 | private class RadioButtonHandler implements ActionListener { |
| 395 | public void actionPerformed(ActionEvent evt) { |
| 396 | Object obj = evt.getSource(); |
| 397 | |
| 398 | if (obj instanceof JRadioButton) { |
| 399 | JRadioButton button = (JRadioButton)obj; |
| 400 | if (button == hRadio) { |
| 401 | setMode(HUE_MODE); |
| 402 | } else if (button == sRadio) { |
| 403 | setMode(SATURATION_MODE); |
| 404 | } else if (button == bRadio) { |
| 405 | setMode(BRIGHTNESS_MODE); |
| 406 | } |
| 407 | } |
| 408 | } |
| 409 | } |
| 410 | |
| 411 | private void setMode(int mode) { |
| 412 | if (currentMode == mode) { |
| 413 | return; |
| 414 | } |
| 415 | |
| 416 | isAdjusting = true; // Ensure no events propagate from changing slider value. |
| 417 | currentMode = mode; |
| 418 | |
| 419 | float[] hsb = getHSBColorFromModel(); |
| 420 | |
| 421 | switch (currentMode) { |
| 422 | case HUE_MODE: |
| 423 | slider.setInverted(true); |
| 424 | slider.setMaximum(MAX_HUE_VALUE); |
| 425 | palette.setValues(HSBImage.HSQUARE, hsb[0], 1.0f, 1.0f); |
| 426 | sliderPalette.setValues(HSBImage.HSLIDER, 0f, 1.0f, 1.0f); |
| 427 | break; |
| 428 | case SATURATION_MODE: |
| 429 | slider.setInverted(false); |
| 430 | slider.setMaximum(MAX_SATURATION_VALUE); |
| 431 | palette.setValues(HSBImage.SSQUARE, hsb[0], hsb[1], 1.0f); |
| 432 | sliderPalette.setValues(HSBImage.SSLIDER, hsb[0], 1.0f, 1.0f); |
| 433 | break; |
| 434 | case BRIGHTNESS_MODE: |
| 435 | slider.setInverted(false); |
| 436 | slider.setMaximum(MAX_BRIGHTNESS_VALUE); |
| 437 | palette.setValues(HSBImage.BSQUARE, hsb[0], 1.0f, hsb[2]); |
| 438 | sliderPalette.setValues(HSBImage.BSLIDER, hsb[0], 1.0f, 1.0f); |
| 439 | break; |
| 440 | } |
| 441 | |
| 442 | isAdjusting = false; |
| 443 | |
| 444 | palette.nextFrame(); |
| 445 | sliderPalette.nextFrame(); |
| 446 | |
| 447 | updateChooser(); |
| 448 | } |
| 449 | |
| 450 | protected JComponent buildSliderPalettePanel() { |
| 451 | |
| 452 | // This slider has to have a minimum of 0. A lot of math in this file is simplified due to this. |
| 453 | slider = new JSlider(JSlider.VERTICAL, 0, MAX_HUE_VALUE, 0); |
| 454 | slider.setInverted(true); |
| 455 | slider.setPaintTrack(false); |
| 456 | slider.setPreferredSize(new Dimension(slider.getPreferredSize().width, PALETTE_DIMENSION + 15)); |
| 457 | slider.addChangeListener(this); |
| 458 | slider.setInheritsPopupMenu(true); |
| 459 | // We're not painting ticks, but need to ask UI classes to |
| 460 | // paint arrow shape anyway, if possible. |
| 461 | slider.putClientProperty("Slider.paintThumbArrowShape", Boolean.TRUE); |
| 462 | paletteLabel = createPaletteLabel(); |
| 463 | addPaletteListeners(); |
| 464 | sliderPaletteLabel = new JLabel(); |
| 465 | |
| 466 | JPanel panel = new JPanel(); |
| 467 | panel.add( paletteLabel ); |
| 468 | panel.add( slider ); |
| 469 | panel.add( sliderPaletteLabel ); |
| 470 | |
| 471 | initializePalettesIfNecessary(); |
| 472 | |
| 473 | return panel; |
| 474 | } |
| 475 | |
| 476 | private void initializePalettesIfNecessary() { |
| 477 | if (palette != null) { |
| 478 | return; |
| 479 | } |
| 480 | |
| 481 | float[] hsb = getHSBColorFromModel(); |
| 482 | |
| 483 | switch(currentMode){ |
| 484 | case HUE_MODE: |
| 485 | palette = new HSBImage(HSBImage.HSQUARE, PALETTE_DIMENSION, PALETTE_DIMENSION, hsb[0], 1.0f, 1.0f); |
| 486 | sliderPalette = new HSBImage(HSBImage.HSLIDER, 16, PALETTE_DIMENSION, 0f, 1.0f, 1.0f); |
| 487 | break; |
| 488 | case SATURATION_MODE: |
| 489 | palette = new HSBImage(HSBImage.SSQUARE, PALETTE_DIMENSION, PALETTE_DIMENSION, 1.0f, hsb[1], 1.0f); |
| 490 | sliderPalette = new HSBImage(HSBImage.SSLIDER, 16, PALETTE_DIMENSION, 1.0f, 0f, 1.0f); |
| 491 | break; |
| 492 | case BRIGHTNESS_MODE: |
| 493 | palette = new HSBImage(HSBImage.BSQUARE, PALETTE_DIMENSION, PALETTE_DIMENSION, 1.0f, 1.0f, hsb[2]); |
| 494 | sliderPalette = new HSBImage(HSBImage.BSLIDER, 16, PALETTE_DIMENSION, 1.0f, 1.0f, 0f); |
| 495 | break; |
| 496 | } |
| 497 | paletteImage = Toolkit.getDefaultToolkit().createImage(palette); |
| 498 | sliderPaletteImage = Toolkit.getDefaultToolkit().createImage(sliderPalette); |
| 499 | |
| 500 | paletteLabel.setIcon(new ImageIcon(paletteImage)); |
| 501 | sliderPaletteLabel.setIcon(new ImageIcon(sliderPaletteImage)); |
| 502 | } |
| 503 | |
| 504 | private void cleanupPalettesIfNecessary() { |
| 505 | if (palette == null) { |
| 506 | return; |
| 507 | } |
| 508 | |
| 509 | palette.aborted = true; |
| 510 | sliderPalette.aborted = true; |
| 511 | |
| 512 | palette.nextFrame(); |
| 513 | sliderPalette.nextFrame(); |
| 514 | |
| 515 | palette = null; |
| 516 | sliderPalette = null; |
| 517 | |
| 518 | paletteImage = null; |
| 519 | sliderPaletteImage = null; |
| 520 | |
| 521 | paletteLabel.setIcon(null); |
| 522 | sliderPaletteLabel.setIcon(null); |
| 523 | } |
| 524 | |
| 525 | protected JLabel createPaletteLabel() { |
| 526 | return new JLabel() { |
| 527 | protected void paintComponent( Graphics g ) { |
| 528 | super.paintComponent( g ); |
| 529 | g.setColor( Color.white ); |
| 530 | g.drawOval( paletteSelection.x - 4, paletteSelection.y - 4, 8, 8 ); |
| 531 | } |
| 532 | }; |
| 533 | } |
| 534 | |
| 535 | public String getDisplayName() { |
| 536 | return UIManager.getString("ColorChooser.hsbNameText", getLocale()); |
| 537 | } |
| 538 | |
| 539 | /** |
| 540 | * Provides a hint to the look and feel as to the |
| 541 | * <code>KeyEvent.VK</code> constant that can be used as a mnemonic to |
| 542 | * access the panel. A return value <= 0 indicates there is no mnemonic. |
| 543 | * <p> |
| 544 | * The return value here is a hint, it is ultimately up to the look |
| 545 | * and feel to honor the return value in some meaningful way. |
| 546 | * <p> |
| 547 | * This implementation looks up the value from the default |
| 548 | * <code>ColorChooser.hsbMnemonic</code>, or if it |
| 549 | * isn't available (or not an <code>Integer</code>) returns -1. |
| 550 | * The lookup for the default is done through the <code>UIManager</code>: |
| 551 | * <code>UIManager.get("ColorChooser.rgbMnemonic");</code>. |
| 552 | * |
| 553 | * @return KeyEvent.VK constant identifying the mnemonic; <= 0 for no |
| 554 | * mnemonic |
| 555 | * @see #getDisplayedMnemonicIndex |
| 556 | * @since 1.4 |
| 557 | */ |
| 558 | public int getMnemonic() { |
| 559 | return getInt("ColorChooser.hsbMnemonic", -1); |
| 560 | } |
| 561 | |
| 562 | /** |
| 563 | * Provides a hint to the look and feel as to the index of the character in |
| 564 | * <code>getDisplayName</code> that should be visually identified as the |
| 565 | * mnemonic. The look and feel should only use this if |
| 566 | * <code>getMnemonic</code> returns a value > 0. |
| 567 | * <p> |
| 568 | * The return value here is a hint, it is ultimately up to the look |
| 569 | * and feel to honor the return value in some meaningful way. For example, |
| 570 | * a look and feel may wish to render each |
| 571 | * <code>AbstractColorChooserPanel</code> in a <code>JTabbedPane</code>, |
| 572 | * and further use this return value to underline a character in |
| 573 | * the <code>getDisplayName</code>. |
| 574 | * <p> |
| 575 | * This implementation looks up the value from the default |
| 576 | * <code>ColorChooser.rgbDisplayedMnemonicIndex</code>, or if it |
| 577 | * isn't available (or not an <code>Integer</code>) returns -1. |
| 578 | * The lookup for the default is done through the <code>UIManager</code>: |
| 579 | * <code>UIManager.get("ColorChooser.hsbDisplayedMnemonicIndex");</code>. |
| 580 | * |
| 581 | * @return Character index to render mnemonic for; -1 to provide no |
| 582 | * visual identifier for this panel. |
| 583 | * @see #getMnemonic |
| 584 | * @since 1.4 |
| 585 | */ |
| 586 | public int getDisplayedMnemonicIndex() { |
| 587 | return getInt("ColorChooser.hsbDisplayedMnemonicIndex", -1); |
| 588 | } |
| 589 | |
| 590 | public Icon getSmallDisplayIcon() { |
| 591 | return null; |
| 592 | } |
| 593 | |
| 594 | public Icon getLargeDisplayIcon() { |
| 595 | return null; |
| 596 | } |
| 597 | |
| 598 | /** |
| 599 | * Class for the slider and palette images. |
| 600 | */ |
| 601 | class HSBImage extends SyntheticImage { |
| 602 | protected float h = .0f; |
| 603 | protected float s = .0f; |
| 604 | protected float b = .0f; |
| 605 | protected float[] hsb = new float[3]; |
| 606 | |
| 607 | protected boolean isDirty = true; |
| 608 | protected int cachedY; |
| 609 | protected int cachedColor; |
| 610 | protected int type; |
| 611 | |
| 612 | private static final int HSQUARE = 0; |
| 613 | private static final int SSQUARE = 1; |
| 614 | private static final int BSQUARE = 2; |
| 615 | private static final int HSLIDER = 3; |
| 616 | private static final int SSLIDER = 4; |
| 617 | private static final int BSLIDER = 5; |
| 618 | |
| 619 | protected HSBImage(int type, int width, int height, float h, float s, float b) { |
| 620 | super(width, height); |
| 621 | setValues(type, h, s, b); |
| 622 | } |
| 623 | |
| 624 | public void setValues(int type, float h, float s, float b) { |
| 625 | this.type = type; |
| 626 | cachedY = -1; |
| 627 | cachedColor = 0; |
| 628 | setHue( h ); |
| 629 | setSaturation( s ); |
| 630 | setBrightness( b ); |
| 631 | } |
| 632 | |
| 633 | public final void setHue( float hue ) { |
| 634 | h = hue; |
| 635 | } |
| 636 | |
| 637 | public final void setSaturation( float saturation ) { |
| 638 | s = saturation; |
| 639 | } |
| 640 | |
| 641 | public final void setBrightness( float brightness ) { |
| 642 | b = brightness; |
| 643 | } |
| 644 | |
| 645 | public final float getHue() { |
| 646 | return h; |
| 647 | } |
| 648 | |
| 649 | public final float getSaturation() { |
| 650 | return s; |
| 651 | } |
| 652 | |
| 653 | public final float getBrightness() { |
| 654 | return b; |
| 655 | } |
| 656 | |
| 657 | protected boolean isStatic() { |
| 658 | return false; |
| 659 | } |
| 660 | |
| 661 | public synchronized void nextFrame() { |
| 662 | isDirty = true; |
| 663 | notifyAll(); |
| 664 | } |
| 665 | |
| 666 | public synchronized void addConsumer(ImageConsumer ic) { |
| 667 | isDirty = true; |
| 668 | super.addConsumer(ic); |
| 669 | } |
| 670 | |
| 671 | private int getRGBForLocation( int x, int y ) { |
| 672 | if (type >= HSLIDER && y == cachedY) { |
| 673 | return cachedColor; |
| 674 | } |
| 675 | |
| 676 | getHSBForLocation( x, y, hsb ); |
| 677 | cachedY = y; |
| 678 | cachedColor = Color.HSBtoRGB( hsb[0], hsb[1], hsb[2] ); |
| 679 | |
| 680 | return cachedColor; |
| 681 | } |
| 682 | |
| 683 | public void getHSBForLocation( int x, int y, float[] hsbArray ) { |
| 684 | switch (type) { |
| 685 | case HSQUARE: { |
| 686 | float saturationStep = ((float)x) / width; |
| 687 | float brightnessStep = ((float)y) / height; |
| 688 | hsbArray[0] = h; |
| 689 | hsbArray[1] = s - saturationStep; |
| 690 | hsbArray[2] = b - brightnessStep; |
| 691 | break; |
| 692 | } |
| 693 | case SSQUARE: { |
| 694 | float brightnessStep = ((float)y) / height; |
| 695 | float step = 1.0f / ((float)width); |
| 696 | hsbArray[0] = x * step; |
| 697 | hsbArray[1] = s; |
| 698 | hsbArray[2] = 1.0f - brightnessStep; |
| 699 | break; |
| 700 | } |
| 701 | case BSQUARE: { |
| 702 | float saturationStep = ((float)y) / height; |
| 703 | float step = 1.0f / ((float)width); |
| 704 | hsbArray[0] = x * step; |
| 705 | hsbArray[1] = 1.0f - saturationStep; |
| 706 | hsbArray[2] = b; |
| 707 | break; |
| 708 | } |
| 709 | case HSLIDER: { |
| 710 | float step = 1.0f / ((float)height); |
| 711 | hsbArray[0] = y * step; |
| 712 | hsbArray[1] = s; |
| 713 | hsbArray[2] = b; |
| 714 | break; |
| 715 | } |
| 716 | case SSLIDER: { |
| 717 | float saturationStep = ((float)y) / height; |
| 718 | hsbArray[0] = h; |
| 719 | hsbArray[1] = s - saturationStep; |
| 720 | hsbArray[2] = b; |
| 721 | break; |
| 722 | } |
| 723 | case BSLIDER: { |
| 724 | float brightnessStep = ((float)y) / height; |
| 725 | hsbArray[0] = h; |
| 726 | hsbArray[1] = s; |
| 727 | hsbArray[2] = b - brightnessStep; |
| 728 | break; |
| 729 | } |
| 730 | } |
| 731 | } |
| 732 | |
| 733 | /** |
| 734 | * Overriden method from SyntheticImage |
| 735 | */ |
| 736 | protected void computeRow( int y, int[] row ) { |
| 737 | if ( y == 0 ) { |
| 738 | synchronized ( this ) { |
| 739 | try { |
| 740 | while ( !isDirty ) { |
| 741 | wait(); |
| 742 | } |
| 743 | } catch (InterruptedException ie) { |
| 744 | } |
| 745 | isDirty = false; |
| 746 | } |
| 747 | } |
| 748 | |
| 749 | if (aborted) { |
| 750 | return; |
| 751 | } |
| 752 | |
| 753 | for ( int i = 0; i < row.length; ++i ) { |
| 754 | row[i] = getRGBForLocation( i, y ); |
| 755 | } |
| 756 | } |
| 757 | } |
| 758 | |
| 759 | public void stateChanged(ChangeEvent e) { |
| 760 | if (e.getSource() == slider) { |
| 761 | boolean modelIsAdjusting = slider.getModel().getValueIsAdjusting(); |
| 762 | |
| 763 | if (!modelIsAdjusting && !isAdjusting) { |
| 764 | int sliderValue = slider.getValue(); |
| 765 | int sliderRange = slider.getMaximum(); |
| 766 | float value = (float)sliderValue / (float)sliderRange; |
| 767 | |
| 768 | float[] hsb = getHSBColorFromModel(); |
| 769 | |
| 770 | switch ( currentMode ){ |
| 771 | case HUE_MODE: |
| 772 | updateHSB(value, hsb[1], hsb[2]); |
| 773 | break; |
| 774 | case SATURATION_MODE: |
| 775 | updateHSB(hsb[0], value, hsb[2]); |
| 776 | break; |
| 777 | case BRIGHTNESS_MODE: |
| 778 | updateHSB(hsb[0], hsb[1], value); |
| 779 | break; |
| 780 | } |
| 781 | } |
| 782 | } else if (e.getSource() instanceof JSpinner) { |
| 783 | float hue = ((Integer)hField.getValue()).floatValue() / 359f; |
| 784 | float saturation = ((Integer)sField.getValue()).floatValue() / 100f; |
| 785 | float brightness = ((Integer)bField.getValue()).floatValue() / 100f; |
| 786 | |
| 787 | updateHSB(hue, saturation, brightness); |
| 788 | } |
| 789 | } |
| 790 | |
| 791 | public void hierarchyChanged(HierarchyEvent he) { |
| 792 | if ((he.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0) { |
| 793 | if (isDisplayable()) { |
| 794 | initializePalettesIfNecessary(); |
| 795 | } else { |
| 796 | cleanupPalettesIfNecessary(); |
| 797 | } |
| 798 | } |
| 799 | } |
| 800 | |
| 801 | } |