diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2010-10-16 02:51:13 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2010-10-19 00:32:36 +0800 |
commit | 51ebf20237270a785af0aa0e614db42275a05c62 (patch) | |
tree | 328b2fe9b7aa111f0cb21f23b11bc2eaf6da3ac8 /e-util | |
parent | 2197e6401ec8c5e1b77fa51e085ac068daa39e6a (diff) | |
download | gsoc2013-evolution-51ebf20237270a785af0aa0e614db42275a05c62.tar gsoc2013-evolution-51ebf20237270a785af0aa0e614db42275a05c62.tar.gz gsoc2013-evolution-51ebf20237270a785af0aa0e614db42275a05c62.tar.bz2 gsoc2013-evolution-51ebf20237270a785af0aa0e614db42275a05c62.tar.lz gsoc2013-evolution-51ebf20237270a785af0aa0e614db42275a05c62.tar.xz gsoc2013-evolution-51ebf20237270a785af0aa0e614db42275a05c62.tar.zst gsoc2013-evolution-51ebf20237270a785af0aa0e614db42275a05c62.zip |
EAlert: Allow arbitrary actions to be added.
You can now amend the predefined actions in an EAlert by calling
e_alert_add_action(). Useful for adding actions from an existing
GtkUIManager.
Call e_alert_peek_actions() to obtain a combined list of predefined
and custom actions. These will typically serve as "related" actions
for GtkButtons (cf. gtk_activatable_set_related_action()).
Also, both EShellWindow and EShellView now implement EAlertSink. Use
EShellWindow for application-wide alerts, EShellView for view-specific
alerts.
Diffstat (limited to 'e-util')
-rw-r--r-- | e-util/e-alert-dialog.c | 100 | ||||
-rw-r--r-- | e-util/e-alert-sink.c | 36 | ||||
-rw-r--r-- | e-util/e-alert.c | 160 | ||||
-rw-r--r-- | e-util/e-alert.h | 14 |
4 files changed, 214 insertions, 96 deletions
diff --git a/e-util/e-alert-dialog.c b/e-util/e-alert-dialog.c index 4d6fcd8e6e..888c9121f9 100644 --- a/e-util/e-alert-dialog.c +++ b/e-util/e-alert-dialog.c @@ -22,10 +22,13 @@ */ #include "e-alert-dialog.h" + #include "e-util.h" +#include "e-alert-action.h" -#define E_ALERT_DIALOG_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((o), E_TYPE_ALERT_DIALOG, EAlertDialogPrivate)) +#define E_ALERT_DIALOG_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_ALERT_DIALOG, EAlertDialogPrivate)) struct _EAlertDialogPrivate { GtkWindow *parent; @@ -94,6 +97,9 @@ alert_dialog_dispose (GObject *object) priv = E_ALERT_DIALOG_GET_PRIVATE (object); if (priv->alert) { + g_signal_handlers_disconnect_matched ( + priv->alert, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, object); g_object_unref (priv->alert); priv->alert = NULL; } @@ -105,61 +111,67 @@ alert_dialog_dispose (GObject *object) static void alert_dialog_constructed (GObject *object) { - EAlertDialog *self = (EAlertDialog*) object; EAlert *alert; - EAlertButton *b; + EAlertDialog *dialog; GtkWidget *action_area; GtkWidget *content_area; GtkWidget *container; GtkWidget *widget; PangoAttribute *attr; PangoAttrList *list; + GList *actions; const gchar *primary, *secondary; - g_return_if_fail (self != NULL); - - alert = e_alert_dialog_get_alert (E_ALERT_DIALOG (self)); + dialog = E_ALERT_DIALOG (object); + alert = e_alert_dialog_get_alert (dialog); - gtk_window_set_title (GTK_WINDOW (self), " "); + gtk_window_set_title (GTK_WINDOW (dialog), " "); - action_area = gtk_dialog_get_action_area (GTK_DIALOG (self)); - content_area = gtk_dialog_get_content_area (GTK_DIALOG (self)); + action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog)); + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); #if !GTK_CHECK_VERSION(2,90,7) - g_object_set (self, "has-separator", FALSE, NULL); + g_object_set (dialog, "has-separator", FALSE, NULL); #endif - gtk_widget_ensure_style (GTK_WIDGET (self)); + gtk_widget_ensure_style (GTK_WIDGET (dialog)); gtk_container_set_border_width (GTK_CONTAINER (action_area), 12); gtk_container_set_border_width (GTK_CONTAINER (content_area), 0); - gtk_window_set_destroy_with_parent (GTK_WINDOW (self), TRUE); - - b = e_alert_peek_buttons (alert); - if (b == NULL) { - gtk_dialog_add_button ( - GTK_DIALOG (self), GTK_STOCK_OK, GTK_RESPONSE_OK); - } else { - for (; b; b=b->next) { - if (b->stock) { - if (b->label != NULL) - gtk_dialog_add_button ( - GTK_DIALOG (self), - b->label, b->response); - else - gtk_dialog_add_button ( - GTK_DIALOG (self), - b->stock, b->response); - } else - gtk_dialog_add_button ( - GTK_DIALOG (self), - b->label, b->response); - } + gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE); + + /* Forward EAlert::response signals to GtkDialog::response. */ + g_signal_connect_swapped ( + alert, "response", + G_CALLBACK (gtk_dialog_response), dialog); + + /* Add buttons from actions. */ + actions = e_alert_peek_actions (alert); + while (actions != NULL) { + GtkWidget *button; + + /* These actions are already wired to trigger an + * EAlert::response signal when activated, which + * will in turn call to gtk_dialog_response(), + * so we can add buttons directly to the action + * area without knowing their response IDs. */ + + button = gtk_button_new (); + + gtk_activatable_set_related_action ( + GTK_ACTIVATABLE (button), + GTK_ACTION (actions->data)); + + gtk_box_pack_end ( + GTK_BOX (action_area), + button, FALSE, TRUE, 0); + + actions = g_list_next (actions); } if (e_alert_get_default_response (alert)) gtk_dialog_set_default_response ( - GTK_DIALOG (self), + GTK_DIALOG (dialog), e_alert_get_default_response (alert)); widget = gtk_hbox_new (FALSE, 12); @@ -210,20 +222,9 @@ alert_dialog_constructed (GObject *object) } static void -alert_dialog_response (GtkDialog *dialog, - gint response_id) -{ - EAlert *alert; - - alert = e_alert_dialog_get_alert (E_ALERT_DIALOG (dialog)); - e_alert_response (alert, response_id); -} - -static void e_alert_dialog_class_init (EAlertDialogClass *class) { GObjectClass *object_class; - GtkDialogClass *dialog_class; g_type_class_add_private (class, sizeof (EAlertDialogPrivate)); @@ -233,9 +234,6 @@ e_alert_dialog_class_init (EAlertDialogClass *class) object_class->dispose = alert_dialog_dispose; object_class->constructed = alert_dialog_constructed; - dialog_class = GTK_DIALOG_CLASS (class); - dialog_class->response = alert_dialog_response; - g_object_class_install_property ( object_class, PROP_ALERT, @@ -250,9 +248,9 @@ e_alert_dialog_class_init (EAlertDialogClass *class) } static void -e_alert_dialog_init (EAlertDialog *self) +e_alert_dialog_init (EAlertDialog *dialog) { - self->priv = E_ALERT_DIALOG_GET_PRIVATE (self); + dialog->priv = E_ALERT_DIALOG_GET_PRIVATE (dialog); } GtkWidget * diff --git a/e-util/e-alert-sink.c b/e-util/e-alert-sink.c index de676ea778..f26f114c71 100644 --- a/e-util/e-alert-sink.c +++ b/e-util/e-alert-sink.c @@ -35,16 +35,13 @@ G_DEFINE_INTERFACE ( GTK_TYPE_WIDGET) static void -alert_sink_submit_alert (EAlertSink *alert_sink, - EAlert *alert) +alert_sink_fallback (GtkWidget *widget, + EAlert *alert) { GtkWidget *dialog; gpointer parent; - /* This is just a lame fallback handler. Implementors - * are strongly encouraged to override this method. */ - - parent = gtk_widget_get_toplevel (GTK_WIDGET (alert_sink)); + parent = gtk_widget_get_toplevel (widget); parent = gtk_widget_is_toplevel (parent) ? parent : NULL; dialog = e_alert_dialog_new (parent, alert); @@ -53,6 +50,15 @@ alert_sink_submit_alert (EAlertSink *alert_sink, } static void +alert_sink_submit_alert (EAlertSink *alert_sink, + EAlert *alert) +{ + /* This is just a lame fallback handler. Implementors + * are strongly encouraged to override this method. */ + alert_sink_fallback (GTK_WIDGET (alert_sink), alert); +} + +static void e_alert_sink_default_init (EAlertSinkInterface *interface) { interface->submit_alert = alert_sink_submit_alert; @@ -67,7 +73,7 @@ e_alert_sink_default_init (EAlertSinkInterface *interface) * well-defined behavior. It's up to the widget implementing the #EAlertSink * interface to decide what to do with them. * - * Either @widget or one of its parents must implement #EAlertSink. + * Either @widget or one of its ancestors must implement #EAlertSink. * * The default behavior is to display the @alert in a dialog. **/ @@ -75,18 +81,20 @@ void e_alert_sink_submit_alert (GtkWidget *widget, EAlert *alert) { - EAlertSinkInterface *interface; + GtkWidget *ancestor; g_return_if_fail (GTK_IS_WIDGET (widget)); g_return_if_fail (E_IS_ALERT (alert)); - while (widget != NULL && !E_IS_ALERT_SINK (widget)) - widget = gtk_widget_get_parent (widget); + ancestor = gtk_widget_get_ancestor (widget, E_TYPE_ALERT_SINK); - g_return_if_fail (E_IS_ALERT_SINK (widget)); + if (E_IS_ALERT_SINK (ancestor)) { + EAlertSinkInterface *interface; - interface = E_ALERT_SINK_GET_INTERFACE (widget); - g_return_if_fail (interface->submit_alert != NULL); + interface = E_ALERT_SINK_GET_INTERFACE (ancestor); + g_return_if_fail (interface->submit_alert != NULL); - interface->submit_alert (E_ALERT_SINK (widget), alert); + interface->submit_alert (E_ALERT_SINK (ancestor), alert); + } else + alert_sink_fallback (widget, alert); } diff --git a/e-util/e-alert.c b/e-util/e-alert.c index 1846e6d1fa..96e9078402 100644 --- a/e-util/e-alert.c +++ b/e-util/e-alert.c @@ -38,6 +38,7 @@ #include "e-util.h" #include "e-util-private.h" #include "e-alert.h" +#include "e-alert-action.h" #include "e-alert-sink.h" #define d(x) @@ -46,6 +47,8 @@ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_ALERT, EAlertPrivate)) +typedef struct _EAlertButton EAlertButton; + struct _e_alert { const gchar *id; GtkMessageType message_type; @@ -61,12 +64,19 @@ struct _e_alert_table { GHashTable *alerts; }; +struct _EAlertButton { + EAlertButton *next; + const gchar *stock_id; + const gchar *label; + gint response_id; +}; + static GHashTable *alert_table; /* ********************************************************************** */ static EAlertButton default_ok_button = { - NULL, "gtk-ok", NULL, GTK_RESPONSE_OK + NULL, GTK_STOCK_OK, NULL, GTK_RESPONSE_OK }; static struct _e_alert default_alerts[] = { @@ -86,6 +96,11 @@ struct _EAlertPrivate { struct _e_alert *definition; GtkMessageType message_type; gint default_response; + + /* It may occur to one that we could use a GtkActionGroup here, + * but we need to preserve the button order and GtkActionGroup + * uses a hash table, which does not preserve order. */ + GQueue actions; }; enum { @@ -233,37 +248,37 @@ e_alert_load (const gchar *path) xmlFree (tmp); } } else if (!strcmp((gchar *)scan->name, "button")) { - EAlertButton *b; + EAlertButton *button; gchar *label = NULL; - gchar *stock = NULL; + gchar *stock_id = NULL; - b = g_malloc0 (sizeof (*b)); + button = g_new0 (EAlertButton, 1); tmp = (gchar *)xmlGetProp(scan, (const guchar *)"stock"); if (tmp) { - stock = g_strdup (tmp); - b->stock = stock; + stock_id = g_strdup (tmp); + button->stock_id = stock_id; xmlFree (tmp); } tmp = (gchar *)xmlGetProp(scan, (const guchar *)"label"); if (tmp) { label = g_strdup (dgettext (table->translation_domain, tmp)); - b->label = label; + button->label = label; xmlFree (tmp); } tmp = (gchar *)xmlGetProp(scan, (const guchar *)"response"); if (tmp) { - b->response = map_response (tmp); + button->response_id = map_response (tmp); xmlFree (tmp); } - if (stock == NULL && label == NULL) { + if (stock_id == NULL && label == NULL) { g_warning("Error file '%s': missing button details in error '%s'", path, e->id); - g_free (stock); + g_free (stock_id); g_free (label); - g_free (b); + g_free (button); } else { - lastbutton->next = b; - lastbutton = b; + lastbutton->next = button; + lastbutton = button; } } } @@ -322,6 +337,18 @@ e_alert_load_tables (void) g_free (base); } +static void +alert_action_activate (EAlert *alert, + GtkAction *action) +{ + GObject *object; + gpointer data; + + object = G_OBJECT (action); + data = g_object_get_data (object, "e-alert-response-id"); + e_alert_response (alert, GPOINTER_TO_INT (data)); +} + static gchar * alert_format_string (const gchar *format, GPtrArray *args) @@ -378,8 +405,6 @@ alert_set_tag (EAlert *alert, g_warn_if_fail (definition); alert->priv->definition = definition; - e_alert_set_message_type (alert, definition->message_type); - e_alert_set_default_response (alert, definition->default_response); } static void @@ -463,6 +488,20 @@ alert_get_property (GObject *object, } static void +alert_dispose (GObject *object) +{ + EAlertPrivate *priv; + + priv = E_ALERT_GET_PRIVATE (object); + + while (!g_queue_is_empty (&priv->actions)) + g_object_unref (g_queue_pop_head (&priv->actions)); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_alert_parent_class)->dispose (object); +} + +static void alert_finalize (GObject *object) { EAlertPrivate *priv; @@ -480,6 +519,49 @@ alert_finalize (GObject *object) } static void +alert_constructed (GObject *object) +{ + EAlert *alert; + EAlertButton *button; + struct _e_alert *definition; + gint ii = 0; + + alert = E_ALERT (object); + definition = alert->priv->definition; + + e_alert_set_message_type (alert, definition->message_type); + e_alert_set_default_response (alert, definition->default_response); + + /* Build actions out of the button definitions. */ + button = definition->buttons; + while (button != NULL) { + GtkAction *action; + gchar *action_name; + + action_name = g_strdup_printf ("alert-response-%d", ii++); + + if (button->stock_id != NULL) { + action = gtk_action_new ( + action_name, NULL, NULL, button->stock_id); + e_alert_add_action ( + alert, action, button->response_id); + g_object_unref (action); + + } else if (button->label != NULL) { + action = gtk_action_new ( + action_name, button->label, NULL, NULL); + e_alert_add_action ( + alert, action, button->response_id); + g_object_unref (action); + } + + g_free (action_name); + + button = button->next; + } +} + +static void e_alert_class_init (EAlertClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); @@ -488,7 +570,9 @@ e_alert_class_init (EAlertClass *class) object_class->set_property = alert_set_property; object_class->get_property = alert_get_property; + object_class->dispose = alert_dispose; object_class->finalize = alert_finalize; + object_class->constructed = alert_constructed; g_object_class_install_property ( object_class, @@ -562,9 +646,11 @@ e_alert_class_init (EAlertClass *class) } static void -e_alert_init (EAlert *self) +e_alert_init (EAlert *alert) { - self->priv = E_ALERT_GET_PRIVATE (self); + alert->priv = E_ALERT_GET_PRIVATE (alert); + + g_queue_init (&alert->priv->actions); } /** @@ -760,11 +846,43 @@ e_alert_get_stock_id (EAlert *alert) return stock_id; } -EAlertButton * -e_alert_peek_buttons (EAlert *alert) +void +e_alert_add_action (EAlert *alert, + GtkAction *action, + gint response_id) { - g_return_val_if_fail (alert && alert->priv && alert->priv->definition, NULL); - return alert->priv->definition->buttons; + g_return_if_fail (E_IS_ALERT (alert)); + g_return_if_fail (GTK_ACTION (action)); + + g_object_set_data ( + G_OBJECT (action), "e-alert-response-id", + GINT_TO_POINTER (response_id)); + + g_signal_connect_swapped ( + action, "activate", + G_CALLBACK (alert_action_activate), alert); + + g_queue_push_tail (&alert->priv->actions, g_object_ref (action)); +} + +GList * +e_alert_peek_actions (EAlert *alert) +{ + g_return_val_if_fail (E_IS_ALERT (alert), NULL); + + /* Make sure we have at least one action. Do this on-demand + * in case the XML definition did not specify any actions but + * other actions were added via e_alert_add_action(). */ + if (g_queue_is_empty (&alert->priv->actions)) { + GtkAction *action; + + action = gtk_action_new ( + "alert-response-0", _("_Dismiss"), NULL, NULL); + e_alert_add_action (alert, action, GTK_RESPONSE_CLOSE); + g_object_unref (action); + } + + return g_queue_peek_head_link (&alert->priv->actions); } GtkWidget * diff --git a/e-util/e-alert.h b/e-util/e-alert.h index e0ad92c7d2..f9f0fc8559 100644 --- a/e-util/e-alert.h +++ b/e-util/e-alert.h @@ -61,15 +61,6 @@ typedef struct _EAlert EAlert; typedef struct _EAlertClass EAlertClass; typedef struct _EAlertPrivate EAlertPrivate; -typedef struct _EAlertButton EAlertButton; - -struct _EAlertButton { - EAlertButton *next; - const gchar *stock; - const gchar *label; - gint response; -}; - struct _EAlert { GObject parent; EAlertPrivate *priv; @@ -103,7 +94,10 @@ const gchar * e_alert_get_secondary_text (EAlert *alert); void e_alert_set_secondary_text (EAlert *alert, const gchar *secondary_text); const gchar * e_alert_get_stock_id (EAlert *alert); -EAlertButton * e_alert_peek_buttons (EAlert *alert); +void e_alert_add_action (EAlert *alert, + GtkAction *action, + gint response_id); +GList * e_alert_peek_actions (EAlert *alert); GtkWidget * e_alert_create_image (EAlert *alert, GtkIconSize size); void e_alert_response (EAlert *alert, |