home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Symantec Visual Cafe for Java 2.5
/
symantec-visual-cafe-2.5-database-dev-edition.iso
/
VCafe
/
prosrc.bin
/
Scroller.java
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
Macintosh to JP
NeXTSTEP
RISC OS/Acorn
Shift JIS
UTF-8
Wrap
Java Source
|
1998-03-18
|
46.6 KB
|
1,547 lines
/*
* Copyright (c) 1996 Krumel & Associates, Inc. All Rights Reserved.
*
* www.krumel.com - controls@krumel.com
*
* Permission is given to the buyer of this package for one software
* developer to use this software on one CPU (one workstation) and to make
* one backup copy. You may uitilize and/or midfy this class for use in your
* projects. You may distribute or sell any executable which results from
* using this code in yur application, except a utility or class of similar
* nature to this product. You may distribute this this product in compiled
* form only, but soley to be used with your cmpiled executable product
* for the puposes of dynamic loading. You may NOT redistribute the source
* code in any form or make it accessible through a network or other
* distribution media to others. Please refer to the file "copyright.html"
* for further important copyright and licensing information.
*
* The source code is the confidential and proprietary information
* of Krumel & Associates, Inc. ("Confidential Information"). You shall
* not disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Krumel & Associates, Inc..
* KRUMEL & ASSOCIATES MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
* SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. KRUMEL & ASSOCIATES SHALL NOT
* BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
* MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*/
package symantec.itools.db.awt.awt;
import java.awt.*;
import java.util.*;
import symantec.itools.db.awt.genutil.*;
import java.awt.event.*;
/**
* A scrollbar implemented entirely in Java.
*/
public class Scroller extends Canvas
implements Observer, Notifiable, Adjustable {
/**
* The background color for the scrollbar (where buttons and slider are not located).
*/
Color bg = new Color(220, 220, 220);
/**
* The foregound color for the scrollbar (color of button faces).
*/
Color fg = Color.lightGray;
/**
* The highlight color for the raised portion that gets the light.
*/
Color upHl = Color.white;
/**
* The highlight color for the raised portion of a button that gets the shadow.
*/
Color downHl = Color.gray;
/**
* The color of the border around the scrollbar.
*/
Color borderColor = Color.gray;
/**
* Color used to draw border around buttons
*/
Color shapeColor = Color.black;
/**
* Value for whether a button is currently being depressed and down.
*/
boolean buttonDown;
/**
* Which button is in the pushed state, if any
*/
int buttonPushed = NONE;
/**
* The minimum value of the Scroller.
*/
int min;
/**
* The maximum value of the Scroller.
*/
int max;
/**
* The size of the visible portion of the Scroller.
*/
int thumbSize;
/**
* The current position of the thumb.
*/
int currentPos;
/**
* The Scroller's orientation--being either horizontal or vertical.
*/
int orientation;
/**
* The amount by which the Scroller value will change when going
* up or down by a page.
*/
int pageIncrement = 10;
/**
* The amount by which the Scroller value will change when going
* up or down by a line.
*/
int lineIncrement = 1;
/**
* Offscreen image used to draw Scroller
*/
Image image;
/**
* The graphics context for the offscreen image
*/
Graphics imageG;
/**
* The size of buttons and scrollbar width/height.
*/
int scroll_ht = DEFAULT_SCROLL_HT;
Timer timer = Timer.getTimer();
transient AdjustmentListener adjustmentListener;
/**
* Synchronization lock independent of AWT
*/
protected final Object LOCK = new Object();
static final int BASE = 555555;
/**
* Event ID for when scrolling is initiated by the user. The following events
* be any standard scroll event defined in the Event class. When all scrolling
* ceases, an END_SCROLL event will be propagated through the containment
* hierarchy.
*/
public static final int BEGIN_SCROLL = BASE + 1;
/**
* Event ID for when a series of scrolling events initiated by the user are
* ceased. Always preceeded by a BEGIN_SCROLL event and at least one scroll
* event as defined in the Event class.
*/
public static final int END_SCROLL = BASE + 2;
/**
* The default size of buttons and scrollbar width/height.
*/
public static int DEFAULT_SCROLL_HT = 17;
//button constants
static final int NONE = -1;
static final int LEFT = 0;
static final int RIGHT = 1;
static final int UP = 2;
static final int DOWN = 3;
static final int SLIDER = 4;
/**
* Constructs a new vertical Scroller.
*/
public Scroller() {
this(VERTICAL);
}
/**
* Constructs a new Scroller with the specified orientation.
* @param orientation either Scroller.HORIZONTAL or Scroller.VERTICAL
* @exception IllegalArgumentException When an illegal Scroller orientation is given.
*/
public Scroller(int orientation) {
//enable mouse events so java.awt.Component will forward them to us.
enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);
switch (orientation) {
case Scroller.HORIZONTAL:
case Scroller.VERTICAL:
this.orientation = orientation;
break;
default:
throw new IllegalArgumentException("illegal Scroller orientation");
}
timer.register(this, 100);
}
/**
* Constructs a new Scroller with the specified orientation,
* value, page size, and minumum and maximum values.
* @param orientation either Scroller.HORIZONTAL or Scroller.VERTICAL
* @param value the Scroller's value
* @param visible the size of the visible portion of the
* scrollable area. The Scroller will use this value when paging up
* or down by a page.
* @param minimum the minimum value of the Scroller
* @param maximum the maximum value of the Scroller
*/
public Scroller(int orientation, int value, int visible, int minimum, int maximum) {
this(orientation);
setValues(value, visible, minimum, maximum);
}
/**
* Returns the orientation for this Scroller.
*/
public int getOrientation() {
return orientation;
}
/**
* Returns the current value of this Scroller.
* @see #getMinimum
* @see #getMaximum
*/
public int getValue() {
return currentPos;
}
/**
* Sets the value of this Scroller to the specified value.
* @param value the new value of the Scroller. If this value is
* below the current minimum or above the current maximum, it becomes the
* new one of those values, respectively.
* @see #getValue
*/
public void setValue(int value) {
if (value < min) {
value = min;
}
if (value > max-thumbSize+1) {
value = max-thumbSize+1;
}
if (value != currentPos) {
currentPos = value;
redoSliderRect();
redrawAsync();
}
}
/**
* Sets the maximum sideways dimension of the scroll buttons.
*/
public void setButtonHeight(int pixels) {
scroll_ht = pixels;
}
/**
* Gets the maximum sideways dimension of the scroll buttons.
*/
public int getButtonHeight() {
return scroll_ht;
}
/**
* Prints a values of the scroller to standard out.
*/
public void printString() {
System.out.println(toString());
}
/**
* Inherited from java.lang.Object.
*/
public String toString() {
return "symantec.itools.db.awt.Scroller: value="+currentPos +" min="+min+" max="+max+" currPos="+currentPos
+ " thumbSize=" + thumbSize
+ " orientation="+((orientation==HORIZONTAL) ?"HORIZONTAL" :"VERTICAL");
}
/**
* Returns the minimum value of this Scroller.
* @see #getMaximum
* @see #getValue
*/
public int getMinimum() {
return min;
}
/**
* Returns the maximum value of this Scroller.
* @see #getMinimum
* @see #getValue
*/
public int getMaximum() {
return max;
}
/**
* Returns the visible amount of the Scroller.
*/
public int getVisible() {
return thumbSize;
}
/**
* Sets the line increment for this Scroller. This is the value
* that will be added (subtracted) when the user hits the line down
* (up) gadgets.
*/
public void setLineIncrement(int l) {
if (l != lineIncrement) {
lineIncrement = l;
redoSliderRect();
redrawAsync();
}
}
/**
* Gets the line increment for this Scroller.
*/
public int getLineIncrement() {
return lineIncrement;
}
/**
* Sets the page increment for this Scroller. This is the value
* that will be added (subtracted) when the user hits the page down
* (up) gadgets.
*/
public void setPageIncrement(int l) {
if (l != pageIncrement) {
pageIncrement = l;
redoSliderRect();
redrawAsync();
}
}
/**
* Gets the page increment for this Scroller.
*/
public int getPageIncrement() {
return pageIncrement;
}
/**
* Sets the unit value increment for the adjustable object.
* @param u the unit increment
*/
public void setUnitIncrement(int u) {
if (u != lineIncrement) {
lineIncrement = u;
redoSliderRect();
redrawAsync();
}
}
/**
* Gets the unit value increment for the adjustable object.
*/
public int getUnitIncrement() {
return lineIncrement;
}
/**
* Sets the block value increment for the adjustable object.
* @param b the block increment
*/
public void setBlockIncrement(int b) {
if (pageIncrement != b) {
pageIncrement = b;
redoSliderRect();
redrawAsync();
}
}
/**
* Gets the block value increment for the adjustable object.
*/
public int getBlockIncrement() {
return pageIncrement;
}
/**
* Sets the length of the proportionl indicator of the
* adjustable object.
* @param v the length of the indicator
*/
public void setVisibleAmount(int v) {
int amount = Math.min(v, max-min+1);
if (thumbSize != amount) {
thumbSize = amount;
redoSliderRect();
redrawAsync();
}
}
/**
* Gets the length of the propertional indicator.
*/
public int getVisibleAmount() {
return thumbSize;
}
/**
* Sets the minimum value of the adjustable object.
* @param min the minimum value
*/
public void setMinimum(int min) {
boolean changed = false;
if (max < min) {
this.min = max;
max = min;
changed = true;
} else if (this.min != min) {
this.min = min;
changed = true;
}
if (changed) {
setVisibleAmount(thumbSize);
redoSliderRect();
redrawAsync();
}
}
/**
* Sets the maximum value of the adjustable object.
* @param max the maximum value
*/
public void setMaximum(int max) {
boolean changed = false;
if (max < min) {
this.max = min;
min = max;
changed = true;
} else if (this.max != max) {
this.max = max;
changed = true;
}
if (changed) {
setVisibleAmount(thumbSize);
redoSliderRect();
redrawAsync();
}
}
/**
* Sets the values for this Scroller.
* @param value is the position in the current window.
* @param visible is the amount visible per page
* @param minimum is the minimum value of the Scroller
* @param maximum is the maximum value of the Scroller
*/
public void setValues(int value, int visible, int minimum, int maximum) {
boolean changed = false;
synchronized(LOCK) {
if (maximum-minimum == 0) {
min = minimum;
max = min + 1;
thumbSize = 1;
value = min;
redrawAsync();
return;
}
if (maximum < minimum) {
int temp = maximum;
maximum = minimum;
minimum = temp;
}
if (min != minimum || maximum != max) {
min = minimum;
max = maximum;
changed = true;
}
if (thumbSize != visible || thumbSize > max-min+1) {
thumbSize = Math.max(Math.min(visible, max-min+1), 1);
changed = true;
}
if (currentPos != value) {
currentPos = value;
changed = true;
}
if (value < min) {
changed = true;
currentPos = min;
}
if (value > max-thumbSize+1) {
changed = true;
currentPos = max-thumbSize+1;
}
if (changed) {
redoSliderRect();
redrawAsync();
}
} //end synchronized
}
/**
* Derived from java.awt.Component.
*/
public void update(Graphics g) {
paint(g);
}
/**
* Derived from java.awt.Component.
*/
public void print(Graphics g) {
if (!isVisible() || lastSize == null) {
return;
}
Graphics realG = imageG;
imageG = g;
try {
computeSliderRect();
drawScroller();
} catch(ArithmeticException ex) {}
imageG = realG;
}
/**
* Derived from java.awt.Component.
*/
public void paint(Graphics g) {
if (g instanceof PrintGraphics) {
print(g);
return;
}
if (image == null) {
redrawAsync();
return;
}
synchronized (LOCK) {
g.drawImage(image, 0, 0, this);
}
}
/**
* Reshapes the Component to the specified bounding box.
* @param x the x coordinate
* @param y the y coordinate
* @param width the width of the component
* @param height the height of the component
* @see #getBounds
* @see #setLocation
* @see #setSize
*/
public void setBounds(int x, int y, int width, int height) {
reshape(x, y, width, height);
}
/**
* @deprecated As of JDK version 1.1,
* replaced by setBounds(int, int, int, int).
*/
public void reshape(int x, int y, int w, int h) {
//hack here - AWT deprecated functions are used as the implementation for the new
//functions - so must continue to use super.reshape()!!!!!
super.reshape(x, y, w, h);
updateSliderRect = true;
redrawAsync();
}
/**
* Derived from java.awt.Component.
*/
public void setBounds(Rectangle r) {
super.setBounds(r);
updateSliderRect = true;
redrawAsync();
}
/**
* Derived from java.awt.Component.
*/
public Dimension getPreferredSize() {
if (!isVisible()) {
return new Dimension(0, 0);
}
if (orientation == HORIZONTAL) {
return new Dimension(scroll_ht*5, scroll_ht);
} else {
return new Dimension(scroll_ht, scroll_ht*5);
}
}
/**
* @deprecated As of JDK version 1.1,
* replaced by getPreferredSize().
*/
public Dimension preferredSize() {
return getPreferredSize();
}
/**
* Derived from java.awt.Component.
*/
public Dimension getMinimumSize() {
return preferredSize();
}
/**
* @deprecated As of JDK version 1.1,
* replaced by getMinimumSize().
*/
public Dimension minimumSize() {
return getMinimumSize();
}
/**
* Creates an AdjustmentEvent with the specified ID with the scroller as the target and
* an Integer containing the current position as the argument. The AdjustmentEvent
* instance is then fired using processEvent.
* @param id The ID of the event to be generated.
*/
protected void generateEvent(int id) {
AdjustmentEvent e = new AdjustmentEvent(this,
AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED,
id,
currentPos);
processEvent(e);
}
/**
* Processes mouse events occurring on this component by
* dispatching them to any registered MouseListener objects.
* NOTE: This method will not be called unless mouse events
* are enabled for this component; this happens when one of the
* following occurs:
* a) A MouseListener object is registered via addMouseListener()
* b) Mouse events are enabled via enableEvents()
* Classes overriding this method should call super.processMouseEvent()
* to ensure default event processing continues normally.
* @param e the mouse event
*/
protected void processMouseEvent(MouseEvent e) {
int id = e.getID();
switch(id) {
case MouseEvent.MOUSE_PRESSED:
mousePressed(e.getX(), e.getY());
break;
case MouseEvent.MOUSE_RELEASED:
mouseReleased(e.getX(), e.getY());
break;
case MouseEvent.MOUSE_CLICKED:
//no op
break;
case MouseEvent.MOUSE_EXITED:
mouseExited(e.getX(), e.getY());
break;
case MouseEvent.MOUSE_ENTERED:
mouseEntered(e.getX(), e.getY());
break;
}
//let the parent class process the event as normal
super.processMouseEvent(e);
}
/**
* Processes mouse motion events occurring on this component by
* dispatching them to any registered MouseMotionListener objects.
* NOTE: This method will not be called unless mouse motion events
* are enabled for this component; this happens when one of the
* following occurs:
* a) A MouseMotionListener object is registered via addMouseMotionListener()
* b) Mouse Motion events are enabled via enableEvents()
* Classes overriding this method should call super.processMouseMotionEvent()
* to ensure default event processing continues normally.
* @param e the mouse motion event
*/
protected void processMouseMotionEvent(MouseEvent e) {
if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
mouseDragged(e.getX(), e.getY());
}
super.processMouseMotionEvent(e);
}
protected final void mousePressed(int x, int y) {
generateEvent(BEGIN_SCROLL);
if (clickOnButton(x, y)) {
handleButtonClick(x, y);
} else if (clickOnSlider(x, y)) {
handleSliderDragStart(x, y);
} else {
handlePageClick(x, y);
}
}
//variable to remember if the mouse button is depressed. This is needed mainly for
//the MAC where mouse capture does not work properly.
boolean down;
/**
* Called by processMouseMovement when mouse has entered scroller.
*/
protected final void mouseDragged(int x, int y) {
if (buttonDown()) {
handleButtonDrag(x, y);
} else if (sliderMoving()) {
handleSliderDrag(x, y);
} else {
handlePageDrag(x, y);
}
down = true;
}
/**
* Called by processMouseEvent when mouse has entered scroller.
*/
protected final void mouseReleased(int x, int y) {
if (buttonDown()) {
handleButtonUp();
} else if (sliderMoving()) {
handleSliderUp();
updateSliderRect = true;
computeSliderRect();
} else {
handlePageUp();
}
generateEvent(END_SCROLL);
down = false;
}
/**
* Called by processMouseEvent when mouse has exited scroller.
*/
protected final void mouseExited(int x, int y) {
if (down) {
mouseDragged(x, y);
}
}
/**
* Derived from java.awt.Component.
*/
public void mouseEntered(int x, int y) {
if (down) {
mouseDragged(x, y);
}
}
/**
* Causes the scrollbar to be redrawn using an asynchronous thread.
*/
public void redrawAsync() {
AsyncNotifier.notify(this);
}
/**
* Callback function implemented from symantec.itools.db.awt.Notifiable.
*/
public void asyncNotify(Vector args) {
redraw();
}
Dimension lastSize;
/**
* Synchronously draws the scrollbar.
*/
private void redraw() {
synchronized (LOCK) {
//check for size change
Dimension size = getSize();
if (size.height <= 0 || size.width <= 0) { return; }
if (lastSize == null || lastSize.width != size.width
|| lastSize.height != size.height)
{
image = createImage(size.width, size.height);
/*<vj>*/
if (image == null) {
return;
}
/*<vj>*/
imageG = image.getGraphics();
if (imageG == null) {
lastSize = null;
return;
}
lastSize = size;
}
//draw scrollbar
try {
computeSliderRect();
drawScroller();
} catch(ArithmeticException ex) { }
paint(getGraphics());
}
}
/**
* Gets a reference to the actual Graphics object used to draw the scrollbar.
*/
protected Graphics getImageGraphics() { return imageG; }
/**
* Called to request the slider be drawn on the scrollbar. This method may be
* overriden to give the slider a different look and feel from the default
* of a solid square.
* @param x The top left x coordinate.
* @param y The top left y coordinate.
* @param w The width of the slider in pixels.
* @param h The height of the slider in pixels.
* @param fg The foreground color to be used to draw the face of the slider.
* @param button Should always be SLIDER.
*/
protected void drawSliderButton(int x, int y, int w, int h, Color fg, int button) {
drawButton(x, y, w, h, fg, button);
}
/**
* Called by Scroller whenever a push button needs to be drawn. In its
* default configuration, the Scroller draws the scroll line up and down
* buttons and the slider using this method to draw the basic shape and
* shading. The arrows on the up and down buttons are drawn following
* this method in the drawScrollButton() method.
* @param x The top left x coordinate.
* @param y The top left y coordinate.
* @param w The width of the slider in pixels.
* @param h The height of the slider in pixels.
* @param fg The foreground color to be used to draw the face of the slider.
* @param button One of UP, DOWN, LEFT, RIGHT, SLIDER.
*/
protected void drawButton(int x, int y, int w, int h, Color fg, int button) {
imageG.setColor(fg);
imageG.fillRect(x+1, y+1, w-3, h-3);
//draw highlight
boolean drawDown = button==buttonPushed && buttonDown;
imageG.setColor(!drawDown ?upHl :downHl);
imageG.drawLine(x+1, y+1, x+1, y+h-2); //left
imageG.drawLine(x+1, y+1, x+w-2, y+1); //top
imageG.setColor(drawDown ?upHl :downHl);
imageG.drawLine(x+w-2, y+1, x+w-2, y+h-2); //right
imageG.drawLine(x+1, y+h-2, x+w-2, y+h-2); //bottom
//draw boundary
imageG.setColor(shapeColor);
imageG.drawRect(x, y, w-1, h-1);
}
/**
* Called whenever a scroll line up/down button needs to be drawn. If calls
* drawButton() then draws the arrow.
* @param x The top left x coordinate.
* @param y The top left y coordinate.
* @param w The width of the slider in pixels.
* @param h The height of the slider in pixels.
* @param direction One of LEFT, RIGHT, UP, or DOWN.
*/
protected void drawScrollButton(int x, int y, int w, int h, int direction)
{
drawButton(x, y, w, h, fg, direction);
Polygon poly = new Polygon();
switch (direction) {
case LEFT:
if (w < 13) { return; }
poly.addPoint(5, h/2);
poly.addPoint(10, h/2+5);
poly.addPoint(10, h/2-5);
poly.addPoint(5, h/2);
break;
case RIGHT:
if (w < 12) { return; }
poly.addPoint(x+w-5, h/2);
poly.addPoint(x+w-10, h/2+5);
poly.addPoint(x+w-10, h/2-5);
poly.addPoint(x+w-5, h/2);
break;
case UP:
if (h < 13) { return; }
poly.addPoint(w/2, 5);
poly.addPoint(w/2+5, 10);
poly.addPoint(w/2-5, 10);
poly.addPoint(w/2, 5);
break;
case DOWN:
if (h < 12) { return; }
poly.addPoint(w/2, y+h-5);
poly.addPoint(w/2+5, y+h-10);
poly.addPoint(w/2-5, y+h-10);
poly.addPoint(w/2, y+h-5);
}
imageG.setColor(shapeColor);
imageG.fillPolygon(poly);
}
/**
* Called by the Scroller whenever the scrollbar needs to be drawn. The
* drawing graphics context is guaranteed to be valid whenever this method
* is called. It draws the scroll buttons, the slider, and fills in the
* background.
*/
protected void drawScroller() {
Dimension size = lastSize;
if (orientation == HORIZONTAL) {
int width = scroll_ht;
if (size.width < width*2) {
width = size.width/2;
}
//draw left scroll button
drawScrollButton(0,
0,
width,
size.height,
LEFT);
//draw right scroll button
drawScrollButton(size.width-width,
0,
width,
size.height,
RIGHT);
//draw slider
Rectangle sr = effectiveSlider();
drawSliderButton(sr.x, sr.y, sr.width, sr.height, fg, SLIDER);
//erase everything else
imageG.setColor(bg);
//erase from left button to scroll button
if (width >= scroll_ht && sr.x > 0) {
imageG.fillRect(width, 1, sr.x-width, size.height-2);
} else {
imageG.fillRect(width, 1, size.width - width*2, size.height-2);
}
//erase from right side of scroll button to right button
if (width >= scroll_ht && sr.x > 0) {
imageG.fillRect(sr.x+sr.width, 1, size.width-(sr.x+sr.width)-width, size.height-2);
}
} else {
//draw vertical Scroller
int height = scroll_ht;
if (size.height < height*2) {
height = size.height/2;
}
//draw top scroll button
drawScrollButton(0,
0,
size.width,
height,
UP);
//draw bottom scroll button
drawScrollButton(0,
size.height - height,
size.width,
height,
DOWN);
//draw slider
Rectangle sr = effectiveSlider();
drawButton(sr.x, sr.y, sr.width, sr.height, fg, SLIDER);
//erase everything else
imageG.setColor(bg);
//erase from left button to scroll button
if (height >= scroll_ht && sr.y > 0) {
imageG.fillRect(1, height, size.width-2, sr.y-height);
} else {
imageG.fillRect(1, height, size.width-2, size.height - height*2);
}
//erase from right side of scroll button to right button
if (height >= scroll_ht && sr.y > 0) {
imageG.fillRect(1, sr.y+sr.height, size.width-2, size.height-(sr.y+sr.height)-height);
}
}
//draw boundary
imageG.setColor(borderColor);
imageG.drawRect(0,
0,
size.width-1,
size.height-1);
}
/**
* Computes the position and dimensions of the slider based on the current
* values.
*/
Rectangle effectiveSlider() {
if (orientation == HORIZONTAL) {
if (lastSize.width < scroll_ht*2 + 7) {
return new Rectangle(-100, -100, 1, 1);
}
return new Rectangle(Math.min(lastSize.width-zeroPt-(int)effThumbSize+1,
Math.max(zeroPt, sliderRect.x)),
sliderRect.y,
sliderRect.width,
sliderRect.height);
} else {
if (lastSize.height < scroll_ht*2 + 7) {
return new Rectangle(-100, -100, 1, 1);
}
return new Rectangle(sliderRect.x,
Math.min(lastSize.height-zeroPt-(int)effThumbSize+1,
Math.max(zeroPt, sliderRect.y)),
sliderRect.width,
sliderRect.height);
}
}
/**
* Determines if the specified coordinates are a button push of some type.
*/
boolean clickOnButton(int x, int y) {
Dimension size = lastSize;
if (orientation == HORIZONTAL
&& (x < scroll_ht || x > size.width - scroll_ht))
{
return true;
} else if (orientation == VERTICAL
&& (y < scroll_ht || y > size.height - scroll_ht))
{
return true;
}
return false;
}
/**
* Perfroms the necessary event gernation and redrawing required when the
* user clicks on a button. It starts the timer up to generate a callback
* after 450 msecs. It is later reduced to 50msecs, but the initial time
* permits the user to daddle for a little with the button down without
* getting fast scrolling.
*/
void handleButtonClick(int x, int y) {
Dimension size = lastSize;
if (orientation == HORIZONTAL) {
int width = scroll_ht;
if (size.width < width*2) {
width = size.width/2;
}
if (x < width) {
int orig = currentPos;
currentPos = Math.max(currentPos-lineIncrement, min);
if (orig != currentPos) {
generateEvent(AdjustmentEvent.UNIT_DECREMENT);
}
buttonPushed = LEFT;
buttonDown = true;
} else if (x > size.width - width) {
int orig = currentPos;
currentPos = Math.min(currentPos+lineIncrement, max-thumbSize+1);
if (orig != currentPos) {
generateEvent(AdjustmentEvent.UNIT_INCREMENT);
}
buttonPushed = RIGHT;
buttonDown = true;
}
} else if (orientation == VERTICAL) {
int height = scroll_ht;
if (size.height < height*2) {
height = size.height/2;
}
if (y < height) {
int orig = currentPos;
currentPos = Math.max(currentPos-lineIncrement, min);
if (orig != currentPos) {
generateEvent(AdjustmentEvent.UNIT_DECREMENT);
}
buttonPushed = UP;
buttonDown = true;
} else if (y > size.height - height) {
int orig = currentPos;
currentPos = Math.min(currentPos+lineIncrement, max-thumbSize+1);
if (orig != currentPos) {
generateEvent(AdjustmentEvent.UNIT_INCREMENT);
}
buttonPushed = DOWN;
buttonDown = true;
}
}
redoSliderRect();
redrawAsync();
timer.activate(this, 450);
}
/**
* Returns whether a scroll button is currently pressed.
*/
protected boolean buttonDown() {
return buttonPushed >= LEFT && buttonPushed <= DOWN;
}
/**
* Called when a scroll button is pushed by the user and the mouse is
* dragged from its last position. If the user moves the mouse off of
* the button area, the button is raised and timer events are ceased.
* When the button is later depressed, timer events are resumed.
*/
void handleButtonDrag(int x, int y) {
Dimension size = lastSize;
boolean before = buttonDown;
switch (buttonPushed) {
case LEFT:
buttonDown = (x<scroll_ht && x>0 && y>0 && y<size.height);
break;
case RIGHT:
buttonDown = (x<size.width && x>size.width - scroll_ht
&&y>0 && y<size.height);
break;
case UP:
buttonDown = (y>0 && y<scroll_ht && x>0 && x<size.width);
break;
case DOWN:
buttonDown = (y > size.height - scroll_ht && y<size.height
&& x>0 && x<size.width);
break;
default:
return;
}
if (buttonDown != before) {
if (buttonDown) {
timer.activate(this);
} else {
timer.inactivate(this);
}
redrawAsync();
}
}
/**
* Helper method called when the mouse button is released after a scroll
* button is released. Redraws the scrollbar and stops timer events.
*/
void handleButtonUp() {
buttonPushed = NONE;
redrawAsync();
timer.inactivate(this);
}
/**
* The current position and dimensions of the slider.
*/
Rectangle sliderRect;
/**
* The end of the up scroll button.
*/
int zeroPt;
/**
* The number of pixels the long/wide the slider is to be drawn.
*/
float effThumbSize;
boolean updateSliderRect;
/**
* Caused the position and dimensions of the slider to be recalculated.
*/
protected void redoSliderRect() {
updateSliderRect = true;
computeSliderRect();
}
/**
* Determines the size and position of the slider rect if it has not already
* be calculated.
*/
void computeSliderRect() {
if ((sliderRect != null && !updateSliderRect)
|| lastSize == null)
{
return;
}
if (max-min == 0) {
sliderRect = new Rectangle(0, 0, 0, 0);
return;
}
updateSliderRect = false;
Dimension size = lastSize;
zeroPt = scroll_ht;
if (orientation == HORIZONTAL) {
if (size.width < zeroPt*2) {
zeroPt = size.width/2;
}
int effWidth = size.width-zeroPt*2;
effThumbSize = (thumbSize*effWidth)/(max-min+1);
effThumbSize = Math.max(effThumbSize, 7);
sliderRect = new Rectangle(
(int)(zeroPt + effWidth * (currentPos-min)/(max-min+1)),
0,
(int)effThumbSize+1,
size.height);
} else {
if (size.height < zeroPt*2) {
zeroPt = size.height/2;
}
int effHeight = size.height-zeroPt*2;
effThumbSize = (thumbSize*effHeight)/(max-min+1);
effThumbSize = Math.max(effThumbSize, 7);
sliderRect = new Rectangle(0,
(int)(zeroPt + effHeight * (currentPos-min)/(max-min+1)),
size.width,
(int)effThumbSize+1);
}
}
/**
* Helper method called to determine if a mouse down occured on the slider.
*/
boolean clickOnSlider(int x, int y) {
computeSliderRect();
return sliderRect.contains(x, y);
}
/**
* Relative start x coordinate for absolute scrolling using the slider. Distance is
* measured relative to the top/front of the slider.
*/
int startDragX;
/**
* Relative start y coordinate for absolute scrolling using the slider. Distance is
* measured relative to the top/front of the slider.
*/
int startDragY;
/**
* Helper method called to handle when the user starts absolute scrolling.
*/
void handleSliderDragStart(int x, int y) {
startDragX = x - sliderRect.x;
startDragY = y - sliderRect.y;
buttonPushed = SLIDER;
buttonDown = true;
redrawAsync();
}
/**
* Gets whether the slider is being absolutely scrolled.
*/
protected boolean sliderMoving() {
return buttonPushed == SLIDER;
}
/**
* Helper method for when absolute scrolling is in progress and the user moves
* the mouse.
*/
void handleSliderDrag(int x, int y) {
Dimension size = lastSize;
int origPos = currentPos;
//could possibly be null so get it
computeSliderRect();
if (orientation == HORIZONTAL) {
int delta = x - sliderRect.x - startDragX;
sliderRect.x += delta;
currentPos = computeValue(sliderRect.x-zeroPt);
} else {
int delta = y - sliderRect.y - startDragY;
sliderRect.y += delta;
currentPos = computeValue(sliderRect.y-zeroPt);
}
if (origPos != currentPos) {
generateEvent(AdjustmentEvent.TRACK);
}
redrawAsync();
}
/**
* Helper method used to determine the value represented by the current
* position of the slider.
* @param val slider positon minus the width of the scroll up button.
*/
int computeValue(int val) {
float pos;
if (orientation == HORIZONTAL) {
pos = ((float)val)/(lastSize.width-zeroPt*2)*(max-min+1)+min;
} else {
pos = ((float)val)/(lastSize.height-zeroPt*2)*(max-min+1)+min;
}
return Math.min(max-thumbSize+1, Math.max(min, (int)pos));
}
/**
* Helper method called when the slider is released following absolute scrolling.
*/
void handleSliderUp() {
buttonPushed = NONE;
buttonDown = false;
redrawAsync();
}
/**
* The direction of paging (PAGING_UP, PAGING_DOWN, or SUSPENDED)
*/
int pageDirection;
/**
* The coordinates the user last caused a mouse event during paging.
*/
Point startPagingPt;
/**
* Paging up.
*/
static final int PAGING_UP = 0;
/**
* Paging down.
*/
static final int PAGING_DOWN = 1;
/**
* Paging suspended because the slider is currently positioned where the mouse
* is located.
*/
static final int SUSPENDED = 2;
/**
* Helper method called to start paging for the user. Generates the required
* events and triggers the timer to start sending events.
*/
void handlePageClick(int x, int y) {
startPagingPt = new Point(x, y);
if (orientation == HORIZONTAL) {
if (x < sliderRect.x) {
pageDirection = PAGING_UP;
currentPos = Math.max(min, currentPos-pageIncrement);
generateEvent(AdjustmentEvent.BLOCK_DECREMENT);
} else {
pageDirection = PAGING_DOWN;
currentPos = Math.min(max-thumbSize+1, currentPos+pageIncrement);
generateEvent(AdjustmentEvent.BLOCK_INCREMENT);
}
} else {
if (y < sliderRect.y) {
pageDirection = PAGING_UP;
currentPos = Math.max(min, currentPos-pageIncrement);
generateEvent(AdjustmentEvent.BLOCK_DECREMENT);
} else {
pageDirection = PAGING_DOWN;
currentPos = Math.min(max-thumbSize+1, currentPos+pageIncrement);
generateEvent(AdjustmentEvent.BLOCK_INCREMENT);
}
}
redoSliderRect();
redrawAsync();
timer.activate(this, 450);
}
/**
* Helper method called to handle a mouse drag during paging. If the mouse is
* dragged to a position where the slider is located, paging is suspended. If
* it is moved in a position such that the slider must move in the same paging
* direction as initially indicated by the user, paging is continued/resumed.
*/
void handlePageDrag(int x, int y) {
startPagingPt = new Point(x, y);
if (pageDirection >= SUSPENDED) {
pageDirection -= SUSPENDED;
timer.activate(this, 50);
}
}
/**
* Helper method called when the user releases the mouse following paging.
*/
void handlePageUp() {
timer.inactivate(this);
}
/**
* Method implemented from the Observer interface. Used by the timer.
*/
public void update(Observable obs, Object o) {
timer.updateInterval(this, 50);
int orig = currentPos;
switch (buttonPushed) {
case DOWN:
currentPos = Math.min(max-thumbSize+1, currentPos+lineIncrement);
if (orig != currentPos) {
generateEvent(AdjustmentEvent.UNIT_INCREMENT);
}
break;
case UP:
currentPos = Math.max(min, currentPos-lineIncrement);
if (orig != currentPos) {
generateEvent(AdjustmentEvent.UNIT_DECREMENT);
}
break;
case LEFT:
currentPos = Math.max(min, currentPos-lineIncrement);
if (orig != currentPos) {
generateEvent(AdjustmentEvent.UNIT_DECREMENT);
}
break;
case RIGHT:
currentPos = Math.min(max-thumbSize+1, currentPos+lineIncrement);
if (orig != currentPos) {
generateEvent(AdjustmentEvent.UNIT_INCREMENT);
}
break;
default:
synchronized(LOCK) {
//must be a page request
if (pageDirection == PAGING_UP) {
if ((orientation == HORIZONTAL && startPagingPt.x >= sliderRect.x)
|| (orientation == VERTICAL && startPagingPt.y >= sliderRect.y))
{
pageDirection += SUSPENDED;
timer.inactivate(this);
} else {
currentPos = Math.max(min, currentPos-pageIncrement);
generateEvent(AdjustmentEvent.BLOCK_DECREMENT);
}
} else if (pageDirection == PAGING_DOWN){
if ((orientation == HORIZONTAL && startPagingPt.x <= sliderRect.x+sliderRect.width)
|| (orientation == VERTICAL && startPagingPt.y <= sliderRect.y+sliderRect.height))
{
pageDirection += SUSPENDED;
timer.inactivate(this);
} else {
currentPos = Math.min(max-thumbSize+1, currentPos+pageIncrement);
generateEvent(AdjustmentEvent.BLOCK_INCREMENT);
}
}
}
}
redoSliderRect();
redrawAsync();
}
//---------------------------------------------------------------------------
// Adustable methods
//---------------------------------------------------------------------------
/**
* Processes events on this scrollbar. If the event is an
* AdjustmentEvent, it invokes the processAdjustmentEvent method,
* else it invokes its superclass's processEvent.
* @param e the event
*/
protected void processEvent(AWTEvent e) {
if (e instanceof AdjustmentEvent) {
processAdjustmentEvent((AdjustmentEvent)e);
return;
}
super.processEvent(e);
}
/**
* Processes adjustment events occurring on this scrollbar by
* dispatching them to any registered AdjustmentListener objects.
* NOTE: This method will not be called unless adjustment events
* are enabled for this component; this happens when one of the
* following occurs:
* a) An AdjustmentListener object is registered via addAdjustmentListener()
* b) Adjustment events are enabled via enableEvents()
* @see Component#enableEvents
* @param e the adjustment event
*/
protected void processAdjustmentEvent(AdjustmentEvent e) {
if (adjustmentListener != null) {
adjustmentListener.adjustmentValueChanged(e);
}
}
/**
* Adds the specified adjustment listener to recieve adjustment events
* from this scroller.
* @param l the adjustment listener
*/
public void addAdjustmentListener(AdjustmentListener l) {
adjustmentListener = AWTEventMulticaster.add(adjustmentListener, l);
}
/**
* Removes the specified adjustment listener so that it no longer
* receives adjustment events from this scroller.
*/
public void removeAdjustmentListener(AdjustmentListener l) {
adjustmentListener = AWTEventMulticaster.remove(adjustmentListener, l);
}
}