/******************************************************************************* * Copyright (c) 2000, 2005 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.draw2d; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import org.eclipse.swt.graphics.Color; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.draw2d.geometry.Transposer; /** * Provides for the scrollbars used by the {@link ScrollPane}. A ScrollBar is made up of * five essential Figures: An 'Up' arrow button, a 'Down' arrow button, a draggable * 'Thumb', a 'Pageup' button, and a 'Pagedown' button. */ public class ScrollBar extends Figure implements Orientable, PropertyChangeListener { private static final int ORIENTATION_FLAG = Figure.MAX_FLAG << 1; /** @see Figure#MAX_FLAG */ protected static final int MAX_FLAG = ORIENTATION_FLAG; private static final Color COLOR_TRACK = FigureUtilities.mixColors( ColorConstants.white, ColorConstants.button); private RangeModel rangeModel = null; private IFigure thumb; private Clickable pageUp, pageDown; private Clickable buttonUp, buttonDown; /** * Listens to mouse events on the scrollbar to take care of scrolling. */ protected ThumbDragger thumbDragger = new ThumbDragger(); private boolean isHorizontal = false; private int pageIncrement = 50; private int stepIncrement = 10; /** * Transposes from vertical to horizontal if needed. */ protected final Transposer transposer = new Transposer(); { setRangeModel(new DefaultRangeModel()); } /** * Constructs a ScrollBar. ScrollBar orientation is vertical by default. Call * {@link #setHorizontal(boolean)} with <code>true</code> to set horizontal orientation. * * @since 2.0 */ public ScrollBar() { initialize(); } /** * Creates the default 'Up' ArrowButton for the ScrollBar. * * @return the up button * @since 2.0 */ protected Clickable createDefaultUpButton() { Button buttonUp = new ArrowButton(); buttonUp.setBorder(new ButtonBorder(ButtonBorder.SCHEMES.BUTTON_SCROLLBAR)); return buttonUp; } /** * Creates the default 'Down' ArrowButton for the ScrollBar. * * @return the down button * @since 2.0 */ protected Clickable createDefaultDownButton() { Button buttonDown = new ArrowButton(); buttonDown.setBorder(new ButtonBorder(ButtonBorder.SCHEMES.BUTTON_SCROLLBAR)); return buttonDown; } /** * Creates the pagedown Figure for the Scrollbar. * * @return the page down figure * @since 2.0 */ protected Clickable createPageDown() { return createPageUp(); } /** * Creates the pageup Figure for the Scrollbar. * * @return the page up figure * @since 2.0 */ protected Clickable createPageUp() { final Clickable clickable = new Clickable(); clickable.setOpaque(true); clickable.setBackgroundColor(COLOR_TRACK); clickable.setRequestFocusEnabled(false); clickable.setFocusTraversable(false); clickable.addChangeListener(new ChangeListener() { public void handleStateChanged(ChangeEvent evt) { if (clickable.getModel().isArmed()) clickable.setBackgroundColor(ColorConstants.black); else clickable.setBackgroundColor(COLOR_TRACK); } }); return clickable; } /** * Creates the Scrollbar's "thumb", the draggable Figure that indicates the Scrollbar's * position. * * @return the thumb figure * @since 2.0 */ protected IFigure createDefaultThumb() { Panel thumb = new Panel(); thumb.setMinimumSize(new Dimension(6, 6)); thumb.setBackgroundColor(ColorConstants.button); thumb.setBorder(new SchemeBorder(SchemeBorder.SCHEMES.BUTTON_CONTRAST)); return thumb; } /** * Returns the figure used as the up button. * @return the up button */ protected IFigure getButtonUp() { // TODO: The set method takes a Clickable while the get method returns an IFigure. // Change the get method to return Clickable (since that's what it's typed as). return buttonUp; } /** * Returns the figure used as the down button. * @return the down button */ protected IFigure getButtonDown() { // TODO: The set method takes a Clickable while the get method returns an IFigure. // Change the get method to return Clickable (since that's what it's typed as). return buttonDown; } /** * Returns the extent. * @return the extent * @see RangeModel#getExtent() */ public int getExtent() { return getRangeModel().getExtent(); } /** * Returns the minumum value. * @return the minimum * @see RangeModel#getMinimum() */ public int getMinimum() { return getRangeModel().getMinimum(); } /** * Returns the maximum value. * @return the maximum * @see RangeModel#getMaximum() */ public int getMaximum() { return getRangeModel().getMaximum(); } /** * Returns the figure used for page down. * @return the page down figure */ protected IFigure getPageDown() { // TODO: The set method takes a Clickable while the get method returns an IFigure. // Change the get method to return Clickable (since that's what it's typed as). return pageDown; } /** * Returns the the amound the scrollbar will move when the page up or page down areas are * pressed. * @return the page increment */ public int getPageIncrement() { return pageIncrement; } /** * Returns the figure used for page up. * @return the page up figure */ protected IFigure getPageUp() { // TODO: The set method takes a Clickable while the get method returns an IFigure. // Change the get method to return Clickable (since that's what it's typed as). return pageUp; } /** * Returns the range model for this scrollbar. * @return the range model */ public RangeModel getRangeModel() { return rangeModel; } /** * Returns the amount the scrollbar will move when the up or down arrow buttons are * pressed. * @return the step increment */ public int getStepIncrement() { return stepIncrement; } /** * Returns the figure used as the scrollbar's thumb. * @return the thumb figure */ protected IFigure getThumb() { return thumb; } /** * Returns the current scroll position of the scrollbar. * @return the current value * @see RangeModel#getValue() */ public int getValue() { return getRangeModel().getValue(); } /** * Returns the size of the range of allowable values. * @return the value range */ protected int getValueRange() { return getMaximum() - getExtent() - getMinimum(); } /** * Initilization of the ScrollBar. Sets the Scrollbar to have a ScrollBarLayout with * vertical orientation. Creates the Figures that make up the components of the ScrollBar. * * @since 2.0 */ protected void initialize() { setLayoutManager(new ScrollBarLayout(transposer)); setUpClickable(createDefaultUpButton()); setDownClickable(createDefaultDownButton()); setPageUp(createPageUp()); setPageDown(createPageDown()); setThumb(createDefaultThumb()); } /** * Returns <code>true</code> if this scrollbar is orientated horizontally, * <code>false</code> otherwise. * @return whether this scrollbar is horizontal */ public boolean isHorizontal() { return isHorizontal; } private void pageDown() { setValue(getValue() + getPageIncrement()); } private void pageUp() { setValue(getValue() - getPageIncrement()); } /** * @see PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event) { if (event.getSource() instanceof RangeModel) { setEnabled(getRangeModel().isEnabled()); if (RangeModel.PROPERTY_VALUE.equals(event.getPropertyName())) { firePropertyChange("value", event.getOldValue(), //$NON-NLS-1$ event.getNewValue()); revalidate(); } if (RangeModel.PROPERTY_MINIMUM.equals(event.getPropertyName())) { firePropertyChange("value", event.getOldValue(), //$NON-NLS-1$ event.getNewValue()); revalidate(); } if (RangeModel.PROPERTY_MAXIMUM.equals(event.getPropertyName())) { firePropertyChange("value", event.getOldValue(), //$NON-NLS-1$ event.getNewValue()); revalidate(); } if (RangeModel.PROPERTY_EXTENT.equals(event.getPropertyName())) { firePropertyChange("value", event.getOldValue(), //$NON-NLS-1$ event.getNewValue()); revalidate(); } } } /** * @see IFigure#revalidate() */ public void revalidate() { // Override default revalidate to prevent going up the parent chain. Reason for this // is that preferred size never changes unless orientation changes. invalidate(); getUpdateManager().addInvalidFigure(this); } /** * Does nothing because this doesn't make sense for a scrollbar. * @see Orientable#setDirection(int) */ public void setDirection(int direction) { //Doesn't make sense for Scrollbar. } /** * Sets the Clickable that represents the down arrow of the Scrollbar to <i>down</i>. * * @param down the down button * @since 2.0 */ public void setDownClickable(Clickable down) { if (buttonDown != null) { remove(buttonDown); } buttonDown = down; if (buttonDown != null) { if (buttonDown instanceof Orientable) ((Orientable)buttonDown).setDirection(isHorizontal() ? Orientable.EAST : Orientable.SOUTH); buttonDown.setFiringMethod(Clickable.REPEAT_FIRING); buttonDown.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { stepDown(); } }); add(buttonDown, ScrollBarLayout.DOWN_ARROW); } } /** * Sets the Clickable that represents the up arrow of the Scrollbar to <i>up</i>. * * @param up the up button * @since 2.0 */ public void setUpClickable(Clickable up) { if (buttonUp != null) { remove(buttonUp); } buttonUp = up; if (up != null) { if (up instanceof Orientable) ((Orientable)up).setDirection(isHorizontal() ? Orientable.WEST : Orientable.NORTH); buttonUp.setFiringMethod(Clickable.REPEAT_FIRING); buttonUp.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { stepUp(); } }); add(buttonUp, ScrollBarLayout.UP_ARROW); } } /** * @see IFigure#setEnabled(boolean) */ public void setEnabled(boolean value) { if (isEnabled() == value) return; super.setEnabled(value); setChildrenEnabled(value); if (getThumb() != null) { getThumb().setVisible(value); revalidate(); } } /** * Sets the extent of the Scrollbar to <i>ext</i> * * @param ext the extent * @since 2.0 */ public void setExtent(int ext) { if (getExtent() == ext) return; getRangeModel().setExtent(ext); } /** * Sets the orientation of the ScrollBar. If <code>true</code>, the Scrollbar will have * a horizontal orientation. If <code>false</code>, the scrollBar will have a vertical * orientation. * * @param value <code>true</code> if the scrollbar should be horizontal * @since 2.0 */ public final void setHorizontal(boolean value) { setOrientation(value ? HORIZONTAL : VERTICAL); } /** * Sets the maximum position to <i>max</i>. * * @param max the maximum position * @since 2.0 */ public void setMaximum(int max) { if (getMaximum() == max) return; getRangeModel().setMaximum(max); } /** * Sets the minimum position to <i>min</i>. * * @param min the minumum position * @since 2.0 */ public void setMinimum(int min) { if (getMinimum() == min) return; getRangeModel().setMinimum(min); } /** * @see Orientable#setOrientation(int) */ public void setOrientation(int value) { if ((value == HORIZONTAL) == isHorizontal()) return; isHorizontal = value == HORIZONTAL; transposer.setEnabled(isHorizontal); setChildrenOrientation(value); super.revalidate(); } /** * Sets the ScrollBar to scroll <i>increment</i> pixels when its pageup or pagedown * buttons are pressed. (Note that the pageup and pagedown buttons are <b>NOT</b> the * arrow buttons, they are the figures between the arrow buttons and the ScrollBar's * thumb figure). * * @param increment the new page increment * @since 2.0 */ public void setPageIncrement(int increment) { pageIncrement = increment; } /** * Sets the pagedown button to the passed Clickable. The pagedown button is the figure * between the down arrow button and the ScrollBar's thumb figure. * * @param down the page down figure * @since 2.0 */ public void setPageDown(Clickable down) { if (pageDown != null) remove(pageDown); pageDown = down; if (pageDown != null) { pageDown.setFiringMethod(Clickable.REPEAT_FIRING); pageDown.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { pageDown(); } }); add(down, ScrollBarLayout.PAGE_DOWN); } } /** * Sets the pageup button to the passed Clickable. The pageup button is the rectangular * figure between the down arrow button and the ScrollBar's thumb figure. * * @param up the page up figure * @since 2.0 */ public void setPageUp(Clickable up) { if (pageUp != null) remove(pageUp); pageUp = up; if (pageUp != null) { pageUp.setFiringMethod(Clickable.REPEAT_FIRING); pageUp.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { pageUp(); } }); add(pageUp, ScrollBarLayout.PAGE_UP); } } /** * Sets the ScrollBar's RangeModel to the passed value. * * @param rangeModel the new range model * @since 2.0 */ public void setRangeModel(RangeModel rangeModel) { if (this.rangeModel != null) this.rangeModel.removePropertyChangeListener(this); this.rangeModel = rangeModel; rangeModel.addPropertyChangeListener(this); } /** * Sets the ScrollBar's step increment to the passed value. The step increment indicates * how many pixels the ScrollBar will scroll when its up or down arrow button is pressed. * * @param increment the new step increment * @since 2.0 */ public void setStepIncrement(int increment) { stepIncrement = increment; } /** * Sets the ScrollBar's thumb to the passed Figure. The thumb is the draggable component * of the ScrollBar that indicates the ScrollBar's position. * * @param figure the thumb figure * @since 2.0 */ public void setThumb(IFigure figure) { if (thumb != null) { thumb.removeMouseListener(thumbDragger); thumb.removeMouseMotionListener(thumbDragger); remove(thumb); } thumb = figure; if (thumb != null) { thumb.addMouseListener(thumbDragger); thumb.addMouseMotionListener(thumbDragger); add(thumb, ScrollBarLayout.THUMB); } } /** * Sets the value of the Scrollbar to <i>v</i> * * @param v the new value * @since 2.0 */ public void setValue(int v) { getRangeModel().setValue(v); } /** * Causes the ScrollBar to scroll down (or right) by the value of its step increment. * * @since 2.0 */ protected void stepDown() { setValue(getValue() + getStepIncrement()); } /** * Causes the ScrollBar to scroll up (or left) by the value of its step increment. * * @since 2.0 */ protected void stepUp() { setValue(getValue() - getStepIncrement()); } class ThumbDragger extends MouseMotionListener.Stub implements MouseListener { protected Point start; protected int dragRange; protected int revertValue; protected boolean armed; public ThumbDragger() { } public void mousePressed(MouseEvent me) { armed = true; start = me.getLocation(); Rectangle area = new Rectangle(transposer.t(getClientArea())); Dimension thumbSize = transposer.t(getThumb().getSize()); if (getButtonUp() != null) area.height -= transposer.t(getButtonUp().getSize()).height; if (getButtonDown() != null) area.height -= transposer.t(getButtonDown().getSize()).height; Dimension sizeDifference = new Dimension(area.width, area.height - thumbSize.height); dragRange = sizeDifference.height; revertValue = getValue(); me.consume(); } public void mouseDragged(MouseEvent me) { if (!armed) return; Dimension difference = transposer.t(me.getLocation().getDifference(start)); int change = getValueRange() * difference.height / dragRange; setValue(revertValue + change); me.consume(); } public void mouseReleased(MouseEvent me) { if (!armed) return; armed = false; me.consume(); } public void mouseDoubleClicked(MouseEvent me) { } } }