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


// @(#)SliderWidget.java, 1.36, 11/28/96





package marimba.gui;





import java.awt.*;


import marimba.persist.*;





/**


 * An advanced horizontal/vertical slider. The slider supports a number


 * of options. You can set the slider orientation as vertical or horizontal,


 * define the width and height of the "slide indicator" and the "slide bar"


 * components, define the increments of the slider, and whether to display


 * tick marks for this widget.


 * The user moves the slide indicator by dragging to a particular location


 * or clicking in the hot zone area of the bar, which moves the slide


 * indicator directly to that location. When the slider widget has input


 * focus, arrow keys can be used to move the slide indicator in te respective


 * direction represented by the key.


 *


 * @author	Klaas Waslander


 * @version 	1.36, 11/28/96


 */


public class SliderWidget extends ValueWidget {


    /**


     * The number of pixels between the slider


     * and the focus rectangle


     */


    final static int  FOCUSSPACE = 5;





    /**


     * A Slider can have a horizontal or vertical orientation.


     * @see #getOrientation


     * @see #setOrientation


     * @see #orientationOptions


     */


    public int  orientation = HORIZONTAL;





    /**


     * The possible options for the orientation.


     * @see #getOrientationOptions


     */


    static Options orientationOptions = new Options();


    static {


	orientationOptions.add("vertical", VERTICAL);


	orientationOptions.add("horizontal", HORIZONTAL);


    }





    /**


     * The direction of the slider: by default the value increases


     * from left to right and from bottom to top.


     * @see #getDirection


     * @see #setDirection


     */


    public int  direction = UP;


    static Options directionOptions = new Options();


    static {


	directionOptions.add("normal", UP);


	directionOptions.add("reversed", DOWN);


    }





    /**


     * Indicated whether the slider indicator is being dragged


     * currently. If it is none, the slider indicator is not being


     * moved.


     */


    int  pressed = NONE;





    /**


     * The number of pixels between the middle of the slider


     * indicator and the location where the user clicked with the


     * mouse.


     */


    int  dragoff;





    /**


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


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


     */


    int  oldValue;





    /**


     * Detects whether an action key is pressed for the first time.


     * This because an Event.KEY_ACTION_PRESS does not exist.


     */


    boolean keyActionPress = true;





    /**


     * The width of the slider indicator.


     * @see #getIndWidth


     * @see #getIndicatorWidth


     * @see #setIndWidth


     */


    public int  indWidth = 9;





    /**


     * The height of the slider indicator.


     * @see #getIndHeight


     * @see #getIndicatorHeight


     * @see #setIndHeight


     */


    public int  indHeight = 15;





    /**


     * The number of values to skip before changing value.


     * @see #getIncrementValue


     * @see #setIncrementValue


     */


    public int  incrementValue = 1;





    /**


     * A slider can display tickMarks to visualize values.


     * @see #getUseTickMarks


     * @see #setUseTickMarks


     */


    public boolean  useTickMarks = true;





    /**


     * The number of values between two tickMark


     * @see #getTickMarkInterval


     * @see #setTickMarkInterval


     */


    public int  tickMarkInterval = 10;





    /**


     * The size of the tickMarks.


     * @see #getTickMarkSize


     * @see #setTickMarkSize


     */


    public int  tickMarkSize = 4;





    /**


     * When a slider is filled, the slider bar and indicator


     * hide the background. A slider is always transparent because


     * there is a large area that has to display the background.


     * @see #isFilled


     * @see #setFilled


     */


    public boolean  filled;





    /**


     * 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;








    /**


     * Constructor.


     */


    public SliderWidget() {


    }





    /**


     * Constructor with orientation


     */


    public SliderWidget(int orientation) {


	this.orientation = orientation;


    }





    /**


     * Get the properties.


     */


    public void getProperties(PropertyList list) {


	super.getProperties(list);


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


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


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


	list.setOption("direction", directionOptions, direction, UP);


	list.setInteger("indWidth", indWidth, 9);


	list.setInteger("indHeight", indHeight, 15);


	list.setInteger("increment", incrementValue, 1);


	list.setBoolean("tickMarks", useTickMarks, true);


	list.setInteger("tickInterval", tickMarkInterval, 10);


	list.setInteger("tickMarkSize", tickMarkSize, 4);


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


    }





    /**


     * Set the properties.


     */


    public void setProperties(PropertyList list) {


	super.setProperties(list);


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


	this.filled = list.getBoolean("filled", false);


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


	setDirection(list.getOption("direction", directionOptions, UP));


	setIndWidth(list.getInteger("indWidth", 9));


	setIndHeight(list.getInteger("indHeight", 15));


	setIncrementValue(list.getInteger("increment", 1));


	this.useTickMarks = list.getBoolean("tickMarks", true);


	setTickMarkInterval(list.getInteger("tickInterval", 10));


	setTickMarkSize(list.getInteger("tickMarkSize", 4));


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


    }





    /**


     * Set all parameters of the Slider at once.


     */


    public synchronized void setParam(int value, int minValue, int maxValue, int orientation, int direction, int indWidth, int indHeight, int incrementValue, boolean useTickMarks, int tickMarkInterval, int tickMarkSize) {


	setParam(value, minValue, maxValue);


	setOrientation(orientation);


	setDirection(direction);


	setIndWidth(indWidth);


	setIndHeight(indHeight);


	setIncrementValue(incrementValue);


	setUseTickMarks(useTickMarks);


	setTickMarkInterval(tickMarkInterval);


	setTickMarkSize(tickMarkSize);


    }





    /**


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


     * @see #trackAll


     */


    public boolean tracksAll() {


	return trackAll;


    }





    /**


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


     * value or only once.


     * @see #trackAll


     */


    public void setTrackAll(boolean trackAll) {


	this.trackAll = trackAll;


    }





    /**


     * Get the possible options for the orientation.


     * @see #orientationOptions


     */


    public Options getOrientationOptions() {


	return orientationOptions;


    }





    /**


     * Get the current orientation.


     * @see #orientation


     */


    public int getOrientation() {


	return orientation;


    }





    /**


     * Set the orientation, using two integers defined in


     * the class WidgetConstants.


     * @see #orientation


     */


    public void setOrientation(int orientation) {


	if (orientation != this.orientation) {


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


		this.orientation = orientation;


		repaint();


	    }


	}


    }





    /**


     * Get the possible options for the direction.


     * @see #directionOptions


     */


    public Options getDirectionOptions() {


	return directionOptions;


    }





    /**


     * Get the direction.


     * @see #direction


     */


    public int getDirection() {


	return direction;


    }





    /**


     * Set the direction, using the integers UP/DOWN defined in


     * the class WidgetConstants.


     * @see #direction


     */


    public void setDirection(int direction) {


	if (direction != this.direction) {


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


		this.direction = direction;


		repaint();


	    }


	}


    }





    /**


     * Get the current indicator width.


     * @see #indWidth


     */


    public int getIndWidth() {


	return indWidth;


    }





    /**


     * Set the indicator width to the given number of pixels.


     * @see #indWidth


     */


    public void setIndWidth(int indWidth) {


	if (indWidth > 0 && indWidth != this.indWidth) {


	    this.indWidth = indWidth;


	    repaint();


	}


    }





    /**


     * Get the current indicator height.


     * @see #indHeight


     */


    public int getIndHeight() {


	return indHeight;


    }





    /**


     * Set the indicator height to the given number of pixels.


     * @see #indHeight


     */


    public void setIndHeight(int indHeight) {


	if (indHeight > 0 && indHeight != this.indHeight) {


	    this.indHeight = indHeight;


	    repaint();


	}


    }





    /**


     * Get the current increment value.


     * @see #incrementValue


     */


    public int getIncrementValue() {


	return incrementValue;


    }





    /**


     * Set the increment value to the given value.


     * @see #incrementValue


     */


    public void setIncrementValue(int incrementValue) {


	if (incrementValue > 0 && incrementValue != this.incrementValue) {


	    this.incrementValue = incrementValue;


	}


    }





    /**


     * Get the value of useTickMarks.


     * @see #useTickMarks


     */


    public boolean getUseTickMarks() {


	return useTickMarks;


    }





    /**


     * Set the value of useTickMarks.


     * @see #useTickMarks


     */


    public void setUseTickMarks(boolean useTickMarks) {


	if (this.useTickMarks != useTickMarks) {


	    this.useTickMarks = useTickMarks;


	    repaint();


	}


    }





    /**


     * Get the current interval between tick marks.


     * @see #tickMarkInterval


     */


    public int getTickMarkInterval() {


	return tickMarkInterval;


    }





    /**


     * Set the interval between tick marks to the given value.


     * @see #tickMarkInterval


     */


    public void setTickMarkInterval(int tickMarkInterval) {


	if (tickMarkInterval > 0 && tickMarkInterval != this.tickMarkInterval) {


	    this.tickMarkInterval = tickMarkInterval;


	    repaint();


	}


    }





    /**


     * Get the current size of the tick marks.


     * @see #tickMarkSize


     */


    public int getTickMarkSize() {


	return tickMarkSize;


    }





    /**


     * Set the size of the tick marks to the given number of pixels.


     * @see #tickMarkSize


     */


    public void setTickMarkSize(int tickMarkSize) {


	if (tickMarkSize > 0 && tickMarkSize != this.tickMarkSize) {


	    this.tickMarkSize = tickMarkSize;


	    repaint();


	}


    }





    /**


     * Check if the sliderwidget is filled.


     * @see #filled


     */


    public boolean isFilled() {


	return filled;


    }





    /**


     * Let the sliderwidget fill itself or not.


     * @see #filled


     */


    public void setFilled(boolean filled) {


	if (this.filled != filled) {


	    this.filled = filled;


	    repaint();


	}


    }





    /**


     * 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  range = newMax - newMin;


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


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


	int  newPixelValue = (int) (progress * getMaxWidth());


	if (orientation == VERTICAL && direction == UP) {


	    newPixelValue = height - 2 * FOCUSSPACE - newPixelValue - indWidth - 2;


	} else if (orientation == HORIZONTAL && direction == DOWN) {


	    newPixelValue = width - 2 * FOCUSSPACE - newPixelValue - indWidth - 2;


	}





	// compare old and new pixelValue


	if (oldPixelValue != newPixelValue) {


	    result = true;


	}


	return result;


    }








    /**


     * Paint the Slider.


     * @see #paintSliderIndicator


     * @see #paintBrightPattern


     * @see #paintTickMarks


     */


    public void paint(Graphics g) {


	g.setColor(background);





	int  margin = ((indHeight - 4) / 2) + 1 + FOCUSSPACE;


	int  pixelValue = valueToPixels(value);


	int  barSize = barSize();





	if (orientation == VERTICAL) {


	    // adjust margin, if tick marks have to be used


	    int  xtraMargin = 0;


	    if (useTickMarks) {


		xtraMargin = tickMarkSize + 2;	// 2 pixels more for space


		margin += xtraMargin;


	    }





	    // draw slider, not beyond the maximum value


	    Bevel.drawRect(g, margin, FOCUSSPACE, 4, pixelValue + 1, false, 1);


	    Bevel.drawRect(g, margin, FOCUSSPACE + pixelValue + indWidth, 4, barSize - pixelValue - indWidth, false, 1);


	    if (pixelValue > 0) {


		if (!disabled || filled) {


		    g.setColor((disabled) ? background : Color.black);


		    g.drawLine(margin+1, FOCUSSPACE+1, margin+1, FOCUSSPACE+pixelValue-1);


		}


		if (filled) {


		    g.setColor(background);


		    g.drawLine(margin+2, FOCUSSPACE+1, margin+2, FOCUSSPACE+pixelValue-1);


		}


	    }


	    if ((pixelValue + indWidth + 2) < barSize) {


		if (!disabled || filled) {


		    g.setColor((disabled) ? background : Color.black);


		    g.drawLine(margin+1, FOCUSSPACE+pixelValue+indWidth, margin+1, FOCUSSPACE+barSize-3);


		}


		if (filled) {


		    g.setColor(background);


		    g.drawLine(margin+2, FOCUSSPACE+pixelValue+indWidth, margin+2, FOCUSSPACE+barSize-2);


		    g.drawLine(margin+1, FOCUSSPACE+barSize-2, margin+1, FOCUSSPACE+barSize-2);


		}


	    }


	    paintSliderIndicator(g, FOCUSSPACE + xtraMargin, FOCUSSPACE + pixelValue);





	} else {


	    margin += 1;


	    Bevel.drawRect(g, FOCUSSPACE, margin, pixelValue + 1, 4, false, 1);


	    Bevel.drawRect(g, FOCUSSPACE + pixelValue + indWidth, margin, barSize - pixelValue - indWidth, 4, false, 1);


	    if (pixelValue > 0) {


		if (!disabled || filled) {


		    g.setColor((disabled) ? background : Color.black);


		    g.drawLine(FOCUSSPACE+1, margin+1, FOCUSSPACE+pixelValue-1, margin+1);


		}


		if (filled) {


		    g.setColor(background);


		    g.drawLine(FOCUSSPACE+1, margin+2, FOCUSSPACE+pixelValue-1, margin+2);


		}


	    }


	    if ((pixelValue + indWidth + 2) < barSize) {


		if (!disabled || filled) {


		    g.setColor((disabled) ? background : Color.black);


		    g.drawLine(FOCUSSPACE+pixelValue+indWidth, margin+1, FOCUSSPACE+barSize-3, margin+1);


		}


		if (filled) {


		    g.setColor(background);


		    g.drawLine(FOCUSSPACE+pixelValue+indWidth, margin+2, FOCUSSPACE+barSize-2, margin+2);


		    g.drawLine(FOCUSSPACE+barSize-2, margin+1, FOCUSSPACE+barSize-2, margin+1);


		}


	    }


	    paintSliderIndicator(g, FOCUSSPACE + pixelValue, FOCUSSPACE);


	}


	if (useTickMarks) {


	    paintTickMarks(g);


	}


	if (hasFocus()) {


	    if (!(getPresentation().getBackground().equals(Color.gray))) {


		g.setColor(Color.gray);


	    } else {


		g.setColor(Color.black);


	    }


	    Dash.drawFocusRect(g, 0, 0, this.width, this.height);


	}


    }





    /**


     * Paint the Slider indicator at the correct position.


     * The incrementValue has to be taken into account.


     * @see #paint


     */


    public void paintSliderIndicator(Graphics g, int x, int y) {


	int  indWidth = getIndicatorWidth();


	int  indHeight = getIndicatorHeight();





	if (orientation == VERTICAL) {


	    g.setColor(background.brighter());


	    g.drawLine(x, y, x + indWidth, y);


	    g.drawLine(x, y + 1, x, y + indHeight);





	    g.setColor(background.darker());


	    g.drawLine(x + 1, y + indHeight, x + indWidth, y + indHeight);


	    g.drawLine(x + indWidth, y + indHeight, x + indWidth, y + 1);





	    g.setColor(Color.black);


	    g.drawLine(x, y + indHeight + 1, x + indWidth + 1, y + indHeight + 1);


	    g.drawLine(x + indWidth + 1, y + indHeight + 1, x + indWidth + 1, y);





	    if (filled) {


		g.setColor(background);


		g.fillRect(x+1, y+1, indWidth-1, indHeight-1);


	    }


	    if (pressed != NONE) {


		paintBrightPattern(g, x + 2, y + 2, indWidth - 2, indHeight - 2);


	    }


	} else {


	    // calculate x and y for the pointer, as if x and y were zero


	    int  pointerX = indWidth / 2;


	    int  pointerY = indHeight + pointerX;


	    boolean  evenWidth = (indWidth%2 == 0);





	    // draw indicator with pointer


	    g.setColor(background.brighter());


	    g.drawLine(x, y, x + indWidth, y);


	    g.drawLine(x, y + 1, x, y + indHeight);


	    g.drawLine(x, y + indHeight, x + pointerX, y + pointerY);





	    g.setColor(background.darker());


	    g.drawLine(x + indWidth, y + 1, x + indWidth, y + indHeight);


	    if (evenWidth) {


		g.drawLine(x + indWidth, y + indHeight, x + pointerX, y + pointerY);


	    } else {


		g.drawLine(x + indWidth, y + indHeight, x + pointerX + 1, y + pointerY);


	    }





	    g.setColor(Color.black);


	    g.drawLine(x + indWidth + 1, y, x + indWidth + 1, y + indHeight);


	    if (evenWidth) {


		g.drawLine(x + indWidth + 1, y + indHeight, x + pointerX, y + pointerY + 1);


	    } else {


		g.drawLine(x + indWidth + 1, y + indHeight, x + pointerX + 1, y + pointerY + 1);


	    }





	    if (filled) {


		g.setColor(background);


		g.fillRect(x+1, y+1, indWidth-1, indHeight);


		for (int index = 0; index < pointerX-1; index++) {


		    int  yPos = y+indHeight+1+index;


		    g.drawLine(x+2+index, yPos, x-2+indWidth-index, yPos);


		}


	    }


	    if (pressed != NONE) {


		paintBrightPattern(g, x + 2, y + 2, indWidth - 2, indHeight - 2);


	    }


	}


    }





    /**


     * Paint a bright pattern on the indicator,


     * showing that the slider indicator is selected.


     * @see #paint


     */


    public void paintBrightPattern(Graphics g, int x, int y, int width, int height) {


	Color  patternColor = (getPresentation().getBackground().equals(Color.white)) ? Color.gray : Color.white;


	Graphics  g2 = g.create();


	try {


	    g2.setColor(patternColor);


	    g2.clipRect(x, y, width, height);





	    int  max = Math.max(width, height);


 	    int  xPos = x;


	    while (xPos <= x + width) {


		g2.drawLine(xPos, y, xPos + max, y + max);


		xPos += 2;


	    }





	    int  yPos = y;


	    while (yPos <= y + height) {


		g2.drawLine(x, yPos, x + max, yPos + max);


		yPos += 2;


	    }


	} finally {


	    g2.dispose();


	}





	// pattern on pointer of indicator must be drawn, if there is a pointer


	if (orientation == HORIZONTAL) {


	    // determine indicator pointer coordinates


	    int  pointerX = width / 2;


	    int  pointerY = y + indHeight + pointerX;


	    pointerX += x;





	    // determine points within indicator pointer and paint them


	    g.setColor(patternColor);


	    int  xPos, xEnd;


	    int  yChange = 0;


	    int  yPos = y + indHeight - 2;


	    int  smaller = indHeight % 2;


	    while (yPos + yChange <= pointerY) {


		xPos = x + yChange + smaller;


		xEnd = x - 1 + width - yChange - smaller;


		while (xPos <= xEnd) {


		    g.drawLine(xPos, yPos + yChange, xPos, yPos + yChange);


		    xPos += 2;


		}


		yChange += 1;


	    }


	}


    }





    /**


     * Paint the tick marks,


     * using tickMarkInterval to determine interval between marks.


     * @see #paint


     */


    public void paintTickMarks(Graphics g) {


	if (!useTickMarks) {


	    return;


	}


	// make local copy of interval and adjust to prevent millions of tickmarks


	int  tickMarkInterval = this.tickMarkInterval;


	if (valueToPixels(minValue + tickMarkInterval) < 2) {


	    tickMarkInterval = pixelsToValue(2);


	    // do nothing if no tickmarks can be painted


	    if (tickMarkInterval >= pixelsToValue(indWidth)) {


		return;


	    }


	}





	// paint the tickmarks


	g.setColor(foreground);


	if (orientation == VERTICAL) {


	    // calculation of xPos2: (2 + 3) pixels more for space plus the tickMarkSize


	    int  xPos1 = FOCUSSPACE;


	    int  xPos2 = FOCUSSPACE + indHeight + (tickMarkSize + 5);


	    int  margin = FOCUSSPACE + indWidth / 2 + 1;


	    for (int val = minValue; val <= maxValue; val += tickMarkInterval) {


		int  yPos = margin + valueToPixels(val);


		g.drawLine(xPos1, yPos, xPos1 + tickMarkSize, yPos);


		g.drawLine(xPos2, yPos, xPos2 + tickMarkSize, yPos);


	    }


	} else {


	    int  margin = FOCUSSPACE + indWidth / 2;


	    int  yPos = margin + indHeight + 3;


	    for (int val = minValue; val <= maxValue; val += tickMarkInterval) {


		int  xPos = margin + valueToPixels(val);


		g.drawLine(xPos, yPos, xPos, yPos + tickMarkSize);


	    }


	}


    }





    /**


     * Event handler for possible mouse and keyboard events.


     * When the user tries to change the value, it can only take the values allowed by


     * `incrementValue'. Through the function `setValue' the value can take other


     * values, but this is not allowed to happen through the user-interface.


     */


    public boolean handleEvent(Event evt) {


      if (disabled) {


	    return super.handleEvent(evt);


	}





	int  x = evt.x;


	int  y = evt.y;


	if (orientation == VERTICAL) {


	    if (useTickMarks) {


		x -= tickMarkSize + 2;    // plus 2 for space between mark and bar


	    } 


	    int  t = x;


	    x = y;


	    y = t;


	}





	switch (evt.id) {


	    case Event.MOUSE_DOWN: {


		int pixVal = valueToPixels(value);


		// check if user pressed on indicator


		int  yMax = FOCUSSPACE + ((orientation == HORIZONTAL) ? indHeight + indWidth / 2 : indHeight);


		if ((x > FOCUSSPACE + pixVal) && (x < (FOCUSSPACE + pixVal + indWidth)) && (y > FOCUSSPACE) && (y < yMax)) {


		    requestFocus();


		    pressed = DRAG;


		    dragoff = x - (FOCUSSPACE + pixVal);


		    repaint();


		    oldValue = value;


		    return false;


		}





		// check if user pressed in 'hot' area of slider


		if (y >= FOCUSSPACE && y <= yMax && x <= barSize()) {


		    requestFocus();


		    setValueIncremental(pixelsToValue(x - (indWidth / 2) - FOCUSSPACE));


		    repaint();


		    if (value != oldValue) {


			action();


			oldValue = value;


		    }


		    return false;


		}


		return false;


	    }





	    case Event.MOUSE_DRAG: {


		if (pressed == DRAG) {


		    float  valuePixel = (maxValue - minValue) / (float)getMaxWidth();


		    int  offset = x - (dragoff + FOCUSSPACE);


		    if (isReversed()) {


			setValueIncremental((int)(maxValue - valuePixel * offset));


		    } else {


			setValueIncremental((int)(minValue + valuePixel * offset));


		    }


		    if (trackAll && value != oldValue) {


			action(Event.SCROLL_ABSOLUTE);


			oldValue = value;


		    } else {


			postEvent(new Event(this, Event.SCROLL_ABSOLUTE, new Integer(value)));


		    }


		}


		return true;


	    }





	    case Event.MOUSE_UP: {


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


		    action();


		    oldValue = value;


		}


		pressed = NONE;


		repaint();


		return true;


	    }





	    case Event.KEY_PRESS: {


		switch (evt.key) {


		    case ' ':


			pressed = DRAG;


			repaint();


			return true;


		}


		break;


	    }





	    case Event.KEY_RELEASE: {


		switch (evt.key) {


		    case ' ':


			pressed = NONE;


			repaint();


			return true;


		}


		break;


	    }





	    case Event.KEY_ACTION_RELEASE: {


		switch (evt.key) {


		    case Event.LEFT:


		    case Event.UP:


		    case Event.PGUP:


		    case Event.RIGHT:


		    case Event.DOWN:


		    case Event.PGDN:


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


			    action();


			    oldValue = value;


			}


			keyActionPress = true;


			return true;


		}


		break;


	    }





	    case Event.KEY_ACTION: {


		switch (evt.key) {


		    case Event.LEFT:


		    case Event.UP: {


			if (keyActionPress) {


			    oldValue = value;


			    keyActionPress = false;


			}


			int  oldPixVal = valueToPixels(value);


			if (isReversed()) {


			    setValueIncremental(value + incrementValue);


			} else {


			    setValueIncremental(value - incrementValue);


			}


			if (trackAll && oldValue != value) {


			    action();


			    oldValue = value;


			}


			return true;			


		    }





		    case Event.PGUP: {


			if (keyActionPress) {


			    oldValue = value;


			    keyActionPress = false;


			}


			int  oldPixVal = valueToPixels(value);


			if (isReversed()) {


			    setValueIncremental(value + incrementValue);


			    if (valueToPixels(value) == oldPixVal) {


				setValueIncremental(pixelsToValue(oldPixVal+2));


			    }


			} else {


			    setValueIncremental(value - incrementValue);


			    if (valueToPixels(value) == oldPixVal) {


				setValueIncremental(pixelsToValue(oldPixVal));


			    }


			}


			if (trackAll && oldValue != value) {


			    action();


			    oldValue = value;


			}


			return true;			


		    }





		    case Event.RIGHT:


		    case Event.DOWN: {


			if (keyActionPress) {


			    oldValue = value;


			    keyActionPress = false;


			}


			if (isReversed()) {


			    setValueIncremental(value - incrementValue);


			} else {


			    setValueIncremental(value + incrementValue);


			}


			if (trackAll && oldValue != value) {


			    action();


			    oldValue = value;


			}


			return true;


		    }





		    case Event.PGDN: {


			if (keyActionPress) {


			    oldValue = value;


			    keyActionPress = false;


			}


			int  oldPixVal = valueToPixels(value);


			if (isReversed()) {


			    setValueIncremental(value - incrementValue);


			    if (valueToPixels(value) == oldPixVal) {


				setValueIncremental(pixelsToValue(oldPixVal));


			    }


			} else {


			    setValueIncremental(value + incrementValue);


			    if (valueToPixels(value) == oldPixVal) {


				setValueIncremental(pixelsToValue(oldPixVal+2));


			    }


			}


			if (trackAll && oldValue != value) {


			    action();


			    oldValue = value;


			}


			return true;


		    }





		    case Event.HOME:


			oldValue = value;


			if (isReversed()) {


			    setValueIncremental(maxValue);


			} else {


			    setValueIncremental(minValue);


			}


			if (oldValue != value) {


			    action();


			    oldValue = value;


			}


			return true;





		    case Event.END:


			oldValue = value;


			if (isReversed()) {


			    setValueIncremental(minValue);


			} else {


			    setValueIncremental(maxValue);


			}


			if (oldValue != value) {


			    action();


			    oldValue = value;


			}


			return true;


		}


		break;


	    }


	}


	return super.handleEvent(evt);


    }





    /**


     * The user has changed the value of the Slider.


     */


    public void action(int id) {


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


	action();


    }





    /**


     * This widget supports input focus.


     */


    public boolean focusInterest() {


	return !disabled;


    }





    /**


     * Debugging.


     */


    public void paramString(StringBuffer buf) {


	super.paramString(buf);


	buf.append(",");


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


    }








    /**


     * Set the value to the IncrementValue closest to the given value.


     */


    public void setValueIncremental(int value) {


	int  newVal = value + incrementValue/2 - minValue;


	setValue(minValue + (newVal / incrementValue) * incrementValue);


    }





   /**


     * The width of the Slider indicator.


     * If the orientation is vertical the width is the indHeight,


     * otherwise it is just the indWidth.


     * @see #indWidth


     */


    public int getIndicatorWidth() {


	return (orientation == VERTICAL) ? indHeight : indWidth;


    }





    /**


     * The height of the Slider indicator.


     * If the orientation is vertical the height is the indWidth,


     * otherwise it is just the indHeight.


     * @see #indHeight


     */


    public int getIndicatorHeight() {


	return (orientation == VERTICAL) ? indWidth : indHeight;


    }





    /**


     * The maximum Slider width. Below the slider is drawn with '=', the area the indicator can


     * point at is drawn with '~' with the left and right border '[' and ']'. The border of the


     * widget itself is drawn using '+', '-' and '|'. What this function returns is the width


     * between '[' and ']', so the maximum area width the indicator can point at.


     * 	+--------------------------------------------+


     * 	|  ========================================  |


     * 	|   [~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~]   |


     * 	+--------------------------------------------+


     */


    public int getMaxWidth() {


	return ((orientation == VERTICAL) ? height : width) - indWidth - 2 - FOCUSSPACE * 2;


    }





    /**


     * The height of the Slider. If the orientation is vertical,


     * this is the width, otherwise it is just the heigth.


     */


    public int getHeight() {


	return (orientation == VERTICAL) ? width : height;


    }





    /**


     * Calculates the number of increments that can be done given `incrementValue'.


     * This depends on the range (minValue and maxValue) and the incrementValue.


     * @see #incrementValue


     */


    public int nrOfIncrements() {


	return (maxValue - minValue) / incrementValue;


    }





    /**


     * Returns if the slider uses the opposite direction of the


     * one this slider was initially designed for.


     * For the horizontal orientation that is the DOWN direction,


     * for the vertical orientation the UP direction.


     * @see #direction


     */


    public boolean isReversed() {


	return ((orientation==VERTICAL && direction==UP)


			|| (orientation==HORIZONTAL && direction==DOWN));


    }





    /**


     * The size of the sliderbar itself,


     * which is smaller then the size of the widget.


     */


    public int barSize() {


	int  result;


	// check for reversed direction


	if (isReversed()) {


	    result = valueToPixels(minValue) + indWidth + 1;


	} else {


	    result = valueToPixels(maxValue) + indWidth + 1;


	}


	return result;


    }





    /**


     * The value translated into pixels.


     * This function does NOT use `FOCUSSPACE': it pretends that `FOCUSSPACE' is zero.


     * @see #pixelsToValue


     */


    public int valueToPixels(int value) {


	int  range = getRange();


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


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


	int  result = (int)(progress * getMaxWidth());





	// reverse pixels if property says so


	if (orientation == VERTICAL && direction == UP) {


	    result = height - 2 * FOCUSSPACE - result - indWidth - 2;


	} else if (orientation == HORIZONTAL && direction == DOWN) {


	    result = width - 2 * FOCUSSPACE - result - indWidth - 2;


	}


	return result;


    }





    /**


     * Number of pixels translated into a value.


     * This functions does NOT use `FOCUSSPACE': it pretends that `FOCUSSPACE' is zero.


     * @see #valueToPixels


     */


    public int pixelsToValue(int pixels) {


	int  range = getRange();


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


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


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


	if (isReversed()) {


	    result = (minValue + maxValue) - result;


	}


	return result;


    }


}


