/*
 * Java-Gnome Bindings Library
 *
 * Copyright 1998-2004 the Java-Gnome Team, all rights reserved.
 *
 * The Java-Gnome bindings library is free software distributed under
 * the terms of the GNU Library General Public License version 2.
 */

package org.gnu.gtk;
import java.util.Vector;

import org.gnu.glib.EventMap;
import org.gnu.glib.EventType;
import org.gnu.glib.GObject;
import org.gnu.glib.Handle;
import org.gnu.glib.Type;
import org.gnu.gtk.event.SpinEvent;
import org.gnu.gtk.event.SpinListener;

/**
 * A SpinButton widget displays a single numeric value that you can
 * change by using the two arrow buttons to its right, or by editing
 * the number directly.
 */
public class SpinButton extends Entry {

	/**
	 * Creates a new SpinButton
	 * @param adjustment The {@link Adjustment} object that this spin button 
	 * should use.
	 * @param climbRate Specifies how much the spin button changes when an 
	 * arrow is clicked on.
	 * @param digits The number of decimal places to display.
	 */
	public SpinButton(Adjustment adjustment, double climbRate, int digits) {
		super(gtk_spin_button_new(adjustment.getHandle(), climbRate, digits));
	}

	/**
	 * Creates a new SpinButton without manually creating an adjustment. 
	 * The value is initially set to the minimum value and a page increment of 
	 * 10 * step is the default. The precision of the spin button is 
	 * equivalent to the precision of step.
	 * @param min  Minimum allowable value
	 * @param max Maximum allowable value
	 * @param step Increment added or subtracted by spinning the widget
	 */
	public SpinButton(double min, double max, double step) {
		super(gtk_spin_button_new_with_range(min, max, step));
	}

	/**
	 * Construct a new SpinButton from a handle to a native resource.
	 */
	public SpinButton(Handle handle) {
		super(handle);
	}

	/**
	 * Changes the properties of an existing spin button. The adjustment, 
	 * climb rate, and number of decimal places are all changed accordingly, 
	 * after this function call.
	 * @param adjustment An {@link Adjustment} object to use.
	 * @param climbRate The new climb rate.
	 * @param digits The number of decimal places to display in the spin button.
	 */
	public void configure(Adjustment adjustment, double climbRate, int digits) {
		gtk_spin_button_configure(getHandle(), adjustment.getHandle(), climbRate, digits);
	}

	/**
	 * Replaces the adjustment object associated with the Spin button
	 * @param adjustment New adjustment object to use
	 */
	public void setAdjustment(Adjustment adjustment) {
		gtk_spin_button_set_adjustment(getHandle(), adjustment.getHandle());
	}

	/**
	 * Returns the Adjustment ojbect used with this object.
	 * @return the adjustment object for this SpinButton
	 */
	public Adjustment getAdjustment() {
	    Handle hndl = gtk_spin_button_get_adjustment(getHandle());
		GObject obj = getGObjectFromHandle(hndl);
		if (null != obj)
			return (Adjustment)obj;
		return new Adjustment(hndl);
	}

	/**
	 * Set the precision to be displayed. Up to 20 digit precision is allowed.
	 * @param digits Number of digits to be displayed
	 */
	public void setPrecision(int digits) {
		gtk_spin_button_set_digits(getHandle(), digits);
	}

	/**
	 * Sets the step and page increments. This affects how quickly the value
	 * changes when the spin button's arrows are activated.
	 * @param step Increment applied for a button 1 press.
	 * @param page Increment applied for a button 2 press.
	 */
	public void setIncrements(double step, double page) {
		gtk_spin_button_set_increments(getHandle(), step, page);
	}

	/**
	 * Sets the minimum and maximum allowable values
	 * @param min Minimum allowable value.
	 * @param max Maximum allowable value.
	 */
	public void setRange(double min, double max) {
		gtk_spin_button_set_range(getHandle(), min, max);
	}

	/**
	 * Returns the current value, as an integer.
	 * The value is stored as a double - this method converts it to an integer.
	 * To get the raw double value, use {@link #getValue()}.
	 * @return Current value of SpinButton, as an integer
	 */
	public int getIntValue() {
		return gtk_spin_button_get_value_as_int(getHandle());
	}

	/**
	 * Sets the value of the spin button
	 * @param value The value for the SpinButton
	 */
	public void setValue(double value) {
		gtk_spin_button_set_value(getHandle(), value);
	}

	/**
	 * Sets the update behavior of a spin button. This determines whether the 
	 * spin button is always updated or only when a valid value is set.
	 * @param policy An update Policy
	 */
	public void setUpdatePolicy(SpinButtonUpdatePolicy policy) {
		gtk_spin_button_set_update_policy(getHandle(), policy.getValue());
	}

	/**
	 * Increment or decrement a spin button's value in a specified direction 
	 * by a specified amount.
	 * @param direction A {@link SpinType} indicating the direction to spin.
	 * @param increment Step increment to apply in the specified direction.
	 */
	public void spin(SpinType direction, double increment) {
		gtk_spin_button_spin(getHandle(), direction.getValue(), increment);
	}

	/**
	 * Sets the flag that determines if a spin button value wraps around to 
	 * the opposite limit when the upper or lower limit of the range is 
	 * exceeded.
	 * @param wrap If true, the value will wrap
	 */
	public void setWrap(boolean wrap) {
		gtk_spin_button_set_wrap(getHandle(), wrap);
	}

	/**
	 * Sets the policy as to whether values are corrected to the nearest 
	 * step increment when a spin button is activated after providing an 
	 * invalid value.
	 * @param snapToTicks A flag indicating if invalid values should be corrected.
	 */
	public void setSnap(boolean snapToTicks) {
		gtk_spin_button_set_snap_to_ticks(getHandle(), snapToTicks);
	}

	/**
	 * Get the value of the SpinButton
	 * @return The current value
	 */
	public double getValue() {
		return gtk_spin_button_get_value(getHandle());
	}

	/**
	 * Sets the flag that determines if non-numeric text can be typed
	 * in the spin button.
	 * @param numeric Set the flag to true or false.
	 */
	public void setNumeric(boolean numeric) {
		gtk_spin_button_set_numeric(getHandle(), numeric);
	}

	/****************************************
	 * Event Handling
	 ****************************************/

	/** Listeners for handling Spin events */
	private Vector spinListeners = null;

	/**
	 * Register an object to handle spin events.
	 * @see org.gnu.gtk.event.SpinListener
	 */
	public void addListener(SpinListener listener) {
		// Don't add the listener a second time if it is in the Vector.
		int i = findListener(spinListeners, listener);
		if (i == -1) {
			if (null == spinListeners) {
				evtMap.initialize(this, SpinEvent.Type.VALUE_CHANGED);
				spinListeners = new Vector();
			}
			spinListeners.addElement(listener);
		}
	}

	/**
	 * Removes a listener
	 * @see #addListener(SpinListener)
	 */
	public void removeListener(SpinListener listener) {
		int i = findListener(spinListeners, listener);
		if (i > -1)
			spinListeners.remove(i);
		if (0 == spinListeners.size()) {
			evtMap.uninitialize(this, SpinEvent.Type.VALUE_CHANGED);
			spinListeners = null;
		}
	}

	protected void fireSpinEvent(SpinEvent event) {
		if (null == spinListeners)
			return;
		int size = spinListeners.size();
		int i = 0;
		while (i < size) {
			SpinListener sl = (SpinListener)spinListeners.elementAt(i);
			sl.spinEvent(event);
			i++;
		}
	}

	private void handleValueChanged() {
		fireSpinEvent(new SpinEvent(this));
	}

	public Class getEventListenerClass(String signal) {
		Class cls = evtMap.getEventListenerClass(signal);
		if (cls == null) cls = super.getEventListenerClass(signal);
		return cls;
	}

	public EventType getEventType(String signal) {
		EventType et = evtMap.getEventType(signal);
		if (et == null) et = super.getEventType(signal);
		return et;
	}

	private static EventMap evtMap = new EventMap();
	static {
		addEvents(evtMap);
	}

	private static void addEvents(EventMap anEvtMap) {
		anEvtMap.addEvent("value_changed", "handleValueChanged", SpinEvent.Type.VALUE_CHANGED, SpinListener.class);
	}

	/**
	 * Retrieve the runtime type used by the GLib library.
	 */
	public static Type getType() {
		return new Type(gtk_spin_button_get_type());
	}

	native static final protected int gtk_spin_button_get_type();
	native static final protected void gtk_spin_button_configure(Handle spin_button,Handle adjustment,double climbRate,int digits);
	native static final protected Handle gtk_spin_button_new(Handle adjustment, double climbRate, int digits);
	native static final protected Handle gtk_spin_button_new_with_range(double min, double max, double step);
	native static final protected void gtk_spin_button_set_adjustment(Handle spin_button, Handle adjustment);
	native static final protected Handle gtk_spin_button_get_adjustment(Handle spin_button);
	native static final protected void gtk_spin_button_set_digits(Handle spin_button, int digits);
	native static final protected int gtk_spin_button_get_digits(Handle spin_button);
	native static final protected void gtk_spin_button_set_increments(Handle spin_button, double step, double page);
	native static final protected void gtk_spin_button_get_increments(Handle spin_button, double[] step, double[] page);
	native static final protected void gtk_spin_button_set_range(Handle spin_button, double min, double max);
	native static final protected void gtk_spin_button_get_range(Handle spin_button, double[] min, double[] max);
	native static final protected double gtk_spin_button_get_value(Handle spin_button);
	native static final protected int gtk_spin_button_get_value_as_int(Handle spin_button);
	native static final protected void gtk_spin_button_set_value(Handle spin_button, double value);
	native static final protected void gtk_spin_button_set_update_policy(Handle spin_button, int policy);
	native static final protected int gtk_spin_button_get_update_policy(Handle spin_button);
	native static final protected void gtk_spin_button_set_numeric(Handle spin_button, boolean numeric);
	native static final protected boolean gtk_spin_button_get_numeric(Handle spin_button);
	native static final protected void gtk_spin_button_spin(Handle spin_button, int direction, double increment);
	native static final protected void gtk_spin_button_set_wrap(Handle spin_button, boolean wrap);
	native static final protected boolean gtk_spin_button_get_wrap(Handle spin_button);
	native static final protected void gtk_spin_button_set_snap_to_ticks(Handle spin_button, boolean snapToTicks);
	native static final protected boolean gtk_spin_button_get_snap_to_ticks(Handle spin_button);
	native static final protected void gtk_spin_button_update(Handle spin_button);
}
