/*
 * 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.RangeEvent;
import org.gnu.gtk.event.RangeListener;

/**
 * This is the base class for the group of widgets that involve graphic
 * display and manipulation of a vale bounded within a specific
 * minumumand maximum value. 
 * @see HScale
 * @see HScrollBar
 * @see VScale
 * @see VScrollBar
 */
public abstract class Range extends Widget {

	protected Range(Handle handle) {
		super(handle);
	}

	/**
	 * Get the {@link Adjustment} which is the "model" object for Range. 
	 * @return The adjustment model
	 */
	public Adjustment getAdjustment() {
	    Handle hndl = gtk_range_get_adjustment(getHandle());
		GObject obj = getGObjectFromHandle(hndl);
		if (null != obj)
			return (Adjustment)obj;
		return new Adjustment(hndl);
	}

	/**
	 * Sets the update policy for the Adjustment
	 */
	public void setUpdatePolicy(UpdateType policy) {
		gtk_range_set_update_policy(getHandle(), policy.getValue());
	}

	/**
	 * Sets the adjustment to be used as the "model" object for this range 
	 * widget. The adjustment indicates the current range value, the minimum 
	 * and maximum range values, the step/page increments used for 
	 * keybindings and scrolling, and the page size. The page size is 
	 * normally 0 for {@link Scale} and nonzero for {@link ScrollBar}, and 
	 * indicates the size of the visible area of the widget being scrolled. 
	 * The page size affects the size of the scrollbar slider.
	 * @param adjustment The model to use
	 */
	public void setAdjustment(Adjustment adjustment) {
		gtk_range_set_adjustment(getHandle(), adjustment.getHandle());
	}

	/**
	 * Ranges normally move from lower to higher values as the slider moves 
	 * from top to bottom or left to right. Inverted ranges have higher values 
	 * at the top or on the right rather than on the bottom or left.
	 * @param setting TRUE to invert the range
	 */
	public void setInverted(boolean setting) {
		gtk_range_set_inverted(getHandle(), setting);
	}

	/**
	 * Gets the vaue set by {@link #setInverted(boolean)}.
	 * @return True of the slider is inverted
	 */
	public boolean getInverted() {
		return gtk_range_get_inverted(getHandle());
	}

	/**
	 * Gets the update policy
	 * @return The current update policy
	 */
	public UpdateType getUpdatePolicy() {
		return UpdateType.intern(gtk_range_get_update_policy(getHandle()));
	}

	/**
	 * Gets the current value of the range.
	 * @return Current value of the range.
	 */
	public double getValue() {
		return gtk_range_get_value(getHandle());
	}

	/**
	 * Sets the step and page sizes for the range. The step size is used 
	 * when the user clicks the {@link ScrollBar} arrows or moves 
	 * {@link Scale} via arrow keys. The page size is used for example when 
	 * moving via Page Up or Page Down keys.
	 * @param step Step size
	 * @param page Page size
	 */
	public void setIncrements(double step, double page) {
		gtk_range_set_increments(getHandle(), step, page);
	}

	/**
	 * Sets the allowable values in the Range, and clamps the range value to 
	 * be between min and max. (If the range has a non-zero page size, it is 
	 * clamped between min and max - page-size.)
	 * @param min Minimum range value
	 * @param max Maximum range value
	 */
	public void setRange(double min, double max) {
		gtk_range_set_range(getHandle(), min, max);
	}

	/**
	 * Sets the current value of the range; if the value is outside the 
	 * minimum or maximum range values, it will be clamped to fit inside them. 
	 * An event is called if the value changes.
	 * @param value The new value of the range
	 */
	public void setValue(double value) {
		gtk_range_set_value(getHandle(), value);
	}

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

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

	/**
	 * Register an object to handle range events.
	 * @see org.gnu.gtk.event.RangeListener
	 */
	public void addRangeListener(RangeListener listener) {
	    addListener(listener);
	}
	/**
	 * Removes a listener
	 * @see #addRangeListener(RangeListener)
	 */
	public void removeRangeListener(RangeListener listener) {
		int i = findListener(rangeListeners, listener);
		if (i > -1)
			rangeListeners.remove(i);
		if (0 == rangeListeners.size()) {
			evtMap.initialize(this, RangeEvent.Type.VALUE_CHANGED);
			rangeListeners = null;
		}
	}

	protected void fireRangeEvent(RangeEvent event) {
		if (null == rangeListeners)
			return;
		int size = rangeListeners.size();
		int i = 0;
		while (i < size) {
			RangeListener sl = (RangeListener)rangeListeners.elementAt(i);
			sl.rangeEvent(event);
			i++;
		}
	}

	private void handleValueChanged() {
		fireRangeEvent(new RangeEvent(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);
	}

	/**
	* Implementation method to build an EventMap for this widget class.
	* Not useful (or supported) for application use.
	*/
	private static void addEvents(EventMap anEvtMap) {
		anEvtMap.addEvent("value_changed", "handleValueChanged", RangeEvent.Type.VALUE_CHANGED, RangeListener.class);
	}

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

	native static final protected int gtk_range_get_type();
	native static final protected void gtk_range_set_update_policy(Handle range, int policy);
	native static final protected int gtk_range_get_update_policy(Handle range);
	native static final protected void gtk_range_set_adjustment(Handle range, Handle adjustment);
	native static final protected Handle gtk_range_get_adjustment(Handle range);
	native static final protected void gtk_range_set_inverted(Handle range, boolean setting);
	native static final protected boolean gtk_range_get_inverted(Handle range);
	native static final protected void gtk_range_set_increments(Handle range, double step, double page);
	native static final protected void gtk_range_set_range(Handle range, double min, double max);
	native static final protected void gtk_range_set_value(Handle range, double value);
	native static final protected double gtk_range_get_value(Handle range);

}
