package kleurapplet.grnuminput; import java.awt.*; import java.awt.event.*; import java.util.Vector; /** * class Slider: draws bar & slider */ class Slider extends Canvas { double huidigePos; // position, value from 0 to 1 private Color enabledSliderColor = Color.red; private Color disabledSliderColor = Color.gray; Color sliderColor = enabledSliderColor; Dimension dd; public Slider(double initp) // initial position { setSize(100,20); // for now fixed size (not needed for functionality) dd = getSize(); huidigePos = initp; } public void paint(Graphics g) { dd = getSize(); g.drawRect(1, 7, dd.width-2, 6); // bar g.setColor(sliderColor); g.fillOval((int)Math.round( huidigePos*(dd.width-7) ), 1, 7, 20); // -7 because of width oval (=7) } protected double getPos( int mousepos ) // working width of bar for scaling MouseEvents { return ((double)(mousepos-3)) / (dd.width-7); } protected void setSlide(double p) // set position to p, 0<=p<=1 { huidigePos = p; repaint(); } public void setEnabled(boolean ena) { if ( ena ) // Color only, NumberSlider handles events { sliderColor = enabledSliderColor; } else { sliderColor = disabledSliderColor; } repaint(); // eigenlijk onnodig } protected void setDisabledSliderColor(Color c) { disabledSliderColor = c; // call to repaint() in NumberSlider } protected void setEnabledSliderColor(Color c) { enabledSliderColor = c; } } /** * NumberSlider is a component for input of numbers. It consists of a * TextField for keyboards entry of numbers plus a 'slider' that can be dragged * across a bar representing the possible range of values. * <br> * Values are doubles, precision can be indicated by specifying the number of decimals. * The meaning of the number must be specified by 'parameter name' also on display. * Also, a units label can be given. * <br> * Display: * <p><tt> * | Parameter name | * | min max | * | --- Slider --------- | * | Value Units | * * </tt><p> * Use is like standard AWT-components: * <br> * - Listener must implement interface NumberListener * <br> * - Listeners register with 'addNumberListener' * <br> * - void numberChanged(String, double) will be called by NumberSlider * (String == parameter name, double holds new value * * @author Paul Bergervoet, additions bij Joost Rommes * @version 3.2 18 februari 2000 * @since JDK 1.1 * * @see NumberListener */ public class NumberSlider extends Panel implements NumberListener, MouseListener, MouseMotionListener { private double waarde; // current value private double initw; // initial value private double minw; // smallest value private double maxw; // biggest value private String grootheid; // name of parameter private String eenheid; // unit name private int decimals; // number of decimals private long truncator; // intermediate when fixing decimals private double fact; // = Math.pow(10,decimals) private NumOnlyField wtekst; private Slider wschuif; private Label lgh; private Label leh; private Label lmin; private Label lmax; private boolean enabled = true; private Vector listeners; /** * Constructor for NumberSlider. * * @param mn minimum value of the NumberSlider * @param mx maximum value of the NumberSlider * @param w initial value * @param dec number of decimals (precision) of value * @param gh parameter name * @param eh parameter unit */ public NumberSlider( double mn, double mx, // min, max double w, // value == initial value int dec, // number of decimals String gh, String eh // parameter name and unit ) { minw = mn; maxw = mx; waarde = w; initw = w; decimals = dec; grootheid = gh; eenheid = eh; fact = Math.pow(10,decimals); listeners = new Vector(); // set up lay out GridBagLayout gridbag = new GridBagLayout(); // set up data-area GridBagConstraints c = new GridBagConstraints(); setLayout(gridbag); // component, Label: parameter name c.gridwidth = GridBagConstraints.REMAINDER; // line holding Name c.anchor = GridBagConstraints.WEST; // left lgh = new Label(grootheid); gridbag.setConstraints(lgh, c); add(lgh); // components, Labels: min and max value, show rounded values, min and max should be ints c.gridwidth = 1; c.anchor = GridBagConstraints.WEST; // left lmin = new Label( String.valueOf((int)Math.round(minw)) ); gridbag.setConstraints(lmin, c); add(lmin); c.gridwidth = GridBagConstraints.REMAINDER; c.anchor = GridBagConstraints.EAST; // right lmax = new Label( String.valueOf((int)Math.round(maxw)) ); gridbag.setConstraints(lmax, c); add(lmax); // component slide bar c.gridwidth = GridBagConstraints.REMAINDER; c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.HORIZONTAL; wschuif = new Slider( valToPos(waarde) ); wschuif.addMouseListener(this); wschuif.addMouseMotionListener(this); gridbag.setConstraints(wschuif, c); add(wschuif); // components: NumOnlyField and Units c.gridwidth = 1 ; c.insets = new Insets(5, 0, 0, 0); // space above TextField c.anchor = GridBagConstraints.EAST; c.fill = GridBagConstraints.NONE; wtekst = new NumOnlyField( minw, maxw, waarde, dec); // no width! (columns) wtekst.setEnabled(true); // also sets color.... wtekst.addNumberListener(this); // NumberSlider listen to NumOnlyField! gridbag.setConstraints(wtekst, c); add(wtekst); c.anchor = GridBagConstraints.WEST; // put left leh = new Label(eenheid); gridbag.setConstraints(leh, c); add(leh); } /** * Add a listener to the NumberSlider. * * @param l NumberListener to be added. */ public void addNumberListener(NumberListener l) { listeners.addElement(l); } private void tellListeners() { for ( int i=0; i<listeners.size(); i++ ) { ( (NumberListener)listeners.elementAt(i) ).numberChanged(grootheid, waarde); } } /* calculate slider position from value. */ private double valToPos(double val) // 0<=p<=1 { return (val-minw) / (maxw-minw); } /* calculate value from slider position. */ private double setDecimals(double val) // returns val, truncated to { truncator = Math.round( val*fact ); // required number of decimals return ((double)truncator) / fact; } /* calculate value and slider position from mouse position. */ private void processSliderChange(int mousepos) { double relpos = wschuif.getPos(mousepos); if (relpos < 0 ) { relpos = 0; } if (relpos > 1 ) { relpos = 1; } waarde = setDecimals( minw + relpos*(maxw-minw) ); wschuif.setSlide( valToPos(waarde) ); wtekst.setValue( waarde ); tellListeners(); } // Public methods: Enable/disable, setValue, getValue /** * Enables or disables the NumberSlider. When disabled it becomes disabledSliderColor. * * @param b True to enable the textfield, False to disable it. */ public void setEnabled(boolean b) { enabled = b; wtekst.setEnabled(b); wschuif.setEnabled(b); repaint(); } /** * Find out if the NumberSlider is enabled. * * @return boolean indicating if the component is enabled. */ public boolean isEnabled() { return enabled; } /** * Sets the value to be displayed in the number textfield & slider. * <br> * Note: Does not tell listeners!!!! * * @param newval The new double value to be displayed. */ public void setValue(double newval) { if ( newval > maxw ) { waarde = maxw; } else if (newval < minw ) { waarde = minw; } else { waarde = setDecimals(newval); // truncate } wtekst.setValue( waarde ); wschuif.setSlide( valToPos(waarde) ); } /** * SetValue with int value. * * @param newval The new int value to be displayed. * * @see NumberSlider#setValue(double newval) */ public void setValue(int newval) { setValue((double)newval); } /** * Resets the value of the NumberSlider to the initial value. * <br> * Note: Does not tell listeners!!!! * */ public void setInitValue() { setValue(initw); } /** * The value of the NumberSlider is returned as double. * * @return value of NumberSlider. */ public double getValue() { return waarde; } /** * Force NumberSlider to check whether the value has changed. * * NumberSlider will check the number value of the textfield. If the value is different * from the current value, the Listeners will be informed in the normal way, using numberChanged. * <br> * Note: useful when you don't want to force the user to press ENTER after typing a new value. * When the user moves to another textfield after typing something, this is noticed through focusLost(). * However, when the user moves the mouse and clicks a button, no loss of keyboard focus occurs. * In this case you may want to check if the value has changed explicitly. */ public void checkValue() { wtekst.checkValue(); } // overriding default settings /** * Sets the number of colums of the textfield of the NumberSlider. * <br> * Note: by default the width of the TextField will be calculated from the min and max values: * number of digits needed + 1. * * @param n New number of colums of the textfield. */ public void setColumns(int n) { wtekst.setColumns(n); } /** * Set the color the handle must have when the NumberSlider is disabled. * * @param c The preferred color. */ public void setDisabledSliderColor(Color c) { wschuif.setDisabledSliderColor( c); repaint(); } /** * Set the color the handle must have when the NumberSlider is enabled. * * @param c The preferred color. */ public void setEnabledSliderColor(Color c) { wschuif.setEnabledSliderColor( c); repaint(); } /** * Set the font of the labels: parameter name, min, max and units. * * @param f The preferred font. */ public void setLabelFont(Font f) { lgh.setFont( f); leh.setFont( f); lmin.setFont( f); lmax.setFont( f); repaint(); } /** * Set the font of the value in the TextField * * @param f The preferred font. */ public void setValueFont(Font f) { wtekst.setFont( f); repaint(); } /** * Set the foregroundcolor of the labels: parameter name, min, max and units. * * @param c The preferred color. */ public void setLabelForeground( Color c ) { lgh.setForeground( c ) ; leh.setForeground( c ) ; lmin.setForeground( c ) ; lmax.setForeground( c ) ; repaint(); } /** * Set the color of the value in the TextField * * @param c The preferred color. */ public void setValueColor( Color c ) { wtekst.setForeground( c ) ; repaint(); } // Event handling // 1. MouseListener /** * Invoked when mouse button is pressed on the NumberSlider. * Move slide to click location. */ public void mousePressed(MouseEvent e) { processSliderChange( e.getX() ); // System.out.println("pressed, new value is "+waarde ); } /** * Invoked when mouse button is released over the NumberSlider. * Does nothing. */ public void mouseReleased(MouseEvent e) { // do nothing } /** * Invoked when mouse button is clicked on the NumberSlider. * Does nothing. */ public void mouseClicked(MouseEvent e) { // do nothing } /** * Invoked when mouse has entered the NumberSlider. * Does nothing. */ public void mouseEntered(MouseEvent e) { // do nothing } /** * Invoked when mouse has exited the NumberSlider. * Does nothing. */ public void mouseExited(MouseEvent e) { // do nothing } // 2. MouseMotionListener /** * Invoked when mouse is dragged the NumberSlider. * Move slide to drag location. */ public void mouseDragged(MouseEvent e) { processSliderChange( e.getX() ); // System.out.println("dragged, new value is "+waarde ); } /** * Invoked when mouse is moved the NumberSlider. * Does nothing. */ public void mouseMoved(MouseEvent e) { // do nothing } // 3. NumberListener: react to change in NumOnlyField /** * Invoked when the number in the textfield has changed. * * @param s Dummy string: name of the NumOnlyTextField. * @param w Double value of the textfield. */ public void numberChanged(String s, double w) // String s doesn't matter, it's the NumOnlyField { waarde = w; wschuif.setSlide( valToPos(waarde) ); tellListeners(); } }