/*
 * 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.io.BufferedReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import org.gnu.glib.EventMap;
import org.gnu.glib.EventType;
import org.gnu.glib.GObject;
import org.gnu.gtk.event.UIManagerEvent;
import org.gnu.gtk.event.UIManagerListener;
import org.gnu.glib.Handle;

/**
 * A class to construct a user interface (menus and toolbars) from one or more
 * UI definitions, which reference actions from one or more action groups. The
 * UI definitions are specified in an XML format.
 * 
 * See the <a
 * href="http://developer.gnome.org/doc/API/2.0/gtk/GtkUIManager.html">GTK
 * documentation of GtkUIManager</a> for details of this class and the UI
 * definitions XML format.
 *
 * @deprecated This class is part of the java-gnome 2.x family of libraries,
 *             which, due to their inefficiency and complexity, are no longer
 *             being maintained and have been abandoned by the java-gnome
 *             project. This class may in the future have an equivalent in
 *             java-gnome 4.0, try looking for
 *             <code>org.gnome.gtk.UIManager</code>.
 *             You should be aware that there is a considerably different API
 *             in the new library: the architecture is completely different
 *             and most notably internals are no longer exposed to public view.
 */
public class UIManager extends GObject {

    public UIManager() {
        super(gtk_ui_manager_new());
    }

    public void setAddTearoffs(boolean addTearoffs) {
        gtk_ui_manager_set_add_tearoffs(getHandle(), addTearoffs);
    }

    public boolean getAddTearoffs() {
        return gtk_ui_manager_get_add_tearoffs(getHandle());
    }

    public void insertActionGroup(ActionGroup group, int position) {
        gtk_ui_manager_insert_action_group(getHandle(), group.getHandle(),
                position);
    }

    public void removeActionGroup(ActionGroup group) {
        gtk_ui_manager_remove_action_group(getHandle(), group.getHandle());
    }

    public List getActionGroups() {
        Handle[] actions = gtk_ui_manager_get_action_groups(getHandle());
        List objs = new ArrayList();
        for (int i = 0; i < actions.length; i++) {
            GObject obj = getGObjectFromHandle(actions[i]);
            if (null != obj)
                objs.add(obj);
            else
                objs.add(ActionGroup.getActionGroup(actions[i]));
        }
        return objs;
    }

    public AccelGroup getAccelGroup() {
        Handle handle = gtk_ui_manager_get_accel_group(getHandle());
        return AccelGroup.getAccelGroup(handle);
    }

    public Widget getWidget(String path) {
        Handle hndl = gtk_ui_manager_get_widget(getHandle(), path);
        return Widget.getWidget(hndl);
    }

    public Widget[] getToplevels(UIManagerItemType types) {
        Handle[] hndls = gtk_ui_manager_get_toplevels(getHandle(), types
                .getValue());
        Widget[] widgets = new Widget[hndls.length];
        for (int i = 0; i < hndls.length; i++) {
            widgets[i] = Widget.getWidget(hndls[i]);
        }
        return widgets;
    }

    public Action getAction(String path) {
        return Action.getAction(gtk_ui_manager_get_action(getHandle(), path));
    }

    /**
     * Parses a string containing a UI definition and merges it with the current
     * contents of self. An enclosing &lt;ui&gt; element is added if it is
     * missing.
     * 
     * @return The merge id for the merged UI. The merge id can be used to
     *         unmerge the UI with <tt>{@link #removeUI}</tt>.
     * @throws RuntimeException
     *             If an error occurred adding the UI from the given String.
     * @deprecated Superceeded by java-gnome 4.0; a method along these lines
     *             may well exist in the new bindings, but if it does it likely
     *             has a different name or signature due to the shift to an
     *             algorithmic mapping of the underlying native libraries.
     */
    public int addUIFromString(String ui) {
        int ret = gtk_ui_manager_add_ui_from_string(getHandle(), ui, ui
                .length());
        if (ret == 0)
            throw new RuntimeException("An error occurred adding "
                    + "UI from String");
        return ret;
    }

    /**
     * Parses a file containing a UI definition and merges it with the current
     * contents of self.
     * 
     * @return The merge id for the merged UI. The merge id can be used to
     *         unmerge the UI with <tt>{@link #removeUI}</tt>.
     * @throws RuntimeException
     *             If an error occurred adding the UI from the given String.
     * @deprecated Superceeded by java-gnome 4.0; a method along these lines
     *             may well exist in the new bindings, but if it does it likely
     *             has a different name or signature due to the shift to an
     *             algorithmic mapping of the underlying native libraries.
     */
    public int addUIFromFile(String filename) {
        int ret = gtk_ui_manager_add_ui_from_file(getHandle(), filename);
        if (ret == 0)
            throw new RuntimeException("An error occurred adding "
                    + "UI from file");
        return ret;
    }

    /**
     * Parse the contents of a <tt>Reader</tt> that contains a UI definition
     * and merges it with the current contents of self. The <tt>reader</tt>
     * will be wrapped in an efficient <tt>Reader</tt> implementation before
     * the contents are read. The <tt>reader</tt> will not be closed by this
     * method.
     * <p>
     * This method reads the contents of the Reader into a String and calls
     * <tt>{@link #addUIFromString}</tt>. You may be interested in using an
     * <tt>InputStreamReader</tt> here if your contents come from an
     * <tt>InputStream</tt> (such as when using
     * <tt>getClass().getResourceAsStream()</tt> or a <tt>URL</tt>).
     * 
     * @return The merge id for the merged UI. The merge id can be used to
     *         unmerge the UI with <tt>{@link #removeUI}</tt>.
     * @throws RuntimeException
     *             If an error occurred adding the UI from the given String.
     * @throws java.io.IOException
     *             If there was an error reading the contents of the
     *             <tt>reader</tt>.
     * @deprecated Superceeded by java-gnome 4.0; a method along these lines
     *             may well exist in the new bindings, but if it does it likely
     *             has a different name or signature due to the shift to an
     *             algorithmic mapping of the underlying native libraries.
     */
    public int addUIFromReader(Reader reader) throws java.io.IOException {
        StringBuffer buf = new StringBuffer();
        BufferedReader bin = new BufferedReader(reader);
        String line;
        while ((line = bin.readLine()) != null) {
            buf.append(line);
            buf.append("\n");
        }
        return addUIFromString(buf.toString());
    }

    public void addUI(int mergeId, String path, String name, String action,
            UIManagerItemType type, boolean top) {
        gtk_ui_manager_add_ui(getHandle(), mergeId, path, name, action, type
                .getValue(), top);
    }

    /**
     * Unmerges the part of this UIManager's content identified by
     * <tt>mergeId</tt>.
     * @deprecated Superceeded by java-gnome 4.0; a method along these lines
     *             may well exist in the new bindings, but if it does it likely
     *             has a different name or signature due to the shift to an
     *             algorithmic mapping of the underlying native libraries.
     */
    public void removeUI(int mergeId) {
        gtk_ui_manager_remove_ui(getHandle(), mergeId);
    }

    public String getUI() {
        return gtk_ui_manager_get_ui(getHandle());
    }

    public void ensureUpdate() {
        gtk_ui_manager_ensure_update(getHandle());
    }

    public int newMergeId() {
        return gtk_ui_manager_new_merge_id(getHandle());
    }

    /***************************************************************************
     * Event Handling
     **************************************************************************/
    private Vector listeners;

    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 anEvtMap) {
        anEvtMap.addEvent("add_widget", "handleAddWidget",
                UIManagerEvent.Type.ADD_WIDGET, UIManagerListener.class);
        anEvtMap.addEvent("actions_changed", "handleActionsChanged",
                UIManagerEvent.Type.ACTIONS_CHANGED, UIManagerListener.class);
        anEvtMap.addEvent("connect_proxy", "handleConnectProxy",
                UIManagerEvent.Type.CONNECT_PROXY, UIManagerListener.class);
        anEvtMap.addEvent("disconnect_proxy", "handleDisconnectProxy",
                UIManagerEvent.Type.DISCONNECT_PROXY, UIManagerListener.class);
        anEvtMap.addEvent("pre_activate", "handlePreActivate",
                UIManagerEvent.Type.PRE_ACTIVATE, UIManagerListener.class);
        anEvtMap.addEvent("post_activate", "handlePostActivate",
                UIManagerEvent.Type.POST_ACTIVATE, UIManagerListener.class);
    }

    /**
     * Register an object to handle spin events.
     * 
     * @see org.gnu.gtk.event.SpinListener
     * @deprecated Superceeded by java-gnome 4.0; a method along these lines
     *             may well exist in the new bindings, but if it does it likely
     *             has a different name or signature due to the shift to an
     *             algorithmic mapping of the underlying native libraries.
     */
    public void addListener(UIManagerListener 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, UIManagerEvent.Type.ACTIONS_CHANGED);
                evtMap.initialize(this, UIManagerEvent.Type.ADD_WIDGET);
                evtMap.initialize(this, UIManagerEvent.Type.CONNECT_PROXY);
                evtMap.initialize(this, UIManagerEvent.Type.DISCONNECT_PROXY);
                evtMap.initialize(this, UIManagerEvent.Type.POST_ACTIVATE);
                evtMap.initialize(this, UIManagerEvent.Type.PRE_ACTIVATE);
                listeners = new Vector();
            }
            listeners.addElement(listener);
        }
    }

    /**
     * Removes a listener
     * 
     * @see #addListener(UIManagerListener)
     * @deprecated Superceeded by java-gnome 4.0; a method along these lines
     *             may well exist in the new bindings, but if it does it likely
     *             has a different name or signature due to the shift to an
     *             algorithmic mapping of the underlying native libraries.
     */
    public void removeListener(UIManagerListener listener) {
        int i = findListener(listeners, listener);
        if (i > -1)
            listeners.remove(i);
        if (0 == listeners.size()) {
            evtMap.uninitialize(this, UIManagerEvent.Type.ACTIONS_CHANGED);
            evtMap.uninitialize(this, UIManagerEvent.Type.ADD_WIDGET);
            evtMap.uninitialize(this, UIManagerEvent.Type.CONNECT_PROXY);
            evtMap.uninitialize(this, UIManagerEvent.Type.DISCONNECT_PROXY);
            evtMap.uninitialize(this, UIManagerEvent.Type.POST_ACTIVATE);
            evtMap.uninitialize(this, UIManagerEvent.Type.PRE_ACTIVATE);
            listeners = null;
        }
    }

    protected void fireUIManagerEvent(UIManagerEvent event) {
        if (null == listeners)
            return;
        int size = listeners.size();
        int i = 0;
        while (i < size) {
            UIManagerListener l = (UIManagerListener) listeners.elementAt(i);
            l.uiManagerEvent(event);
            i++;
        }
    }

    private void handleAddWidget(Handle widget) {
        // UIManagerEvent evt = new UIManagerEvent(this,
        // UIManagerEvent.Type.ADD_WIDGET);
        // evt.setWidget(new Widget(widget));
        // fireUIManagerEvent(evt);
    }

    private void handleActionsChanged() {
        // fireUIManagerEvent(new UIManagerEvent(this,
        // UIManagerEvent.Type.ACTIONS_CHANGED));
    }

    private void handleConnectProxy(Handle action, Handle proxy) {
        // UIManagerEvent evt = new UIManagerEvent(this,
        // UIManagerEvent.Type.CONNECT_PROXY);
        // evt.setAction(new Action(action));
        // evt.setWidget(new Widget(proxy));
        // fireUIManagerEvent(evt);
    }

    private void handleDisconnectProxy(Handle action, Handle proxy) {
        // UIManagerEvent evt = new UIManagerEvent(this,
        // UIManagerEvent.Type.DISCONNECT_PROXY);
        // evt.setAction(new Action(action));
        // evt.setWidget(new Widget(proxy));
        // fireUIManagerEvent(evt);
    }

    private void handlePreActivate(Handle action) {
        // UIManagerEvent evt = new UIManagerEvent(this,
        // UIManagerEvent.Type.PRE_ACTIVATE);
        // evt.setAction(new Action(action));
        // fireUIManagerEvent(evt);
    }

    private void handlePostActivate(Handle action) {
        // UIManagerEvent evt = new UIManagerEvent(this,
        // UIManagerEvent.Type.POST_ACTIVATE);
        // evt.setAction(new Action(action));
        // fireUIManagerEvent(evt);
    }

    /**
     * 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.
     * @deprecated Superceeded by java-gnome 4.0; a method along these lines
     *             may well exist in the new bindings, but if it does it likely
     *             has a different name or signature due to the shift to an
     *             algorithmic mapping of the underlying native libraries.
     */
    protected static int findListener(Vector list, Object listener) {
        if (null == list || null == listener)
            return -1;
        return list.indexOf(listener);
    }

    native static final protected int gtk_ui_manager_get_type();

    native static final protected Handle gtk_ui_manager_new();

    native static final protected void gtk_ui_manager_set_add_tearoffs(
            Handle uim, boolean value);

    native static final protected boolean gtk_ui_manager_get_add_tearoffs(
            Handle uim);

    native static final protected void gtk_ui_manager_insert_action_group(
            Handle uim, Handle actionGroup, int pos);

    native static final protected void gtk_ui_manager_remove_action_group(
            Handle uim, Handle actionGroup);

    native static final protected Handle[] gtk_ui_manager_get_action_groups(
            Handle uim);

    native static final protected Handle gtk_ui_manager_get_accel_group(
            Handle uim);

    native static final protected Handle gtk_ui_manager_get_widget(Handle uim,
            String path);

    native static final protected Handle[] gtk_ui_manager_get_toplevels(
            Handle manager, int types);

    native static final protected Handle gtk_ui_manager_get_action(Handle uim,
            String path);

    native static final protected int gtk_ui_manager_add_ui_from_string(
            Handle uim, String buffer, int length);

    native static final protected int gtk_ui_manager_add_ui_from_file(
            Handle uim, String filename);

    native static final protected void gtk_ui_manager_add_ui(Handle uim,
            int mergeId, String path, String name, String action, int type,
            boolean top);

    native static final protected void gtk_ui_manager_remove_ui(Handle uim,
            int mergeId);

    native static final protected String gtk_ui_manager_get_ui(Handle uim);

    native static final protected void gtk_ui_manager_ensure_update(Handle uim);

    native static final protected int gtk_ui_manager_new_merge_id(Handle uim);
}
