/* * @(#)ColorSliderUI.java * * Copyright (c) 2004-2010 Werner Randelshofer, Immensee, Switzerland. * All rights reserved. * * You may not use, copy or modify this file, except in compliance with the * license agreement you entered into with Werner Randelshofer. * For details see accompanying license terms. */ package ch.randelshofer.quaqua.colorchooser; import ch.randelshofer.quaqua.border.VisualMarginBorder; import ch.randelshofer.quaqua.*; import java.awt.*; import java.awt.event.*; import java.beans.*; import javax.swing.*; import javax.swing.plaf.*; import javax.swing.plaf.basic.*; /** * A UI delegate for color sliders. The track of the slider visualizes how * changing the value of the slider affects the color. * * * @author Werner Randelshofer * @version $Id: ColorSliderUI.java 414 2011-07-29 20:55:05Z wrandelshofer $ */ public class ColorSliderUI extends BasicSliderUI { private final static Color foreground = new Color(0x949494); private final static Color trackBackground = new Color(0xffffff); private ColorTrackImageProducer colorTrackImageProducer; private Image colorTrackImage; private static final Dimension PREFERRED_HORIZONTAL_SIZE = new Dimension(36, 16); private static final Dimension PREFERRED_VERTICAL_SIZE = new Dimension(26, 100); private static final Dimension MINIMUM_HORIZONTAL_SIZE = new Dimension(36, 16); private static final Dimension MINIMUM_VERTICAL_SIZE = new Dimension(26, 36); /** Creates a new instance. */ public ColorSliderUI(JSlider b) { super(b); } public static ComponentUI createUI(JComponent b) { return new ColorSliderUI((JSlider)b); } @Override protected void installDefaults( JSlider slider ) { super.installDefaults(slider); focusInsets = new Insets(0,0,0,0); slider.setOpaque(false); if (slider.getOrientation() == JSlider.HORIZONTAL) { slider.setBorder(new VisualMarginBorder(0,1,-1,1)); } else { slider.setBorder(new VisualMarginBorder(0,0,0,1)); } //slider.setRequestFocusEnabled(QuaquaManager.getBoolean("Slider.requestFocusEnabled")); slider.setRequestFocusEnabled(true); } @Override protected Dimension getThumbSize() { Icon thumb = getThumbIcon(); return new Dimension(thumb.getIconWidth(), thumb.getIconHeight()); } @Override public Dimension getPreferredHorizontalSize() { return PREFERRED_HORIZONTAL_SIZE; } @Override public Dimension getPreferredVerticalSize() { return PREFERRED_VERTICAL_SIZE; } @Override public Dimension getMinimumHorizontalSize() { return MINIMUM_HORIZONTAL_SIZE; } @Override public Dimension getMinimumVerticalSize() { return MINIMUM_VERTICAL_SIZE; } @Override protected void calculateThumbLocation() { super.calculateThumbLocation(); if ( slider.getOrientation() == JSlider.HORIZONTAL ) { thumbRect.y -= 3; } else { thumbRect.x -= 3; } } /* public void paint( Graphics g, JComponent c ) { g.setColor(Color.green); g.fillRect(0,0,c.getWidth(), c.getHeight()); super.paint(g,c); } */ protected Icon getThumbIcon() { if (slider.getOrientation() == JSlider.HORIZONTAL) { return UIManager.getIcon("ColorChooser.ColorSlider.northThumb.small"); } else { return UIManager.getIcon("ColorChooser.ColorSlider.westThumb.small"); } } @Override public void paintThumb(Graphics g) { Rectangle knobBounds = thumbRect; int w = knobBounds.width; int h = knobBounds.height; getThumbIcon().paintIcon(slider, g, knobBounds.x, knobBounds.y); /* g.setColor(Color.green); ((Graphics2D) g).draw(knobBounds); */ } @Override public void paintTrack(Graphics g) { int cx, cy, cw, ch; int pad; Rectangle trackBounds = trackRect; if ( slider.getOrientation() == JSlider.HORIZONTAL ) { pad = trackBuffer;// - thumbRect.width / 2 + 2; cx = trackBounds.x - pad + 1; cy = trackBounds.y; //cy = (trackBounds.height / 2) - 4; cw = trackBounds.width + pad * 2 - 2; ch = trackBounds.height; } else { pad = trackBuffer; //cx = (trackBounds.width / 2) - 4; //cx = (trackBounds.width / 2); //cx = thumbRect.x + 2; cx = trackBounds.x; //cy = pad; cy = contentRect.y + 2; cw = trackBounds.width - 1; //ch = trackBounds.height; ch = trackBounds.height + pad * 2 - 5; } g.setColor(trackBackground); g.fillRect(cx,cy,cw,ch); g.setColor(foreground); g.drawRect(cx,cy,cw - 1,ch - 1); paintColorTrack(g, cx + 2, cy + 2, cw - 4, ch - 4, trackBuffer); } @Override public void paintTicks(Graphics g) { Rectangle tickBounds = tickRect; int centerEffect, tickHeight; g.setColor(foreground); if ( slider.getOrientation() == JSlider.HORIZONTAL ) { g.translate( 0, tickBounds.y); int value = slider.getMinimum(); int xPos = 0; if ( slider.getMinorTickSpacing() > 0 ) { while ( value <= slider.getMaximum() ) { xPos = xPositionForValue( value ); paintMinorTickForHorizSlider( g, tickBounds, xPos ); value += slider.getMinorTickSpacing(); } } if ( slider.getMajorTickSpacing() > 0 ) { value = slider.getMinimum(); while ( value <= slider.getMaximum() ) { xPos = xPositionForValue( value ); paintMajorTickForHorizSlider( g, tickBounds, xPos ); value += slider.getMajorTickSpacing(); } } g.translate( 0, -tickBounds.y); } else { g.translate(tickBounds.x, 0); int value = slider.getMinimum(); int yPos = 0; if ( slider.getMinorTickSpacing() > 0 ) { int offset = 0; if(!QuaquaUtilities.isLeftToRight(slider)) { offset = tickBounds.width - tickBounds.width / 2; g.translate(offset, 0); } while ( value <= slider.getMaximum() ) { yPos = yPositionForValue( value ); paintMinorTickForVertSlider( g, tickBounds, yPos ); value += slider.getMinorTickSpacing(); } if(!QuaquaUtilities.isLeftToRight(slider)) { g.translate(-offset, 0); } } if ( slider.getMajorTickSpacing() > 0 ) { value = slider.getMinimum(); if(!QuaquaUtilities.isLeftToRight(slider)) { g.translate(2, 0); } while ( value <= slider.getMaximum() ) { yPos = yPositionForValue( value ); paintMajorTickForVertSlider( g, tickBounds, yPos ); value += slider.getMajorTickSpacing(); } if(!QuaquaUtilities.isLeftToRight(slider)) { g.translate(-2, 0); } } g.translate(-tickBounds.x, 0); } /* g.setColor(Color.red); ((Graphics2D) g).draw(tickBounds); */ } @Override protected void paintMajorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x) { g.drawLine(x, 0, x, tickBounds.height - 1); } @Override protected void paintMinorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) { //g.drawLine( x, 0, x, tickBounds.height / 2 - 1 ); g.drawLine(x, 0, x, tickBounds.height - 1); } @Override protected void paintMinorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) { g.drawLine( tickBounds.width / 2, y, tickBounds.width / 2 - 1, y); } @Override protected void paintMajorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) { g.drawLine(0, y, tickBounds.width - 1, y); } @Override public void paintFocus(Graphics g) { } public void paintColorTrack(Graphics g, int x, int y, int width, int height, int buffer) { //g.setColor(Color.black); //g.fillRect(x, y, width, height); if (colorTrackImage==null||colorTrackImageProducer == null || colorTrackImageProducer.getWidth() != width || colorTrackImageProducer.getHeight() != height) { if (colorTrackImage != null) { colorTrackImage.flush(); } colorTrackImageProducer = new ColorTrackImageProducer(width, height, buffer + 2, slider.getOrientation() == JSlider.HORIZONTAL); if (slider.getClientProperty("ColorSliderModel") != null) { colorTrackImageProducer.setColorSliderModel((ColorSliderModel) slider.getClientProperty("ColorSliderModel")); } if (slider.getClientProperty("ColorComponentIndex") != null) { colorTrackImageProducer.setColorComponentIndex(((Integer) slider.getClientProperty("ColorComponentIndex")).intValue()); } colorTrackImageProducer.generateColorTrack(); colorTrackImage = slider.createImage(colorTrackImageProducer); } else { colorTrackImageProducer.regenerateColorTrack(); } g.drawImage(colorTrackImage, x, y, null); } @Override protected void calculateTrackRect() { int centerSpacing = 0; // used to center sliders added using BorderLayout.CENTER (bug 4275631) if ( slider.getOrientation() == JSlider.HORIZONTAL ) { centerSpacing = thumbRect.height; if ( slider.getPaintTicks() ) centerSpacing += getTickLength(); if ( slider.getPaintLabels() ) centerSpacing += getHeightOfTallestLabel(); trackRect.x = contentRect.x + trackBuffer + 1; //trackRect.y = contentRect.y + (contentRect.height - centerSpacing - 1)/2; trackRect.height = 13; trackRect.y = contentRect.y + contentRect.height - trackRect.height; trackRect.width = contentRect.width - (trackBuffer * 2) - 1; } else { /* centerSpacing = thumbRect.width; if (! QuaquaUtilities.isLeftToRight(slider)) { if ( slider.getPaintTicks() ) centerSpacing += getTickLength(); if ( slider.getPaintLabels() ) centerSpacing += getWidthOfWidestLabel(); } else { if ( slider.getPaintTicks() ) centerSpacing -= getTickLength(); if ( slider.getPaintLabels() ) centerSpacing -= getWidthOfWidestLabel(); } trackRect.x = contentRect.x + (contentRect.width - centerSpacing - 1)/2 + 2; */ trackRect.width = 14; trackRect.x = contentRect.x + contentRect.width - trackRect.width; trackRect.y = contentRect.y + trackBuffer; trackRect.height = contentRect.height - (trackBuffer * 2) + 1; } } @Override protected void calculateTickRect() { if ( slider.getOrientation() == JSlider.HORIZONTAL ) { tickRect.x = trackRect.x; //tickRect.y = trackRect.y + trackRect.height; tickRect.y = trackRect.y - getTickLength(); tickRect.width = trackRect.width; tickRect.height = getTickLength(); if ( !slider.getPaintTicks() ) { --tickRect.y; tickRect.height = 0; } } else { /* if(! QuaquaUtilities.isLeftToRight(slider)) { tickRect.x = trackRect.x + trackRect.width; tickRect.width = getTickLength(); } else { tickRect.width = getTickLength(); tickRect.x = trackRect.x - tickRect.width; }*/ tickRect.width = getTickLength(); tickRect.x = contentRect.x;//trackRect.x - tickRect.width - 1; tickRect.y = trackRect.y; tickRect.height = trackRect.height; if ( !slider.getPaintTicks() ) { --tickRect.x; tickRect.width = 0; } } } /** * Gets the height of the tick area for horizontal sliders and the width of the * tick area for vertical sliders. BasicSliderUI uses the returned value to * determine the tick area rectangle. If you want to give your ticks some room, * make this larger than you need and paint your ticks away from the sides in paintTicks(). */ @Override protected int getTickLength() { return 4; } @Override protected PropertyChangeListener createPropertyChangeListener( JSlider slider ) { return new CSUIPropertyChangeHandler(); } public class CSUIPropertyChangeHandler extends BasicSliderUI.PropertyChangeHandler { @Override public void propertyChange( PropertyChangeEvent e ) { String propertyName = e.getPropertyName(); if (propertyName.equals( "Frame.active" )) { //calculateGeometry(); slider.repaint(); } else if (propertyName.equals( "ColorSliderModel" )) { if (colorTrackImageProducer != null) { colorTrackImageProducer.setColorSliderModel(((ColorSliderModel) e.getNewValue())); if (colorTrackImageProducer.needsGeneration()) { slider.repaint(); } } } else if (propertyName.equals( "snapToTicks" )) { if (colorTrackImageProducer != null) { colorTrackImageProducer.markAsDirty(); slider.repaint(); } } else if (propertyName.equals( "ColorComponentIndex" )) { if (colorTrackImageProducer != null && e.getNewValue() != null) { colorTrackImageProducer.setColorComponentIndex(((Integer) e.getNewValue()).intValue()); if (colorTrackImageProducer.needsGeneration()) { slider.repaint(); } } } else if (propertyName.equals( "ColorComponentChange" )) { Integer value = (Integer) e.getNewValue(); if (value != null && colorTrackImageProducer != null) { colorTrackImageProducer.componentChanged(value.intValue()); if (colorTrackImageProducer.needsGeneration()) { slider.repaint(); } } } else if (propertyName.equals( "ColorComponentValue" )) { Integer value = (Integer) slider.getClientProperty("ColorComponentChange"); if (value != null && colorTrackImageProducer != null) { colorTrackImageProducer.componentChanged(value.intValue()); if (colorTrackImageProducer.needsGeneration()) { slider.repaint(); } } } else if (propertyName.equals( "Orientation" )) { if (slider.getOrientation() == JSlider.HORIZONTAL) { slider.setBorder(new VisualMarginBorder(0,1,-1,1)); } else { slider.setBorder(new VisualMarginBorder(0,0,0,1)); } } super.propertyChange(e); } } @Override protected TrackListener createTrackListener( JSlider slider ) { return new QuaquaTrackListener(); } /** * Track mouse movements. * * This inner class is marked "public" due to a compiler bug. * This class should be treated as a "protected" inner class. * Instantiate it only within subclasses of <Foo>. */ public class QuaquaTrackListener extends BasicSliderUI.TrackListener { /** * If the mouse is pressed above the "thumb" component * then reduce the scrollbars value by one page ("page up"), * otherwise increase it by one page. If there is no * thumb then page up if the mouse is in the upper half * of the track. */ @Override public void mousePressed(MouseEvent e) { if ( !slider.isEnabled() ) return; currentMouseX = e.getX(); currentMouseY = e.getY(); if (slider.isRequestFocusEnabled()) { slider.requestFocus(); } // Clicked inside the Thumb area? if (thumbRect.contains(currentMouseX, currentMouseY) ) { super.mousePressed(e); } else { switch ( slider.getOrientation() ) { case JSlider.VERTICAL: slider.setValue(valueForYPosition(currentMouseY)); break; case JSlider.HORIZONTAL: slider.setValue(valueForXPosition(currentMouseX)); break; } // FIXME: // We should set isDragging to false here. Unfortunately, // we can not access this variable in class BasicSliderUI. } } } }