diff options
author | Havoc Pennington <hp@redhat.com> | 2000-10-20 23:14:41 +0000 |
---|---|---|
committer | Havoc Pennington <hp@src.gnome.org> | 2000-10-20 23:14:41 +0000 |
commit | 1c9f0c0bac5e73d80968fc791568f1566e3ec5e6 (patch) | |
tree | a274b213438121a8a69c6a61318322b94164d59e /gtk/gtkdialog.c | |
parent | 779e461e89dc5d423d9d5440d76e1f607b074b21 (diff) | |
download | gtk+-1c9f0c0bac5e73d80968fc791568f1566e3ec5e6.tar.gz |
create some stock buttons with the default accel group (create_image):
2000-10-04 Havoc Pennington <hp@redhat.com>
* gtk/testgtk.c (create_buttons): create some stock buttons
with the default accel group
(create_image): test some new GtkImage features
(make_message_dialog): test GtkMessageDialog
(create_modal_window): fix someone's bizzarro indentation
* gtk/gtkwindow.h, gtk/gtkwindow.c: Implement
GTK_WIN_POS_CENTER_ON_PARENT.
Add "destroy with parent" setting, which means the window goes
away with its transient parent.
(gtk_window_get_default_accel_group): get the default accel group
for the window.
(gtk_window_set_destroy_with_parent): set/unset destroy with
parent flag
(gtk_window_read_rcfiles): invalidate icon set caches
after reloading rcfiles
* gtk/gtkenums.h (GtkWindowPosition): add
GTK_WIN_POS_CENTER_ON_PARENT, which centers a dialog
on its parent window when the dialog is mapped for the first time.
* gtk/gtkmessagedialog.h, gtk/gtkmessagedialog.c: Add
a simple message dialog class
* gtk/gtkdialog.c (gtk_dialog_init): Connect delete event
handler to emit response signal, and maybe later it would
honor a hide_on_delete flag - though that isn't there yet.
Set border width on the vbox to 2, so we get some padding.
Use a button box for the action area.
(gtk_dialog_key_press): synthesize a delete event if Esc
is pressed and the GtkWidget key press handler didn't
handle the escape key.
(gtk_dialog_new_with_buttons): new function creates a dialog
with some default buttons in it.
(gtk_dialog_add_action_widget): add an activatable widget
as a button in the dialog - you can also add a non-activatable
widget by accessing the action area directly.
(gtk_dialog_add_button): add a simple button - stock ID or
label - to the action area
(gtk_dialog_response): emit response signal
(gtk_dialog_run): block waiting for the dialog, return
the response. Override normal delete_event behavior, so that
delete_event does nothing inside gtk_dialog_run().
* gtk/gtkdialog.h, gtk/gtkdialog.c: Add "response" signal
emitted when an action widget is clicked or the dialog gets
delete_event
* gtk/gtk.h: add gtkmessagedialog.h
* gtk/Makefile.am: add gtkmessagedialog.[hc]
2000-10-20 Havoc Pennington <hp@redhat.com>
* gtk/gtk-sections.txt: Add dialog docs
Diffstat (limited to 'gtk/gtkdialog.c')
-rw-r--r-- | gtk/gtkdialog.c | 550 |
1 files changed, 546 insertions, 4 deletions
diff --git a/gtk/gtkdialog.c b/gtk/gtkdialog.c index 41d8a3e236..d21be69c24 100644 --- a/gtk/gtkdialog.c +++ b/gtk/gtkdialog.c @@ -26,14 +26,34 @@ #include "gtkbutton.h" #include "gtkdialog.h" -#include "gtkhbox.h" +#include "gtkhbbox.h" #include "gtkhseparator.h" #include "gtkvbox.h" - +#include "gtksignal.h" +#include "gdkkeysyms.h" +#include "gtkmain.h" static void gtk_dialog_class_init (GtkDialogClass *klass); static void gtk_dialog_init (GtkDialog *dialog); +static gint gtk_dialog_key_press (GtkWidget *widget, + GdkEventKey *key); + +static void gtk_dialog_add_buttons_valist (GtkDialog *dialog, + const gchar *first_button_text, + va_list args); + +static gint gtk_dialog_delete_event_handler (GtkWidget *widget, + GdkEventAny *event, + gpointer user_data); + + +enum { + RESPONSE, + LAST_SIGNAL +}; +static gpointer parent_class; +static guint dialog_signals[LAST_SIGNAL]; GtkType gtk_dialog_get_type (void) @@ -63,6 +83,26 @@ gtk_dialog_get_type (void) static void gtk_dialog_class_init (GtkDialogClass *class) { + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + + object_class = (GtkObjectClass*) class; + widget_class = (GtkWidgetClass*) class; + + parent_class = g_type_class_peek_parent (class); + + dialog_signals[RESPONSE] = + gtk_signal_new ("response", + GTK_RUN_LAST, + GTK_CLASS_TYPE (object_class), + GTK_SIGNAL_OFFSET (GtkDialogClass, response), + gtk_marshal_NONE__INT, + GTK_TYPE_NONE, 1, + GTK_TYPE_INT); + + gtk_object_class_add_signals (object_class, dialog_signals, LAST_SIGNAL); + + widget_class->key_press_event = gtk_dialog_key_press; } static void @@ -70,13 +110,30 @@ gtk_dialog_init (GtkDialog *dialog) { GtkWidget *separator; + /* To avoid breaking old code that prevents destroy on delete event + * by connecting a handler, we have to have the FIRST signal + * connection on the dialog. + */ + gtk_signal_connect (GTK_OBJECT (dialog), + "delete_event", + GTK_SIGNAL_FUNC (gtk_dialog_delete_event_handler), + NULL); + dialog->vbox = gtk_vbox_new (FALSE, 0); + + gtk_container_set_border_width (GTK_CONTAINER (dialog->vbox), 2); + gtk_container_add (GTK_CONTAINER (dialog), dialog->vbox); gtk_widget_show (dialog->vbox); - dialog->action_area = gtk_hbox_new (TRUE, 5); + dialog->action_area = gtk_hbutton_box_new (); + + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog->action_area), + GTK_BUTTONBOX_SPREAD); + gtk_container_set_border_width (GTK_CONTAINER (dialog->action_area), 10); - gtk_box_pack_end (GTK_BOX (dialog->vbox), dialog->action_area, FALSE, TRUE, 0); + gtk_box_pack_end (GTK_BOX (dialog->vbox), dialog->action_area, + FALSE, TRUE, 0); gtk_widget_show (dialog->action_area); separator = gtk_hseparator_new (); @@ -84,8 +141,493 @@ gtk_dialog_init (GtkDialog *dialog) gtk_widget_show (separator); } +static gint +gtk_dialog_delete_event_handler (GtkWidget *widget, + GdkEventAny *event, + gpointer user_data) +{ + /* emit response signal */ + gtk_dialog_response (GTK_DIALOG (widget), GTK_RESPONSE_NONE); + + /* Do the destroy by default */ + return FALSE; +} + +static gint +gtk_dialog_key_press (GtkWidget *widget, + GdkEventKey *key) +{ + GdkEventAny event; + + event.type = GDK_DELETE; + event.window = widget->window; + event.send_event = TRUE; + + if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, key)) + return TRUE; + + if (key->keyval != GDK_Escape) + return FALSE; + + /* Synthesize delete_event on key press. */ + g_object_ref (G_OBJECT (event.window)); + + gtk_main_do_event ((GdkEvent*)&event); + + g_object_unref (G_OBJECT (event.window)); + + return TRUE; +} + GtkWidget* gtk_dialog_new (void) { return GTK_WIDGET (gtk_type_new (GTK_TYPE_DIALOG)); } + +static GtkWidget* +gtk_dialog_new_empty (const gchar *title, + GtkWindow *parent, + GtkDialogFlags flags) +{ + GtkDialog *dialog; + + dialog = GTK_DIALOG (g_object_new (GTK_TYPE_DIALOG, NULL)); + + if (title) + gtk_window_set_title (GTK_WINDOW (dialog), title); + + if (parent) + gtk_window_set_transient_for (GTK_WINDOW (dialog), parent); + + if (flags & GTK_DIALOG_MODAL) + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + + if (flags & GTK_DIALOG_DESTROY_WITH_PARENT) + gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE); + + return GTK_WIDGET (dialog); +} + +/** + * gtk_dialog_new_with_buttons: + * @title: Title of the dialog, or NULL + * @parent: Transient parent of the dialog, or NULL + * @flags: from #GtkDialogFlags + * @first_button_text: stock ID or text to go in first button, or NULL + * @Varargs: response ID for first button, then additional buttons, ending with NULL + * + * Creates a new #GtkDialog with title @title (or NULL for the default + * title; see gtk_window_set_title()) and transient parent @parent (or + * NULL for none; see gtk_window_set_transient_for()). The @flags + * argument can be used to make the dialog modal (GTK_DIALOG_MODAL) + * and/or to have it destroyed along with its transient parent + * (GTK_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_BUTTON_OK, or some arbitrary text. A response ID can be + * any positive number, or one of the values in the #GtkResponseType + * enumeration. If the user clicks one of these dialog buttons, + * #GtkDialog will emit the "response" signal with the corresponding + * response ID. If a #GtkDialog receives the "delete_event" signal, it + * will emit "response" with a response ID of GTK_RESPONSE_NONE. + * However, destroying a dialog does not emit the "response" signal; + * so be careful relying on "response" when using + * the GTK_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. + * + * Here's a simple example: + * <programlisting> + * GtkWidget *dialog = gtk_dialog_new_with_buttons ("My dialog", + * main_app_window, + * GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + * GTK_STOCK_BUTTON_OK, + * GTK_RESPONSE_ACCEPT, + * GTK_STOCK_BUTTON_CANCEL, + * GTK_RESPONSE_NONE, + * NULL); + * </programlisting> + * + * Return value: a new #GtkDialog + **/ +GtkWidget* +gtk_dialog_new_with_buttons (const gchar *title, + GtkWindow *parent, + GtkDialogFlags flags, + const gchar *first_button_text, + ...) +{ + GtkDialog *dialog; + va_list args; + + dialog = GTK_DIALOG (gtk_dialog_new_empty (title, parent, flags)); + + va_start (args, first_button_text); + + gtk_dialog_add_buttons_valist (dialog, + first_button_text, + args); + + va_end (args); + + return GTK_WIDGET (dialog); +} + +typedef struct _ResponseData ResponseData; + +struct _ResponseData +{ + gint response_id; +}; + +static ResponseData* +get_response_data (GtkWidget *widget) +{ + ResponseData *ad = gtk_object_get_data (GTK_OBJECT (widget), + "gtk-dialog-response-data"); + + if (ad == NULL) + { + ad = g_new (ResponseData, 1); + + gtk_object_set_data_full (GTK_OBJECT (widget), + "gtk-dialog-response-data", + ad, + g_free); + } + + return ad; +} + +static void +action_widget_activated (GtkWidget *widget, GtkDialog *dialog) +{ + ResponseData *ad; + gint response_id; + + g_return_if_fail (GTK_IS_DIALOG (dialog)); + + response_id = GTK_RESPONSE_NONE; + + ad = get_response_data (widget); + + g_assert (ad != NULL); + + response_id = ad->response_id; + + gtk_dialog_response (dialog, response_id); +} +/** + * gtk_dialog_add_action_widget: + * @dialog: a #GtkDialog + * @child: an activatable widget + * @response_id: response ID for @child + * + * Adds an activatable widget to the action area of a #GtkDialog, + * 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. If you want to add a + * non-activatable widget, simply pack it into the + * <literal>action_area</literal> field of the #GtkDialog struct. + * + **/ +void +gtk_dialog_add_action_widget (GtkDialog *dialog, + GtkWidget *child, + gint response_id) +{ + ResponseData *ad; + + g_return_if_fail (GTK_IS_DIALOG (dialog)); + g_return_if_fail (GTK_IS_WIDGET (child)); + + ad = get_response_data (child); + + ad->response_id = response_id; + + if (GTK_WIDGET_GET_CLASS (child)->activate_signal != 0) + { + const gchar* name = + gtk_signal_name (GTK_WIDGET_GET_CLASS (child)->activate_signal); + + gtk_signal_connect_while_alive (GTK_OBJECT (child), + name, + GTK_SIGNAL_FUNC (action_widget_activated), + dialog, + GTK_OBJECT (dialog)); + } + else + g_warning ("Only 'activatable' widgets can be packed into the action area of a GtkDialog"); + + gtk_box_pack_end (GTK_BOX (dialog->action_area), + child, + FALSE, TRUE, 5); +} + +/** + * gtk_dialog_add_button: + * @dialog: a #GtkDialog + * @button_text: text of button, or stock ID + * @response_id: response ID for the button + * + * 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. The button is appended to the + * end of the dialog's action area. + * + **/ +void +gtk_dialog_add_button (GtkDialog *dialog, + const gchar *button_text, + gint response_id) +{ + GtkWidget *button; + + g_return_if_fail (GTK_IS_DIALOG (dialog)); + g_return_if_fail (button_text != NULL); + + button = gtk_button_new_stock (button_text, + gtk_window_get_default_accel_group (GTK_WINDOW (dialog))); + + gtk_widget_show (button); + + gtk_dialog_add_action_widget (dialog, + button, + response_id); +} + +static void +gtk_dialog_add_buttons_valist(GtkDialog *dialog, + const gchar *first_button_text, + va_list args) +{ + const gchar* text; + gint response_id; + + if (first_button_text == NULL) + return; + + text = first_button_text; + response_id = va_arg (args, gint); + + while (text != NULL) + { + gtk_dialog_add_button (dialog, text, response_id); + + text = va_arg (args, gchar*); + if (text == NULL) + break; + response_id = va_arg (args, int); + } +} + +/** + * gtk_dialog_add_buttons: + * @dialog: a #GtkDialog + * @first_button_text: button text or stock ID + * @Varargs: response ID for first button, then more text-response_id pairs + * + * Adds more buttons, same as calling gtk_dialog_add_button() + * repeatedly. The variable argument list should be NULL-terminated + * as with gtk_dialog_new_with_buttons(). Each button must have both + * text and response ID. + * + **/ +void +gtk_dialog_add_buttons (GtkDialog *dialog, + const gchar *first_button_text, + ...) +{ + + va_list args; + + va_start (args, first_button_text); + + gtk_dialog_add_buttons_valist (dialog, + first_button_text, + args); + + va_end (args); +} + +/** + * gtk_dialog_response: + * @dialog: a #GtkDialog + * @response_id: response ID + * + * Emits the "response" signal with the given response ID. Used to + * indicate that the user has responded to the dialog in some way; + * typically either you or gtk_dialog_run() will be monitoring the + * "response" signal and take appropriate action. + **/ +void +gtk_dialog_response (GtkDialog *dialog, + gint response_id) +{ + g_return_if_fail (dialog != NULL); + g_return_if_fail (GTK_IS_DIALOG (dialog)); + + gtk_signal_emit (GTK_OBJECT (dialog), + dialog_signals[RESPONSE], + response_id); +} + +typedef struct +{ + GtkDialog *dialog; + gint response_id; + GMainLoop *loop; +} RunInfo; + +static void +shutdown_loop (RunInfo *ri) +{ + if (ri->loop != NULL) + { + g_main_quit (ri->loop); + g_main_destroy (ri->loop); + ri->loop = NULL; + } +} + +static void +run_destroy_handler (GtkDialog *dialog, gpointer data) +{ + RunInfo *ri = data; + + shutdown_loop (ri); +} + +static void +run_response_handler (GtkDialog *dialog, + gint response_id, + gpointer data) +{ + RunInfo *ri; + + ri = data; + + ri->response_id = response_id; + + shutdown_loop (ri); +} + +static gint +run_delete_handler (GtkDialog *dialog, + GdkEventAny *event, + gpointer data) +{ + RunInfo *ri = data; + + shutdown_loop (ri); + + /* emit response signal */ + gtk_dialog_response (dialog, GTK_RESPONSE_NONE); + + return TRUE; /* Do not destroy */ +} + +/** + * gtk_dialog_run: + * @dialog: a #GtkDialog + * + * Blocks in a recursive main loop until the @dialog either emits the + * response signal, or is destroyed. If the dialog is destroyed, + * gtk_dialog_run() returns GTK_RESPONSE_NONE. Otherwise, it returns + * the response ID from the "response" signal emission. Before + * entering the recursive main loop, gtk_dialog_run() calls + * gtk_widget_show() on the dialog for you. Note that you still + * need to show any children of the dialog yourself. + * + * During gtk_dialog_run(), the default behavior of "delete_event" is + * disabled; if the dialog receives "delete_event", it will not be + * destroyed as windows usually are, and gtk_dialog_run() will return + * GTK_RESPONSE_NONE. Also, during gtk_dialog_run() the dialog will be + * modal. You can force gtk_dialog_run() to return at any time by + * calling gtk_dialog_response() to emit the "response" + * signal. Destroying the dialog during gtk_dialog_run() is a very bad + * idea, because your post-run code won't know whether the dialog was + * destroyed or not. + * + * After gtk_dialog_run() returns, you are responsible for hiding or + * destroying the dialog if you wish to do so. + * + * Typical usage of this function might be: + * <programlisting> + * gint result = gtk_dialog_run (GTK_DIALOG (dialog)); + * switch (result) + * { + * case GTK_RESPONSE_ACCEPT: + * do_application_specific_something (); + * break; + * default: + * do_nothing_since_dialog_was_cancelled (); + * break; + * } + * gtk_widget_destroy (dialog); + * </programlisting> + * + * Return value: response ID + **/ +gint +gtk_dialog_run (GtkDialog *dialog) +{ + RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL }; + gboolean was_modal; + guint response_handler; + guint destroy_handler; + guint delete_handler; + + g_return_val_if_fail (GTK_IS_DIALOG (dialog), -1); + + gtk_object_ref (GTK_OBJECT (dialog)); + + if (!GTK_WIDGET_VISIBLE (dialog)) + gtk_widget_show (GTK_WIDGET (dialog)); + + was_modal = GTK_WINDOW (dialog)->modal; + if (!was_modal) + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + + response_handler = + gtk_signal_connect (GTK_OBJECT (dialog), + "response", + GTK_SIGNAL_FUNC (run_response_handler), + &ri); + + destroy_handler = + gtk_signal_connect (GTK_OBJECT (dialog), + "destroy", + GTK_SIGNAL_FUNC (run_destroy_handler), + &ri); + + delete_handler = + gtk_signal_connect (GTK_OBJECT (dialog), + "delete_event", + GTK_SIGNAL_FUNC (run_delete_handler), + &ri); + + ri.loop = g_main_new (FALSE); + + g_main_run (ri.loop); + + g_assert (ri.loop == NULL); + + if (!GTK_OBJECT_DESTROYED (dialog)) + { + if (!was_modal) + gtk_window_set_modal (GTK_WINDOW(dialog), FALSE); + + gtk_signal_disconnect (GTK_OBJECT (dialog), destroy_handler); + gtk_signal_disconnect (GTK_OBJECT (dialog), response_handler); + gtk_signal_disconnect (GTK_OBJECT (dialog), delete_handler); + } + + gtk_object_unref (GTK_OBJECT (dialog)); + + return ri.response_id; +} + + + + |