/*  Inti: Integrated Foundation Classes
 *  Copyright (C) 2002 The Inti Development Team.
 *  Copyright (C) 2000 Red Hat, Inc.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
 
//! @file inti/gtk/dialog.h
//! @brief A GtkDialog C++ wrapper interface.

#ifndef INTI_GTK_DIALOG_H
#define INTI_GTK_DIALOG_H

#ifndef INTI_GTK_WINDOW_H
#include <inti/gtk/window.h>
#endif

#ifndef __GTK_DIALOG_H__
#include <gtk/gtkdialog.h>
#endif

namespace Inti {

namespace Gtk {
	
class Button;
class DialogClass;
class HButtonBox;
class VBox;

//! @enum DialogFlags
//! Construction/destruction flags.

enum DialogFlags
{
	DIALOG_MODAL = GTK_DIALOG_MODAL, //!< Calls Gtk::Window::set_modal(true).
	DIALOG_DESTROY_WITH_PARENT = GTK_DIALOG_DESTROY_WITH_PARENT, //!< Calls Gtk::Window::set_destroy_with_parent().
	DIALOG_NO_SEPARATOR = GTK_DIALOG_NO_SEPARATOR //!< No separator bar above the action buttons.
};

//! DialogFlagsField holds one or more flags from the DialogFlags enumeration OR'd together.

typedef unsigned int DialogFlagsField;

//! @enum ResponseType
//! Used to indicate the response_id of the action widget.

enum ResponseType
{
	RESPONSE_NONE = GTK_RESPONSE_NONE, //!< The action widget has no response_id.
	RESPONSE_REJECT = GTK_RESPONSE_REJECT, //!< Returned only if passed as the response for the action widget.
	RESPONSE_ACCEPT = GTK_RESPONSE_ACCEPT, //!< Returned only if passed as the response for the action widget.
	RESPONSE_DELETE_EVENT = GTK_RESPONSE_DELETE_EVENT, //!< The dialog received a 'delete' event.
	RESPONSE_OK = GTK_RESPONSE_OK, //!< The OK response, when the action widget is clicked.
	RESPONSE_CANCEL = GTK_RESPONSE_CANCEL, //!< The cancel response, when the action widget is clicked.
	RESPONSE_CLOSE = GTK_RESPONSE_CLOSE, //!< The close response, when the action widget is clicked.
	RESPONSE_YES = GTK_RESPONSE_YES, //!< The yes response, when the action widget is clicked.
	RESPONSE_NO = GTK_RESPONSE_NO, //!< The no response, when the action widget is clicked.
	RESPONSE_APPLY = GTK_RESPONSE_APPLY, //!< The apply response, when the action widget is clicked.
	RESPONSE_HELP = GTK_RESPONSE_HELP //!< The help response, when the action widget is clicked.
};

//! @class Dialog dialog.h inti/gtk/dialog.h
//! @brief A GtkDialog C++ wrapper class.
//!
//! Dialog boxes are a convenient way to prompt the user for a small amount of input, 
//! eg. to display a message, ask a question, or anything else that does not require
//! extensive effort on the user's part. GTK+ treats a dialog as a window split 
//! vertically. The top section is the client area (a VBox), and is where widgets such
//! as a Label or an Entry should be packed. The bottom area is known as the action area.
//! 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 HSeparator. They can be
//! accessed with client_area() and action_area(), as can be seen from the example below.
//!
//! A 'modal' dialog (that is, one which freezes the rest of the application from user
//! input), can be created by calling Gtk::Window::set_modal() on the dialog. When 
//! constructing a dialog you can also pass the DIALOG_MODAL flag to make a dialog modal.
//! If you add buttons to the dialog, clicking the button will emit a signal called
//! "response" with a response ID that you specified. GTK+ will never assign a meaning
//! to positive response IDs; these are entirely user-defined. But for convenience, you
//! can use the response IDs in the ResponseType enumeration (these all have values less
//! than zero). If a dialog receives a delete event, the "response" signal will be emitted
//! with a response ID of RESPONSE_NONE.
//!
//! If you want to block waiting for a dialog to return before returning control flow to
//! your code, you can call Gtk::Dialog::run(). This method 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. For the simple dialog in the following example, in 
//! reality, you'd probably use MessageDialog to save yourself some effort. But you'd need
//! to create the dialog contents manually if you had more than a simple message in the dialog.
//!
//! <B>Example:</B> A function to open a simple dialog box displaying the message provided.
//! @code
//! void MyWindow::quick_message(const String& message)
//! {
//! 	using namespace Gtk;
//!
//! 	// Creating the dialog on the stack is OK because it's only temporary. When finished, we dispose of it.
//!	Dialog dialog("Message", this, DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, RESPONSE_NONE, 0);
//!
//! 	// Add a label to the dialog to display the message.
//! 	Label *label = new Label(message);
//!  	dialog.client_area()->add(*label);
//! 	label->show();
//!
//! 	// Run the dialog. You must call dispose(), not unref(), because the dialog is in a modal loop.
//! 	if (dialog.run())
//! 		dialog.dispose();
//! }
//! @endcode

class Dialog : public Window
{
	friend class G::Object;
	friend class DialogClass;

	Dialog(const Dialog&);
	Dialog& operator=(const Dialog&);

protected:
//! @name Constructors
//! @{

	explicit Dialog(GtkDialog *dialog, bool reference = false);
	//!< Construct a new Dialog from an existing GtkDialog.
	//!< @param dialog A pointer to a GtkDialog.
	//!< @param reference Set false if the initial reference count is floating, set true if it's not.
	//!<
	//!< <BR>The <EM>dialog</EM> can be a newly created GtkDialog or an existing
	//!< GtkDialog. (see G::Object::Object).

//! @}
//! @name Signal Handlers
//! @{

	virtual void on_response(int response_id);
	//!< Called when an action widget is activated, the dialog receives a delete event,
	//!< or the application programmer calls response().
	//!< @param response_id The response ID of the action widget.
	//!<
	//!< <BR>On a delete event, the response ID is RESPONSE_NONE. Otherwise, it depends
	//!< on which action widget was activated.

	virtual void on_close();
	//!< Called when the user closes the dialog box by pressing the escape key (GDK_Escape).

//! @}
//  Properties

	typedef G::Property<bool> HasSeparatorPropertyType;
	typedef G::PropertyProxy<G::Object, HasSeparatorPropertyType> HasSeparatorPropertyProxy;
	static const HasSeparatorPropertyType has_separator_property;

//  Signals

	typedef G::Signal1<void, int> ResponseSignalType;
	typedef G::SignalProxy<TypeInstance, ResponseSignalType> ResponseSignalProxy;
	static const ResponseSignalType response_signal;

	typedef G::Signal0<void> CloseSignalType;
	typedef G::SignalProxy<TypeInstance, CloseSignalType> CloseSignalProxy;
	static const CloseSignalType close_signal;

public:
//! @name Constructors
//! @{

	Dialog();
	//!< Construct a new Dialog.
	//!< <BR>Widgets should not be packed into this window directly, but into the client area
	//!< and action area, as described above. You can add one or more buttons with add_button()
	//!< or add_buttons().

	Dialog(const String& title, Window *parent = 0, DialogFlagsField flags = DIALOG_DESTROY_WITH_PARENT);
	//!< Construct a new Dialog with the specified title, parent and creation flags.
	//!< @param title The title of the dialog.
	//!< @param parent Transient parent of the dialog, or null.
	//!< @param flags One or more flags from the DialogFlags enumeration OR'd together.
	//!<
	//!< <BR>Widgets should not be packed into this window directly, but into the client area
	//!< and action area, as described above. You can add one or more buttons with add_button()
	//!< or add_buttons(). The flags argument can be used to make the dialog modal (DIALOG_MODAL)
	//!< and/or to have it destroyed along with its transient parent (DIALOG_DESTROY_WITH_PARENT).

	Dialog(const String& title, Window *parent, DialogFlagsField flags, const char *first_button_text, ...);
	//!< Construct a new Dialog with the specified title, parent, creation flags and buttons.
	//!< @param title The title of the dialog.
	//!< @param parent Transient parent of the dialog, or null.
	//!< @param flags One or more flags from the DialogFlags enumeration OR'd together.
	//!< @param first_button_text The stock ID or text to go in first button, or null.
	//!< @param ... The response ID for first button, then additional button text/response ID pairs,
	//!<            ending with null.
	//!<
	//!< <BR>Widgets should not be packed into this window directly, but into the client area and
	//!< action area, as described above. You can add one or more buttons with add_button() or
	//!< add_buttons(). The flags argument can be used to make the dialog modal (DIALOG_MODAL) and/or
	//!< to have it destroyed along with its transient parent (DIALOG_DESTROY_WITH_PARENT). After
	//!< flags, button text/response ID pairs should be listed, with a NULL pointer ending the list.
	//!< Button text can be either a stock ID such as GTK_STOCK_OK, or some arbitrary text. A response
	//!< ID can be any positive number, or one of the values in the ResponseType enumeration. If the
	//!< user clicks one of these dialog buttons, Dialog will emit the "response" signal with the
	//!< corresponding response ID. If a Dialog receives the "delete_event" signal, it will emit
	//!< "response" with a response ID of RESPONSE_DELETE_EVENT. However, destroying a dialog does
	//!< not emit the "response" signal; so be careful relying on "response" when using the
	//!< DIALOG_DESTROY_WITH_PARENT flag. Buttons are from left to right, so the first button in the
	//!< list will be the leftmost button in the dialog.

	virtual ~Dialog();
	//!< Destructor.

//! @}
//! @name Accessors
//! @{

	GtkDialog* gtk_dialog() const { return (GtkDialog*)instance; }
	//!< Get a pointer to the GtkDialog structure.

	GtkDialogClass* gtk_dialog_class() const;
	//!< Get a pointer to the GtkDialogClass structure.

	operator GtkDialog* () const;
	//!< Conversion operator; safely converts a Dialog to a GtkDialog pointer.

	VBox* client_area() const;
	//!< Returns the VBox which is the client area of the dialog box. You pack your widgets into
	//!< this box.

	HButtonBox* action_area() const;
	//!< Returns the HButtonBox which is the action area packed below the dividing HSeparator
	//!< in the dialog. It is treated exactly the same as any other HButtonBox. You add your
	//!< action widgets, usually buttons, to this area.

	bool get_has_separator() const;
	//!< Returns true if the dialog has a separator.

//! @}
//! @name Methods
//! @{

	void add_action_widget(Widget& child, int response_id);
	//!< Adds an activatable widget to the action area of a Dialog, connecting a signal handler
	//!< that will emit the "response" signal on the dialog when the widget is activated.
	//!< @param child An activatable widget.
	//!< @param response_id The response ID for child.
	//!<
	//!< <BR>The widget is appended to the end of the dialog's action area. If you want to add
	//!< a non-activatable widget, simply pack it into the action_area of the Dialog.

	Button* add_button(const char *button_text, int response_id);
	//!< Adds a button with the given text (or a stock button, if button_text is a stock ID)
	//!< and sets things up so that clicking the button will emit the "response" signal with 
	//!< the given response_id.
	//!< @param button_text The text of button, or stock ID.
	//!< @param response_id The response ID for the button.
	//!< @return The button widget that was added.
	//!<
	//!< <BR>The button is appended to the end of the dialog's action area. The button widget
	//!< is returned, but usually you don't need it.

	void add_buttons(const char *first_button_text, ...);
	//!< Adds more buttons, as specified in the null-terminated variable list of
	//!< button text/response ID pairs.
	//!< @param first_button_text The stock ID or text to go in first button, or null.
	//!< @param ... The response ID for first button, then additional button text/response ID
	//!<            pairs, ending with null.
	//!<
	//!< <BR>Widgets should not be packed into this window directly, but into the client_area
	//!< and action_area, as described above. After first_button_text, button text/response ID
	//!< pairs should be listed, with a null pointer ending the list. Button text can be either
	//!< a stock ID such as GTK_STOCK_OK, or some arbitrary text. A response ID can be any 
	//!< positive number, or one of the values in the ResponseType enumeration. If the user
	//!< clicks one of these dialog buttons, Dialog will emit the "response" signal with the
	//!< corresponding response ID. If a Dialog receives the "delete_event" signal, it will emit
	//!< "response" with a response ID of RESPONSE_DELETE_EVENT. However, destroying a dialog does
	//!< not emit the "response" signal; so be careful relying on "response" when using the
	//!< DIALOG_DESTROY_WITH_PARENT flag. Buttons are from left to right, so the first button in
	//!< the list will be the leftmost button in the dialog.
	//!< 
	//!< <EM>This method is the same as calling add_button repeatedly</EM>.

	void set_response_sensitive(int response_id, bool setting);
	//!< Calls Gtk::Widget::set_sensitive(setting) for each widget in the dialog's action area
	//!< with the given response_id. 
	//!< @param response_id A response ID.
	//!< @param setting <EM>true</EM> for sensitive.
	//!<
	//!< <BR>A convenient way to sensitize/desensitize dialog buttons.
	
	void set_default_response(int response_id);
	//!< Sets the last widget in the dialog's action area with the given response_id as the default
	//!< widget for the dialog.
	//!< @param response_id A response ID.
	//!<
	//!< <BR>Pressing "Enter" normally activates the default widget.

	void response(int response_id);
	//!< Emits the "response" signal with the given response ID.
	//!< @param response_id A response ID.
	//!<
	//!< <BR>Used to indicate that the user has responded to the dialog in some way; typically either
	//!< you, or run() will be monitoring the "response" signal and take appropriate action.
	
	int run();
	//!< Blocks in a recursive main loop until the dialog either emits the response signal, or is
	//!< destroyed. 
	//!< @return Response ID
	//!<
	//!< <BR>If the dialog is destroyed during the call to run(), run() returns RESPONSE_NONE. Otherwise,
	//!< it returns the response ID from the "response" signal emission. Before entering the recursive
	//!< main loop, run() calls Gtk::Widget::show() on the dialog for you. Note that you still need to
	//!< show any children of the dialog yourself.
	//!< 
	//!< During run(), the default behavior of "delete_event" is disabled; if the dialog receives
	//!< a "delete_event", it will not be destroyed as windows usually are, and run() will return
	//!< RESPONSE_DELETE_EVENT. Also, during run() the dialog will be modal. You can force run()
	//!< to return at any time by calling response() to emit the "response" signal. Destroying the
	//!< dialog during run() is a very bad idea, because your post-run code won't know whether the
	//!< dialog was destroyed or not. After run() returns, you are responsible for hiding or 
	//!< destroying the dialog if you wish to do so.
	//!<
	//!< <B>Example:</B> Typical usage of this method might be
	//!< @code
	//!< int result = dialog->run();
	//!< switch (result)
	//!< {
	//!< case RESPONSE_ACCEPT:
	//!< 	do_application_specific_something();
 	//!< 	break;
	//!< default:
	//!< 	do_nothing_since_dialog_was_cancelled();
	//!< 	break;
	//!< }
	//!< dialog->dispose();
	//!< @endcode
	
	void set_has_separator(bool setting);
	//!< Sets whether the dialog has a separator above the buttons; <EM>true</EM> by default.
	//!< @param setting <EM>true</EM> to have a separator.

//! @}
//! @name Property Proxies
//! @{

	const HasSeparatorPropertyProxy prop_has_separator()
	{
		return HasSeparatorPropertyProxy(this, &has_separator_property);
	}
	//!< The dialog has a separator bar above its buttons (bool : Read / Write).

//! @}
//! @name Signal Proxies
//! @{

	const ResponseSignalProxy sig_response()
	{
		return ResponseSignalProxy(this, &response_signal);
	}
	//!< Connect to the response_signal; emmitted when an action widget is activated, the
	//!< dialog receives a delete event, or the application programmer calls response().

	const CloseSignalProxy sig_close()
	{
		return CloseSignalProxy(this, &close_signal);
	}
	//!< Connect to the close_signel; emitted when the user closes the dialog box by pressing
	//!< the escape key (GDK_Escape).
	
//! @}
};

} // namespace Gtk

} // namespace Inti

#endif // INTI_GTK_DIALOG_H

