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

/**
 * This object stores geometry information, such as upper and lower
 * bounds, step and page increments, and the size of a page. Other
 * object use an Adjustment object to store their internal dimenstion
 * settings. It also is passed as an argument to specify geometry.
 * @see SpinButton
 * @see Range
 * @see HScrollBar
 * @see VScrollBar
 * @see HScale
 * @see VScale
 */
public class Adjustment extends GtkObject {

	/**
	 * The list of objects interested in focus events.
	 */
	private Vector adjustmentListeners = null;

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

	/**
	 * Constructs a new adjustment
	 * @param value The initial value.
	 * @param lower The minimum value.
	 * @param upper The maximum value.
	 * @param stepIncrement The step increment.
	 * @param pageIncrement The page increment.
	 * @param pageSize The page size.
	 */
	public Adjustment(double value,double lower,double upper,double stepIncrement,
		double pageIncrement,double pageSize) {
		super(gtk_adjustment_new(value, lower, upper, stepIncrement, pageIncrement, pageSize));
	}

	/**
	 * Sets the GtkAdjustment value.
	 */
	public void setValue(double value) {
		gtk_adjustment_set_value(getHandle(), value);
	}

	/**
	 * Gets the current value of the adjustment.
	 * @return The current value of the adjustment.
	 */
	public double getValue() {
		return gtk_adjustment_get_value(getHandle());
	}

	/**
	 * Updates the GtkAdjustment value to ensure that the range
	 * between lower and upper is in the current page (ie between
	 * value and value + pageSize). If the range is larger than
	 * the page size, then only the start of it will be in the
	 * current page. A "changed" signal will be emitted if the
	 * value is changed.
	 */
	public void clampPage(double lower, double upper) {
		gtk_adjustment_clamp_page(getHandle(), lower, upper);
	}

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

	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", AdjustmentEvent.Type.VALUE_CHANGED, AdjustmentListener.class);
		anEvtMap.addEvent("changed", "handleChanged", AdjustmentEvent.Type.CHANGED, AdjustmentListener.class);
	}

	/**
	 * Register an object to handle spin events.
	 * @see org.gnu.gtk.event.SpinListener
	 */
	public void addListener(AdjustmentListener listener) {
		// Don't add the listener a second time if it is in the Vector.
		int i = findListener(adjustmentListeners, listener);
		if (i == -1) {
			if (null == adjustmentListeners) {
				evtMap.initialize(this, AdjustmentEvent.Type.VALUE_CHANGED);
				evtMap.initialize(this, AdjustmentEvent.Type.CHANGED);
				adjustmentListeners = new Vector();
			}
			adjustmentListeners.addElement(listener);
		}
	}
	/**
	 * Removes a listener
	 * @see #addListener(AdjustmentListener)
	 */
	public void removeListener(AdjustmentListener listener) {
		int i = findListener(adjustmentListeners, listener);
		if (i > -1)
			adjustmentListeners.remove(i);
		if (0 == adjustmentListeners.size()) {
			evtMap.uninitialize(this, AdjustmentEvent.Type.VALUE_CHANGED);
			evtMap.uninitialize(this, AdjustmentEvent.Type.CHANGED);
			adjustmentListeners = null;
		}
	}

	protected void fireAdjustmentEvent(AdjustmentEvent event) {
		if (null == adjustmentListeners)
			return;
		int size = adjustmentListeners.size();
		int i = 0;
		while (i < size) {
			AdjustmentListener al = (AdjustmentListener)adjustmentListeners.elementAt(i);
			al.adjustmentEvent(event);
			i++;
		}
	}

	private void handleValueChanged() {
		fireAdjustmentEvent(new AdjustmentEvent(this, AdjustmentEvent.Type.VALUE_CHANGED));
	}
	
	public void handleChanged() {
		fireAdjustmentEvent(new AdjustmentEvent(this, AdjustmentEvent.Type.CHANGED));
	}

	/**
	 * Give us a way to locate a specific listener in a Vector.
	* @param list The Vector of listeners to search.
	* @param listener The object that is to be located in the Vector.
	* @return Returns the index of the listener in the Vector, or -1 if
	*                 the listener is not contained in the Vector.
	 */
	protected static int findListener(Vector list, Object listener) {
		if (null == list || null == listener)
			return -1;
		return list.indexOf(listener);
	}

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


	native static final protected double getLower(Handle cptr);
	native static final protected double getUpper(Handle cptr);
	native static final protected double getStepIncrement(Handle cptr);
	native static final protected double getPageIncrement(Handle cptr);
	native static final protected double getPageSize(Handle cptr);
	native static final protected int gtk_adjustment_get_type();
	native static final protected Handle gtk_adjustment_new(double value, double lower, double upper, double stepIncrement, double pageIncrement, double pageSize);
	native static final protected void gtk_adjustment_changed(Handle adjustment);
	native static final protected void gtk_adjustment_value_changed(Handle adjustment);
	native static final protected void gtk_adjustment_clamp_page(Handle adjustment, double lower, double upper);
	native static final protected double gtk_adjustment_get_value(Handle adjustment);
	native static final protected void gtk_adjustment_set_value(Handle adjustment, double value);

}
