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

/**
 * 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(int handle) {
		super(handle);
		initializeEventHandlers();
	}

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

	/**
	 * 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 (i.e. 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
	 ****************************************/

	/**
	 * Initialize this widget to receive its' own events so it can
	 * propogate them to the listeners.
	 */
	protected void initializeEventHandlers() {
		if (!eventsInitialized) {
			evtMap.initialize(this);
			eventsInitialized = true;
		}
	}

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

	public GtkEventType getEventType(String signal) {
		GtkEventType 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)
				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())
			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());
	}

	/****************************************
	 * BEGINNING OF JNI CODE
	 ****************************************/
	native static final protected double getLower(int cptr);
	native static final protected double getUpper(int cptr);
	native static final protected double getStepIncrement(int cptr);
	native static final protected double getPageIncrement(int cptr);
	native static final protected double getPageSize(int cptr);
	native static final protected int gtk_adjustment_get_type();
	native static final protected int gtk_adjustment_new(double value, double lower, double upper, double stepIncrement, double pageIncrement, double pageSize);
	native static final protected void gtk_adjustment_changed(int adjustment);
	native static final protected void gtk_adjustment_value_changed(int adjustment);
	native static final protected void gtk_adjustment_clamp_page(int adjustment, double lower, double upper);
	native static final protected double gtk_adjustment_get_value(int adjustment);
	native static final protected void gtk_adjustment_set_value(int adjustment, double value);
	/****************************************
	 * END OF JNI CODE
	 ****************************************/
}
