| /* MetalScrollBarUI.java |
| Copyright (C) 2005 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package javax.swing.plaf.metal; |
| |
| import java.awt.Color; |
| import java.awt.Dimension; |
| import java.awt.Graphics; |
| import java.awt.Insets; |
| import java.awt.Rectangle; |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| |
| import javax.swing.JButton; |
| import javax.swing.JComponent; |
| import javax.swing.JScrollBar; |
| import javax.swing.SwingConstants; |
| import javax.swing.UIManager; |
| import javax.swing.plaf.ComponentUI; |
| import javax.swing.plaf.basic.BasicScrollBarUI; |
| |
| /** |
| * A UI delegate for the {@link JScrollBar} component. |
| */ |
| public class MetalScrollBarUI extends BasicScrollBarUI |
| { |
| |
| /** |
| * A property change handler for the UI delegate that monitors for |
| * changes to the "JScrollBar.isFreeStanding" property, and updates |
| * the buttons and track rendering as appropriate. |
| */ |
| class MetalScrollBarPropertyChangeHandler |
| extends BasicScrollBarUI.PropertyChangeHandler |
| { |
| /** |
| * Creates a new handler. |
| * |
| * @see #createPropertyChangeListener() |
| */ |
| public MetalScrollBarPropertyChangeHandler() |
| { |
| // Nothing to do here. |
| } |
| |
| /** |
| * Handles a property change event. If the event name is |
| * <code>JSlider.isFreeStanding</code>, this method updates the |
| * delegate, otherwise the event is passed up to the super class. |
| * |
| * @param e the property change event. |
| */ |
| public void propertyChange(PropertyChangeEvent e) |
| { |
| if (e.getPropertyName().equals(FREE_STANDING_PROP)) |
| { |
| Boolean prop = (Boolean) e.getNewValue(); |
| isFreeStanding = prop == null ? true : prop.booleanValue(); |
| if (increaseButton != null) |
| increaseButton.setFreeStanding(isFreeStanding); |
| if (decreaseButton != null) |
| decreaseButton.setFreeStanding(isFreeStanding); |
| } |
| else |
| super.propertyChange(e); |
| } |
| } |
| |
| /** The name for the 'free standing' property. */ |
| public static final String FREE_STANDING_PROP = "JScrollBar.isFreeStanding"; |
| |
| /** The minimum thumb size for a scroll bar that is not free standing. */ |
| private static final Dimension MIN_THUMB_SIZE = new Dimension(15, 15); |
| |
| /** The minimum thumb size for a scroll bar that is free standing. */ |
| private static final Dimension MIN_THUMB_SIZE_FREE_STANDING |
| = new Dimension(17, 17); |
| |
| /** The button that increases the value in the scroll bar. */ |
| protected MetalScrollButton increaseButton; |
| |
| /** The button that decreases the value in the scroll bar. */ |
| protected MetalScrollButton decreaseButton; |
| |
| /** |
| * The scroll bar width. |
| */ |
| protected int scrollBarWidth; |
| |
| /** |
| * A flag that indicates whether the scroll bar is "free standing", which |
| * means it has complete borders and can be used anywhere in the UI. A |
| * scroll bar which is not free standing has borders missing from one |
| * side, and relies on being part of another container with its own borders |
| * to look right visually. */ |
| protected boolean isFreeStanding = true; |
| |
| /** |
| * The color for the scroll bar shadow (this is read from the UIDefaults in |
| * the installDefaults() method). |
| */ |
| Color scrollBarShadowColor; |
| |
| /** |
| * Constructs a new instance of <code>MetalScrollBarUI</code>, with no |
| * specific initialisation. |
| */ |
| public MetalScrollBarUI() |
| { |
| super(); |
| } |
| |
| /** |
| * Returns a new instance of <code>MetalScrollBarUI</code>. |
| * |
| * @param component the component for which we return an UI instance |
| * |
| * @return An instance of MetalScrollBarUI |
| */ |
| public static ComponentUI createUI(JComponent component) |
| { |
| return new MetalScrollBarUI(); |
| } |
| |
| /** |
| * Installs the defaults. |
| */ |
| protected void installDefaults() |
| { |
| // need to initialise isFreeStanding before calling the super class, |
| // so that the value is set when createIncreaseButton() and |
| // createDecreaseButton() are called (unless there is somewhere earlier |
| // that we can do this). |
| Boolean prop = (Boolean) scrollbar.getClientProperty(FREE_STANDING_PROP); |
| isFreeStanding = prop == null ? true : prop.booleanValue(); |
| scrollBarShadowColor = UIManager.getColor("ScrollBar.shadow"); |
| super.installDefaults(); |
| } |
| |
| /** |
| * Creates a property change listener for the delegate to use. This |
| * overrides the method to provide a custom listener for the |
| * {@link MetalLookAndFeel} that can handle the |
| * <code>JScrollBar.isFreeStanding</code> property. |
| * |
| * @return A property change listener. |
| */ |
| protected PropertyChangeListener createPropertyChangeListener() |
| { |
| return new MetalScrollBarPropertyChangeHandler(); |
| } |
| |
| /** |
| * Creates a new button to use as the control at the lower end of the |
| * {@link JScrollBar}. |
| * |
| * @param orientation the orientation of the button ({@link #NORTH}, |
| * {@link #SOUTH}, {@link #EAST} or {@link #WEST}). |
| * |
| * @return The button. |
| */ |
| protected JButton createDecreaseButton(int orientation) |
| { |
| scrollBarWidth = UIManager.getInt("ScrollBar.width"); |
| decreaseButton = new MetalScrollButton(orientation, scrollBarWidth, |
| isFreeStanding); |
| return decreaseButton; |
| } |
| |
| /** |
| * Creates a new button to use as the control at the upper end of the |
| * {@link JScrollBar}. |
| * |
| * @param orientation the orientation of the button ({@link #NORTH}, |
| * {@link #SOUTH}, {@link #EAST} or {@link #WEST}). |
| * |
| * @return The button. |
| */ |
| protected JButton createIncreaseButton(int orientation) |
| { |
| scrollBarWidth = UIManager.getInt("ScrollBar.width"); |
| increaseButton = new MetalScrollButton(orientation, scrollBarWidth, |
| isFreeStanding); |
| return increaseButton; |
| } |
| |
| /** |
| * Paints the track for the scrollbar. |
| * |
| * @param g the graphics device. |
| * @param c the component. |
| * @param trackBounds the track bounds. |
| */ |
| protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) |
| { |
| g.setColor(MetalLookAndFeel.getControl()); |
| g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width, |
| trackBounds.height); |
| if (scrollbar.getOrientation() == HORIZONTAL) |
| paintTrackHorizontal(g, c, trackBounds.x, trackBounds.y, |
| trackBounds.width, trackBounds.height); |
| else |
| paintTrackVertical(g, c, trackBounds.x, trackBounds.y, |
| trackBounds.width, trackBounds.height); |
| |
| } |
| |
| /** |
| * Paints the track for a horizontal scrollbar. |
| * |
| * @param g the graphics device. |
| * @param c the component. |
| * @param x the x-coordinate for the track bounds. |
| * @param y the y-coordinate for the track bounds. |
| * @param w the width for the track bounds. |
| * @param h the height for the track bounds. |
| */ |
| private void paintTrackHorizontal(Graphics g, JComponent c, |
| int x, int y, int w, int h) |
| { |
| if (c.isEnabled()) |
| { |
| g.setColor(MetalLookAndFeel.getControlDarkShadow()); |
| g.drawLine(x, y, x, y + h - 1); |
| g.drawLine(x, y, x + w - 1, y); |
| g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); |
| |
| g.setColor(scrollBarShadowColor); |
| g.drawLine(x + 1, y + 1, x + 1, y + h - 1); |
| g.drawLine(x + 1, y + 1, x + w - 2, y + 1); |
| |
| if (isFreeStanding) |
| { |
| g.setColor(MetalLookAndFeel.getControlDarkShadow()); |
| g.drawLine(x, y + h - 2, x + w - 1, y + h - 2); |
| g.setColor(scrollBarShadowColor); |
| g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); |
| } |
| } |
| else |
| { |
| g.setColor(MetalLookAndFeel.getControlDisabled()); |
| if (isFreeStanding) |
| g.drawRect(x, y, w - 1, h - 1); |
| else |
| { |
| g.drawLine(x, y, x + w - 1, y); |
| g.drawLine(x, y, x, y + h - 1); |
| g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); |
| } |
| } |
| } |
| |
| /** |
| * Paints the track for a vertical scrollbar. |
| * |
| * @param g the graphics device. |
| * @param c the component. |
| * @param x the x-coordinate for the track bounds. |
| * @param y the y-coordinate for the track bounds. |
| * @param w the width for the track bounds. |
| * @param h the height for the track bounds. |
| */ |
| private void paintTrackVertical(Graphics g, JComponent c, |
| int x, int y, int w, int h) |
| { |
| if (c.isEnabled()) |
| { |
| g.setColor(MetalLookAndFeel.getControlDarkShadow()); |
| g.drawLine(x, y, x, y + h - 1); |
| g.drawLine(x, y, x + w - 1, y); |
| g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); |
| |
| g.setColor(scrollBarShadowColor); |
| g.drawLine(x + 1, y + 1, x + w - 1, y + 1); |
| g.drawLine(x + 1, y + 1, x + 1, y + h - 2); |
| |
| if (isFreeStanding) |
| { |
| g.setColor(MetalLookAndFeel.getControlDarkShadow()); |
| g.drawLine(x + w - 2, y, x + w - 2, y + h - 1); |
| g.setColor(MetalLookAndFeel.getControlHighlight()); |
| g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); |
| } |
| } |
| else |
| { |
| g.setColor(MetalLookAndFeel.getControlDisabled()); |
| if (isFreeStanding) |
| g.drawRect(x, y, w - 1, h - 1); |
| else |
| { |
| g.drawLine(x, y, x + w - 1, y); |
| g.drawLine(x, y, x, y + h - 1); |
| g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); |
| } |
| } |
| } |
| |
| /** |
| * Paints the slider button of the ScrollBar. |
| * |
| * @param g the Graphics context to use |
| * @param c the JComponent on which we paint |
| * @param thumbBounds the rectangle that is the slider button |
| */ |
| protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) |
| { |
| // a disabled scrollbar has no thumb in the metal look and feel |
| if (!c.isEnabled()) |
| return; |
| if (scrollbar.getOrientation() == HORIZONTAL) |
| paintThumbHorizontal(g, c, thumbBounds); |
| else |
| paintThumbVertical(g, c, thumbBounds); |
| |
| // Draw the pattern when the theme is not Ocean. |
| if (! (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme)) |
| { |
| MetalUtils.fillMetalPattern(c, g, thumbBounds.x + 3, thumbBounds.y + 3, |
| thumbBounds.width - 6, |
| thumbBounds.height - 6, |
| thumbHighlightColor, |
| thumbLightShadowColor); |
| } |
| } |
| |
| /** |
| * Paints the thumb for a horizontal scroll bar. |
| * |
| * @param g the graphics device. |
| * @param c the scroll bar component. |
| * @param thumbBounds the thumb bounds. |
| */ |
| private void paintThumbHorizontal(Graphics g, JComponent c, |
| Rectangle thumbBounds) |
| { |
| int x = thumbBounds.x; |
| int y = thumbBounds.y; |
| int w = thumbBounds.width; |
| int h = thumbBounds.height; |
| |
| // First we fill the background. |
| MetalTheme theme = MetalLookAndFeel.getCurrentTheme(); |
| if (theme instanceof OceanTheme |
| && UIManager.get("ScrollBar.gradient") != null) |
| { |
| MetalUtils.paintGradient(g, x + 2, y + 2, w - 4, h - 2, |
| SwingConstants.VERTICAL, |
| "ScrollBar.gradient"); |
| } |
| else |
| { |
| g.setColor(thumbColor); |
| if (isFreeStanding) |
| g.fillRect(x, y, w, h - 1); |
| else |
| g.fillRect(x, y, w, h); |
| } |
| |
| // then draw the dark box |
| g.setColor(thumbLightShadowColor); |
| if (isFreeStanding) |
| g.drawRect(x, y, w - 1, h - 2); |
| else |
| { |
| g.drawLine(x, y, x + w - 1, y); |
| g.drawLine(x, y, x, y + h - 1); |
| g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); |
| } |
| |
| // then the highlight |
| g.setColor(thumbHighlightColor); |
| if (isFreeStanding) |
| { |
| g.drawLine(x + 1, y + 1, x + w - 3, y + 1); |
| g.drawLine(x + 1, y + 1, x + 1, y + h - 3); |
| } |
| else |
| { |
| g.drawLine(x + 1, y + 1, x + w - 3, y + 1); |
| g.drawLine(x + 1, y + 1, x + 1, y + h - 1); |
| } |
| |
| // draw the shadow line |
| g.setColor(UIManager.getColor("ScrollBar.shadow")); |
| g.drawLine(x + w, y + 1, x + w, y + h - 1); |
| |
| // For the OceanTheme, draw the 3 lines in the middle. |
| if (theme instanceof OceanTheme) |
| { |
| g.setColor(thumbLightShadowColor); |
| int middle = x + w / 2; |
| g.drawLine(middle - 2, y + 4, middle - 2, y + h - 5); |
| g.drawLine(middle, y + 4, middle, y + h - 5); |
| g.drawLine(middle + 2, y + 4, middle + 2, y + h - 5); |
| g.setColor(UIManager.getColor("ScrollBar.highlight")); |
| g.drawLine(middle - 1, y + 5, middle - 1, y + h - 4); |
| g.drawLine(middle + 1, y + 5, middle + 1, y + h - 4); |
| g.drawLine(middle + 3, y + 5, middle + 3, y + h - 4); |
| } |
| } |
| |
| /** |
| * Paints the thumb for a vertical scroll bar. |
| * |
| * @param g the graphics device. |
| * @param c the scroll bar component. |
| * @param thumbBounds the thumb bounds. |
| */ |
| private void paintThumbVertical(Graphics g, JComponent c, |
| Rectangle thumbBounds) |
| { |
| int x = thumbBounds.x; |
| int y = thumbBounds.y; |
| int w = thumbBounds.width; |
| int h = thumbBounds.height; |
| |
| // First we fill the background. |
| MetalTheme theme = MetalLookAndFeel.getCurrentTheme(); |
| if (theme instanceof OceanTheme |
| && UIManager.get("ScrollBar.gradient") != null) |
| { |
| MetalUtils.paintGradient(g, x + 2, y + 2, w - 2, h - 4, |
| SwingConstants.HORIZONTAL, |
| "ScrollBar.gradient"); |
| } |
| else |
| { |
| g.setColor(thumbColor); |
| if (isFreeStanding) |
| g.fillRect(x, y, w - 1, h); |
| else |
| g.fillRect(x, y, w, h); |
| } |
| |
| // then draw the dark box |
| g.setColor(thumbLightShadowColor); |
| if (isFreeStanding) |
| g.drawRect(x, y, w - 2, h - 1); |
| else |
| { |
| g.drawLine(x, y, x + w - 1, y); |
| g.drawLine(x, y, x, y + h - 1); |
| g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); |
| } |
| |
| // then the highlight |
| g.setColor(thumbHighlightColor); |
| if (isFreeStanding) |
| { |
| g.drawLine(x + 1, y + 1, x + w - 3, y + 1); |
| g.drawLine(x + 1, y + 1, x + 1, y + h - 3); |
| } |
| else |
| { |
| g.drawLine(x + 1, y + 1, x + w - 1, y + 1); |
| g.drawLine(x + 1, y + 1, x + 1, y + h - 3); |
| } |
| |
| // draw the shadow line |
| g.setColor(UIManager.getColor("ScrollBar.shadow")); |
| g.drawLine(x + 1, y + h, x + w - 2, y + h); |
| |
| // For the OceanTheme, draw the 3 lines in the middle. |
| if (theme instanceof OceanTheme) |
| { |
| g.setColor(thumbLightShadowColor); |
| int middle = y + h / 2; |
| g.drawLine(x + 4, middle - 2, x + w - 5, middle - 2); |
| g.drawLine(x + 4, middle, x + w - 5, middle); |
| g.drawLine(x + 4, middle + 2, x + w - 5, middle + 2); |
| g.setColor(UIManager.getColor("ScrollBar.highlight")); |
| g.drawLine(x + 5, middle - 1, x + w - 4, middle - 1); |
| g.drawLine(x + 5, middle + 1, x + w - 4, middle + 1); |
| g.drawLine(x + 5, middle + 3, x + w - 4, middle + 3); |
| } |
| } |
| |
| /** |
| * Returns the minimum thumb size. For a free standing scroll bar the |
| * minimum size is <code>17 x 17</code> pixels, whereas for a non free |
| * standing scroll bar the minimum size is <code>15 x 15</code> pixels. |
| * |
| * @return The minimum thumb size. |
| */ |
| protected Dimension getMinimumThumbSize() |
| { |
| Dimension retVal; |
| if (scrollbar != null) |
| { |
| if (isFreeStanding) |
| retVal = MIN_THUMB_SIZE_FREE_STANDING; |
| else |
| retVal = MIN_THUMB_SIZE; |
| } |
| else |
| retVal = new Dimension(0, 0); |
| return retVal; |
| } |
| |
| /** |
| * Returns the <code>preferredSize</code> for the specified scroll bar. |
| * For a vertical scrollbar the height is the sum of the preferred heights |
| * of the buttons plus <code>30</code>. The width is fetched from the |
| * <code>UIManager</code> property <code>ScrollBar.width</code>. |
| * |
| * For horizontal scrollbars the width is the sum of the preferred widths |
| * of the buttons plus <code>30</code>. The height is fetched from the |
| * <code>UIManager</code> property <code>ScrollBar.height</code>. |
| * |
| * @param c the scrollbar for which to calculate the preferred size |
| * |
| * @return the <code>preferredSize</code> for the specified scroll bar |
| */ |
| public Dimension getPreferredSize(JComponent c) |
| { |
| int height; |
| int width; |
| height = width = 0; |
| |
| if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL) |
| { |
| width += incrButton.getPreferredSize().getWidth(); |
| width += decrButton.getPreferredSize().getWidth(); |
| width += 30; |
| height = UIManager.getInt("ScrollBar.width"); |
| } |
| else |
| { |
| height += incrButton.getPreferredSize().getHeight(); |
| height += decrButton.getPreferredSize().getHeight(); |
| height += 30; |
| width = UIManager.getInt("ScrollBar.width"); |
| } |
| |
| Insets insets = scrollbar.getInsets(); |
| |
| height += insets.top + insets.bottom; |
| width += insets.left + insets.right; |
| |
| return new Dimension(width, height); |
| } |
| } |
| |