package kleurapplet.grnuminput;
import java.awt.*;
import java.awt.event.*;
import java.text.DecimalFormat;
import java.util.Vector;
/**
* class ArrowCanvas
* Helper class for NumberArrow, displays picture of arrows and handles events.
*/
class ArrowCanvas extends Canvas
{ private Color disabledArrowColor = Color.gray;
private Color enabledArrowColor = Color.red;
private Color activeArrowColor = Color.green;
private Dimension dd;
private Polygon up;
private boolean upActive = false;
private Polygon down;
private boolean downActive = false;
private boolean enabled = true;
/**
* Constructor for ArrowCanvas
* Creates Canvas with arrows drawn on it.
*/
public ArrowCanvas() // initial position
{ dd = new Dimension(19, 23); // fixed size.... ok?
setSize(dd);
// triangles for up and down arrows
up = new Polygon();
up.addPoint(10, 2);
up.addPoint(3, 10);
up.addPoint(17, 10);
down = new Polygon();
down.addPoint(3, 13);
down.addPoint(17, 13);
down.addPoint(10, 21);
}
// painting the arrows
private void paintPolygon(Graphics g, Polygon p, boolean active)
{ if ( enabled )
{ if ( active )
{ g.setColor(activeArrowColor);
} else
{ g.setColor(enabledArrowColor);
}
} else
{ g.setColor(disabledArrowColor);
}
g.drawPolygon(p); // draw and fill, will they look the same
g.fillPolygon(p); // using fill only, the up-arrow will be smaller!
}
public void paint(Graphics g)
{ g.setColor(Color.black);
g.drawRect(0, 0, dd.width-1, dd.height-1);
g.setColor(Color.white);
g.fillRect(1, 1, dd.width-2, dd.height-2); // background
paintPolygon(g, up, upActive);
paintPolygon(g, down, downActive);
}
// Set and get by NumberArrow
protected int getArrow(int clicky) // threshold down arrow
{ int dir = 0; // no direction (yet)
if ( clicky > 12 )
{ downActive = true;
dir = -1;
repaint();
} else if ( clicky < 11 )
{ upActive = true;
dir = 1;
repaint();
}
return dir;
}
protected void setInactive()
{ upActive = false;
downActive = false;
repaint();
}
/**
* Enable or disable the NumberArrow depending on the value of b.
* An enabled NumberArrow can respond to user input and generates events.
* NumberArrows are enabled initially by default.
*
* @param b If true this NumberArrow is enabled, otherwise the arrow is disabled.
*
*/
public void setEnabled(boolean b)
{ enabled = b;
}
protected void setDisabledArrowColor(Color c)
{ disabledArrowColor = c; // call to repaint() in NumberArrow
}
protected void setEnabledArrowColor(Color c)
{ enabledArrowColor = c;
}
protected void setActiveArrowColor(Color c)
{ activeArrowColor = c;
}
} // class ArrowCanvas
/**
* class ArrowRunner
* Helper class for NumberArrow, it will time how long arrow is being 'mouseheld'
* and generates 'kicks' for NumberArrow
*/
class ArrowRunner extends Thread
{ private NumberArrow owner;
private boolean running = true;
public ArrowRunner(NumberArrow o)
{ owner = o;
setDaemon(true);
}
public void run()
{ int teller = 0; // count how many times the thread 'kicks'
try
{ sleep(500);
} catch ( InterruptedException e ) { }
while (running) // will be stopped by release of mouse!
{ teller++; // increase count (first kick will be 1)
owner.kick(teller); // pass on count (so NumberArrow can increase steps
try // while 'mouse hold' lasts
{ sleep(250);
} catch ( InterruptedException e ) { }
}
}
public void neatstop()
{ running = false;
}
}
/**
*
* NumberArrow is a component for input of numbers. It consists of a
* TextField for keyboards entry of numbers plus up and down arrows
* to increase/decrease the number value in the TextField.
* <br>
* Values are doubles, precision can be indicated by specifying the number of decimals.
* The step size on arrowclick can be specified.
* <br>
* When an arrow is being held, the steps will increase to give an accelaration in the
* change of value. This accelaration can be limited by setting 'MaxFactor' of steps.
* 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 |
* | � Value Units |
*
* </tt><p>
* Value is Textfield holding value, � is picture with up- and down-arrow.
*
* 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 NumberArrow
* (String == parameter name, double holds new value
*
* @author Paul Bergervoet
* @version 3.1 5 maart 1999
* @since JDK 1.1
*
* @see NumberListener
*/
public class NumberArrow extends Panel
implements NumberListener, MouseListener
{ private double waarde; // current value
private double initw; // initial value
private double minw; // smallest value
private double maxw; // biggest value
private double stap; // size of step +/- at arrowclick
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 ArrowCanvas warrow;
private ArrowRunner arwr;
private int dir; // 1 = up, -1 = down
private int maxStepFactor;
private Label lgh;
private Label leh;
private boolean enabled = true;
private Vector listeners;
/**
* Constructor for NumberArrow.
* Creates a NumberArrow with the two arrows left of the textfield.
*
* @param mn minimum value of the numberarrow
* @param mx maximum value of the numberarrow
* @param w initial value
* @param s step of the number arrow (change of value at arrow click)
* @param dec number of decimals (precision) of value
* @param gh parameter name
* @param eh parameter unit
*/
public NumberArrow( double mn, double mx, // min, max
double w, // value == initial value
double s, // step+/-
int dec, // number of decimals
String gh, String eh // parameter name and unit
)
{ minw = mn;
maxw = mx;
waarde = w;
initw = w;
stap = s;
maxStepFactor = 1; // default, see second constructor
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: Arrows, 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;
warrow = new ArrowCanvas();
warrow.addMouseListener(this);
gridbag.setConstraints(warrow, c);
add(warrow);
wtekst = new NumOnlyField( minw, maxw, waarde, dec); // no width! (columns)
wtekst.setEnabled(true); // also sets color....
wtekst.addNumberListener(this); // NumberArrow listen to NumOnlyField!
gridbag.setConstraints(wtekst, c);
add(wtekst);
c.anchor = GridBagConstraints.WEST; // put left
c.fill = GridBagConstraints.HORIZONTAL;
leh = new Label(eenheid);
gridbag.setConstraints(leh, c);
add(leh);
}
/**
* Add a listener to the numberarrow.
*
* @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);
}
}
private double setDecimals(double val) // returns val, truncated to
{ truncator = Math.round( val*fact ); // required number of decimals
return ((double)truncator) / fact;
}
// Public methods: Enable/disable, setValue, getValue
/**
* Enables or disables the NumberArrow. When disabled it becomes disabledArrowColor.
*
* @param b True to enable the textfield, False to disable it.
*/
public void setEnabled(boolean b)
{ enabled = b;
wtekst.setEnabled(b);
wtekst.repaint();
warrow.setEnabled(b);
warrow.repaint();
}
/**
* Find out if the NumberArrow 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.
* Note: Does not tell listeners!!!!
*
* @param newval The new double value to be displayed.
*/
public void setValue(double newval) // NB: Does not tell listeners!!!!
{ if ( newval > maxw )
{ waarde = maxw;
} else if (newval < minw )
{ waarde = minw;
} else
{ waarde = setDecimals(newval); // truncate
}
wtekst.setValue( waarde );
}
/**
* SetValue with int value.
*
* @param newval The new int value to be displayed.
*
* @see NumberArrow#setValue(double newval)
*/
public void setValue(int newval) // int-waarde zetten
{ setValue((double)newval); //
}
/**
* Resets the value of the NumberArrow to the initial value.
* Note: Does not tell listeners!!!!
*
*/
public void setInitValue()
{ setValue(initw);
}
/**
* The value of the NumberArrow is returned as double.
*
* @return value of NumberArrow.
*/
public double getValue()
{ return waarde;
}
/**
* Force NumberArrow to check whether the value has changed.
*
* NumberArrow 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 NumberArrow.
* <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);
}
/**
* Sets the maximum multiplication factor of the step size to a new value.
* <br>
* When the arrow button is pushed in for a longer period the step size
* is multiplied by an increasing factor: 2, 3, 4 etc..
* Therefore the number change increases when you hold the number.
* MaxFactor limits the increase.
* <br>
* Default value is 1, meaning constant step size.
*
* @param m New factor value.
*/
public void setMaxFactor(int m)
{ maxStepFactor = m;
}
/**
* Set the color the arrows must have when the NumberArrow is disabled.
*
* @param c The preferred color.
*
*/
public void setDisabledArrowColor(Color c)
{ warrow.setDisabledArrowColor( c);
repaint();
}
/**
* Set the color the arrows must have when the NumberArrow is enabled, but not touched by the mouse.
*
* @param c The preferred color.
*/
public void setEnabledArrowColor(Color c)
{ warrow.setEnabledArrowColor( c);
repaint();
}
/**
* Set the color of an active arrow, that is: someone is pressing the mouse on the arrow.
*
* @param c The preferred color.
*/
public void setActiveArrowColor(Color c)
{ warrow.setActiveArrowColor( c);
repaint();
}
/**
* Set the font of the labels: parameter name and units.
*
* @param f The preferred font.
*/
public void setLabelFont(Font f)
{ lgh.setFont( f);
leh.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 and units.
*
* @param c The preferred color.
*/
public void setLabelForeground( Color c )
{ lgh.setForeground( c ) ;
leh.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
private void stopThread()
{ if ( arwr !=null )
{ arwr.neatstop();
warrow.setInactive();
arwr = null;
}
}
/**
* Invoked by timer when mouse is held on an arrow.
*
* @param count Counter of kicks, for accelaration.
*/
protected void kick(int count) // mouse held
{ setValue(waarde + dir*Math.min(count, maxStepFactor)*stap); // 1 or -1 times count times stap
tellListeners(); // count at most 5
}
/**
* Invoked when mouse button is pressed on the NumberArrow.
* Does step and starts ArrowRunner.
*/
public void mousePressed(MouseEvent e)
{ dir = warrow.getArrow( e.getY() ); // get height of click to decide up or down
if ( enabled && dir!=0 )
{ stopThread(); // make sure!
setValue(waarde + dir*stap); // 1 or -1 times stap
tellListeners();
arwr = new ArrowRunner(this); // will kick as long as mouse is held
arwr.start();
}
// System.out.println("pressed, new value is "+waarde );
}
/**
* Invoked when mouse button is released over the NumberArrow.
* stops ArrowRunner.
*/
public void mouseReleased(MouseEvent e)
{ stopThread();
}
/**
* Invoked when mouse button is clicked on the NumberArrow.
* Does nothing.
*/
public void mouseClicked(MouseEvent e)
{ // do nothing
}
/**
* Invoked when mouse has entered the NumberArrow.
* Does nothing.
*/
public void mouseEntered(MouseEvent e)
{ // do nothing
}
/**
* Invoked when mouse has exited the NumberArrow.
* stops ArrowRunner.
*/
public void mouseExited(MouseEvent e)
{ stopThread();
}
// 2. 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) // dummy String from NumOnlyField
{ waarde = w;
// no need to do anything with arrows
tellListeners();
}
}