/*
 * Java-Gnome Bindings Library
 *
 * Copyright 1998-2005 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.gtk.event.IconViewEvent;
import org.gnu.gtk.event.IconViewListener;
import org.gnu.glib.Handle;

/**
 */
public class IconView extends Container {

    /**
     * Create a new IconView
     */
    public IconView() {
        super(gtk_icon_view_new());
    }

    /**
     * Create a new IconView specifying the model.
     * 
     * @param model
     */
    public IconView(TreeModel model) {
        super(gtk_icon_view_new_with_model(model.getHandle()));
    }

    /**
     * Create a new IconView with a handle to a native resource returned from a
     * call to the native libraries. This constructor should only be used
     * internally by Java-Gnome.
     * 
     * @param handle
     *            The handle that represents a pointer to a native resource.
     */
    public IconView(Handle handle) {
        super(handle);
    }

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

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

        return obj;
    }

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

    /**
     * Returns the model that the IconView is using. It will return null if a
     * model is the IconView currently does not have a model.
     */
    public TreeModel getModel() {
        Handle hndl = gtk_icon_view_get_model(getHandle());
        return TreeModel.getTreeModel(hndl);
    }

    /**
     * Sets the column with text for this IconView to be the specified column.
     * The text column must be of type DataColumnString.
     * 
     * @param column
     */
    public void setTextColumn(int column) {
        gtk_icon_view_set_text_column(getHandle(), column);
    }

    /**
     * Returns the column with text fro this IconView.
     */
    public int getTextColumn() {
        return gtk_icon_view_get_text_column(getHandle());
    }

    /**
     * Sets the column with markup information for this IconView to be the
     * specified column. The markup column must be of type DataColumnString. If
     * the markup column is set to something it overrides the text column set by
     * <code>setTextColumn</code>
     * 
     * @param column
     */
    public void setMarkupColumn(int column) {
        gtk_icon_view_set_markup_column(getHandle(), column);
    }

    /**
     * Returns the column with markup text for this IconView.
     */
    public int getMarkupColumn() {
        return gtk_icon_view_get_markup_column(getHandle());
    }

    /**
     * Sets the column with Pixbufs for this IconView to be the specified
     * column. This pixubf column must be of type DataColumnPixbuf.
     * 
     * @param column
     */
    public void setPixbufColumn(int column) {
        gtk_icon_view_set_pixbuf_column(getHandle(), column);
    }

    /**
     * Returns the column with Pixbufs for this IconView.
     */
    public int getPixbufColumn() {
        return gtk_icon_view_get_pixbuf_column(getHandle());
    }

    /**
     * Sets the orientation of this IconView which determines whether the labels
     * are drawn beside instead of below the icons.
     * 
     * @param orientation
     */
    public void setOrientation(Orientation orientation) {
        gtk_icon_view_set_orientation(getHandle(), orientation.getValue());
    }

    /**
     * Returns the Orientation for this IconView.
     */
    public Orientation getOrientation() {
        return Orientation.intern(gtk_icon_view_get_orientation(getHandle()));
    }

    /**
     * Set the number of columns for this IconView.
     * 
     * @param columns
     */
    public void setColumns(int columns) {
        gtk_icon_view_set_columns(getHandle(), columns);
    }

    /**
     * Returns the number of columns for this IconView.
     */
    public int getColumns() {
        return gtk_icon_view_get_columns(getHandle());
    }

    /**
     * Sets the width of each item.
     * 
     * @param width
     */
    public void setItemWidth(int width) {
        gtk_icon_view_set_item_width(getHandle(), width);
    }

    /**
     * Returns the width of each item.
     */
    public int getItemWidth() {
        return gtk_icon_view_get_item_width(getHandle());
    }

    /**
     * @param spacing
     */
    public void setSpacing(int spacing) {
        gtk_icon_view_set_spacing(getHandle(), spacing);
    }

    /**
     */
    public int getSpacing() {
        return gtk_icon_view_get_spacing(getHandle());
    }

    /**
     * 
     * @param rowSpacing
     */
    public void setRowSpacing(int rowSpacing) {
        gtk_icon_view_set_row_spacing(getHandle(), rowSpacing);
    }

    /**
     */
    public int getRowSpacing() {
        return gtk_icon_view_get_row_spacing(getHandle());
    }

    /**
     * 
     * @param columnSpacing
     */
    public void setColumnSpacing(int columnSpacing) {
        gtk_icon_view_set_column_spacing(getHandle(), columnSpacing);
    }

    /**
     * 
     */
    public int getColumnSpacing() {
        return gtk_icon_view_get_column_spacing(getHandle());
    }

    /**
     * 
     * @param margin
     */
    public void setMargin(int margin) {
        gtk_icon_view_set_margin(getHandle(), margin);
    }

    /**
     * 
     */
    public int getMargin() {
        return gtk_icon_view_get_margin(getHandle());
    }

    /**
     * Convenience method that allows the caller to set whether models that
     * support {@link TreeDragSource} and {@link TreeDragDest} should be
     * reorderable by the users. Both {@link TreeStore} and {@link ListStore}
     * support these.
     * <p>
     * 
     * If <code>reorderable</code> is <code>true</code>, then the user can
     * reorder the model by dragging and dropping rows. Those interested in
     * notification when these changes take place should add a TreeModelListener
     * to the model, and listen to TreeModelEvents of type ROW_INSERTED and
     * ROW_DELETED.
     * <p>
     * 
     * This method does not give any degree of control over the order -- any
     * reordering is allowed. If more control is needed, drag and drop should be
     * handled manually.
     * 
     * @param reorderable
     *            if the list of items can be reordered.
     * @since 2.8.1
     */
    public void setReorderable(boolean reorderable) {
        gtk_icon_view_set_reorderable(getHandle(), reorderable);
    }

    /**
     * Retrieves whether the user can reorder the list via drag-and-drop.
     * 
     * @return <code>true</code> if the list can be reordered.
     * @since 2.8.1
     */
    public boolean getReorderable() {
        return gtk_icon_view_get_reorderable(getHandle());
    }

    /**
     * Find the path at the point (x,y) relative to widget coordinates.
     * 
     * @param x
     * @param y
     */
    public TreePath getPathAtPosition(int x, int y) {
        Handle handle = gtk_icon_view_get_path_at_pos(getHandle(), x, y);
        return TreePath.getTreePath(handle);
    }

    /**
     * Sets the selection mode for this IconView.
     * 
     * @param mode
     */
    public void setSelectionMode(SelectionMode mode) {
        gtk_icon_view_set_selection_mode(getHandle(), mode.getValue());
    }

    /**
     * Returns the selection mode for this IconView.
     */
    public SelectionMode getSelectionMode() {
        return SelectionMode
                .intern(gtk_icon_view_get_selection_mode(getHandle()));
    }

    /**
     * Selects the row at the specified path.
     * 
     * @param path
     */
    public void selectPath(TreePath path) {
        gtk_icon_view_select_path(getHandle(), path.getHandle());
    }

    /**
     * Unselects the row at the specified path.
     * 
     * @param path
     */
    public void unselectPath(TreePath path) {

        gtk_icon_view_unselect_path(getHandle(), path.getHandle());
    }

    /**
     * returns true if the path is currently selected.
     * 
     * @param path
     */
    public boolean pathIsSelected(TreePath path) {
        return gtk_icon_view_path_is_selected(getHandle(), path.getHandle());
    }

    /**
     * Returns an array of all selected paths.
     */
    public TreePath[] getSelectedItems() {
        Handle[] hndls = gtk_icon_view_get_selected_items(getHandle());
        if (null == hndls)
            return null;
        TreePath[] paths = new TreePath[hndls.length];
        for (int i = 0; i < hndls.length; i++) {
            paths[i] = TreePath.getTreePath(hndls[i]);
        }
        return paths;
    }

    /**
     * Selects all of the icons. The IconView must have its' selection mode set
     * to SelectionMode.MULTIPLE.
     */
    public void selectAll() {
        gtk_icon_view_select_all(getHandle());
    }

    /**
     * Unselects all of the icons.
     */
    public void unselectAll() {
        gtk_icon_view_unselect_all(getHandle());
    }

    /**
     * Activate the item specified by path.
     * 
     * @param path
     */
    public void itemActivated(TreePath path) {
        gtk_icon_view_item_activated(getHandle(), path.getHandle());
    }

    /**
     * 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 item.
     * If renderer is not null, then focus is given to the cell specified by it.
     * Additionally, if startEditing is true, then editing should be started in
     * the specified cell.
     * <p>
     * Due to a bug in gtk+, calling this method will cause the virtual machine
     * to crash (because of a segmentation fault) if the CellRenderer is not a
     * part of this IconView and startEditing is set to true. This issue is
     * fixed in gtk+ 2.8.17 and higher and will cause an assertion failure
     * instead (which does not crash the virtual machine).
     * 
     * @param path
     *            A TreePath
     * @param renderer
     *            A CellRenderer of this IconView or null
     * @param startEditing
     *            true if the specified cell should start being edited.
     * @throws IllegalArgumentException
     *             If renderer is null and startEditing is true
     */
    public void setCursor(TreePath path, CellRenderer renderer,
            boolean startEditing) {
        // Get the renderer handle knowing that it can be null
        Handle rendererHandle = null;
        if (renderer != null) {
            rendererHandle = renderer.getHandle();
        } else if (startEditing) {
            throw new IllegalArgumentException("Cannot start editing on a "
                    + "null CellRenderer");
        }
        gtk_icon_view_set_cursor(getHandle(), path.getHandle(), rendererHandle,
                startEditing);
    }

    /**
     * Returns the current cursor path. If the cursor isn't currently set, then
     * null will be returned.
     * 
     * @return current cursor TreePath or null.
     */
    public TreePath getCursorPath() {
        Handle handle = gtk_icon_view_get_cursor_path(getHandle());
        return TreePath.getTreePath(handle);
    }

    /**
     * Returns the current cursor cell. If no cell currently has focus, then
     * null will be returned.
     * 
     * @return current cursor CellRenderer or null.
     */
    public CellRenderer getCursorCellRenderer() {
        Handle handle = gtk_icon_view_get_cursor_cell_renderer(getHandle());
        return CellRenderer.getCellRenderer(handle);
    }

    /**
     * Finds the cell at the point (x,y), relative to widget coordinates.
     * 
     * @param x
     *            The x position to be identified.
     * @param y
     *            The y position to be identified.
     * @return CellRenderer responsible for the cell at (x,y) or null if no item
     *         exists at the specified position.
     */
    public CellRenderer getCellRendererAtPosition(int x, int y) {
        Handle handle = gtk_icon_view_get_cell_renderer_at_pos(getHandle(), x,
                y);
        return CellRenderer.getCellRenderer(handle);
    }

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

    /**
     * Listeners for handling IconView events
     */
    private Vector ivListeners = null;

    /**
     * Register an object to handle IconView events.
     * 
     * @see IconViewListener
     */
    public void addListener(IconViewListener listener) {
        // Don't add the listener a second time if it is in the Vector.
        int i = findListener(ivListeners, listener);
        if (i == -1) {
            if (null == ivListeners) {
                evtMap.initialize(this,
                        IconViewEvent.Type.SET_SCROLL_ADJUSTMENTS);
                evtMap.initialize(this, IconViewEvent.Type.ITEM_ACTIVATED);
                evtMap.initialize(this, IconViewEvent.Type.SELECTION_CHANGED);
                ivListeners = new Vector();
            }
            ivListeners.addElement(listener);
        }
    }

    /**
     * Removes a listener.
     * 
     * @see #addListener(IconViewListener)
     */
    public void removeListener(IconViewListener listener) {
        int i = findListener(ivListeners, listener);
        if (i > -1) {
            ivListeners.remove(i);
        }
        if (0 == ivListeners.size()) {
            evtMap
                    .uninitialize(this,
                            IconViewEvent.Type.SET_SCROLL_ADJUSTMENTS);
            evtMap.uninitialize(this, IconViewEvent.Type.ITEM_ACTIVATED);
            evtMap.uninitialize(this, IconViewEvent.Type.SELECTION_CHANGED);
            ivListeners = null;
        }
    }

    protected void fireIconViewEvent(IconViewEvent event) {
        if (null == ivListeners) {
            return;
        }
        int size = ivListeners.size();
        int i = 0;
        while (i < size) {
            IconViewListener ivl = (IconViewListener) ivListeners.elementAt(i);
            ivl.iconViewEvent(event);
            i++;
        }
    }

    private void handleSetScrollAdjustment(Handle hAdj, Handle vAdj) {
        IconViewEvent evt = new IconViewEvent(this,
                IconViewEvent.Type.SET_SCROLL_ADJUSTMENTS);
        Adjustment h = Adjustment.getAdjustment(hAdj);
        Adjustment v = Adjustment.getAdjustment(vAdj);
        evt.setHorizontalAdjustment(h);
        evt.setVerticalAdjustment(v);
        fireIconViewEvent(evt);
    }

    private void handleItemActivated(Handle path) {
        if (path == null) {
            return;
        }
        IconViewEvent evt = new IconViewEvent(this,
                IconViewEvent.Type.ITEM_ACTIVATED);
        evt.setPath(TreePath.getTreePath(path));
        fireIconViewEvent(evt);
    }

    private void handleSelectionChanged() {
        IconViewEvent evt = new IconViewEvent(this,
                IconViewEvent.Type.SELECTION_CHANGED);
        fireIconViewEvent(evt);
    }

    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("set_scroll_adjustments",
                "handleSetScrollAdjustment",
                IconViewEvent.Type.SET_SCROLL_ADJUSTMENTS,
                IconViewListener.class);
        anEvtMap.addEvent("item_activated", "handleItemActivated",
                IconViewEvent.Type.ITEM_ACTIVATED, IconViewListener.class);
        anEvtMap.addEvent("selection_changed", "handleSelectionChanged",
                IconViewEvent.Type.SELECTION_CHANGED, IconViewListener.class);
    }

    native static final protected int gtk_icon_view_get_type();

    native static final protected Handle gtk_icon_view_new();

    native static final protected Handle gtk_icon_view_new_with_model(
            Handle model);

    native static final protected void gtk_icon_view_set_model(Handle view,
            Handle model);

    native static final protected Handle gtk_icon_view_get_model(Handle view);

    native static final protected void gtk_icon_view_set_text_column(
            Handle view, int column);

    native static final protected int gtk_icon_view_get_text_column(Handle view);

    native static final protected void gtk_icon_view_set_markup_column(
            Handle view, int column);

    native static final protected int gtk_icon_view_get_markup_column(
            Handle view);

    native static final protected void gtk_icon_view_set_pixbuf_column(
            Handle view, int column);

    native static final protected int gtk_icon_view_get_pixbuf_column(
            Handle view);

    native static final protected void gtk_icon_view_set_orientation(
            Handle view, int orientation);

    native static final protected int gtk_icon_view_get_orientation(Handle view);

    native static final protected void gtk_icon_view_set_columns(Handle view,
            int columns);

    native static final protected int gtk_icon_view_get_columns(Handle view);

    native static final protected void gtk_icon_view_set_item_width(
            Handle view, int width);

    native static final protected int gtk_icon_view_get_item_width(Handle view);

    native static final protected void gtk_icon_view_set_spacing(Handle view,
            int spacing);

    native static final protected int gtk_icon_view_get_spacing(Handle view);

    native static final protected void gtk_icon_view_set_row_spacing(
            Handle view, int spacing);

    native static final protected int gtk_icon_view_get_row_spacing(Handle view);

    native static final protected void gtk_icon_view_set_column_spacing(
            Handle view, int spacing);

    native static final protected int gtk_icon_view_get_column_spacing(
            Handle view);

    native static final protected void gtk_icon_view_set_margin(Handle view,
            int margin);

    native static final protected int gtk_icon_view_get_margin(Handle view);

    native static final protected Handle gtk_icon_view_get_path_at_pos(
            Handle view, int x, int y);

    native static final protected void gtk_icon_view_set_selection_mode(
            Handle view, int mode);

    native static final protected int gtk_icon_view_get_selection_mode(
            Handle view);

    native static final protected void gtk_icon_view_select_path(Handle view,
            Handle path);

    native static final protected void gtk_icon_view_unselect_path(Handle view,
            Handle path);

    native static final protected boolean gtk_icon_view_path_is_selected(
            Handle view, Handle path);

    native static final protected Handle[] gtk_icon_view_get_selected_items(
            Handle view);

    native static final protected void gtk_icon_view_select_all(Handle view);

    native static final protected void gtk_icon_view_unselect_all(Handle view);

    native static final protected void gtk_icon_view_item_activated(
            Handle view, Handle path);

    // new for gtk 2.8
    native static final protected Handle gtk_icon_view_get_cell_renderer_at_pos(
            Handle view, int x, int y);

    native static final protected void gtk_icon_view_set_cursor(Handle view,
            Handle path, Handle cell, boolean startEditing);

    native static final protected Handle gtk_icon_view_get_cursor_path(
            Handle view);

    native static final protected Handle gtk_icon_view_get_cursor_cell_renderer(
            Handle cell);

    native static final protected boolean gtk_icon_view_get_reorderable(
            Handle view);

    native static final protected void gtk_icon_view_set_reorderable(
            Handle view, boolean reorderable);
}
