/*
 * Java-Gnome Bindings Library
 *
 * Copyright 1998-2004 the Java-Gnome Team, all rights reserved.
 *
 * The Java-Gnome bindings library is free software distributed under
 * the terms of the GNU Library General Public License version 2.
 */

package org.gnu.gtk;

import java.util.Vector;

import org.gnu.glib.EventMap;
import org.gnu.glib.EventType;
import org.gnu.glib.GObject;
import org.gnu.glib.Type;
import org.gnu.gtk.event.DialogEvent;
import org.gnu.gtk.event.DialogListener;
import org.gnu.glib.Handle;

/**
 * Dialog boxes are a convenient way to prompt the user for a small amount of
 * input, to display a message, ask a question, or anything else that does not
 * require extensive effort on the user's part.
 * 
 * <p>
 * GTK+ treats a dialog as a window split vertically. The top section is a
 * {@link VBox}, and is where widgets such as a {@link Label} or a
 * {@link Entry} should be packed. The bottom area is known as the
 * <em>actionArea</em>. This is generally used for packing buttons into the
 * dialog which may perform functions such as cancel, ok, or apply. The two
 * areas are separated by a {@link HSeparator}.
 * 
 * <p>
 * A 'modal' dialog (that is, one which freezes the rest of the application from
 * user input), can be created by calling {@link Window#setModal(boolean)} on
 * the dialog.
 * 
 * <p>
 * If you want to block waiting for a dialog to return before returning control
 * flow to your code, you can call {@link #run()}. This function enters a
 * recursive main loop and waits for the user to respond to the dialog,
 * returning the response ID corresponding to the button the user clicked.
 */
public class Dialog extends Window {

    /**
     * Construct a new Dialog object
     */
    public Dialog() {
        super(Dialog.gtk_dialog_new());
    }

    /**
     * Construct a dialog using a handle to a native resource.
     */
    public Dialog(Handle handle) {
        super(handle);
    }

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

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

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

        return obj;
    }

    /**
     * Blocks in a recursive main loop until the <i>dialog</i> either emits a
     * response signal or is destroyed. The return value can be compared against
     * a {@link ResponseType} to determine if a standard return value is
     * emitted. The developer can also set the response type when a button or
     * widget is added to the dialog. If the dialog is destroyed the run method
     * will return {@link ResponseType#NONE}.
     * 
     * @return The response that indicates which button the user pressed or
     *         {@link ResponseType#NONE} if the window was destroyed.
     */
    public int run() {
        checkState();
        return Dialog.gtk_dialog_run(getHandle());
    }

    /**
     * Adds a button with the given text and sets things up so that clicking the
     * button will emit the "response" event with the given
     * <code>responseId</code>.
     * 
     * @param buttonText
     *            The text string to display on the button.
     * @param responseId
     *            The response ID to emit when the user clicks the button that
     *            is to be added.
     */
    public void addButton(String buttonText, int responseId) {
        checkState();
        Dialog.gtk_dialog_add_button(getHandle(), buttonText, responseId);
    }

    /**
     * Adds a button with the given stock button and sets things up so that
     * clicking the button will emit the "response" event with the given
     * <code>responseId</code>.
     * 
     * @param stockItem
     *            The stock ID for the button to be added.
     * @param responseId
     *            The response ID to emit when the user clicks the button that
     *            is to be added.
     */
    public void addButton(GtkStockItem stockItem, int responseId) {
        checkState();
        addButton(stockItem.getString(), responseId);
    }

    /**
     * Adds an activatable widget to the action area of the Dialog, connecting a
     * signal handler that will emit the "response" signal on the dialog when
     * the widget is activated. The Widget is appended to the end of the
     * Dialog's action area.
     * 
     * @param child
     *            An activatable Widget to add to the dialog.
     * @param responseId
     *            The response ID to emit when the user clicks the widget.
     */
    public void addWidget(Widget child, int responseId) {
        checkState();
        Dialog.gtk_dialog_add_action_widget(getHandle(), child.getHandle(),
                responseId);
    }

    /**
     * Sets the last widget in the dialog's action area with the given
     * <i>responseId</i> as the default widget for the dialog. Pressing "Enter"
     * normally activates the default widget.
     * 
     * @param responseId
     *            The response ID.
     */
    public void setDefaultResponse(int responseId) {
        checkState();
        Dialog.gtk_dialog_set_default_response(getHandle(), responseId);
    }

    /**
     * The upper area of the Dialog where widgets can be added is a VBox. This
     * method will return that layout control.
     * 
     * @return The VBox that is the layout control for the dialog.
     */
    public VBox getDialogLayout() {
        checkState();
        Handle hndl = getVbox(getHandle());
        return VBox.getVBox(hndl);
    }

    /**
     * The lower area of the Dialog where the buttons are located is a
     * HButtonBox. This method will return that container.
     * 
     * @return The HButton box that is known as the action area.
     */
    public HButtonBox getActionArea() {
        checkState();
        Handle hndl = Dialog.getActionArea(getHandle());
        return HButtonBox.getHButtonBox(hndl);
    }

    /**
     * Sets whether the dialog has a separator above the buttons. This is
     * <i>true</i> by default.
     * 
     * @param setting
     *            Indicates if a separator should be added to the dialog.
     */
    public void setHasSeparator(boolean setting) {
        checkState();
        Dialog.gtk_dialog_set_has_separator(getHandle(), setting);
    }

    /**
     * Access for whether the dialog has a separator.
     * 
     * @return true if the Dialog has a separator.
     */
    public boolean getHasSeparator() {
        checkState();
        return Dialog.gtk_dialog_get_has_separator(getHandle());
    }

    /**
     * Gets the response id of a widget in the action area of a dialog.
     * 
     * @param widget
     *            a widget in the action area of this Dialog.
     * @return the response id of the widget, or ResponseType.NONE if the widget
     *         doesn't have a response id set.
     */
    public ResponseType getResponseForWidget(Widget widget) {
        checkState();
        return ResponseType.intern(gtk_dialog_get_response_for_widget(
                getHandle(), widget.getHandle()));
    }

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

    /**
     * Listeners for handling dialog events.
     */
    private Vector dialogListeners = null;

    /**
     * Register an object to handle dialog events.
     * 
     * @see org.gnu.gtk.event.DialogListener
     */
    public void addListener(DialogListener listener) {
        checkState();
        // Don't add the listener a second time if it is in the Vector.
        int i = findListener(dialogListeners, listener);
        if (i == -1) {
            if (null == dialogListeners) {
                evtMap.initialize(this, DialogEvent.Type.CLOSE);
                evtMap.initialize(this, DialogEvent.Type.RESPONSE);
                dialogListeners = new Vector();
            }
            dialogListeners.addElement(listener);
        }
    }

    /**
     * Removes a listener
     * 
     * @see #addListener(org.gnu.gtk.event.DialogListener)
     */
    public void removeListener(DialogListener listener) {
        checkState();
        int i = findListener(dialogListeners, listener);
        if (i > -1) {
            dialogListeners.remove(i);
        }
        if (0 == dialogListeners.size()) {
            evtMap.uninitialize(this, DialogEvent.Type.CLOSE);
            evtMap.uninitialize(this, DialogEvent.Type.RESPONSE);
            dialogListeners = null;
        }
    }

    protected void fireButtonEvent(DialogEvent event) {
        if (null == dialogListeners) {
            return;
        }
        int size = dialogListeners.size();
        int i = 0;
        while (i < size) {
            DialogListener dl = (DialogListener) dialogListeners.elementAt(i);
            dl.dialogEvent(event);
            i++;
        }
    }

    private void handleClose() {
        fireButtonEvent(new DialogEvent(this, DialogEvent.Type.CLOSE));
    }

    private void handleResponse(int response) {
        DialogEvent de = new DialogEvent(this, DialogEvent.Type.RESPONSE);
        de.setResponse(response);
        fireButtonEvent(de);
    }

    public Class getEventListenerClass(String signal) {
        checkState();
        Class cls = evtMap.getEventListenerClass(signal);
        if (cls == null)
            cls = super.getEventListenerClass(signal);
        return cls;
    }

    public EventType getEventType(String signal) {
        checkState();
        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("close", "handleClose", DialogEvent.Type.CLOSE,
                DialogListener.class);
        anEvtMap.addEvent("response", "handleResponse",
                DialogEvent.Type.RESPONSE, DialogListener.class);
    }

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

    native static final protected Handle getVbox(Handle cptr);

    native static final protected Handle getActionArea(Handle cptr);

    native static final protected int gtk_dialog_get_type();

    native static final protected Handle gtk_dialog_new();

    native static final protected void gtk_dialog_add_action_widget(
            Handle dialog, Handle child, int responseId);

    native static final protected Handle gtk_dialog_add_button(Handle dialog,
            String buttonText, int responseId);

    native static final protected void gtk_dialog_set_response_sensitive(
            Handle dialog, int responseId, boolean setting);

    native static final protected void gtk_dialog_set_default_response(
            Handle dialog, int responseId);

    native static final protected void gtk_dialog_set_has_separator(
            Handle dialog, boolean setting);

    native static final protected boolean gtk_dialog_get_has_separator(
            Handle dialog);

    native static final protected void gtk_dialog_response(Handle dialog,
            int responseId);

    native static final protected int gtk_dialog_run(Handle dialog);

    // new for gtk 2.8
    native static final protected int gtk_dialog_get_response_for_widget(
            Handle dialog, Handle widget);

}
