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

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

    /**
     * Internal static factory method to be used by Java-Gnome only.
     */
    public static ComboBox getComboBox(Handle handle) {
        if (handle == null) {
            return null;
        }

        ComboBox obj = (ComboBox) GObject.getGObjectFromHandle(handle);

        if (obj == null) {
            obj = new ComboBox(handle);
        }

        return obj;
    }

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

    /**
     * Creates a new ComboBox. If <code>model</code> is not null, the model of
     * the ComboBox is initialized to the supplied one. Otherwise an empty
     * ComboBox is created.
     * <p>
     * 
     * 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
     *            A TreeModel or null.
     */
    public ComboBox(TreeModel model) {
        super((model == null) ? gtk_combo_box_new()
                : gtk_combo_box_new_with_model(model.getHandle()));
    }

    /**
     * Sets the wrap width of the ComboBox. The wrap width 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 iterHandle = gtk_combo_box_get_active_iter(getHandle());
        return TreeIter.getTreeIter(iterHandle, getModel());
    }

    /**
     * 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());
        return TreeModel.getTreeModel(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 = TreeIter.getTreeIter(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 Handle gtk_combo_box_get_active_iter(
            Handle combo);

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