aboutsummaryrefslogtreecommitdiffstats
path: root/e-util
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2010-10-16 02:51:13 +0800
committerMatthew Barnes <mbarnes@redhat.com>2010-10-19 00:32:36 +0800
commit51ebf20237270a785af0aa0e614db42275a05c62 (patch)
tree328b2fe9b7aa111f0cb21f23b11bc2eaf6da3ac8 /e-util
parent2197e6401ec8c5e1b77fa51e085ac068daa39e6a (diff)
downloadgsoc2013-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.c100
-rw-r--r--e-util/e-alert-sink.c36
-rw-r--r--e-util/e-alert.c160
-rw-r--r--e-util/e-alert.h14
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,