/*
 * Java-Gnome Bindings Library
 *
 * Copyright 1998-2006 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.Type;
import org.gnu.gtk.event.TreeViewEvent;
import org.gnu.gtk.event.TreeViewListener;
import org.gnu.glib.Handle;

/**
 * TreeView is GTK's Widget for displaying trees and lists. A TreeView itself is
 * the parent container for <i>presenting</i> the data on screen; other classes
 * control the layout of that presentation and the mapping from underlying data
 * to that layout.
 * 
 * <h1>Gtk Tree and List Widgets Overview</h1>
 * <p>
 * <i>The standard tree and list widgets in GTK+ are very powerful, but
 * unfortunately are also rather complex and there is only so much that can be
 * done to mitigate this. If you patiently work though the following
 * explaination and review the tutorial on the java-gnome website, you should
 * find yourself quickly up to speed.</i>
 * 
 * <h2>{@link TreeView}</h2>
 * <p>
 * There is only one widget which is placed in any applications to create trees,
 * lists and tables. This is the {@link TreeView}. An application can have any
 * number of treeviews and they can be placed as can normal widgets. The data
 * for the widget, and the method in which it is displayed is controlled by
 * other classes. Gtk has been designed so that any number of treeview widgets
 * can be linked to the same data store. {@link TreeViewColumn}s,
 * {@link CellRenderer}s and {@link TreeSelection}s are created for each view,
 * so Views can use the same data store but have their own column layout, data
 * display within those columns (linked to any of the dataBlocks in the store);
 * and their own selections.
 * 
 * <h2>{@link TreeModel}</h2>
 * <p>
 * Models are used to store data. Data is stored in what could be considered a
 * table. There are a number of {@link DataColumn}s, which could be considered
 * the columns of that table (in fact, in the C version of GTK, they are always
 * referred to as columns; but this can get confused with TreeViewColumns which
 * are quite a different matter). These DataColumns each store one type of data
 * (String, boolean, int, etc.). The 'rows' of this data table (the individual
 * records) can be accessed using iterators called {@link TreeIter}s. These are
 * used extensively in many methods. Setting data involves getting an iterator
 * (creating a new row gets you a TreeIter) and then setting the value for each
 * of the DataColumns. The ordering of the DataColumns in the Model has no
 * meaning. For presentation, you decide exactly which DataColumns are to be
 * used on screen by mapping them to a a TreeViewColumn via a CellRenderer (see
 * below).
 * <p>
 * GTK provides two implementations of {@link TreeModel}:
 * <p>
 * {@link ListStore} - This is used for tables and lists. Data is organised in
 * rows and columns.
 * <p>
 * {@link TreeStore} - This is for data organized in hierarchical trees. You
 * order your data using {@link TreePath}s.
 * 
 * <h2>{@link TreeViewColumn}</h2>
 * <p>
 * Both modes of TreeView (displaying a tree or displaying a list) can have
 * multiple columns of data. As each TreeViewColumns is set up, you add it to
 * the parent {@link TreeView} you're creating. The TreeViewColumns determine
 * how the data is displayed. They have settings such as the column title,
 * whether the column can be resized, and even whether the columns can be
 * reorganized (by dragging the columns). Each {@link TreeView} widget has it's
 * own set of TreeViewColumns. Determining how the data is displayed in the
 * columns is done by CellRenderers (see below). Any number of CellRenderers can
 * be packed into a given column.
 * 
 * <h2>{@link CellRenderer}</h2>
 * <p>
 * Tree and list `cells' may contain a large variety of data types. Determining
 * how they are displayed is done by the CellRenderer family of classes. If the
 * data is unusual, or you want to combine a number of data types in a single
 * column, you may need to construct your own renderer. However, you are
 * recommended to stick with the regular choices:
 * <dl>
 * <dt>{@link CellRendererPixbuf}
 * <dd>
 * <dt>{@link CellRendererText}
 * <dd>For displaying Strings
 * <dt>{@link CellRendererToggle}
 * <dd>For displaying boolean data, either as individual checkboxes or as radio
 * buttons.
 * </dl>
 * <p>
 * The CellRenderers need to be told what data to display. This is done using
 * the {@link TreeViewColumn#addAttributeMapping(CellRenderer,
 * CellRendererAttribute, DataColumn)}. The renderer attributes vary with each
 * renderer, for example CellRendererText has a TEXT attribute for the text the
 * be displayed. The final parameter is for the DataColumn in the store in which
 * the data is contained.
 * <p>
 * Java-Gnome comes with a number of simple example applications involving
 * trees. They may be useful for learning the functionality of these classes.
 * <p>
 * Finally, some people may find use for a convenience wrapper that Java-Gnome
 * derived; see {@link SimpleList} to display a single column list of string
 * values.
 * 
 * @author Mark Howard &lt;mh@debian.org&gt;
 */
public class TreeView extends Container {

    private TreeViewRowSeparatorMethod separatorFunc = null;

    /**
     * Create a new TreeView object.
     */
    public TreeView() {
        super(gtk_tree_view_new());
    }

    /**
     * Creates a new tree view from a native handle
     */
    public TreeView(Handle handle) {
        super(handle);
    }

    /**
     * Creates a new tree view from a native handle
     */
    public static TreeView getTreeView(Handle handle) {
        if (handle == null)
            return null;

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

        return obj;
    }

    /**
     * Creates a new TreeView Widget with the initial model set
     */
    public TreeView(TreeModel model) {
        super(gtk_tree_view_new_with_model(model.getHandle()));
    }

    /**
     * Returns the model associated with this tree.
     */
    public TreeModel getModel() {
        Handle hndl = gtk_tree_view_get_model(getHandle());
        return TreeModel.getTreeModel(hndl);
    }

    /**
     * Appends column to the list of columns.
     * 
     * @param column
     *            The GtkTreeViewColumn to add.
     * @return The number of columns in tree_view after appending.
     */
    public int appendColumn(TreeViewColumn column) {
        return gtk_tree_view_append_column(getHandle(), column.getHandle());
    }

    /**
     * This inserts the column into the tree_view at position. If position is
     * -1, then the column is inserted at the end.
     * 
     * @param column
     *            The GtkTreeViewColumn to be inserted.
     * @param position
     *            The position to insert column in.
     * @return The number of columns in tree_view after insertion.
     */
    public int insertColumn(TreeViewColumn column, int position) {
        return gtk_tree_view_insert_column(getHandle(), column.getHandle(),
                position);
    }

    /**
     * Removes column from tree_view.
     * 
     * @param column
     *            The GtkTreeViewColumn to remove.
     * @return The number of columns in tree_view after removing.
     */
    public int removeColumn(TreeViewColumn column) {
        return gtk_tree_view_remove_column(getHandle(), column.getHandle());
    }

    /**
     * Gets the TreeSelection associated with this widget
     */
    public TreeSelection getSelection() {
        Handle hndl = gtk_tree_view_get_selection(getHandle());
        return TreeSelection.getTreeSelection(hndl);
    }

    /**
     * Gets the GtkAdjustment currently being used for the horizontal aspect.
     * 
     * @return A GtkAdjustment object, or null if none is currently being used.
     */
    public Adjustment getHAdjustment() {
        Handle hndl = gtk_tree_view_get_hadjustment(getHandle());
        return Adjustment.getAdjustment(hndl);
    }

    /**
     * Sets the Adjustment for the current horizontal aspect.
     */
    public void setHAdjustment(Adjustment hadj) {
        gtk_tree_view_set_hadjustment(getHandle(), hadj.getHandle());
    }

    /**
     * Gets the Adjustment currently being used for the vertical aspect.
     */
    public Adjustment getVAdjustment() {
        return Adjustment
                .getAdjustment(gtk_tree_view_get_vadjustment(getHandle()));
    }

    /**
     * Sets the Adjustment for the current vertical aspect.
     */
    public void setVAdjustment(Adjustment vadj) {
        gtk_tree_view_set_vadjustment(getHandle(), vadj.getHandle());
    }

    /**
     * Sets the the visibility state of the headers.
     * 
     * @param headersVisible
     *            true if the headers are visible
     */
    public void setHeadersVisible(boolean headersVisible) {
        gtk_tree_view_set_headers_visible(getHandle(), headersVisible);
    }

    /**
     * Returns true if the headers on the TreeView are visible.
     */
    public boolean getHeadersVisible() {
        return gtk_tree_view_get_headers_visible(getHandle());
    }

    /**
     * Allow the column title buttons to be clicked.
     * 
     * @param setting
     *            true if the columns are clickable.
     */
    public void setHeadersClickable(boolean setting) {
        gtk_tree_view_set_headers_clickable(getHandle(), setting);
    }

    /**
     * This function tells GTK+ that the user interface for your application
     * requires users to read across tree rows and associate cells with one
     * another. By default, GTK+ will then render the tree with alternating row
     * colors. Do not use it just because you prefer the appearance of the ruled
     * tree; that's a question for the theme. Some themes will draw tree rows in
     * alternating colors even when rules are turned off, and users who prefer
     * that appearance all the time can choose those themes. You should call
     * this function only as a semantic hint to the theme engine that your tree
     * makes alternating colors useful from a functional standpoint (since it
     * has lots of columns, generally).
     * 
     * @param setting
     *            true if the tree requires reading across rows
     */
    public void setAlternateRowColor(boolean setting) {
        gtk_tree_view_set_rules_hint(getHandle(), setting);
    }

    public boolean getAlternateRowColor() {
        return gtk_tree_view_get_rules_hint(getHandle());
    }

    /**
     * Gets the column at the given position in the tree view.
     * 
     * @param position
     *            The position of the column, counting from 0.
     * @return The TreeViewColumn, or <code>null</code> if the position is
     *         outside the range of columns.
     */
    public TreeViewColumn getColumn(int position) {
        Handle h = gtk_tree_view_get_column(getHandle(), position);
        return TreeViewColumn.getTreeViewColumn(h);
    }

    /**
     * Moves column to be after to baseColumn. If baseColumn is null, then
     * column is placed in the first position.
     * 
     * @param column
     *            The GtkTreeViewColumn to be moved.
     * @param baseColumn
     *            The GtkTreeViewColumn to be moved relative to, or null.
     */
    public void moveColumn(TreeViewColumn column, TreeViewColumn baseColumn) {
        Handle baseColumnHandle = (baseColumn == null ? null : baseColumn
                .getHandle());
        gtk_tree_view_move_column_after(getHandle(), column.getHandle(),
                baseColumnHandle);
    }

    /**
     * Sets the column to draw the expander arrow at. If column is null, then
     * the expander arrow is always at the first visible column.
     * 
     * @param column
     *            null, or the column to draw the expander arrow at.
     */
    public void setExpanderColumn(TreeViewColumn column) {
        Handle expanderColumn = (column == null ? null : column.getHandle());
        gtk_tree_view_set_expander_column(getHandle(), expanderColumn);
    }

    public TreeViewColumn getExpanderColumn() {
        Handle hndl = gtk_tree_view_get_expander_column(getHandle());
        return TreeViewColumn.getTreeViewColumn(hndl);
    }

    /**
     * todo: set column drag function Interface.
     */

    /**
     * Moves the alignments of the view to the position specified by </tt>column</tt>
     * and <tt>path</tt>. <tt>rowAlign</tt> determines where the row is
     * placed, and <tt>colAlign</tt> determines where column is placed. Both
     * are expected to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0
     * means right/bottom alignment, 0.5 means center.
     * 
     * @param path
     *            The path of the row to move to
     * @param column
     *            The TreeViewColumn to move horizontally to
     * @param rowAlign
     *            The vertical alignment of the row specified by path.
     * @param colAlign :
     *            The horizontal alignment of the column specified by column.
     */
    public void scrollToCell(TreePath path, TreeViewColumn column,
            double rowAlign, double colAlign) {
        Handle pathHandle = (path == null ? null : path.getHandle());
        Handle columnHandle = (column == null ? null : column.getHandle());
        gtk_tree_view_scroll_to_cell(getHandle(), pathHandle, columnHandle,
                true, rowAlign, colAlign);
    }

    /**
     * Scrolls the TreeView such that the top-left corner of the visible area is
     * x, y, where x and y are specified in tree window coordinates. If either x
     * or y are -1 then that direction isn't scrolled.
     * 
     * @param x
     * @param y
     */
    public void scrollToPoint(int x, int y) {
        gtk_tree_view_scroll_to_point(getHandle(), x, y);
    }

    /**
     * Moves the alignments of the view to the position specified by column and
     * path.
     * 
     * @param path
     *            The path of the row to move to
     * @param column
     *            The TreeViewColumn to move horizontally to
     */
    public void scrollToCell(TreePath path, TreeViewColumn column) {
        gtk_tree_view_scroll_to_cell(getHandle(), path.getHandle(), column
                .getHandle(), false, 0, 0);
    }

    /**
     * Moves the alignments of the view to the position specified by column
     * 
     * @param column
     *            The TreeViewColumn to move horizontally to
     */
    public void scrollToCell(TreeViewColumn column) {
        gtk_tree_view_scroll_to_cell(getHandle(), null, column.getHandle(),
                false, 0, 0);
    }

    /**
     * Moves the alignments of the view to the position specified by path.
     * 
     * @param path
     *            The path of the row to move to, or null.
     */
    public void scrollToCell(TreePath path) {
        gtk_tree_view_scroll_to_cell(getHandle(), path.getHandle(), null,
                false, 0, 0);
    }

    /**
     * Sets the current keyboard focus to be at path, and selects it. This is
     * useful when you want to focus the user's attention on a particular row.
     * If column is specified, and startEditing is true, then editing should be
     * started in the specified cell. This function is often followed by
     * {@link Widget#grabFocus()} in order to give keyboard focus to the widget.
     * Please note that editing can only happen when the widget is realized.
     * 
     * @param path
     *            A TreePath
     * @param focusColumn
     *            A TreeViewColumn, or null
     * @param startEditing
     *            true if the specified cell should start being edited.
     */
    public void setCursor(TreePath path, TreeViewColumn focusColumn,
            boolean startEditing) {
        Handle focusColumnHandle = (focusColumn == null ? null : focusColumn
                .getHandle());
        gtk_tree_view_set_cursor(getHandle(), path.getHandle(),
                focusColumnHandle, startEditing);
    }

    /**
     * Returns the current path
     */
    public TreePath getCursorPath() {
        Handle path = gtk_tree_view_get_cursor_path(getHandle());
        return TreePath.getTreePath(path);
    }

    /**
     * Returns the current column
     */
    public TreeViewColumn getCursorColumn() {
        Handle column = gtk_tree_view_get_cursor_column(getHandle());
        return TreeViewColumn.getTreeViewColumn(column);
    }

    /**
     * Activates the cell determined by path and column.
     * 
     * @param path
     *            The TreePath to be activated.
     * @param column
     *            The TreeViewColumn to be activated.
     */
    public void activateCell(TreePath path, TreeViewColumn column) {
        gtk_tree_view_row_activated(getHandle(), path.getHandle(), column
                .getHandle());
    }

    /**
     * Recursively expands all nodes
     */
    public void expandAll() {
        gtk_tree_view_expand_all(getHandle());
    }

    /**
     * Recursively collapses all visible, expanded nodes.
     */
    public void collapseAll() {
        gtk_tree_view_collapse_all(getHandle());
    }

    /**
     * Opens the row so its children are visible.
     * 
     * @param path
     *            Path to a row
     * @param openAll
     *            Whether to recursively expand, or just expand immediate
     *            children
     * @return true if the row existed and had children
     */
    public boolean expandRow(TreePath path, boolean openAll) {
        return gtk_tree_view_expand_row(getHandle(), path.getHandle(), openAll);
    }

    /**
     * Collapses a row (hides its child rows, if they exist).
     * 
     * @param path
     *            Path to a row in the view
     * @return true if the row was collapsed.
     */
    public boolean collapseRow(TreePath path) {
        return gtk_tree_view_collapse_row(getHandle(), path.getHandle());
    }

    /**
     * TODO: map_expanded_rows
     */

    /**
     * Returns true if the node pointed to by path is expanded.
     * 
     * @param path
     *            A TreePath to test expansion state.
     * @return true if path is expanded.
     */
    public boolean getRowExpanded(TreePath path) {
        return gtk_tree_view_row_expanded(getHandle(), path.getHandle());
    }

    /**
     * This function is a convenience function to allow you to reorder models
     * that support the DragSourceIface and the DragDestIface. Both
     * {@link TreeStore} and {@link ListStore} support these. If reorderable is
     * true, then the user can reorder the model by dragging and dropping rows.
     * The developer can listen to these changes by adding listeners.
     * 
     * <p>
     * This function does not give you any degree of control over the order --
     * any reorderering is allowed. If more control is needed, you should
     * probably handle drag and drop manually.
     * 
     * @param reorderable
     *            true, if the tree can be reordered.
     */
    public void setReorderable(boolean reorderable) {
        gtk_tree_view_set_reorderable(getHandle(), reorderable);
    }

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

    /**
     * Sets the model for a GtkTreeView. If the {@link TreeView} already has a
     * model set, it will remove it before setting the new model. If model is
     * null, then it will unset the old model.
     * 
     * @param model
     *            the new model for the {@link TreeView}
     */
    public void setModel(TreeModel model) {
        Handle modelHandle = (model == null ? null : model.getHandle());
        gtk_tree_view_set_model(getHandle(), modelHandle);
    }

    /**
     * Resizes all columns to their optimal width. Only works after the treeview
     * has been realized.
     */
    public void autoSizeColumns() {
        gtk_tree_view_columns_autosize(getHandle());
    }

    /**
     * Returns an array of all the columns currently in the view
     */
    public TreeViewColumn[] getColumns() {
        Handle[] hndls = gtk_tree_view_get_columns(getHandle());
        if (null == hndls)
            return null;
        TreeViewColumn[] columns = new TreeViewColumn[hndls.length];
        for (int i = 0; i < hndls.length; i++) {
            columns[i] = TreeViewColumn.getTreeViewColumn(hndls[i]);
        }
        return columns;
    }

    /**
     * <p>
     * Shows a data column of the model in this <code>TreeView</code>. This
     * method will try to guess the renderer to use, and because of that, it has
     * some limitations. For instance, you should only use it to show these
     * kinds of DataColumns: <code>DataColumnBoolean</code>,
     * <code>DataColumnString</code>, <code>DataColumnPixbuf</code>,
     * <code>DataColumnStockItem</code>.
     * </p>
     * <p>
     * For other kinds of DataColumn, the method
     * <code>showDataColumn(int, CellRenderer, CellRendererAttribute)</code>
     * should be used, which is more flexible, allowing you to specify which
     * renderer to use, and its rendering attributes.
     * </p>
     * 
     * @param aCol
     *            the number of the column in the model to be shown; starting
     *            from 0.
     * @see #showDataColumn(int, boolean)
     * @see #showDataColumn(int, CellRenderer, CellRendererAttribute)
     * @see DataColumn
     * @return an instance to the new shown column.
     */
    public TreeViewColumn showDataColumn(int aCol) {

        TreeViewColumn viewCol = null;
        TreeModel model = getModel();

        if (model instanceof ListStore) {

            ListStore store = (ListStore) model;
            DataColumn dataCol = store.getDataColumn(aCol);
            CellRenderer renderer = null;
            CellRendererAttribute rendererAttr = null;

            if (dataCol instanceof DataColumnBoolean) {
                renderer = new CellRendererToggle();
                rendererAttr = CellRendererToggle.Attribute.RADIO;

            } else if (dataCol instanceof DataColumnString) {
                renderer = new CellRendererText();
                rendererAttr = CellRendererText.Attribute.TEXT;

            } else if (dataCol instanceof DataColumnPixbuf) {
                renderer = new CellRendererPixbuf();
                rendererAttr = CellRendererPixbuf.Attribute.PIXBUF;

            } else if (dataCol instanceof DataColumnStockItem) {
                renderer = new CellRendererPixbuf();
                rendererAttr = CellRendererPixbuf.Attribute.STOCK_ID;

            } else {
                throw new IllegalArgumentException(
                        "Since column "
                                + aCol
                                + "isn't a "
                                + "DataColumnBoolean, DataColumnString, DataColumnPixbuf,"
                                + "or DataColumnStockItem, "
                                + "Java-Gnome can't guess which renderer to use; "
                                + "thesefore you should explicitly specify the renderer");
            }

            viewCol = new TreeViewColumn();
            viewCol.packStart(renderer, false);
            viewCol.addAttributeMapping(renderer, rendererAttr, dataCol);

            this.appendColumn(viewCol);
        }

        return viewCol;
    }

    /**
     * <p>
     * Shows a data column of the model in this <code>TreeView</code>. This
     * method will try to guess the renderer to use, and because of that, it has
     * some limitations. For instance, you should only use it to show these
     * kinds of DataColumns: <code>DataColumnBoolean</code>,
     * <code>DataColumnString</code>, <code>DataColumnPixbuf</code>,
     * <code>DataColumnStockItem</code>.
     * </p>
     * <p>
     * For other kinds of DataColumn, the method
     * <code>showDataColumn(int, CellRenderer, CellRendererAttribute)</code>
     * should be used, which is more flexible, allowing you to specify which
     * renderer to use, and its rendering attributes.
     * </p>
     * 
     * @param aCol
     *            the number of the column in the model to be shown; starting
     *            from 0.
     * @param expand
     *            <code>true</code> if this column should expand to fill the
     *            available space in the row, <code>false</code> otherwise.
     * @see #showDataColumn(int, boolean)
     * @see #showDataColumn(int, CellRenderer, CellRendererAttribute)
     * @see DataColumn
     * @return an instance to the new shown column.
     */
    public TreeViewColumn showDataColumn(int aCol, boolean expand) {

        TreeViewColumn viewCol = null;
        TreeModel model = getModel();

        if (model instanceof ListStore) {

            ListStore store = (ListStore) model;
            DataColumn dataCol = store.getDataColumn(aCol);
            CellRenderer renderer = null;
            CellRendererAttribute rendererAttr = null;

            if (dataCol instanceof DataColumnBoolean) {
                renderer = new CellRendererToggle();
                rendererAttr = CellRendererToggle.Attribute.RADIO;

            } else if (dataCol instanceof DataColumnString) {
                renderer = new CellRendererText();
                rendererAttr = CellRendererText.Attribute.TEXT;

            } else if (dataCol instanceof DataColumnPixbuf) {
                renderer = new CellRendererPixbuf();
                rendererAttr = CellRendererPixbuf.Attribute.PIXBUF;

            } else if (dataCol instanceof DataColumnStockItem) {
                renderer = new CellRendererPixbuf();
                rendererAttr = CellRendererPixbuf.Attribute.STOCK_ID;

            } else {
                throw new IllegalArgumentException(
                        "Since column "
                                + aCol
                                + "isn't a "
                                + "DataColumnBoolean, DataColumnString, DataColumnPixbuf,"
                                + "or DataColumnStockItem, "
                                + "Java-Gnome can't guess which renderer to use; "
                                + "thesefore you should explicitly specify the renderer");
            }

            viewCol = new TreeViewColumn();
            viewCol.packStart(renderer, expand);
            viewCol.addAttributeMapping(renderer, rendererAttr, dataCol);

            this.appendColumn(viewCol);
        }

        return viewCol;
    }

    /**
     * Shows a data column of the model in this <code>TreeView</code>, with a
     * specific renderer and rendering attributes.
     * 
     * @param aCol
     *            the number of the column in the model to be shown; starting
     *            from 0.
     * @param aRenderer
     *            the renderer to be used
     * @param aAttr
     *            the rendering attributes to be used
     * @see #showDataColumn(int)
     * @see #showDataColumn(int, boolean)
     * @see DataColumn
     * @return an instance to the new shown column.
     */
    public TreeViewColumn showDataColumn(int aCol, CellRenderer aRenderer,
            CellRendererAttribute aAttr) {

        TreeViewColumn viewCol = null;
        TreeModel model = getModel();

        if (model instanceof ListStore) {

            ListStore store = (ListStore) model;
            DataColumn dataCol = store.getDataColumn(aCol);
            CellRenderer renderer = aRenderer;
            CellRendererAttribute rendererAttr = aAttr;

            viewCol = new TreeViewColumn();
            viewCol.packStart(renderer, false);
            viewCol.addAttributeMapping(renderer, rendererAttr, dataCol);

            this.appendColumn(viewCol);
        }

        return viewCol;
    }

    /**
     * Shows a data column of the model in this <code>TreeView</code>, with a
     * specific renderer and rendering attributes.
     * 
     * @param aCol
     *            the number of the column in the model to be shown; starting
     *            from 0.
     * @param aRenderer
     *            the renderer to be used
     * @param aAttr
     *            the rendering attributes to be used
     * @param expand
     *            <code>true</code> if this column should expand to fill the
     *            available space in the row, <code>false</code> otherwise.
     * @see #showDataColumn(int)
     * @see #showDataColumn(int, boolean)
     * @see DataColumn
     * @return an instance to the new shown column.
     */
    public TreeViewColumn showDataColumn(int aCol, CellRenderer aRenderer,
            CellRendererAttribute aAttr, boolean expand) {

        TreeViewColumn viewCol = null;
        TreeModel model = getModel();

        if (model instanceof ListStore) {

            ListStore store = (ListStore) model;
            DataColumn dataCol = store.getDataColumn(aCol);
            CellRenderer renderer = aRenderer;
            CellRendererAttribute rendererAttr = aAttr;

            viewCol = new TreeViewColumn();
            viewCol.packStart(renderer, expand);
            viewCol.addAttributeMapping(renderer, rendererAttr, dataCol);

            this.appendColumn(viewCol);
        }

        return viewCol;
    }

    /**
     * Shows a data column of the model in this <code>TreeView</code>, with a
     * specific renderer and rendering attributes.
     * 
     * @param aCol
     *            the number of the column in the model to be shown; starting
     *            from 0.
     * @param aRenderer
     *            the renderer to be used
     * @param aAttr
     *            the rendering attributes to be used
     * @see #showDataColumn(int)
     * @see #showDataColumn(int, boolean)
     * @see DataColumn
     * @return an instance to the new shown column.
     */
    public TreeViewColumn showDataColumn(int aCol, CellRenderer aRenderer,
            CellRendererAttribute aAttr[]) {

        TreeViewColumn viewCol = null;
        TreeModel model = getModel();

        if (model instanceof ListStore) {

            ListStore store = (ListStore) model;
            DataColumn dataCol = store.getDataColumn(aCol);
            CellRenderer renderer = aRenderer;

            viewCol = new TreeViewColumn();
            viewCol.packStart(renderer, false);

            for (int i = 0; aCol < aAttr.length; aCol++) {
                viewCol.addAttributeMapping(renderer, aAttr[i], dataCol);
            }

            this.appendColumn(viewCol);
        }

        return viewCol;
    }

    /**
     * Shows a data column of the model in this <code>TreeView</code>, with a
     * specific renderer and rendering attributes.
     * 
     * @param aCol
     *            the number of the column in the model to be shown; starting
     *            from 0.
     * @param aRenderer
     *            the renderer to be used
     * @param aAttr
     *            the rendering attributes to be used
     * @param expand
     *            <code>true</code> if this column should expand to fill the
     *            available space in the row, <code>false</code> otherwise.
     * @see #showDataColumn(int)
     * @see #showDataColumn(int, boolean)
     * @see DataColumn
     * @return an instance to the new shown column.
     */
    public TreeViewColumn showDataColumn(int aCol, CellRenderer aRenderer,
            CellRendererAttribute aAttr[], boolean expand) {

        TreeViewColumn viewCol = null;
        TreeModel model = getModel();

        if (model instanceof ListStore) {

            ListStore store = (ListStore) model;
            DataColumn dataCol = store.getDataColumn(aCol);
            CellRenderer renderer = aRenderer;

            viewCol = new TreeViewColumn();
            viewCol.packStart(renderer, expand);

            for (int i = 0; aCol < aAttr.length; aCol++) {
                viewCol.addAttributeMapping(renderer, aAttr[i], dataCol);
            }

            this.appendColumn(viewCol);
        }

        return viewCol;
    }

    /**
     * <p>
     * Appends a data column of the model to an existing
     * <code>TreeViewColumn</code>, previously created with one of the
     * <code>showDataColumn</code> methods. It should be used when it's
     * desired to have more than one data in just one column.
     * </p>
     * <p>
     * This method will try to guess the renderer to use, and because of that,
     * it has some limitations. For instance, you should only use it to show
     * these kinds of DataColumns: <code>DataColumnBoolean</code>,
     * <code>DataColumnString</code>, <code>DataColumnPixbuf</code>,
     * <code>DataColumnStockItem</code>.
     * </p>
     * <p>
     * For other kinds of DataColumn, the method
     * <code>appendDataColumn(TreeViewColumn, int, CellRenderer, CellRendererAttribute)</code>
     * should be used, which is more flexible, allowing you to specify which
     * renderer to use, and its rendering attributes.
     * </p>
     * 
     * @param aExistingCol
     *            an existing <code>TreeViewColumn</code>.
     * @param aCol
     *            the number of the column in the model to be shown; starting
     *            from 0.
     * @see #showDataColumn(int)
     * @see #appendDataColumn(TreeViewColumn, int, CellRenderer,
     *      CellRendererAttribute)
     * @see DataColumn
     * @return an instance to the updated column.
     */
    public TreeViewColumn appendDataColumn(TreeViewColumn aExistingCol, int aCol) {

        TreeViewColumn viewCol = aExistingCol;
        TreeModel model = getModel();

        if (model instanceof ListStore) {

            ListStore store = (ListStore) model;
            DataColumn dataCol = store.getDataColumn(aCol);
            CellRenderer renderer = null;
            CellRendererAttribute rendererAttr = null;

            if (dataCol instanceof DataColumnBoolean) {
                renderer = new CellRendererToggle();
                rendererAttr = CellRendererToggle.Attribute.RADIO;

            } else if (dataCol instanceof DataColumnString) {
                renderer = new CellRendererText();
                rendererAttr = CellRendererText.Attribute.TEXT;

            } else if (dataCol instanceof DataColumnPixbuf) {
                renderer = new CellRendererPixbuf();
                rendererAttr = CellRendererPixbuf.Attribute.PIXBUF;

            } else if (dataCol instanceof DataColumnStockItem) {
                renderer = new CellRendererPixbuf();
                rendererAttr = CellRendererPixbuf.Attribute.STOCK_ID;

            } else {
                throw new IllegalArgumentException(
                        "Since column "
                                + aCol
                                + "isn't a "
                                + "DataColumnBoolean, DataColumnString, DataColumnPixbuf,"
                                + "or DataColumnStockItem, "
                                + "Java-Gnome can't guess which renderer to use; "
                                + "thesefore you should explicitly specify the renderer");
            }

            viewCol.packStart(renderer, false);
            viewCol.addAttributeMapping(renderer, rendererAttr, dataCol);
        }

        return viewCol;
    }

    /**
     * <p>
     * Appends a data column of the model to an existing
     * <code>TreeViewColumn</code>, previously created with one of the
     * <code>showDataColumn</code> methods. It should be used when it's
     * desired to have more than one data in just one column.
     * </p>
     * <p>
     * This method will try to guess the renderer to use, and because of that,
     * it has some limitations. For instance, you should only use it to show
     * these kinds of DataColumns: <code>DataColumnBoolean</code>,
     * <code>DataColumnString</code>, <code>DataColumnPixbuf</code>,
     * <code>DataColumnStockItem</code>.
     * </p>
     * <p>
     * For other kinds of DataColumn, the method
     * <code>appendDataColumn(TreeViewColumn, int, CellRenderer, CellRendererAttribute)</code>
     * should be used, which is more flexible, allowing you to specify which
     * renderer to use, and its rendering attributes.
     * </p>
     * 
     * @param aExistingCol
     *            an existing <code>TreeViewColumn</code>.
     * @param aCol
     *            the number of the column in the model to be shown; starting
     *            from 0.
     * @param expand
     *            <code>true</code> if this column should expand to fill the
     *            available space in the row, <code>false</code> otherwise.
     * @see #showDataColumn(int)
     * @see #appendDataColumn(TreeViewColumn, int, CellRenderer,
     *      CellRendererAttribute)
     * @see DataColumn
     * @return an instance to the updated column.
     */
    public TreeViewColumn appendDataColumn(TreeViewColumn aExistingCol,
            int aCol, boolean expand) {

        TreeViewColumn viewCol = aExistingCol;
        TreeModel model = getModel();

        if (model instanceof ListStore) {

            ListStore store = (ListStore) model;
            DataColumn dataCol = store.getDataColumn(aCol);
            CellRenderer renderer = null;
            CellRendererAttribute rendererAttr = null;

            if (dataCol instanceof DataColumnBoolean) {
                renderer = new CellRendererToggle();
                rendererAttr = CellRendererToggle.Attribute.RADIO;

            } else if (dataCol instanceof DataColumnString) {
                renderer = new CellRendererText();
                rendererAttr = CellRendererText.Attribute.TEXT;

            } else if (dataCol instanceof DataColumnPixbuf) {
                renderer = new CellRendererPixbuf();
                rendererAttr = CellRendererPixbuf.Attribute.PIXBUF;

            } else if (dataCol instanceof DataColumnStockItem) {
                renderer = new CellRendererPixbuf();
                rendererAttr = CellRendererPixbuf.Attribute.STOCK_ID;

            } else {
                throw new IllegalArgumentException(
                        "Since column "
                                + aCol
                                + "isn't a "
                                + "DataColumnBoolean, DataColumnString, DataColumnPixbuf,"
                                + "or DataColumnStockItem, "
                                + "Java-Gnome can't guess which renderer to use; "
                                + "thesefore you should explicitly specify the renderer");
            }

            viewCol.packStart(renderer, expand);
            viewCol.addAttributeMapping(renderer, rendererAttr, dataCol);
        }

        return viewCol;
    }

    /**
     * <p>
     * Appends a data column of the model to an existing
     * <code>TreeViewColumn</code>, previously created with one of the
     * <code>showDataColumn</code> methods, and lets you use a specific
     * renderer and rendering attributes.
     * </p>
     * <p>
     * It should be used when it's desired to have more than one data in just
     * one column.
     * </p>
     * 
     * @param aCol
     *            the number of the column in the model to be shown; starting
     *            from 0.
     * @param aRenderer
     *            the renderer to be used
     * @param aAttr
     *            the rendering attributes to be used
     * @see #showDataColumn(int)
     * @see #appendDataColumn(TreeViewColumn, int)
     * @see DataColumn
     * @return an instance to the updated column.
     */
    public TreeViewColumn appendDataColumn(TreeViewColumn aExistingCol,
            int aCol, CellRenderer aRenderer, CellRendererAttribute aAttr) {

        TreeViewColumn viewCol = aExistingCol;
        TreeModel model = getModel();

        if (model instanceof ListStore) {

            ListStore store = (ListStore) model;
            DataColumn dataCol = store.getDataColumn(aCol);
            CellRenderer renderer = aRenderer;
            CellRendererAttribute rendererAttr = aAttr;

            viewCol.packStart(renderer, false);
            viewCol.addAttributeMapping(renderer, rendererAttr, dataCol);
        }

        return viewCol;
    }

    /**
     * <p>
     * Appends a data column of the model to an existing
     * <code>TreeViewColumn</code>, previously created with one of the
     * <code>showDataColumn</code> methods, and lets you use a specific
     * renderer and rendering attributes.
     * </p>
     * <p>
     * It should be used when it's desired to have more than one data in just
     * one column.
     * </p>
     * 
     * @param aCol
     *            the number of the column in the model to be shown; starting
     *            from 0.
     * @param aRenderer
     *            the renderer to be used
     * @param aAttr
     *            the rendering attributes to be used
     * @param expand
     *            <code>true</code> if this column should expand to fill the
     *            available space in the row, <code>false</code> otherwise.
     * @see #showDataColumn(int)
     * @see #appendDataColumn(TreeViewColumn, int)
     * @see DataColumn
     * @return an instance to the updated column.
     */
    public TreeViewColumn appendDataColumn(TreeViewColumn aExistingCol,
            int aCol, CellRenderer aRenderer, CellRendererAttribute aAttr,
            boolean expand) {

        TreeViewColumn viewCol = aExistingCol;
        TreeModel model = getModel();

        if (model instanceof ListStore) {

            ListStore store = (ListStore) model;
            DataColumn dataCol = store.getDataColumn(aCol);
            CellRenderer renderer = aRenderer;
            CellRendererAttribute rendererAttr = aAttr;

            viewCol.packStart(renderer, expand);
            viewCol.addAttributeMapping(renderer, rendererAttr, dataCol);
        }

        return viewCol;
    }

    /**
     * <p>
     * Appends a data column of the model to an existing
     * <code>TreeViewColumn</code>, previously created with one of the
     * <code>showDataColumn</code> methods, and lets you use a specific
     * renderer and rendering attributes.
     * </p>
     * <p>
     * It should be used when it's desired to have more than one data in just
     * one column.
     * </p>
     * 
     * @param aCol
     *            the number of the column in the model to be shown; starting
     *            from 0.
     * @param aRenderer
     *            the renderer to be used
     * @param aAttr
     *            the rendering attributes to be used
     * @see #showDataColumn(int)
     * @see #appendDataColumn(TreeViewColumn, int)
     * @see DataColumn
     * @return an instance to the updated column.
     */
    public TreeViewColumn appendDataColumn(TreeViewColumn aExistingCol,
            int aCol, CellRenderer aRenderer, CellRendererAttribute aAttr[]) {

        TreeViewColumn viewCol = aExistingCol;
        TreeModel model = getModel();

        if (model instanceof ListStore) {

            ListStore store = (ListStore) model;
            DataColumn dataCol = store.getDataColumn(aCol);
            CellRenderer renderer = aRenderer;

            viewCol.packStart(renderer, false);

            for (int i = 0; i < aAttr.length; i++) {
                viewCol.addAttributeMapping(renderer, aAttr[i], dataCol);
            }
        }

        return viewCol;
    }

    /**
     * <p>
     * Appends a data column of the model to an existing
     * <code>TreeViewColumn</code>, previously created with one of the
     * <code>showDataColumn</code> methods, and lets you use a specific
     * renderer and rendering attributes.
     * </p>
     * <p>
     * It should be used when it's desired to have more than one data in just
     * one column.
     * </p>
     * 
     * @param aCol
     *            the number of the column in the model to be shown; starting
     *            from 0.
     * @param aRenderer
     *            the renderer to be used
     * @param aAttr
     *            the rendering attributes to be used
     * @param expand
     *            <code>true</code> if this column should expand to fill the
     *            available space in the row, <code>false</code> otherwise.
     * @see #showDataColumn(int)
     * @see #appendDataColumn(TreeViewColumn, int)
     * @see DataColumn
     * @return an instance to the updated column.
     */
    public TreeViewColumn appendDataColumn(TreeViewColumn aExistingCol,
            int aCol, CellRenderer aRenderer, CellRendererAttribute aAttr[],
            boolean expand) {

        TreeViewColumn viewCol = aExistingCol;
        TreeModel model = getModel();

        if (model instanceof ListStore) {

            ListStore store = (ListStore) model;
            DataColumn dataCol = store.getDataColumn(aCol);
            CellRenderer renderer = aRenderer;

            viewCol.packStart(renderer, expand);

            for (int i = 0; i < aAttr.length; i++) {
                viewCol.addAttributeMapping(renderer, aAttr[i], dataCol);
            }
        }

        return viewCol;
    }

    /**
     * Sets the current keyboard focus to be at path, and selects it. This is
     * useful when you want to focus the user's attention on a particular row.
     * If column is specified, and startEditing is true, then editing should be
     * started in the specified cell. If focusCell is also specified, the
     * particular cellRenderer in that column will get focusThis function is
     * often followed by {@link Widget#grabFocus()} in order to give keyboard
     * focus to the widget. Please note that editing can only happen when the
     * widget is realized.
     * 
     * @since 2.2
     * 
     * @param path
     *            A TreePath
     * @param focusColumn
     *            A TreeViewColumn, or null
     * @param focusCell
     *            A cellrenderer, or null
     * @param startEditing
     *            true if the specified cell should start being edited.
     */
    public void setCursor(TreePath path, TreeViewColumn focusColumn,
            CellRenderer focusCell, boolean startEditing) {
        Handle columnHandle = (focusColumn == null ? null : focusColumn
                .getHandle());
        Handle cellHandle = (focusCell == null ? null : focusCell.getHandle());
        gtk_tree_view_set_cursor_on_cell(getHandle(), path.getHandle(),
                columnHandle, cellHandle, startEditing);
    }

    /**
     * Expands the row at path. This will also expand all parent rows of path as
     * necessary.
     * 
     * @since 2.2
     * 
     * @param path
     *            path to a row
     */
    public void expandToPath(TreePath path) {
        gtk_tree_view_expand_to_path(getHandle(), path.getHandle());
    }

    /**
     * Does anyone want this type of functionality? todo:
     * gtk_tree_view_enable_model_drag_dest () todo:
     * gtk_tree_view_enable_model_drag_source ()
     * gtk_tree_view_unset_rows_drag_source ()
     * gtk_tree_view_unset_rows_drag_dest () gtk_tree_view_set_drag_dest_row ()
     * gtk_tree_view_get_drag_dest_row () gtk_tree_view_get_dest_row_at_pos ()
     * 
     */

    /**
     * If enable search is set, then the user can type in text to search through
     * the tree interactively.
     * 
     * @param enableSearch
     *            true, if the user can search interactively
     */
    public void setEnableSearch(boolean enableSearch) {
        gtk_tree_view_set_enable_search(getHandle(), enableSearch);
    }

    public boolean getEnableSearch() {
        return gtk_tree_view_get_enable_search(getHandle());
    }

    /**
     * @deprecated This method is broken and has been deprecated in favor of the
     *             {@link #setSearchDataColumn} method.
     */
    public void setSearchColumn(TreeViewColumn column) {
        throw new RuntimeException("This method is deprecated."
                + "please use the method: "
                + "TreeView#setSearchDataColumn(DataColumn)");
    }

    /**
     * @deprecated This method is broken and has been deprecated in favor of the
     *             {@link #getSearchDataColumn} method.
     */
    public TreeViewColumn getSearchColumn() {
        throw new RuntimeException("This method is deprecated."
                + "please use the method: " + "TreeView#getSearchDataColumn()");
    }

    /**
     * Sets column as the column where the interactive search code should search
     * in. Additionally, turns on interactive searching.
     * 
     * @param column
     *            The column of the data model to search in.
     */
    public void setSearchDataColumn(DataColumn column) {
        gtk_tree_view_set_search_column(getHandle(), column.getColumn());
    }

    /**
     * Gets the column searched on by the interactive search code.
     * 
     * @return A DataColumn object representing the column of the data model in
     *         which the interactive search takes place.
     */
    public DataColumn getSearchDataColumn() {
        int datacol = gtk_tree_view_get_search_column(getHandle());
        DataColumn dataColumn = new DataColumnInt();
        dataColumn.setColumn(datacol);
        return dataColumn;
    }

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

    /**
     * Enables or disables the fixed height mode of this TreeView. Fixed height
     * mode speeds up TreeView by assuming that all rows have the same height.
     * Only enable this option if all rows are the same height and all columns
     * are of type {@link TreeViewColumnSizing#FIXED}.
     * 
     * @param enable
     *            true to enable fixed height mode.
     */
    public void setFixedHeightMode(boolean enable) {
        gtk_tree_view_set_fixed_height_mode(getHandle(), enable);
    }

    /**
     * Returns whether fixed height mode is turned on for this TreeView.
     */
    public boolean getFixedHeightMode() {
        return gtk_tree_view_get_fixed_height_mode(getHandle());
    }

    /**
     * Enables of disables the hover selection mode of this TreeView. Hover
     * selection makes the selected row follow the pointer. Currently, this
     * works only for the selection modes {@link SelectionMode#SINGLE} and
     * {@link SelectionMode#BROWSE}.
     * 
     * @param hover
     *            true to enable hover selection mode.
     */
    public void setHoverSelection(boolean hover) {
        gtk_tree_view_set_hover_selection(getHandle(), hover);
    }

    /**
     * Returns whether hover selection mode is turned on for this TreeView.
     */
    public boolean getHoverSelection() {
        return gtk_tree_view_get_hover_selection(getHandle());
    }

    /**
     * Enables or disables the hover expansion mode of this TreeView. Hover
     * expansion makes rows expand or collapse if the pointer moves over them.
     * 
     * @param expand
     *            true to enable hover selection mode.
     */
    public void setHoverExpand(boolean expand) {
        gtk_tree_view_set_hover_expand(getHandle(), expand);
    }

    /**
     * Returns whether hover expansion mode is turned on for TreeView.
     */
    public boolean getHoverExpand() {
        return gtk_tree_view_get_hover_expand(getHandle());
    }

    /**
     * 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_tree_view_set_row_separator_func(getHandle(), null,
                    "handleSeparatorFunc");
        } else {
            gtk_tree_view_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 Handler Related code
    //

    /**
     * Listeners for handling toggle events
     */
    private Vector listeners = null;

    /**
     * Register an object to handle events related to user actions on the
     * TreeView widget.
     * 
     * @see org.gnu.gtk.event.TreeViewListener
     */
    public void addListener(TreeViewListener listener) {
        // Don't add the listener a second time if it is in the Vector.
        int i = findListener(listeners, listener);
        if (i == -1) {
            if (null == listeners) {
                evtMap.initialize(this, TreeViewEvent.Type.COLUMNS_CHANGED);
                evtMap.initialize(this,
                        TreeViewEvent.Type.EXPAND_COLLAPSE_CURSOR_ROW);
                evtMap.initialize(this, TreeViewEvent.Type.ROW_ACTIVATED);
                evtMap.initialize(this, TreeViewEvent.Type.ROW_EXPANDED);
                evtMap.initialize(this, TreeViewEvent.Type.ROW_COLLAPSED);
                evtMap.initialize(this, TreeViewEvent.Type.SELECT_ALL);
                evtMap
                        .initialize(this,
                                TreeViewEvent.Type.SELECT_CURSOR_PARENT);
                evtMap.initialize(this, TreeViewEvent.Type.SELECT_CURSOR_ROW);
                evtMap.initialize(this,
                        TreeViewEvent.Type.START_INTERACTIVE_SEARCH);
                evtMap.initialize(this, TreeViewEvent.Type.TOGGLE_CURSOR_ROW);
                evtMap.initialize(this, TreeViewEvent.Type.UNSELECT_ALL);
                evtMap.initialize(this, TreeViewEvent.Type.MOVE_CURSOR);
                listeners = new Vector();
            }
            listeners.addElement(listener);
        }
    }

    /**
     * Removes a listener
     * 
     * @see #addListener(TreeViewListener)
     */
    public void removeListener(TreeViewListener listener) {
        int i = findListener(listeners, listener);
        if (i > -1) {
            listeners.remove(i);
        }
        if (0 == listeners.size()) {
            evtMap.uninitialize(this, TreeViewEvent.Type.COLUMNS_CHANGED);
            evtMap.uninitialize(this,
                    TreeViewEvent.Type.EXPAND_COLLAPSE_CURSOR_ROW);
            evtMap.uninitialize(this, TreeViewEvent.Type.ROW_ACTIVATED);
            evtMap.uninitialize(this, TreeViewEvent.Type.ROW_EXPANDED);
            evtMap.uninitialize(this, TreeViewEvent.Type.ROW_COLLAPSED);
            evtMap.uninitialize(this, TreeViewEvent.Type.SELECT_ALL);
            evtMap.uninitialize(this, TreeViewEvent.Type.SELECT_CURSOR_PARENT);
            evtMap.uninitialize(this, TreeViewEvent.Type.SELECT_CURSOR_ROW);
            evtMap.uninitialize(this,
                    TreeViewEvent.Type.START_INTERACTIVE_SEARCH);
            evtMap.uninitialize(this, TreeViewEvent.Type.TOGGLE_CURSOR_ROW);
            evtMap.uninitialize(this, TreeViewEvent.Type.UNSELECT_ALL);
            evtMap.uninitialize(this, TreeViewEvent.Type.MOVE_CURSOR);
            listeners = null;
        }
    }

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

    protected void fireTreeViewEvent(TreeViewEvent event) {
        if (null == listeners) {
            return;
        }
        int size = listeners.size();
        int i = 0;
        while (i < size) {
            TreeViewListener tl = (TreeViewListener) listeners.elementAt(i);
            tl.treeViewEvent(event);
            i++;
        }
    }

    private void handleRowActivated(Handle path, Handle column) {
        TreeViewEvent event = new TreeViewEvent(this,
                TreeViewEvent.Type.ROW_ACTIVATED);
        TreePath aPath = TreePath.getTreePath(path);
        event.setTreePath(aPath);
        event.setTreeIter(this.getModel().getIter(aPath));
        event.setTreeColumn(TreeViewColumn.getTreeViewColumn(column));
        fireTreeViewEvent(event);
    }

    private void handleRowExpanded(Handle iter, Handle path) {
        TreeViewEvent event = new TreeViewEvent(this,
                TreeViewEvent.Type.ROW_EXPANDED);
        event.setTreePath(TreePath.getTreePath(path));
        event.setTreeIter(TreeIter.getTreeIter(iter, getModel()));
        event.setExpand(true);
        fireTreeViewEvent(event);
    }

    private void handleRowCollapsed(Handle iter, Handle path) {
        TreeViewEvent event = new TreeViewEvent(this,
                TreeViewEvent.Type.ROW_COLLAPSED);
        event.setTreePath(TreePath.getTreePath(path));
        event.setTreeIter(TreeIter.getTreeIter(iter, getModel()));
        fireTreeViewEvent(event);
    }

    private void handleColumnsChanged() {
        TreeViewEvent event = new TreeViewEvent(this,
                TreeViewEvent.Type.COLUMNS_CHANGED);
        fireTreeViewEvent(event);
    }

    private boolean handleSelectAll() {
        TreeViewEvent event = new TreeViewEvent(this,
                TreeViewEvent.Type.SELECT_ALL);
        fireTreeViewEvent(event);
        return true;
    }

    private boolean handleUnselectAll() {
        TreeViewEvent event = new TreeViewEvent(this,
                TreeViewEvent.Type.UNSELECT_ALL);
        fireTreeViewEvent(event);
        return true;
    }

    private boolean handleSelectCursorRow(boolean startEditing) {
        TreeViewEvent event = new TreeViewEvent(this,
                TreeViewEvent.Type.SELECT_CURSOR_ROW);
        event.setStartEditing(startEditing);
        fireTreeViewEvent(event);
        return true;
    }

    private boolean handleToggleCursorRow() {
        TreeViewEvent event = new TreeViewEvent(this,
                TreeViewEvent.Type.TOGGLE_CURSOR_ROW);
        fireTreeViewEvent(event);
        return true;
    }

    private boolean handleExpandCollapseCursorRow(boolean logical,
            boolean expand, boolean openAll) {
        TreeViewEvent event = new TreeViewEvent(this,
                TreeViewEvent.Type.EXPAND_COLLAPSE_CURSOR_ROW);
        event.setLogical(logical);
        event.setExpand(expand);
        event.setOpenAll(openAll);
        fireTreeViewEvent(event);
        return true;
    }

    private boolean handleSelectCursorParent() {
        TreeViewEvent event = new TreeViewEvent(this,
                TreeViewEvent.Type.SELECT_CURSOR_PARENT);
        fireTreeViewEvent(event);
        return true;
    }

    private boolean handleStartInteractiveSearch() {
        TreeViewEvent event = new TreeViewEvent(this,
                TreeViewEvent.Type.START_INTERACTIVE_SEARCH);
        fireTreeViewEvent(event);
        return true;
    }

    /*
     * Sticking with the stupid parameter names in the GTK API itself.
     */
    private boolean handleMoveCursor(int arg1, int arg2) {
        TreeViewEvent event = new TreeViewEvent(this,
                TreeViewEvent.Type.MOVE_CURSOR);
        MovementStep whichOne = MovementStep.intern(arg1);
        event.setMovementStep(whichOne);
        event.setHowMany(arg2);
        fireTreeViewEvent(event);
        return true;
    }

    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("row_activated", "handleRowActivated",
                TreeViewEvent.Type.ROW_ACTIVATED, TreeViewListener.class);
        anEvtMap.addEvent("row_expanded", "handleRowExpanded",
                TreeViewEvent.Type.ROW_EXPANDED, TreeViewListener.class);
        anEvtMap.addEvent("row_collapsed", "handleRowCollapsed",
                TreeViewEvent.Type.ROW_COLLAPSED, TreeViewListener.class);
        anEvtMap.addEvent("columns_changed", "handleColumnsChanged",
                TreeViewEvent.Type.COLUMNS_CHANGED, TreeViewListener.class);
        anEvtMap.addEvent("select_all", "handleSelectAll",
                TreeViewEvent.Type.SELECT_ALL, TreeViewListener.class);
        anEvtMap.addEvent("unselect_all", "handleUnselectAll",
                TreeViewEvent.Type.UNSELECT_ALL, TreeViewListener.class);
        anEvtMap.addEvent("select_cursor_row", "handleSelectCursorRow",
                TreeViewEvent.Type.SELECT_CURSOR_ROW, TreeViewListener.class);
        anEvtMap.addEvent("toggle_cursor_row", "handleToggleCursorRow",
                TreeViewEvent.Type.TOGGLE_CURSOR_ROW, TreeViewListener.class);
        anEvtMap.addEvent("expand_collapse_cursor_row",
                "handleExpandCollapseCursorRow",
                TreeViewEvent.Type.EXPAND_COLLAPSE_CURSOR_ROW,
                TreeViewListener.class);
        anEvtMap
                .addEvent("select_cursor_parent", "handleSelectCursorParent",
                        TreeViewEvent.Type.SELECT_CURSOR_PARENT,
                        TreeViewListener.class);
        anEvtMap.addEvent("start_interactive_search",
                "handleStartInteractiveSearch",
                TreeViewEvent.Type.START_INTERACTIVE_SEARCH,
                TreeViewListener.class);
        anEvtMap.addEvent("move-cursor", "handleMoveCursor",
                TreeViewEvent.Type.MOVE_CURSOR, TreeViewListener.class);
    }

    native static final protected int gtk_tree_view_get_type();

    native static final protected Handle gtk_tree_view_new();

    native static final protected Handle gtk_tree_view_new_with_model(
            Handle model);

    native static final protected Handle gtk_tree_view_get_model(Handle treeView);

    native static final protected void gtk_tree_view_set_model(Handle treeView,
            Handle model);

    native static final protected Handle gtk_tree_view_get_selection(
            Handle treeView);

    native static final protected Handle gtk_tree_view_get_hadjustment(
            Handle treeView);

    native static final protected void gtk_tree_view_set_hadjustment(
            Handle treeView, Handle adjustment);

    native static final protected Handle gtk_tree_view_get_vadjustment(
            Handle treeView);

    native static final protected void gtk_tree_view_set_vadjustment(
            Handle treeView, Handle adjustment);

    native static final protected boolean gtk_tree_view_get_headers_visible(
            Handle treeView);

    native static final protected void gtk_tree_view_set_headers_visible(
            Handle treeView, boolean headersVisible);

    native static final protected void gtk_tree_view_columns_autosize(
            Handle treeView);

    native static final protected void gtk_tree_view_set_headers_clickable(
            Handle treeView, boolean setting);

    native static final protected void gtk_tree_view_set_rules_hint(
            Handle treeView, boolean setting);

    native static final protected boolean gtk_tree_view_get_rules_hint(
            Handle treeView);

    native static final protected int gtk_tree_view_append_column(
            Handle treeView, Handle column);

    native static final protected int gtk_tree_view_remove_column(
            Handle treeView, Handle column);

    native static final protected int gtk_tree_view_insert_column(
            Handle treeView, Handle column, int position);

    native static final protected Handle gtk_tree_view_get_column(
            Handle treeView, int n);

    native static final protected Handle[] gtk_tree_view_get_columns(
            Handle treeView);

    native static final protected void gtk_tree_view_move_column_after(
            Handle treeView, Handle column, Handle baseColumn);

    native static final protected void gtk_tree_view_set_expander_column(
            Handle treeView, Handle column);

    native static final protected Handle gtk_tree_view_get_expander_column(
            Handle treeView);

    native static final protected void gtk_tree_view_scroll_to_point(
            Handle treeView, int treeX, int treeY);

    native static final protected void gtk_tree_view_scroll_to_cell(
            Handle treeView, Handle path, Handle column, boolean useAlign,
            double rowAlign, double colAlign);

    native static final protected void gtk_tree_view_row_activated(
            Handle treeView, Handle path, Handle column);

    native static final protected void gtk_tree_view_expand_all(Handle treeView);

    native static final protected void gtk_tree_view_collapse_all(
            Handle treeView);

    native static final protected void gtk_tree_view_expand_to_path(
            Handle treeView, Handle path);

    native static final protected boolean gtk_tree_view_expand_row(
            Handle treeView, Handle path, boolean openAll);

    native static final protected boolean gtk_tree_view_collapse_row(
            Handle treeView, Handle path);

    native static final protected boolean gtk_tree_view_row_expanded(
            Handle treeView, Handle path);

    native static final protected void gtk_tree_view_set_reorderable(
            Handle treeView, boolean reorderable);

    native static final protected boolean gtk_tree_view_get_reorderable(
            Handle treeView);

    native static final protected void gtk_tree_view_set_cursor(
            Handle treeView, Handle path, Handle focusColumn,
            boolean startEditing);

    native static final protected void gtk_tree_view_set_cursor_on_cell(
            Handle treeView, Handle path, Handle focusColumn, Handle focusCell,
            boolean startEditin);

    native static final protected Handle gtk_tree_view_get_cursor_path(
            Handle treeView);

    native static final protected Handle gtk_tree_view_get_cursor_column(
            Handle treeView);

    native static final protected Handle gtk_tree_view_get_bin_window(
            Handle treeView);

    native static final protected boolean gtk_tree_view_get_path_at_pos(
            Handle treeView, int x, int y, Handle path, Handle column,
            int cellX, int cellY);

    // native static final protected int gtk_tree_view_get_cell_area(int
    // treeView, int path, int column);
    // native static final protected int gtk_tree_view_get_background_area(int
    // treeView, int path, int column);
    // native static final protected int gtk_tree_view_get_visible_rect(int
    // treeView);
    // native static final protected void
    // gtk_tree_view_widget_to_tree_coords(int treeView, int wx, int wy, int[]
    // tx, int[] ty);
    // native static final protected void
    // gtk_tree_view_tree_to_widget_coords(int treeView, int tx, int ty, int[]
    // wx, int[] wy);
    // native static final protected void gtk_tree_view_get_visible_range(Handle
    // treeView, Handle startPath, Handle endPath);
    // native static final protected void
    // gtk_tree_view_enable_model_drag_source(int treeView, int buttonMask,
    // int[] targets, int action);
    // native static final protected void
    // gtk_tree_view_enable_model_drag_dest(int treeView, int[] targets, int
    // action);
    // native static final protected void
    // gtk_tree_view_unset_rows_drag_source(int treeView);
    // native static final protected void gtk_tree_view_unset_rows_drag_dest(int
    // treeView);
    // native static final protected int gtk_tree_view_create_row_drag_icon(int
    // treeView, int path);
    native static final protected void gtk_tree_view_set_enable_search(
            Handle treeView, boolean enableSearch);

    native static final protected boolean gtk_tree_view_get_enable_search(
            Handle treeView);

    native static final protected int gtk_tree_view_get_search_column(
            Handle treeView);

    native static final protected void gtk_tree_view_set_search_column(
            Handle treeView, int dataColumn);

    // GTK 2.6 additions.
    native static final private void gtk_tree_view_set_fixed_height_mode(
            Handle tree_view, boolean enable);

    native static final private boolean gtk_tree_view_get_fixed_height_mode(
            Handle tree_view);

    native static final private void gtk_tree_view_set_hover_selection(
            Handle tree_view, boolean hover);

    native static final private boolean gtk_tree_view_get_hover_selection(
            Handle tree_view);

    native static final private void gtk_tree_view_set_hover_expand(
            Handle tree_view, boolean expand);

    native static final private boolean gtk_tree_view_get_hover_expand(
            Handle tree_view);

    native static final private void gtk_tree_view_set_row_separator_func(
            Handle tree_view, TreeView tview, String callback);
    // native static final private int
    // gtk_tree_view_get_row_separator_func(Handle tree_view);

}
