// Copyright 1996, Marimba Inc. All Rights Reserved.


// @(#)ScrollbarWidget.java, 1.53, 12/19/96





package marimba.gui;





import java.awt.*;





import marimba.persist.*;


import marimba.util.*;





/**


 * A horizontal/vertical scrollbar. It can be horizontal or vertical,


 * supports a proportional scroll box which means that the box


 * represents the current page size and offers many other useful


 * properties.


 * The scrollbar repaints have been optimized in the sense that it


 * only repaints when absolutely necessary.


 *


 * @author	Klaas Waslander


 * @version 	1.53, 12/19/96


 */


public class ScrollbarWidget extends ValueWidget implements TimerClient {


    /**


     * The possible options for the orientation.


     * @see #getOrientationOptions


     * @see #orientation


     */


    public static Options  orientationOptions = new Options();


    static {


	orientationOptions.add("vertical", VERTICAL);


	orientationOptions.add("horizontal", HORIZONTAL);


    }





    /**


     * A scrollbar can be horizontal and vertical.


     * @see #getOrientation


     * @see #setOrientation


     * @see #orientationOptions


     */


    public int  orientation = HORIZONTAL;








    /**


     * A scrollbar can be proportional, which means that the


     * size of the scroll box represents the pagesize.


     * If it is not proportional, the scroll box is always


     * square.


     * @see #isProportional


     * @see #setProportional


     */


    public boolean  proportional = true;





    /**


     * This controls how much you scroll when pressing


     * a scroll arrow.


     * @see #setLineSize


     * @see #getLineSize


     */


    public int  lineSize = 10;





    /**


     * Determines the visible portion of the scrollbar.


     * This controls the size of the scroll box.


     * @see #getPageSize


     * @see #setPageSize


     */


    public int  pageSize = 10;





    /**


     * The repeat rate in times/second for the scroll arrows.


     * This controls how fast the scroll box moves.


     * @see #getSpeed


     * @see #setSpeed


     */


    public int  speed = 10;





    /**


     * A scrollbar can automatically set the value based


     * on the action.


     * REMIND: THIS WILL BE DELETED ONCE A NEW RICHTEXTBOX IS READY


     * @see #getAutoSetValue


     * @see #setAutoSetValue


     */


    public boolean  autoSetValue = true;





    /**


     * Constrain to maxValue - pageSize. This means that it is not


     * possible to scroll further using the down arrow when the end


     * has been reached.


     * @see #isConstrained


     * @see #setConstrained


     */


    public boolean  constrained = true;





    /**


     * The maximum value of the scrollbar when pressing the down/right


     * arrow can be made larger using this variable.


     * It must be used when you want to enable the scrollbar to scroll


     * further (or less) then one page when the scrollbar is unconstrained.


     * @see #getScrollMore


     * @see #setScrollMore


     */


    public int  scrollMore = 0;





    /**


     * If true, action() is called after every change of the value.


     * Otherwise action() is only called when the (mouse)button is released.


     * @see #tracksAll


     * @see #setTrackAll


     */


    public boolean  trackAll = true;








    /** size of the scroll arrows */


    int  buttonSize;





    /** the current press with the mouse: UP, DOWN, PAGE_UP or PAGE_DOWN */


    int  pressed = NONE;





    /** the offset when dragging */


    float  dragoff;





    /** the position of the mouse pointer when doing a page up/down */


    int  changePagePos;





    /** indicated whether the scrollbar is scrolling down pages */


    boolean  pageScrolling = false;





    /** indicates whether one of the scroll arrows is down right now */


    boolean  down = false;





    /**


     * The value that was set, before the user started dragging.


     * Needed in handleEvent to know whether the value has changed.


     */


    int  oldValue;





    /**


     * The value set by the user, but not set as the scrollbarValue.


     * This is necessary if autoSetValue is false.


     * REMIND: THIS WILL BE DELETED ONCE A NEW RICHTEXTBOX IS READY


     */


    int inputValue;








    /**


     * Constructor.


     */


    public ScrollbarWidget() {


	this(HORIZONTAL);


    }





    /**


     * Constructor with orientation.


     */


    public ScrollbarWidget(int orientation) {


	this.orientation = orientation;


	setHilite(new Color(232, 232, 232));


    }





    /**


     * Get the properties.


     */


    public void getProperties(PropertyList list) {


	super.getProperties(list);


	list.setBoolean("disabled", disabled, false);


	list.setBoolean("filled", !transparent, false);


	list.setBoolean("proportional", proportional, true);


	list.setInteger("linesize", lineSize, 10);


	list.setInteger("pagesize", pageSize, lineSize);


	list.setInteger("speed", speed, 10);


	list.setOption("orientation", orientationOptions, orientation, HORIZONTAL);


	list.setBoolean("constrained", constrained, true);


	list.setInteger("scrollMore", scrollMore, 0);


	list.setBoolean("trackAll", trackAll, true);


    }





    /**


     * Set the properties.


     */


    public void setProperties(PropertyList list) {


	super.setProperties(list);


	disabled = list.getBoolean("disabled", false);


	transparent = !list.getBoolean("filled", false);


	proportional = list.getBoolean("proportional", true);


	lineSize = list.getInteger("linesize", 10);


	pageSize = list.getInteger("pagesize", lineSize);


	speed = list.getInteger("speed", 10);


	orientation = list.getOption("orientation", orientationOptions, HORIZONTAL);


	constrained = list.getBoolean("constrained", constrained);


	setScrollMore(list.getInteger("scrollMore", 0));


	trackAll = list.getBoolean("trackAll", true);


    }





    /**


     * Check if the scrollbar is filled. If the scrollbar is filled


     * it is not transparent.


     * @see Widget#transparent


     * @see setFilled


     */


    public boolean isFilled() {


	return !transparent;


    }





    /**


     * Let the scrollbar fill itself. If the scrollbar is filled


     * it is not transparent.


     * @see Widget#transparent


     * @see isFilled


     */


    public void setFilled(boolean filled) {


	if (filled == transparent) {


	    transparent = !filled;


	    repaint();


	}


    }





    /**


     * Get the possible options for the orientation.


     * @see #orientationOptions


     */


    public Options getOrientationOptions() {


	return orientationOptions;


    }





    /**


     * Get the orientation of the scrollbar.


     * @see #orientation


     */


    public int getOrientation() {


	return orientation;


    }





    /**


     * Set the orientation to horizontal or vertical.


     * @see #orientation


     */


    public void setOrientation(int orientation) {


	if (orientation != this.orientation) {


	    if (orientation==HORIZONTAL || orientation==VERTICAL) {


		this.orientation = orientation;


		repaint();


	    }


	}


    }





    /**


     * Check whether this scroll bar is proportional.


     * @see #proportional


     */


    public boolean isProportional() {


	return proportional;


    }





    /**


     * Let the scroll box always be square or let it


     * represent the current page size.


     * @see #proportional


     */


    public void setProportional(boolean proportional) {


	if (this.proportional != proportional) {


	    this.proportional = proportional;


	    repaint();


	}


    }





    /**


     * Validates the scroll bar.


     * Calculates sizes of some drawing parameters.


     */


    public void validate() {


	buttonSize = (orientation == VERTICAL) ? width - 1 : height - 1;


	if (buttonSize > 20) {


	    buttonSize = 20;


	}


	if (constrained) {


	    setInputValue(value);


	}


    }





    /**


     * Check if this scrollbar has to set the value based on the


     * action automatically.


     * REMIND: THIS WILL BE DELETED ONCE A NEW RICHTEXTBOX IS READY


     * @see #autoSetValue


     */


    public boolean getAutoSetValue() {


	return autoSetValue;


    }





    /**


     * Turn on or off automatically setting the value based on the


     * action.


     * REMIND: THIS WILL BE DELETED ONCE A NEW RICHTEXTBOX IS READY


     * @see #autoSetValue


     */


    public void setAutoSetValue(boolean autoSetValue) {


	this.autoSetValue = autoSetValue;


    }





    /**


     * Check if this scrollbar is constrained.


     * @see #constrained


     */


    public boolean isConstrained() {


	return constrained;


    }





    /**


     * Turn on or off automatically constraining


     * the value based on the page size.


     * @see #constrained


     */


    public void setConstrained(boolean constrained) {


	if (this.constrained != constrained) {


	    this.constrained = constrained;


	    repaint();


	}


    }





    /**


     * Get the adjustment of the maxValue for the unconstrained


     * mode of this scrollbar.


     * @see #scrollMore


     */


    public int getScrollMore() {


	return scrollMore;


    }





    /**


     * Set the adjusting value of the scrollbar for unconstrained mode.


     * Set it to zero for the default behaviour of being able to scroll


     * one page further when unconstrained.


     * @see #scrollMore


     */


    public void setScrollMore(int scrollMore) {


	if (scrollMore < -pageSize) {


	    scrollMore = -pageSize;


	}


	this.scrollMore = scrollMore;


    }





    /**


     * Check if all changes of the value are being tracked.


     * @see #trackAll


     */


    public boolean tracksAll() {


	return trackAll;


    }





    /**


     * Let the scrollbar call action() after every change of the


     * value or only once.


     * @see #trackAll


     */


    public void setTrackAll(boolean trackAll) {


	this.trackAll = trackAll;


    }





    /**


     * Check the line size: how much the scroll box skips


     * when pressing one of the scroll arrows.


     * @see #lineSize


     */


    public int getLineSize() {


	return lineSize;


    }





    /**


     * Set the line size. This controls how much the scroll box skips


     * when pressing one of the scroll arrows.


     * @see #lineSize


     */


    public synchronized void setLineSize(int lineSize) {


	if (lineSize < 1) {


	    lineSize = 1;


	}


	if (lineSize != this.lineSize) {


	    this.lineSize = lineSize;


	}


    }





    /**


     * Get the current size of the page, which controls


     * the size of the scroll box.


     * @see #pageSize


     */


    public int getPageSize() {


	return pageSize;


    }





    /**


     * Set the current size of the page.


     * This controls the size of the scrolling bar.


     * @see #pageSize


     */


    public synchronized void setPageSize(int pageSize) {


	if (pageSize < lineSize) {


	    pageSize = lineSize;


	}


	if (pageSize != this.pageSize) {


	    this.pageSize = pageSize;


	    if (proportional) {


		repaint();


	    }


	}


    }





    /**


     * Set all parameters of the scroll bar at once.


     */


    public synchronized void setParam(int value, int minValue, int maxValue, int lineSize, int pageSize) {


	setParam(value, minValue, maxValue);


	if (lineSize < 1) {


	    lineSize = 1;


	}


	if (pageSize < lineSize) {


	    pageSize = lineSize;


	}


	if (pageSize != this.pageSize) {


	    this.lineSize = lineSize;


	    this.pageSize = pageSize;


	    if (proportional) {


		repaint();


	    }


	}


    }





    /**


     * Check the repeat rate in times/second for the scroll arrows.


     * @see #speed


     */


    public int getSpeed() {


	return speed;


    }





    /**


     * Set the repeat rate in times/second for the scroll arrows.


     * @see #speed


     */


    public void setSpeed(int speed) {


	this.speed = speed;


    }





    /**


     * Get the size of the scroll bar shaft in pixels.


     */


    public int getShaftSize() {


	int  barSize = (orientation == VERTICAL) ? height : width;


	return Math.max(0, barSize - 2 * buttonSize);


    }





    /**


     * Get the size of the scroll box in pixels,


     * based on the pageSize or just square if the scrollbar


     * is not proportional.


     */


    public int getBoxSize() {


	int  result;


	if (proportional) {


	    if (maxValue-minValue <= 0 || pageSize <= 0 || pageSize >= maxValue-minValue) {


		result = 0;


	    } else {


		int  range = getRange();


		range = (range == 0) ? 1 : range;


		float  valuePixel = getShaftSize() / (float)range;


		result = Math.max(buttonSize / 2, (int)(valuePixel * pageSize));


	    }


	} else {


	    result = (orientation == HORIZONTAL) ? this.height : this.width;


	    if (result >= getShaftSize()) {


		result = 0;


	    }


	}


	return result;


    }





    /**


     * Get the maximum scroll value, which is the maximum value when


     * dragging the scroll box. This always equals maxValue - pageSize.


     */


    public int getMaxScrollValue() {


	return (pageSize > 0) ? maxValue - pageSize : maxValue;


    }





    /**


     * Set the value, minValue and maxValue all at once.


     * The value can get larger than the maxValue if the scrollbar


     * is unconstrained: the value will go up to 'scrollMax'.


     */


    public synchronized void setParam(int value, int minValue, int maxValue) {


	// adjust maxValue if necessary


	if (maxValue < minValue) {


	    maxValue = minValue;


	}


	// set parameters


	if (constrained || value <= maxValue) {


	    super.setParam(value, minValue, maxValue);


	} else if (!constrained) {


	    if (value > maxValue + scrollMore) {


		value = maxValue + scrollMore;


	    }


	    boolean  newLook = repaintFor(value, minValue, maxValue);


	    this.value = value;


	    this.minValue = minValue;


	    this.maxValue = maxValue;


	    if (newLook) {


		repaint();


	    }


	}


    }





    /**


     * Translates the given value to the number of pixels


     * between the scroll box and the left/bottom.


     */


    public int valueToPixels(int value) {


	int  max = getMaxScrollValue();


	if (value < minValue) {


	    value = minValue;


	} else if (value > max) {


	    value = max;


	}


	int  range = max - minValue;


	range = (range == 0) ? 1 : range;


	float  progress = (value - minValue) / (float)range;


	int  result = (int)(progress * (getShaftSize() - getBoxSize()));





	// make sure the result is valid


	if (result < 0) {


	    result = 0;


	} else if (result > getShaftSize() - getBoxSize()) {


	    result = getShaftSize() - getBoxSize();


	}


	return result;


    }





    /**


     * Returns the scrollbar value given the specified pixelValue.


     */


    public int pixelsToValue(int pixels) {


	int  result;


	if (pixels <= 0) {


	    result = minValue;


	} else if (pixels >= getShaftSize() - getBoxSize()) {


	    result = getMaxScrollValue();


	} else {


	    int  max = getMaxScrollValue();


	    int  range = max - minValue;


	    range = (range == 0) ? 1 : range;


	    float  valuePixel = range / (float)(getShaftSize() - getBoxSize());


	    result = (int)(pixels * valuePixel + minValue);


	}


	return result;


    }





    /**


     * Assumes the given values are valid. Checks whether these values


     * would result in a new look that requires a repaint.


     */


    protected boolean repaintFor(int newValue, int newMin, int newMax) {


	boolean  result = false;





	// get old pixelValue


	int  oldPixelValue = valueToPixels(value);





	// calculate new pixelValue


	int  max = (pageSize > 0) ? newMax - pageSize : newMax;


	if (newValue < newMin) {


	    newValue = newMin;


	} else if (newValue > max) {


	    newValue = max;


	}


	int  range = max - minValue;


	range = (range == 0) ? 1 : range;


	float  progress = (newValue - newMin) / (float)range;


	int  newPixelValue = (int)(progress * (getShaftSize() - getBoxSize()));





	// make sure the new pixel value is valid


	if (newPixelValue < 0) {


	    newPixelValue = 0;


	} else if (newPixelValue > getShaftSize() - getBoxSize()) {


	    newPixelValue = getShaftSize() - getBoxSize();


	}





	// compare old and new pixelValue


	if (oldPixelValue != newPixelValue) {


	    result = true;


	}


	return result;


    }





    /**


     * Paint the scrollbar.


     * @see #paintScrollBox


     */


    public void paint(Graphics g) {


	// check whether the scrollbar must be filled


	g.setColor(background);


	if (transparent) {


	    Bevel.drawRect(g, 0, 0, width, height, false, 1);


	} else {


	    Bevel.fillRect(g, 0, 0, width, height, false, 1);


	}





	if (orientation == VERTICAL) {


	    // draw the scroll arrows


	    Bevel.drawBorder(g, 1, 1, width - 2, buttonSize - 1, !(down && pressed == UP));


	    Bevel.drawBorder(g, 1, height - buttonSize, width - 2, buttonSize - 1, !(down && pressed == DOWN));


	    if (buttonSize > 10) {


		g.setColor(foreground);


		int  x1 = (width-1) / 2;


		int  x2 = width / 2;


		int  y = buttonSize / 2 - ((buttonSize%2==0) ? 2 : 1);


		int  offset = (down && pressed == UP) ? 1 : 0;


		for (int i = buttonSize/4; i-- > 0;) {


		    g.drawLine(offset+x1-i, offset+y+i, offset+x2+i, offset+y+i);


		}


		y = height - buttonSize/2 - ((buttonSize%2==0) ? 0 : 1);


		offset = (down && pressed == DOWN) ? 1 : 0;


		for (int i = buttonSize/4; i-- > 0;) {


		    g.drawLine(offset+x1-i, offset+y-i, offset+x2+i, offset+y-i);


		}


	    }


	    paintScrollBox(g, 1, valueToPixels(this.value) + buttonSize, this.width-2, getBoxSize());


	} else if (orientation == HORIZONTAL) {


	    // draw the scroll arrows


	    Bevel.drawBorder(g, 1, 1, buttonSize-1, height-2, !(down && pressed == UP));


	    Bevel.drawBorder(g, width-buttonSize, 1, buttonSize-1, height-2, !(down && pressed == DOWN));


	    if (buttonSize > 10) {


		g.setColor(foreground);


		int  y1 = (height-1) / 2;


		int  y2 = height / 2;


		int  x = buttonSize / 2 - ((buttonSize%2==0) ? 2 : 1);


		int  offset = (down && pressed == UP) ? 1 : 0;


		for (int i = buttonSize/4; i-- > 0;) {


		    g.drawLine(offset+x+i, offset+y1-i, offset+x+i, offset+y2+i);


		}


		x = width - buttonSize/2 - ((buttonSize%2==0) ? 0 : 1);


		offset = (down && pressed == DOWN) ? 1 : 0;


		for (int i = buttonSize/4; i-- > 0;) {


		    g.drawLine(offset+x-i, offset+y1-i, offset+x-i, offset+y2+i);


		}


	    }


	    paintScrollBox(g, valueToPixels(this.value) + buttonSize, 1, getBoxSize(), this.height-2);


	}


    }





    /**


     * Paint the scroll box at the given position and fill the scroll shaft


     * above and below the box with the hilite color if the scroll bar is filled.


     * If the scrollbar is currently paging down or up, the scroll shaft is filled


     * with an appropriate color.


     * @see #paint


     */


    protected void paintScrollBox(Graphics g, int x, int y, int boxWidth, int boxHeight) {


	g.setColor(background);


	if (!disabled && getBoxSize() >= buttonSize / 2 && pageSize > 0 && pageSize < maxValue-minValue) {


	    Bevel.drawBorder(g, x, y, boxWidth, boxHeight, true);


	}





	Color  pageColor = (background.equals(Color.black)) ? Color.gray : Color.black;


	if (!transparent) {


	    pageColor = (hilite.equals(Color.black)) ? Color.gray : Color.black;


	    g.setColor(hilite);


	    if (orientation == VERTICAL) {


		g.fillRect(x, buttonSize, boxWidth, y-buttonSize);


		int  y2 = y + boxHeight;


		g.fillRect(x, y2, boxWidth, height-y2-buttonSize);


	    } else {


		g.fillRect(buttonSize, y, x-buttonSize, boxHeight);


		int  x2 = x + boxWidth;


		g.fillRect(x2, y, width-x2-buttonSize, boxHeight);


	    }


	}





	if (pageScrolling) {


	    if (pressed == PAGE_UP) {


		g.setColor(pageColor);


		if (orientation == VERTICAL) {


		    g.fillRect(x, buttonSize, boxWidth, y-buttonSize);


		} else {


		    g.fillRect(buttonSize, y, x-buttonSize, boxHeight);


		}





	    } else if (pressed == PAGE_DOWN) {


		g.setColor(pageColor);


		if (orientation == VERTICAL) {


		    int  y2 = y + boxHeight;


		    g.fillRect(x, y2, boxWidth, height-y2-buttonSize);


		} else {


		    int  x2 = x + boxWidth;


		    g.fillRect(x2, y, width-x2-buttonSize, boxHeight);


		}


	    }


	}


    }





    /**


     * Auto repeat


     */


    public long tick(long tm, Object arg) {


	switch (pressed) {


	    case UP:


		setInputValue((value - lineSize > minValue) ? (value - lineSize) : minValue);


		action(Event.SCROLL_LINE_UP, trackAll);


		break;


	    case DOWN:


		int  max = (constrained) ? getMaxScrollValue() : maxValue + scrollMore;


		setInputValue((value + lineSize < max) ? (value + lineSize) : max);


		action(Event.SCROLL_LINE_DOWN, trackAll);


		break;


	    case PAGE_UP: {


		int  newVal = value - Math.max(lineSize, (pageSize*3)/4);


		setInputValue(newVal);


		action(Event.SCROLL_PAGE_UP, trackAll);





		// stop paging up if scroll box has reached the mouse pointer


		int  target;


		if (orientation == HORIZONTAL) {


		    target = findTarget(changePagePos, 1);


		} else {


		    target = findTarget(1, changePagePos);


		}


		if (target != PAGE_UP) {


		    pageScrolling = false;


		    repaint();


		    return -1;


		}


		break;


	    }


	    case PAGE_DOWN: {


		int  newVal = value + Math.max(lineSize, (pageSize*3)/4);


		setInputValue(newVal);


		action(Event.SCROLL_PAGE_DOWN, trackAll);





		// stop paging down if scroll box has reached the mouse pointer


		int  target;


		if (orientation == HORIZONTAL) {


		    target = findTarget(changePagePos, 1);


		} else {


		    target = findTarget(1, changePagePos);


		}


		if (target != PAGE_DOWN) {


		    pageScrolling = false;


		    repaint();


		    return -1;


		}


		break;


	    }


	}


	return (speed <= 0) ? -1 : System.currentTimeMillis() + Math.max(50, 1000/speed);


    }





    /**


     * Sets the value of the scrollbar only if autoSetValue is true.


     * The inputValue is remembered, even if autoSetValue is false.


     * When the scrollbar is unconstrained this method will set the


     * input value up to scrollMax.


     * REMIND: THIS **MIGHT** BE DELETED ONCE A NEW RICHTEXTBOX IS READY


     */


    protected void setInputValue(int value) {


	// adjust value to legal value


	int  max = (constrained) ? getMaxScrollValue() : maxValue + scrollMore;


	if (value > max) {


	    value = max;


	} else if (value < minValue) {


	    value = minValue;


	}





	// set appropriate values


	if (autoSetValue) {


	    setValue(value);


	}


	inputValue = value;


    }





    /**


     * Start auto-scrolling pages.


     */


    protected void startPageScrolling(int type, int mousePos) {


	if (type == PAGE_UP) {


	    setInputValue(value - Math.max(lineSize, (pageSize*3)/4));


	    action(Event.SCROLL_PAGE_UP, trackAll);


	} else {


	    setInputValue(value + Math.max(lineSize, (pageSize*3)/4));


	    action(Event.SCROLL_PAGE_DOWN, trackAll);


	}


	pressed = type;


	changePagePos = mousePos;


	pageScrolling = true;


	Timer.master.add(this, System.currentTimeMillis() + 500, null);


	repaint();


    }





    /**


     * Find the scrollbar target under the given coordinates.


     * This can be UP, DOWN, PAGE_UP, PAGE_DOWN or -1 if no target is found.


     */


    protected int findTarget(int x, int y) {


	if (orientation == HORIZONTAL) {


	    if (y >= 0 && y <= height) {


	        if (x >= 0 && x <= buttonSize) {


		    return UP;


		}


		if (x >= width-buttonSize && x <= width) {


		    return DOWN;


		}


		int  pixVal = valueToPixels(value) + buttonSize;


		if (x >= buttonSize && x <= pixVal) {


		    return PAGE_UP;


		}


		if (x >= pixVal + getBoxSize() && x <= width-buttonSize) {


		    return PAGE_DOWN;


		}


	    }


	} else {


	    if (x >= 0 &&  x <= width) {


		if (y >= 0 && y <= buttonSize) {


		    return UP;


		}


		if (y >= height-buttonSize && y <= height) {


		    return DOWN;


		}


		int  pixVal = valueToPixels(value) + buttonSize;


		if (y >= buttonSize && y <= pixVal) {


		    return PAGE_UP;


		}


		if (y >= pixVal + getBoxSize() && y <= height-buttonSize) {


		    return PAGE_DOWN;


		}


	    }


	}


	return -1;


    }





    /**


     * Event handler for mouse events.


     */


    public boolean handleEvent(Event evt) {


	// do nothing if there is no scroll box or scrollbar is disabled


	if (disabled || getBoxSize() <= 0) {


	    return false;


	}


	switch (evt.id) {


	    case Event.MOUSE_DOWN: {


		oldValue = value;


		int  barSize = (orientation == HORIZONTAL) ? this.width : this.height;


		int  boxSize = getBoxSize();


		int  pos = (orientation == HORIZONTAL) ? evt.x : evt.y;





		if (pos < buttonSize) {


		    //// line up ////


		    setInputValue(value - lineSize);


		    action(Event.SCROLL_LINE_UP, trackAll);


		    pressed = UP;


		    down = true;


		    Timer.master.add(this, System.currentTimeMillis() + 500, null);


		    repaint();


		    return false;


		}


		if (pos > getShaftSize() + buttonSize) {


		    //// line down ////


		    setInputValue(value + lineSize);


		    action(Event.SCROLL_LINE_DOWN, trackAll);


		    pressed = DOWN;


		    down = true;


		    Timer.master.add(this, System.currentTimeMillis() + 500, null);


		    repaint();


		    return false;


		}


		int  pixelValue = valueToPixels(value);


		if (pos < pixelValue + buttonSize) {


		    //// page up ////


		    int  mousePos = (orientation == HORIZONTAL) ? evt.x : evt.y;


		    startPageScrolling(PAGE_UP, mousePos);


		    return false;


		}


		if (pos < pixelValue + buttonSize + boxSize) {


		    //// drag ////


		    pressed = DRAG;


		    dragoff = (float) (pos - pixelValue) / boxSize;


		    int  max = getMaxScrollValue();


		    if (inputValue > max) {


			setInputValue(max);


			action(Event.SCROLL_ABSOLUTE, trackAll);


		    }


		    return false;


		}


		//// page down ////


		int  mousePos = (orientation == HORIZONTAL) ? evt.x : evt.y;


		startPageScrolling(PAGE_DOWN, mousePos);


		return false;


	    }





	    case Event.MOUSE_DRAG:


		if (pressed == UP || pressed == DOWN) {


		    if (findTarget(evt.x, evt.y) != pressed) {


			if (down) {


			    down = false;


			    Timer.master.remove(this);


			    repaint();


			}


		    } else {


			if (!down) {


			    down = true;


			    Timer.master.add(this);


			    repaint();


			}


		    }


		} else if (pressed == DRAG) {


		    int  pos = (orientation == HORIZONTAL) ? evt.x : evt.y;


		    pos -= (dragoff * getBoxSize());


		    setInputValue(pixelsToValue(pos));


		    action(Event.SCROLL_ABSOLUTE, trackAll);





		} else if (pressed == PAGE_UP || pressed == PAGE_DOWN) {


		    //// stop auto-scrolling or adjust the 'changePagePos' ////


		    changePagePos = (orientation == HORIZONTAL) ? evt.x : evt.y;


		    if (findTarget(evt.x, evt.y) != pressed) {


			if (pageScrolling) {


			    pageScrolling = false;


			    Timer.master.remove(this);


			    repaint();


			}


		    } else {


			if (!pageScrolling) {


			    pageScrolling = true;


			    Timer.master.add(this);


			    repaint();


			}


		    }


		}


		return true;





	    case Event.MOUSE_UP:


		if (!trackAll && value != oldValue) {


		    action();


		}


		Timer.master.remove(this);


		pressed = NONE;


		repaint();


		return true;


	}


	return super.handleEvent(evt);


    }





    /**


     * The user has changed the value of the scrollbar.


     */


    public void action(int id) {


	postEvent(new Event(this, id, new Integer(inputValue)));


	action();


    }





    /**


     * Posts the event and calls action() if the boolean is true.


     */


    protected void action(int id, boolean action) {


	postEvent(new Event(this, id, new Integer(inputValue)));


	if (action) {


	    action();


	}


    }





    /**


     * Debugging.


     */


    public void paramString(StringBuffer buf) {


	super.paramString(buf);


	buf.append(",page=");


	buf.append(pageSize);


	buf.append(",line=");


	buf.append(lineSize);


	buf.append(",");


	buf.append(orientationOptions.get(orientation));


    }


}


