/*
 * 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.atk.AtkObject;
import org.gnu.glib.EventMap;
import org.gnu.glib.EventType;
import org.gnu.glib.GObject;
import org.gnu.glib.Handle;
import org.gnu.gtk.event.ComboBoxEvent;
import org.gnu.gtk.event.ComboBoxListener;

/**
 * A widget used to choose from a list of items.
 */
public class ComboBox extends Bin implements CellLayout {
	
    private TreeViewRowSeparatorMethod separatorFunc = null;

	// for glade
	public ComboBox(Handle hndl) {
		super(hndl);
	}
	
	/**
	 * Construct a new ComboBox that will contain only Strings.
	 * If you need to include objects other than strings you must
	 * use the constructor that takes a TreeModel.  When you use
	 * this constructor you should use the appendText, insertText,
	 * prependText, and removeText methods to add or remove text
	 * from the comboBox.
	 */
	public ComboBox() {
		super(gtk_combo_box_new_text());
	}
	
	/**
	 * Create a new ComboBox with the provided model.  If you
	 * use this constructor you should not use the appendText,
	 * insertText, prependText, or removeText methods.  You should
	 * update the model when you need to change the values in the
	 * ComboBox. 
	 * @param model
	 */
	public ComboBox(TreeModel model) {
		super(gtk_combo_box_new_with_model(model.getHandle()));
	}
	
	/**
	 * Sets the wrap width of the ComboBox.  The wrap widgth is the
	 * preferred number of columns when you want the popup to be layed
	 * out in a table.
	 * @param width
	 */
	public void setWrapWidth(int width) {
		gtk_combo_box_set_wrap_width(getHandle(), width);
	}
	
	public int getWrapWidth() {
		return gtk_combo_box_get_wrap_width(getHandle());
	}
	
	/**
	 * Sets the row span for the ComboBox.  The row span is the 
	 * number of rows an item should span.
	 * @param rowSpan
	 */
	public void setRowSpanColumn(int rowSpan) {
		gtk_combo_box_set_row_span_column(getHandle(), rowSpan);
	}
	
	public int getRowSpanColumn() {
		return gtk_combo_box_get_row_span_column(getHandle());
	}
	
	/**
	 * Sets the column span for the ComboBox.  The column span
	 * is the number of columns an item should span.
	 * @param columnSpan
	 */	
	public void setColumnSpanColumn(int columnSpan) {
		gtk_combo_box_set_column_span_column(getHandle(), columnSpan);
	}
	
	public int getColumnSpanColumn() {
		return gtk_combo_box_get_column_span_column(getHandle());
	}
	
	public boolean getAddTearoffs() {
		return gtk_combo_box_get_add_tearoffs(getHandle());
	}
	
	public void setAddTearoffs(boolean addTearoffs) {
		gtk_combo_box_set_add_tearoffs(getHandle(), addTearoffs);
	}
	
	public boolean getFocusOnClick() {
		return gtk_combo_box_get_focus_on_click(getHandle());
	}
	
	public void setFocusOnClick(boolean focusOnClick) {
		gtk_combo_box_set_focus_on_click(getHandle(), focusOnClick);
	}
	
	/**
	 * Returns the index of the currently active item or -1 if there's
	 * no active item.
	 */
	public int getActive() {
		return gtk_combo_box_get_active(getHandle());
	}
	
	/**
	 * Sets the active item of the ComboBox to be index.
	 * @param index
	 */
	public void setActive(int index) {
		gtk_combo_box_set_active(getHandle(), index);
	}
	
	/**
	 * Returns a TreeIter that is pointing to the currently active
	 * item in the ComboBox.
	 */
	public TreeIter getActiveIter() {
		Handle iter = GObject.getNullHandle();
		boolean ret = gtk_combo_box_get_active_iter(getHandle(), iter);
		if (ret) {
                    return new TreeIter(iter, getModel());
		}
		return null;
	}
	
	/**
	 * Sets the current active item to the one referenced by iter.
	 * @param iter
	 */
	public void setActiveIter(TreeIter iter) {
		gtk_combo_box_set_active_iter(getHandle(), iter.getHandle());
	}
	
	/**
	 * Returns the TreeModel for the ComboBox.
	 */
	public TreeModel getModel() {
	    Handle hndl = gtk_combo_box_get_model(getHandle());
		GObject obj = getGObjectFromHandle(hndl);
		if (null != obj)
			return (TreeModel)obj;
		return new TreeModel(hndl);
	}
	
	/**
	 * Sets the model for the ComboBox.  
	 * @param model
	 */
	public void setModel(TreeModel model) {
		gtk_combo_box_set_model(getHandle(), model.getHandle());
	}
	
	/**
	 * Appends a string to the list of strings in the ComboBox.  This
	 * method can only be called if you created the ComboBox with the
	 * no-op constructor.
	 * @param text
	 */
	public void appendText(String text) {
		gtk_combo_box_append_text(getHandle(), text);
	}
	
	/**
	 * Inserts a string into the list of strings in the ComboBox at
	 * the provided position.  This method can only be called if you
	 * created the ComboBox with the no-op constructor.
	 * @param position
	 * @param text
	 */
	public void insertText(int position, String text) {
		gtk_combo_box_insert_text(getHandle(), position, text);
	}
	
	/**
	 * Prepends a string to the list of strings in the ComboBox.  This
	 * method can only be called if you created the ComboBox with the
	 * no-op constructor.
	 * @param text
	 */
	public void prependText(String text) {
		gtk_combo_box_prepend_text(getHandle(), text);
	}
	
	/**
	 * Removes the string at the provided position from the list of
	 * strings in the ComboBox.  This method can only be called if 
	 * you created the ComboBox with the no-op constructor.
	 * @param position
	 */
	public void removeText(int position) {
		gtk_combo_box_remove_text(getHandle(), position);
	}
	
	public String getActiveText() {
		return gtk_combo_box_get_active_text(getHandle());
	}
	
	/**
	 * Pops up the menu or dropdown list of the ComboBox.
	 */
	public void popup() {
		gtk_combo_box_popup(getHandle());
	}
	
	/**
	 * Hides the menu or dropdown list of the ComboBox.
	 */
	public void popdown() {
		gtk_combo_box_popdown(getHandle());
	}
	
	public AtkObject getPopupAccessible() {
		return new AtkObject(gtk_combo_box_get_popup_accessible(getHandle()));
	}

    //
    // CellLayout interface implementation.
    //

    /**
     * Packs the cell into the beginning of the CellLayout. If expand is
     * FALSE, then the cell is allocated no more space than it
     * needs. Any unused space is divided evenly between cells for
     * which expand is TRUE.
     */
    public void packStart( CellRenderer renderer, boolean expand ) {
        CellLayoutHelper.packStart( this, renderer, expand );
    }
    /**
     * Adds the cell to the end of the CellLayout. If expand is FALSE,
     * then the cell is allocated no more space than it needs. Any
     * unused space is divided evenly between cells for which expand is
     * TRUE.
     */
    public void packEnd( CellRenderer renderer, boolean expand ) {
        CellLayoutHelper.packEnd( this, renderer, expand );
    }
    /**
     * Unsets all the mappings on all renderers on the CellLayout and
     * removes all renderers from the CellLayout.
     */
    public void clear() {
        CellLayoutHelper.clear( this );
    }
    /**
     * Adds an attribute mapping to the list in the CellLayout. The
     * column is the column of the model to get a value from, and the
     * attribute is the parameter on cell to be set from the value.
     */
    public void addAttributeMapping( CellRenderer renderer, 
                                     CellRendererAttribute attribute,
                                     DataColumn column ) {
        CellLayoutHelper.addAttributeMapping( this, renderer, 
                                              attribute, column );
    }
    /**
     * Clears all existing attributes previously set with
     * {@link #addAttributeMapping}.
     */
    public void clearAttributeMappings( CellRenderer renderer ) {
        CellLayoutHelper.clearAttributeMappings( this, renderer );
    }
    /**
     * Re-inserts the renderer at position. Note that the renderer has 
     * already to be packed into the CellLayout for this to function properly.
     */
    public void reorder( CellRenderer renderer, int position ) {
        CellLayoutHelper.reorder( this, renderer, position );
    }

    /**
     * Set the object which is used to determine whether a row should be drawn
     * as a separator.
     *
     * @param method The <tt>TreeViewRowSeparatorMethod</tt> or
     * <tt>null</tt> if no separators are to be drawn (the default).
     */
    public void setRowSeparatorMethod( TreeViewRowSeparatorMethod method ) {
        separatorFunc = method;
        if ( method == null ) {
            gtk_combo_box_set_row_separator_func( getHandle(), null,
                                                  "handleSeparatorFunc" );
        } else {
            gtk_combo_box_set_row_separator_func( getHandle(), this,
                                                  "handleSeparatorFunc" );
        }
    }

    protected boolean handleSeparatorFunc( Handle model, Handle iter ) {
        TreeModel mod = (TreeModel)getGObjectFromHandle(model);
        TreeIter it = new TreeIter( iter, mod );
        return separatorFunc.isSeparator( mod, it );
    }

    /**
     * Get the object which is used to determine whether a row should be
     * drawn as a separator.
     *
     * @return The <tt>TreeViewRowSeparatorMethod</tt> or <tt>null</tt>
     * if no method has been set.
     */
    public TreeViewRowSeparatorMethod getRowSeparatorMethod() {
        return separatorFunc;
    }

	/* **************************************
	 * EVENT LISTENERS
	 ****************************************/

	/**
	 * Listeners for handling ComboBox events
	 */
	private Vector cbListeners = null;

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

	protected void fireComboBoxEvent(ComboBoxEvent event) {
		if (null == cbListeners) {
			return;
		}
		int size = cbListeners.size();
		int i = 0;
		while (i < size) {
			ComboBoxListener cbl = (ComboBoxListener)cbListeners.elementAt(i);
			cbl.comboBoxEvent(event);
			i++;
		}
	}

	private void handleChanged() {
		fireComboBoxEvent(new ComboBoxEvent(this, ComboBoxEvent.Type.CHANGED));
	}

	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("changed", "handleChanged", ComboBoxEvent.Type.CHANGED, ComboBoxListener.class);
	}


	native static final protected int gtk_combo_box_get_type ();
	native static final protected Handle gtk_combo_box_new();
	native static final protected Handle gtk_combo_box_new_with_model(Handle model);
	native static final protected int gtk_combo_box_get_wrap_width(Handle combo);
	native static final protected void gtk_combo_box_set_wrap_width(Handle combo, int width);
	native static final protected int gtk_combo_box_get_row_span_column(Handle combo);
	native static final protected void gtk_combo_box_set_row_span_column(Handle box, int rowSpan);
	native static final protected int gtk_combo_box_get_column_span_column(Handle combo); 
	native static final protected void gtk_combo_box_set_column_span_column(Handle box, int columnSpan);
	native static final protected boolean gtk_combo_box_get_add_tearoffs(Handle combo);
	native static final protected void gtk_combo_box_set_add_tearoffs(Handle combo, boolean addTearoffs);
	native static final protected boolean gtk_combo_box_get_focus_on_click(Handle combo);
	native static final protected void gtk_combo_box_set_focus_on_click(Handle combo, boolean focusOnclick);
	native static final protected int gtk_combo_box_get_active(Handle combo);
	native static final protected void gtk_combo_box_set_active(Handle combo, int index);
	native static final protected boolean gtk_combo_box_get_active_iter(Handle combo, Handle iter);
	native static final protected void gtk_combo_box_set_active_iter(Handle combo, Handle iter);
	native static final protected void gtk_combo_box_set_model(Handle combo, Handle model);
	native static final protected Handle gtk_combo_box_get_model(Handle combo);
	native static final protected Handle gtk_combo_box_new_text();
	native static final protected void gtk_combo_box_append_text(Handle combo, String text);
	native static final protected void gtk_combo_box_insert_text(Handle combo, int position, String text);
	native static final protected void gtk_combo_box_prepend_text(Handle combo, String text);
	native static final protected void gtk_combo_box_remove_text(Handle combo, int position);
	native static final protected String gtk_combo_box_get_active_text(Handle combo);
	native static final protected void gtk_combo_box_popup(Handle combo);
	native static final protected void gtk_combo_box_popdown(Handle combo);
	native static final protected Handle gtk_combo_box_get_popup_accessible(Handle combo);

    native static final private void gtk_combo_box_set_row_separator_func(Handle combo_box, ComboBox box, String callback);
    /* Not implemented. Use getRowSeparatorMethod().
    native static final private int gtk_combo_box_get_row_separator_func(Handle combo_box);
    */
}
