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

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

	/**
	 * 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 RESPONSE_NONE.
	 * 
	 * @return The response that indicates which button the user pressed or
	 * RESPONSE_NONE if the window was destroyed.
	 */
	public int run() {
		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) {
		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 buttonText 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) {
		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) {
		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) {
		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() {
	    Handle hndl = getVbox(getHandle());
		GObject obj = getGObjectFromHandle(hndl);
		if (null != obj)
			return (VBox)obj;
		return new VBox(hndl);
	}

	/**
	 * The lower area of the Dialog where the buttons are located is a HButtonBox.
	 * This methos will return that container.
	 * 
	 * @return The HButton box that is known as the action area.
	 */
	public HButtonBox getActionArea() {
	    Handle hndl = Dialog.getActionArea(getHandle());
		GObject obj = getGObjectFromHandle(hndl);
		if (null != obj) 
			return (HButtonBox)obj;
		return new HButtonBox(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) {
		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() {
		return Dialog.gtk_dialog_get_has_separator(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) {
		// 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) {
		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) {
		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("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);

}
