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


// @(#)SpinBoxWidget.java, 1.20, 12/19/96





package marimba.gui;





import java.awt.*;


import marimba.persist.*;


import marimba.util.*;





/**


 * A SpinBox accepts a limited set of dicrete ordered input values


 * that make up a circular loop. A SpinBox is a combination of a


 * TextBox and a pair of buttons (an up-down control).


 *


 * @author	Klaas Waslander


 * @version 	1.20, 12/19/96


 */


public class SpinBoxWidget extends ValueWidget implements TimerClient {


    /**


     * The value can be displayed at the right, left or centered.


     * @see #getAlign


     * @see #setAlign


     * @see #alignOptions


     */


    public int  align = LEFT;





    /**


     * The possible options for the alignment.


     * @see #getAlignOptions


     * @see #align


     */


    public static Options  alignOptions = new Options();


    static {


	alignOptions.add("left", LEFT);


	alignOptions.add("right", RIGHT);


	alignOptions.add("center", CENTER);


    }





    /**


     * The fillmode of the SpinBox: none, left, right or filled.


     * This means it can fill the content, the buttons or both.


     * The SpinBox is not transparent if it is filled and the


     * buttons are filled too.


     * @see #getFillMode


     * @see #setFillMode


     * @see WidgetConstants


     */


    public int  fillMode = CONTENT;





    /**


     * The possible options for the fillMode.


     * @see #getFillOptions


     * @see #fillMode


     */


    public static Options  fillOptions = new Options();


    static {


	fillOptions.add("none", NONE);


	fillOptions.add("content", CONTENT);


	fillOptions.add("buttons", BUTTON);


	fillOptions.add("both", FILLED);


    }





    /**


     * If true, action() is called by SpinTextBoxWidget


     * after every change of the value.


     * Otherwise action() is only called when the


     * (mouse)button is released.


     * @see #tracksAll


     * @see #setTrackAll


     */


    public boolean  trackAll;





    /**


     * The textbox used to display the spinbox.


     * @see #getSpinBoxTextWidget


     * @see #newTextBox


     */


    public SpinBoxTextWidget  spin;





    /**


     * The button that has been pressed: UP, DOWN or NONE.


     */


    public int  pressedButton = NONE;








    /**


     * Default constructor.


     */


    public SpinBoxWidget() {


	newTextBox();


    }





    /**


     * Get the properties of this widget.


     */


    public void getProperties(PropertyList list) {


	super.getProperties(list);


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


	list.setOption("fillmode", fillOptions, fillMode, CONTENT);


	list.setOption("align", alignOptions, align, LEFT);


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


    }





    /**


     * Get the properties for the children of this widget.


     */


    public void getChildProperties(PropertyList list) {


    }





    /**


     * Set the properties of this widget.


     */


    public void setProperties(PropertyList list) {


	super.setProperties(list);


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


	fillMode = list.getOption("fillmode", fillOptions, CONTENT);


	transparent = (fillMode != FILLED);


	align = list.getOption("align", alignOptions, LEFT);


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


	newTextBox();


	


	// REMIND: added for release 0.92, should be removed with next release!


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


	boolean  fillButtons = list.getBoolean("fillButtons", false);


	if (filled || fillButtons) {


	    if (filled && fillButtons) {


		fillMode = FILLED;


		transparent = false;


	    } else if (filled) {


		fillMode = CONTENT;


	    } else {


		fillMode = BUTTON;


	    }


	}


    }





    /**


     * Get the possible options for the alignment.


     * @see #alignOptions


     */


    public Options getAlignOptions() {


	return alignOptions;


    }





    /**


     * Get align.


     * @see #align


     */


    public int getAlign() {


	return align;


    }





    /**


     * Set align.


     * @see #align


     */


    public void setAlign(int align) {


	if (this.align != align) {


	    this.align = align;


	    if (spin != null) {


		spin.setAlign(align);


	    }


	}


    }





    /**


     * Get the possible options for the fillMode.


     * @see #fillOptions


     */


    public Options getFillOptions() {


	return fillOptions;


    }





    /**


     * Check what the ListBoxWidget fills.


     * @see #fillMode


     */


    public int getFillMode() {


	return fillMode;


    }





    /**


     * Let the SpinBoxWidget fill something.


     * @see #fillMode


     */


    public void setFillMode(int fillMode) {


	if (this.fillMode != fillMode) {


	    int  style = FILLED;


	    boolean  fillButtons = false;


	    switch (fillMode) {


		case NONE:


		    this.fillMode = fillMode;


		    style = BOXED;


		    break;


		case CONTENT:


		    this.fillMode = fillMode;


		    break;


		case BUTTON:


		    this.fillMode = fillMode;


		    style = BOXED;


		    fillButtons = true;


		    break;


		case FILLED:


		    this.fillMode = fillMode;


		    fillButtons = true;


		    transparent = false;


		    break;


	    }


	    if (spin != null) {


		spin.setStyle(style);


		spin.setFillButtons(fillButtons);


		spin.repaint();


	    }


	}


    }





    /**


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


     * @see #trackAll


     */


    public boolean tracksAll() {


	return trackAll;


    }





    /**


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


     * value or only once.


     * @see #trackAll


     */


    public void setTrackAll(boolean trackAll) {


	this.trackAll = trackAll;


    }





    /**


     * When this widget is disabled, the SpinBoxTextWidget is disabled.


     */


    public void disable(boolean disabled) {


	spin.disable(disabled);


    }





    /**


     * Set the current value. If the value is < minValue


     * the value is set to maxValue, if the value is > maxValue


     * the value is set to minValue.


     */


    public void setValue(int value) {


	if (value < minValue) {


	    value = minValue;


	} else if (value > maxValue) {


	    value = maxValue;


	}


	if (value != this.value) {


	    this.value = value;


	    repaint();


	}





	if (spin != null  &&  this.value == value) {


	    spin.setText(String.valueOf(value));


	}


    }





    /**


     * Get the textWidget that is used for the SpinBox.


     * @see #spin


     */


    public SpinBoxTextWidget getSpinBoxTextWidget() {


	return spin;


    }





    /**


     * Creates a new SpinBoxTextWidget.


     * @see #spin


     */


    protected void newTextBox() {


	spin = new SpinBoxTextWidget();


	spin.setBackground(background);


	spin.setAlign(align);


	spin.setEditable(true);





	if (fillMode==CONTENT || fillMode==FILLED) {


            spin.setStyle(FILLED);


	} else {


	    spin.setStyle(BOXED);


	}


	if (fillMode==BUTTON || fillMode==FILLED) {


	    spin.setFillButtons(true);


	} else {


	    spin.setFillButtons(false);


	}





	spin.setText(String.valueOf(value));


	spin.disable(disabled);


        add(spin);


    }





    /**


     * Reshape the SpinBoxTextWidget to the correct size.


     */


    public void layout() {


	spin.reshape(0, 0, width, height);


    }





    /**


     * Handle user events.


     */


    public boolean handleEvent(Event evt) {


	switch (evt.id) {


	    case Event.LOST_FOCUS: {


		int  newValue;


		if (spin.getLength() > 0) {


		    newValue = Integer.parseInt(spin.getText());


		} else {


		    newValue = minValue;


		}


		if (newValue > maxValue) {


		    setValue(maxValue);


		    action();


		} else if (newValue < minValue) {


		    setValue(minValue);


		    action();


		} else {


		    setValue(newValue);


		    action();


		}


		break;


	    }





	    case Event.SCROLL_LINE_UP:


		if (value >= maxValue) {


		    setValue(minValue);


		} else {


		    setValue(value+1);


		}


		pressedButton = UP;


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


		return true;





	    case Event.SCROLL_LINE_DOWN:


		if (value <= minValue) {


		    setValue(maxValue);


		} else {


		    setValue(value-1);


		}


		pressedButton = DOWN;


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


		return true;





	    case Event.MOUSE_UP:


		action();


		Timer.master.remove(this);


		return true;





	    case Event.KEY_PRESS:


		if (spin.getLength() > 0) {


		    this.value = value;


		    repaint();


		}


		return true;





	}


	return super.handleEvent(evt);


    }





    /**


     * Auto repeat.


     */


    public long tick(long tm, Object arg) {


	if (pressedButton == UP) {


	    if (value >= maxValue) {


		setValue(minValue);


	    } else {


		setValue(value+1);


	    }


	    if (trackAll) {


		action();


	    }


	} else if (pressedButton == DOWN) {


	    if (value <= minValue) {


		setValue(maxValue);


	    } else {


		setValue(value-1);


	    }


	    if (trackAll) {


		action();


	    }


	}


	return System.currentTimeMillis() + 50;


    }


}


