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

/**
 * The TreeSelection object is a helper object to manage the selection for a 
 * {@link TreeView} widget. The TreeSelection object is automatically created 
 * when a newTreeView widget is created, and cannot exist independentally of 
 * this widget. The primary reason the TreeSelection objects exists is for 
 * cleanliness of code and API. That is, there is no conceptual reason all 
 * these functions could not be methods on the TreeView widget instead of a 
 * separate function.
 *
 * <p>The TreeSelection object is gotten from a TreeView by calling 
 * {@link TreeView#getSelection()}. It can be manipulated to check the 
 * selection status of the tree, as well as select and deselect individual 
 * rows. Selection is done completely view side. As a result, multiple views 
 * of the same model can have completely different selections. Additionally, 
 * you cannot change the selection of a row on the model that is not currently 
 * displayed by the view without expanding its parents first.
 *
 * <p>One of the important things to remember when monitoring the selection 
 * of a view is that the "changed" event is mostly a hint. That is, it may only 
 * emit one signal when a range of rows is selected. Additionally, it may on 
 * occasion emit a "changed" event when nothing has happened (mostly as a 
 * result of programmers calling selectRow on an already selected row).
 * @see TreeView
 */
public class TreeSelection extends GObject {
	/**
	 * constructs a new selection using handle from a native function..
	 */
	protected TreeSelection(Handle handle) {
		super(handle);
	}

	/**
	 * Calls the forEach method of the specified class for every selected value.
	 */
	public void forEachSelected(TreeSelectionForEach implementor) {
		curForEach = implementor;
		startForEach(getHandle(), this);
	}

	private TreeSelectionForEach curForEach;
	
	/** This is called from the jni code to operate the foreach method */
	private void doEach(Handle model, Handle path, Handle iter) {
		TreeModel m; 
		GObject obj = getGObjectFromHandle(model);
		if (null != obj)
			m = (TreeModel)obj;
		else
			m = new TreeModel(model);
		curForEach.forEach( m, new TreePath(path), new TreeIter(iter, m));
	}

	/**
	 * Sets the selection mode of the selection. If the previous type was 
	 * SelectionMode.MULTIPLE, then the anchor is kept selected, if it was 
	 * previously selected.
	 * @param mode The selection mode to use.
	 */
	public void setMode(SelectionMode mode) {
		gtk_tree_selection_set_mode(getHandle(), mode.getValue());
	}
	
	public SelectionMode getMode() {
		return SelectionMode.intern(gtk_tree_selection_get_mode(getHandle()));
	}

	/**
	 * Returns the {@link TreeView} associated with the selection.
	 * @return Associated widget
	 */
	public TreeView getTreeView() {
	    Handle hndl = gtk_tree_selection_get_tree_view(getHandle());
		GObject obj = getGObjectFromHandle(hndl);
		if (null != obj)
			return (TreeView)obj;
		return new TreeView(hndl);
	}

	/**
	 * Select the row at path.
	 * @param path The path to be selected.
	 */
	public void select(TreePath path) {
		gtk_tree_selection_select_path(getHandle(), path.getHandle());
	}

	/**
	 * Unselects the row at path.
	 * @param path The path to be deselected
	 */
	public void unselect(TreePath path) {
		gtk_tree_selection_unselect_path(getHandle(), path.getHandle());
	}

	/**
	 * Returns TRUE if the row pointed to by path is currently selected. If 
	 * path does not point to a valid location, FALSE is returned
	 * @param path A patch to check the selection on
	 * @return True if the path is selected.
	 */
	public boolean getSelected(TreePath path) {
		return gtk_tree_selection_path_is_selected(getHandle(), path.getHandle());
	}

	/**
	 * Selects the specified iterator.
	 * @param iter The {@link TreeIter} to be selected.
	 */
	public void select(TreeIter iter) {
		gtk_tree_selection_select_iter(getHandle(), iter.getHandle());
	}

	/**
	 * Unselects the specified iterator.
	 * @param iter The {@link TreeIter} to be unselected.
	 */
	public void unselect(TreeIter iter) {
		gtk_tree_selection_unselect_iter(getHandle(), iter.getHandle());
	}

	/**
	 * Returns TRUE if the row pointed to by iter is currently selected.
	 * @param iter The iter to test the selection of
	 * @return true if the iter is selected.
	 */
	public boolean getSelected(TreeIter iter) {
		return gtk_tree_selection_iter_is_selected(getHandle(), iter.getHandle());
	}

	/**
	 * Selects all the nodes. Mode must be set to SelectionMode.MULTIPLE
	 */
	public void selectAll() {
		gtk_tree_selection_select_all(getHandle());
	}

	/**
	 * Unselects all the nodes.
	 */
	public void unselectAll() {
		gtk_tree_selection_unselect_all(getHandle());
	}

	/**
	 * Selects a range of nodes, determined by startPath and endPath inclusive.
	 * @param startPath The initial node of the range.
	 * @param endPath The final node of the range.
	 */
	public void select(TreePath startPath, TreePath endPath) {
		gtk_tree_selection_select_range(getHandle(), startPath.getHandle(), endPath.getHandle());
	}

	public void unselect(TreePath startPath, TreePath endPath) {
		gtk_tree_selection_unselect_range(getHandle(), startPath.getHandle(), endPath.getHandle());
	}

	/**
	 * Returns an array of rows which are currently selected
	 * @since 2.2
	 */
	public TreePath[] getSelectedRows(){
	    Handle[] hndls = gtk_tree_selection_get_selected_rows(getHandle());
		if (null == hndls)
			return null;
		TreePath[] paths = new TreePath[hndls.length];
		for (int i = 0; i < hndls.length; i++) {
			paths[i] = new TreePath(hndls[i]);
		}
		return paths;
	}

	/**
	 * Returns the number of rows that have been selected.
	 * @since 2.2
	 */
	public int countRows(){
		return gtk_tree_selection_count_selected_rows(getHandle());
	}

	///////////////////////////////////////////////////////////////////////////////
	// EVENTS - the Changed signal.
	//
	/** listeners for selection changing */
	private Vector selectionListeners = null;

	/** 
	 * Register an object to handle Selection change events which are emitted
	 * whenever the selection has (possibly) changed
	 * @see org.gnu.gtk.event.TreeSelectionListener
	 */
	public void addListener(TreeSelectionListener listener) {
		// Don't add the listener a second time if it is in the Vector.
		int i = Widget.findListener(selectionListeners, listener);
		if (i == -1) {
			if (null == selectionListeners) {
				evtMap.initialize(this, TreeSelectionEvent.Type.CHANGED);
				selectionListeners = new Vector();
			}
			selectionListeners.addElement(listener);
		}
	}

	/**
	 * Removes a listener
	 * @see #addListener(TreeSelectionListener)
	 */
	public void removeListener(TreeSelectionListener listener) {
		int i = Widget.findListener(selectionListeners, listener);
		if (i > -1) {
			selectionListeners.remove(i);
		}
		if (0 == selectionListeners.size()) {
			evtMap.uninitialize(this, TreeSelectionEvent.Type.CHANGED);
			selectionListeners = null;
		}
	}

	protected void fireSelectionEvent(TreeSelectionEvent event) {
		if (null == selectionListeners) {
			return;
		}
		int size = selectionListeners.size();
		int i = 0;
		while (i < size) {
			TreeSelectionListener bl = (TreeSelectionListener)selectionListeners.elementAt(i);
			bl.selectionChangedEvent(event);
			i++;
		}
	}
	
	private void handleChanged() {
		fireSelectionEvent(new TreeSelectionEvent(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 evtMap) {
	    evtMap.addEvent("changed", "handleChanged",
			    TreeSelectionEvent.Type.CHANGED,
			    TreeSelectionListener.class);
	}

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

        native static final private void startForEach(Handle handle, Object o);
	native static final protected int gtk_tree_selection_get_type();
	native static final protected void gtk_tree_selection_set_mode(Handle selection, int type);
	native static final protected int gtk_tree_selection_get_mode(Handle selection);
//	native static final protected int gtk_tree_selection_get_user_data(Handle selection);
	native static final protected Handle gtk_tree_selection_get_tree_view(Handle selection);
//	native static final protected int gtk_tree_selection_get_selected(Handle selection);
	native static final protected Handle[] gtk_tree_selection_get_selected_rows(Handle selection);
	native static final protected int gtk_tree_selection_count_selected_rows(Handle selection);
	native static final protected void gtk_tree_selection_select_path(Handle selection, Handle path);
	native static final protected void gtk_tree_selection_unselect_path(Handle selection, Handle path);
	native static final protected void gtk_tree_selection_select_iter(Handle selection, Handle iter);
	native static final protected void gtk_tree_selection_unselect_iter(Handle selection, Handle iter);
	native static final protected boolean gtk_tree_selection_path_is_selected(Handle selection, Handle path);
	native static final protected boolean gtk_tree_selection_iter_is_selected(Handle selection, Handle iter);
	native static final protected void gtk_tree_selection_select_all(Handle selection);
	native static final protected void gtk_tree_selection_unselect_all(Handle selection);
	native static final protected void gtk_tree_selection_select_range(Handle selection, Handle startPath, Handle endPath);
	native static final protected void gtk_tree_selection_unselect_range(Handle selection, Handle startPath, Handle endPath);
}
