/*
 * 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.Type;
import org.gnu.gtk.event.SpinEvent;
import org.gnu.gtk.event.SpinListener;
import org.gnu.glib.Handle;

/**
 * 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);
    }

    /**
     * Internal static factory method to be used by Java-Gnome only.
     */
    public static SpinButton getSpinButton(Handle handle) {
        if (handle == null)
            return null;

        SpinButton obj = (SpinButton) getGObjectFromHandle(handle);
        if (obj == null)
            obj = new SpinButton(handle);

        return obj;
    }

    /**
     * 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 object used with this object.
     * 
     * @return the adjustment object for this SpinButton
     */
    public Adjustment getAdjustment() {
        Handle hndl = gtk_spin_button_get_adjustment(getHandle());
        return Adjustment.getAdjustment(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);
}
