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

/**
 * The TreeViewColumn object is a visible column in a {@link TreeView} widget,
 * which is the base widget for all Tree, list and table widgets.
 */
public class TreeViewColumn extends GObject {
    /**
     * Creates a new Column
     */
    public TreeViewColumn() {
        super(gtk_tree_view_column_new());
    }

    /**
     */
    protected TreeViewColumn(Handle handle) {
        super(handle);
    }

    /**
     * Internal static factory method to be used by Java-Gnome only.
     */
    protected static TreeViewColumn getTreeViewColumn(Handle handle) {
        if (handle == null)
            return null;

        TreeViewColumn obj = (TreeViewColumn) getGObjectFromHandle(handle);
        if (obj == null)
            obj = new TreeViewColumn(handle);

        return obj;
    }

    /**
     * Packs the cell into the beginning column. If expand is TRUE, then the
     * cell is allocated a share of all available space that the column has.
     * 
     * @param cell
     *            The CellRenderer.
     * @param expand
     *            TRUE if cell is to be given extra space allocated to box.
     */
    public void packStart(CellRenderer cell, boolean expand) {
        gtk_tree_view_column_pack_start(getHandle(), cell.getHandle(), expand);
    }

    /**
     * Packs the cell into the column. If expand is TRUE, then the cell is
     * allocated a share of all available space that the column has.
     * 
     * @param cell
     *            The CellRenderer.
     * @param expand
     *            TRUE if cell is to be given extra space allocated to box.
     */
    public void packEnd(CellRenderer cell, boolean expand) {
        gtk_tree_view_column_pack_end(getHandle(), cell.getHandle(), expand);
    }

    /**
     * Removes all the renderers from the column.
     */
    public void clear() {
        gtk_tree_view_column_clear(getHandle());
    }

    /**
     * Returns an array of CellRenderers in the column in no particular order.
     */
    public CellRenderer[] getCellRenderers() {
        Handle[] hndls = gtk_tree_view_column_get_cell_renderers(getHandle());
        if (null == hndls)
            return null;
        CellRenderer[] cr = new CellRenderer[hndls.length];
        for (int i = 0; i < hndls.length; i++) {
            cr[i] = CellRenderer.getCellRenderer(hndls[i]);
        }
        return cr;
    }

    /**
     * Sets the spacing field of the column, which is the number of pixels to
     * place between cell renderers packed into it.
     * 
     * @param spacing
     *            Distance between cell renderers in pixels.
     */
    public void setSpacing(int spacing) {
        gtk_tree_view_column_set_spacing(getHandle(), spacing);
    }

    public int getSpacing() {
        return gtk_tree_view_column_get_spacing(getHandle());
    }

    /**
     * Sets the visibility of the column
     * 
     * @param visible
     *            If true, the column s visible.
     */
    public void setVisible(boolean visible) {
        gtk_tree_view_column_set_visible(getHandle(), visible);
    }

    public boolean getVisible() {
        return gtk_tree_view_column_get_visible(getHandle());
    }

    /**
     * If resizable is TRUE, then the user can explicitly resize the column by
     * grabbing the outer edge of the column button.
     * 
     * @param resizable
     *            TRUE, if the column can be resized.
     */
    public void setResizable(boolean resizable) {
        gtk_tree_view_column_set_resizable(getHandle(), resizable);
    }

    public boolean getResizable() {
        return gtk_tree_view_column_get_resizable(getHandle());
    }

    /**
     * Sets the growth behavior of the column
     * 
     * @param type
     *            The TreeViewColumnSizing.
     */
    public void setSizing(TreeViewColumnSizing type) {
        gtk_tree_view_column_set_sizing(getHandle(), type.getValue());
    }

    public TreeViewColumnSizing getSizing() {
        return TreeViewColumnSizing
                .intern(gtk_tree_view_column_get_sizing(getHandle()));
    }

    /**
     * Returns the current size of the column in pixels
     * 
     * @return The current width, in pizels.
     */
    public int getWidth() {
        return gtk_tree_view_column_get_width(getHandle());
    }

    /**
     * Sets the size of the column in pixels. This is meaningful only if the
     * sizing type is {@link TreeViewColumnSizing#FIXED}. The size of the
     * column is clamped to the min/max width for the column. Please note that
     * the min/max width of the column doesn't actually affect the "fixedWidth"
     * property of the widget, just the actual size when displayed.
     * 
     * @param fixedWidth
     *            The size to set the column to. Must be greater than 0.
     */
    public void setFixedWidth(int fixedWidth) {
        gtk_tree_view_column_set_fixed_width(getHandle(), fixedWidth);
    }

    public int getFixedWidth() {
        return gtk_tree_view_column_get_fixed_width(getHandle());
    }

    /**
     * Sets the minimum width of the column. If minWidth is -1, then the minimum
     * width is unset.
     * 
     * @param minWidth
     *            The minimum width of the column in pixels, or -1.
     */
    public void setMinWidth(int minWidth) {
        gtk_tree_view_column_set_min_width(getHandle(), minWidth);
    }

    public int getMinWidth() {
        return gtk_tree_view_column_get_min_width(getHandle());
    }

    /**
     * Sets the maximum width of the column. If maxWidth is -1, then the maximum
     * width is unset. Note, the column can actually be wider than max width if
     * it's the last column in a view. In this case, the column expands to fill
     * any extra space.
     * 
     * @param maxWidth
     *            The maximum width of the column in pixels, or -1.
     */
    public void setMaxWidth(int maxWidth) {
        gtk_tree_view_column_set_max_width(getHandle(), maxWidth);
    }

    public int getMaxWidth() {
        return gtk_tree_view_column_get_max_width(getHandle());
    }

    /**
     * Creates a Clicked event. This function will only work if tree_column is
     * clickable.
     */
    public void click() {
        gtk_tree_view_column_clicked(getHandle());
    }

    /**
     * Sets the title of the column. If a custom widget has been set, then this
     * value is ignored.
     * 
     * @param title
     *            The title of the column
     */
    public void setTitle(String title) {
        gtk_tree_view_column_set_title(getHandle(), title);
    }

    public String getTitle() {
        return gtk_tree_view_column_get_title(getHandle());
    }

    /**
     * Sets the header to be active if <code>active</code> is TRUE. When the
     * header is active, then it can take keyboard focus, and can be clicked.
     * 
     * @param clickable
     *            TRUE if the header is active.
     */
    public void setClickable(boolean clickable) {
        gtk_tree_view_column_set_clickable(getHandle(), clickable);
    }

    public boolean getClickable() {
        return gtk_tree_view_column_get_clickable(getHandle());
    }

    /**
     * Sets the widget in the header to be <code>widget<code>. If widget is 
     * <code>null</code>, then the header button is set with a {@link Label}
     * set to the title of column.
     * @param widget : A child {@link Widget}, or null.
     */
    public void setWidget(Widget widget) {
        Handle widgetHandle = (widget == null ? null : widget.getHandle());
        gtk_tree_view_column_set_widget(getHandle(), widgetHandle);
    }

    public Widget getWidget() {
        Handle hndl = gtk_tree_view_column_get_widget(getHandle());
        return Widget.getWidget(hndl);
    }

    /**
     * Sets the alignment of the title or custom widget inside the column
     * header. The alignment determines its location inside the button -- 0.0
     * for left, 0.5 for center, 1.0 for right.
     * 
     * @param align :
     *            The alignment, which is between [0.0 and 1.0] inclusive.
     */
    public void setAlignment(double align) {
        gtk_tree_view_column_set_alignment(getHandle(), align);
    }

    public double getAlignment() {
        return gtk_tree_view_column_get_alignment(getHandle());
    }

    /**
     * If <code>reorderable</code> is TRUE, then the column can be reordered
     * by the end user dragging the header. By using this method, the developer
     * does not have to deal with the drag events.
     * 
     * @param reorderable
     *            TRUE, if the column can be reordered.
     */
    public void setReorderable(boolean reorderable) {
        gtk_tree_view_column_set_reorderable(getHandle(), reorderable);
    }

    public boolean getReorderable() {
        return gtk_tree_view_column_get_reorderable(getHandle());
    }

    /**
     * Sets the logical DataColumn that this column sorts on when this column is
     * selected for sorting. Doing so makes the column header clickable.
     * 
     * @param column
     *            The DataColumn of the model to sort on.
     */
    public void setSortColumn(DataColumn column) {
        gtk_tree_view_column_set_sort_column_id(getHandle(), column.getColumn());
    }

    /**
     * Call this function with a setting of <code>TRUE</code> to display an
     * arrow in the header button indicating the column is sorted. Call
     * {@link #setSortOrder(SortType)} to change the direction of the arrow.
     * 
     * @param setting
     *            TRUE to display an indicator that the column is sorted
     */
    public void setSortIndicator(boolean setting) {
        gtk_tree_view_column_set_sort_indicator(getHandle(), setting);
    }

    public boolean getSortIndicator() {
        return gtk_tree_view_column_get_sort_indicator(getHandle());
    }

    /**
     * Changes the appearance of the sort indicator.
     * <p>
     * This does not actually sort the model. Use {@link #setSortColumn} if you
     * want automatic sorting support. This function is primarily for custom
     * sorting behavior, and should be used in conjunction with
     * {@link #setSortColumn} to do that. For custom models, the mechanism will
     * vary.
     * <p>
     * The sort indicator changes direction to indicate normal sort or reverse
     * sort. Note that you must have the sort indicator enabled to see anything
     * when calling this function;
     * 
     * @see #setSortIndicator(boolean)
     * @see #setSortColumn
     * @param order
     *            Sort order that the sort indicator should indicate
     */
    public void setSortOrder(SortType order) {
        gtk_tree_view_column_set_sort_order(getHandle(), order.getValue());
    }

    public SortType getSortOrder() {
        return SortType
                .intern(gtk_tree_view_column_get_sort_order(getHandle()));
    }

    /**
     * Returns TRUE if any of the cells packed into the column are visible.
     */
    public boolean getIsVisible() {
        return gtk_tree_view_column_cell_is_visible(getHandle());
    }

    /**
     * {@link TreeView} widgets contain {@link TreeViewColumn} objects. These
     * contain {@link CellRenderer}s to display data on the screen. This data
     * is stored in a {@link ListStore} or {@link TreeStore}. In order for data
     * to be displayed, a mapping has to be made between the data in the data
     * store and the attributes of the CellRenderers which determine what they
     * render. This method creates those mappings.
     * 
     * @param renderer
     *            The cell renderer to add the mapping to. This must have been
     *            addded to this column.
     * @param attribute
     *            The attribute to be mapped. This must be one of the attributes
     *            for the given renderer.
     * @param dataBlock
     *            the dataBlock in the store in which the data is contained.
     *            This should be of the type required by the renderer attribute.
     */
    public void addAttributeMapping(CellRenderer renderer,
            CellRendererAttribute attribute, DataColumn dataBlock) {
        gtk_tree_view_column_add_attribute(getHandle(), renderer.getHandle(),
                attribute.toString(), dataBlock.getColumn());
    }

    /**
     * Clears all attribute mappings
     * 
     * @see #addAttributeMapping(CellRenderer, CellRendererAttribute,
     *      DataColumn)
     */
    public void clearAttributeMappings(CellRenderer renderer) {
        gtk_tree_view_column_clear_attributes(getHandle(), renderer.getHandle());
    }

    /**
     * Sets the column to take available extra space. This space is shared
     * equally amongst all columns that have the expand set to TRUE. If no
     * column has this option set, then the last column gets all extra space. By
     * default, every column is created with this FALSE.
     * 
     * @since 2.4
     * 
     * @param expand
     */
    public void setExpand(boolean expand) {
        gtk_tree_view_column_set_expand(getHandle(), expand);
    }

    /**
     * Return TRUE if the column expands to take any available space.
     * 
     * @since 2.4
     * 
     * @return true, if the column expands
     */
    public boolean getExpand() {
        return gtk_tree_view_column_get_expand(getHandle());
    }

    /**
     * Sets the current keyboard focus to be at cell, if the column contains 2
     * or more editable and activatable cells.
     * 
     * @since 2.2
     */
    public void focusCell(CellRenderer cell) {
        gtk_tree_view_column_focus_cell(getHandle(), cell.getHandle());
    }

    // ////////////////////////////////////////////
    // CLICKED EVENT HANDLER

    /** listeners for selection changing */
    private Vector columnClickedListeners = null;

    /**
     * Register an object to handle events on the column header.
     * 
     * @see org.gnu.gtk.event.TreeViewColumnListener
     */
    public void addListener(TreeViewColumnListener listener) {
        // Don't add the listener a second time if it is in the Vector.
        int i = Widget.findListener(columnClickedListeners, listener);
        if (i == -1) {
            if (null == columnClickedListeners) {
                evtMap.initialize(this, TreeViewColumnEvent.Type.CLICKED);
                columnClickedListeners = new Vector();
            }
            columnClickedListeners.addElement(listener);
        }
    }

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

    protected void fireSelectionEvent(TreeViewColumnEvent event) {
        if (null == columnClickedListeners) {
            return;
        }
        int size = columnClickedListeners.size();
        int i = 0;
        while (i < size) {
            TreeViewColumnListener bl = (TreeViewColumnListener) columnClickedListeners
                    .elementAt(i);
            bl.columnClickedEvent(event);
            i++;
        }
    }

    private void handleClicked() {
        fireSelectionEvent(new TreeViewColumnEvent(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("clicked", "handleClicked",
                TreeViewColumnEvent.Type.CLICKED, TreeViewColumnListener.class);
    }

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

    native static final protected int gtk_tree_view_column_get_type();

    native static final protected Handle gtk_tree_view_column_new();

    native static final protected void gtk_tree_view_column_pack_start(
            Handle treeColumn, Handle cell, boolean expand);

    native static final protected void gtk_tree_view_column_pack_end(
            Handle treeColumn, Handle cell, boolean expand);

    native static final protected void gtk_tree_view_column_clear(
            Handle treeColumn);

    native static final protected Handle[] gtk_tree_view_column_get_cell_renderers(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_add_attribute(
            Handle treeColumn, Handle cellRenderer, String attribute, int column);

    native static final protected void gtk_tree_view_column_clear_attributes(
            Handle treeColumn, Handle cellRenderer);

    native static final protected void gtk_tree_view_column_set_spacing(
            Handle treeColumn, int spacing);

    native static final protected int gtk_tree_view_column_get_spacing(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_set_visible(
            Handle treeColumn, boolean visible);

    native static final protected boolean gtk_tree_view_column_get_visible(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_set_resizable(
            Handle treeColumn, boolean resizable);

    native static final protected boolean gtk_tree_view_column_get_resizable(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_set_sizing(
            Handle treeColumn, int type);

    native static final protected int gtk_tree_view_column_get_sizing(
            Handle treeColumn);

    native static final protected int gtk_tree_view_column_get_width(
            Handle treeColumn);

    native static final protected int gtk_tree_view_column_get_fixed_width(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_set_fixed_width(
            Handle treeColumn, int fixedWidth);

    native static final protected void gtk_tree_view_column_set_min_width(
            Handle treeColumn, int minWidth);

    native static final protected int gtk_tree_view_column_get_min_width(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_set_max_width(
            Handle treeColumn, int maxWidth);

    native static final protected int gtk_tree_view_column_get_max_width(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_clicked(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_set_title(
            Handle treeColumn, String title);

    native static final protected String gtk_tree_view_column_get_title(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_set_expand(
            Handle treeColumn, boolean expand);

    native static final protected boolean gtk_tree_view_column_get_expand(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_set_clickable(
            Handle treeColumn, boolean clickable);

    native static final protected boolean gtk_tree_view_column_get_clickable(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_set_widget(
            Handle treeColumn, Handle widget);

    native static final protected Handle gtk_tree_view_column_get_widget(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_set_alignment(
            Handle treeColumn, double xalign);

    native static final protected double gtk_tree_view_column_get_alignment(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_set_reorderable(
            Handle treeColumn, boolean reorderable);

    native static final protected boolean gtk_tree_view_column_get_reorderable(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_set_sort_column_id(
            Handle treeColumn, int sortColumnID);

    native static final protected int gtk_tree_view_column_get_sort_column_id(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_set_sort_indicator(
            Handle treeColumn, boolean setting);

    native static final protected boolean gtk_tree_view_column_get_sort_indicator(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_set_sort_order(
            Handle treeColumn, int order);

    native static final protected int gtk_tree_view_column_get_sort_order(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_cell_set_cell_data(
            Handle treeColumn, Handle treeModel, Handle iter,
            boolean isExpander, boolean isExpanded);

    native static final protected void gtk_tree_view_column_cell_get_size(
            Handle treeColumn, Handle cellRectangle, int[] xOffset,
            int[] yOffset, int[] width, int[] height);

    native static final protected boolean gtk_tree_view_column_cell_is_visible(
            Handle treeColumn);

    native static final protected void gtk_tree_view_column_focus_cell(
            Handle treeColumn, Handle cell);
}
