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 | |
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.
-rw-r--r-- | doc/reference/shell/eshell-sections.txt | 1 | ||||
-rw-r--r-- | doc/reference/shell/tmpl/e-shell-window.sgml | 5 | ||||
-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 | ||||
-rw-r--r-- | modules/offline-alert/Makefile.am | 26 | ||||
-rw-r--r-- | modules/offline-alert/evolution-offline-alert.error.xml | 13 | ||||
-rw-r--r-- | shell/e-shell-content.c | 34 | ||||
-rw-r--r-- | shell/e-shell-content.h | 3 | ||||
-rw-r--r-- | shell/e-shell-window-actions.c | 2 | ||||
-rw-r--r-- | shell/e-shell-window-private.c | 7 | ||||
-rw-r--r-- | shell/e-shell-window-private.h | 8 | ||||
-rw-r--r-- | shell/e-shell-window.c | 110 | ||||
-rw-r--r-- | shell/e-shell-window.h | 1 | ||||
-rw-r--r-- | widgets/misc/e-alert-bar.c | 112 |
16 files changed, 458 insertions, 174 deletions
diff --git a/doc/reference/shell/eshell-sections.txt b/doc/reference/shell/eshell-sections.txt index 518a2317f5..2c0e82de89 100644 --- a/doc/reference/shell/eshell-sections.txt +++ b/doc/reference/shell/eshell-sections.txt @@ -321,6 +321,7 @@ e_shell_window_get_shell e_shell_window_get_shell_view e_shell_window_peek_shell_view e_shell_window_get_shell_view_action +e_shell_window_get_alert_bar e_shell_window_get_focus_tracker e_shell_window_get_ui_manager e_shell_window_get_action diff --git a/doc/reference/shell/tmpl/e-shell-window.sgml b/doc/reference/shell/tmpl/e-shell-window.sgml index 817e43f29b..e70971d2a8 100644 --- a/doc/reference/shell/tmpl/e-shell-window.sgml +++ b/doc/reference/shell/tmpl/e-shell-window.sgml @@ -39,6 +39,11 @@ EShellWindow </para> +<!-- ##### ARG EShellWindow:alert-bar ##### --> +<para> + +</para> + <!-- ##### ARG EShellWindow:focus-tracker ##### --> <para> 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, diff --git a/modules/offline-alert/Makefile.am b/modules/offline-alert/Makefile.am new file mode 100644 index 0000000000..cdad615797 --- /dev/null +++ b/modules/offline-alert/Makefile.am @@ -0,0 +1,26 @@ +module_LTLIBRARIES = libevolution-module-offline-alert.la + +libevolution_module_offline_alert_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir) \ + -DG_LOG_DOMAIN=\"evolution-offline-alert\" \ + $(GNOME_PLATFORM_CFLAGS) + +libevolution_module_offline_alert_la_SOURCES = \ + evolution-offline-alert.c + +libevolution_module_offline_alert_la_LIBADD = \ + $(top_builddir)/e-util/libeutil.la \ + $(top_builddir)/shell/libeshell.la \ + $(GNOME_PLATFORM_LIBS) + +libevolution_module_offline_alert_la_LDFLAGS = \ + -module -avoid-version $(NO_UNDEFINED) + +error_DATA = evolution-offline-alert.error +errordir = $(privdatadir)/errors +@EVO_PLUGIN_RULE@ + +EXTRA_DIST = evolution-offline-alert.error.xml + +-include $(top_srcdir)/git.mk diff --git a/modules/offline-alert/evolution-offline-alert.error.xml b/modules/offline-alert/evolution-offline-alert.error.xml new file mode 100644 index 0000000000..65a1ec7099 --- /dev/null +++ b/modules/offline-alert/evolution-offline-alert.error.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<error-list domain="offline-alert"> + <error id="offline" type="info"> + <_primary>Evolution is currently offline.</_primary> + <_secondary>Click 'Work Online' to return to online mode.</_secondary> + <button _label="_Dismiss" response="GTK_RESPONSE_OK"/> + </error> + <error id="no-network" type="info"> + <_primary>Evolution is currently offline due to a network outage.</_primary> + <_secondary>Evolution will return to online mode once a network connection is established.</secondary> + <button _label="_Dismiss" response="GTK_RESPONSE_OK"/> + </error> +</error-list> diff --git a/shell/e-shell-content.c b/shell/e-shell-content.c index 2648783831..afd69ffdfa 100644 --- a/shell/e-shell-content.c +++ b/shell/e-shell-content.c @@ -36,6 +36,7 @@ #include "e-util/e-alert-dialog.h" #include "filter/e-rule-editor.h" #include "widgets/misc/e-action-combo-box.h" +#include "widgets/misc/e-alert-bar.h" #include "widgets/misc/e-hinted-entry.h" #include "e-shell-backend.h" @@ -244,29 +245,35 @@ shell_content_size_allocate (GtkWidget *widget, GtkAllocation child_allocation; GtkRequisition child_requisition; GtkWidget *child; + gint remaining_height; priv = E_SHELL_CONTENT_GET_PRIVATE (widget); + remaining_height = allocation->height; gtk_widget_set_allocation (widget, allocation); child_allocation.x = allocation->x; + child_allocation.y = allocation->y; child_allocation.width = allocation->width; - /* Alert bar gets to be as tall as it wants. */ + child_requisition.height = 0; + + /* Alert bar gets to be as tall as it wants (if visible). */ child = priv->alert_bar; - child_allocation.y = allocation->y; + child_allocation.y += child_requisition.height; if (gtk_widget_get_visible (child)) gtk_widget_size_request (child, &child_requisition); else child_requisition.height = 0; + remaining_height -= child_requisition.height; child_allocation.height = child_requisition.height; gtk_widget_size_allocate (child, &child_allocation); - /* So does the search bar (if we have one). */ + /* Search bar gets to be as tall as it wants (if we have one). */ child = priv->searchbar; child_allocation.y += child_requisition.height; @@ -276,6 +283,7 @@ shell_content_size_allocate (GtkWidget *widget, else child_requisition.height = 0; + remaining_height -= child_requisition.height; child_allocation.height = child_requisition.height; if (child != NULL) @@ -284,8 +292,7 @@ shell_content_size_allocate (GtkWidget *widget, /* The GtkBin child gets whatever vertical space is left. */ child_allocation.y += child_requisition.height; - child_allocation.height = - allocation->height - child_requisition.height; + child_allocation.height = remaining_height; child = gtk_bin_get_child (GTK_BIN (widget)); if (child != NULL) @@ -346,9 +353,8 @@ shell_content_submit_alert (EAlertSink *alert_sink, EShellView *shell_view; EShellWindow *shell_window; EShellContent *shell_content; - EAlertBar *alert_bar; + GtkWidget *alert_bar; GtkWidget *dialog; - GtkWindow *parent; shell_content = E_SHELL_CONTENT (alert_sink); shell_view = e_shell_content_get_shell_view (shell_content); @@ -359,12 +365,13 @@ shell_content_submit_alert (EAlertSink *alert_sink, case GTK_MESSAGE_INFO: case GTK_MESSAGE_WARNING: case GTK_MESSAGE_ERROR: - e_alert_bar_add_alert (alert_bar, alert); + e_alert_bar_add_alert ( + E_ALERT_BAR (alert_bar), alert); break; default: - parent = GTK_WINDOW (shell_window); - dialog = e_alert_dialog_new (parent, alert); + dialog = e_alert_dialog_new ( + GTK_WINDOW (shell_window), alert); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); break; @@ -395,7 +402,8 @@ e_shell_content_class_init (EShellContentClass *class) container_class->remove = shell_content_remove; container_class->forall = shell_content_forall; - /* EShellContent:alert-bar + /** + * EShellContent:alert-bar * * Displays informational and error messages. **/ @@ -543,12 +551,12 @@ e_shell_content_focus_search_results (EShellContent *shell_content) * * Returns: the #EAlertBar for @shell_content **/ -EAlertBar * +GtkWidget * e_shell_content_get_alert_bar (EShellContent *shell_content) { g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), NULL); - return E_ALERT_BAR (shell_content->priv->alert_bar); + return shell_content->priv->alert_bar; } /** diff --git a/shell/e-shell-content.h b/shell/e-shell-content.h index 099c841655..58c20e38ad 100644 --- a/shell/e-shell-content.h +++ b/shell/e-shell-content.h @@ -22,7 +22,6 @@ #ifndef E_SHELL_CONTENT_H #define E_SHELL_CONTENT_H -#include <misc/e-alert-bar.h> #include <shell/e-shell-common.h> /* Standard GObject macros */ @@ -79,7 +78,7 @@ void e_shell_content_set_searchbar (EShellContent *shell_content, guint32 e_shell_content_check_state (EShellContent *shell_content); void e_shell_content_focus_search_results (EShellContent *shell_content); -EAlertBar * e_shell_content_get_alert_bar (EShellContent *shell_content); +GtkWidget * e_shell_content_get_alert_bar (EShellContent *shell_content); struct _EShellView * e_shell_content_get_shell_view (EShellContent *shell_content); const gchar * e_shell_content_get_view_id (EShellContent *shell_content); diff --git a/shell/e-shell-window-actions.c b/shell/e-shell-window-actions.c index 740adcc9f7..a77bd35e97 100644 --- a/shell/e-shell-window-actions.c +++ b/shell/e-shell-window-actions.c @@ -22,9 +22,7 @@ #include "e-shell-window-private.h" #include "e-preferences-window.h" -#include <e-util/e-util-private.h> #include <e-util/e-dialog-utils.h> -#include <e-util/e-alert-dialog.h> #include <e-util/e-print.h> #include <gal-define-views-dialog.h> diff --git a/shell/e-shell-window-private.c b/shell/e-shell-window-private.c index 5c4e1217ca..dee6450bcd 100644 --- a/shell/e-shell-window-private.c +++ b/shell/e-shell-window-private.c @@ -289,10 +289,6 @@ e_shell_window_private_constructed (EShellWindow *shell_window) e_shell_window_actions_init (shell_window); - /* Do this after intializing actions because it - * triggers shell_window_update_close_action_cb(). */ - e_shell_watch_window (shell, window); - accel_group = gtk_ui_manager_get_accel_group (ui_manager); gtk_window_add_accel_group (GTK_WINDOW (shell_window), accel_group); @@ -481,6 +477,8 @@ e_shell_window_private_constructed (EShellWindow *shell_window) id = "org.gnome.evolution.shell"; e_plugin_ui_register_manager (ui_manager, id, shell_window); e_plugin_ui_enable_manager (ui_manager, id); + + e_shell_watch_window (shell, window); } void @@ -514,6 +512,7 @@ e_shell_window_private_dispose (EShellWindow *shell_window) g_hash_table_remove_all (priv->loaded_views); + DISPOSE (priv->alert_bar); DISPOSE (priv->content_pane); DISPOSE (priv->content_notebook); DISPOSE (priv->sidebar_notebook); diff --git a/shell/e-shell-window-private.h b/shell/e-shell-window-private.h index 960fc74773..79f15acb68 100644 --- a/shell/e-shell-window-private.h +++ b/shell/e-shell-window-private.h @@ -27,9 +27,16 @@ #include <string.h> #include <glib/gi18n.h> +#include <gconf/gconf-client.h> + #include <e-util/e-util.h> +#include <e-util/e-util-private.h> +#include <e-util/e-alert-dialog.h> +#include <e-util/e-alert-sink.h> +#include <e-util/e-extensible.h> #include <e-util/e-plugin-ui.h> #include <e-util/gconf-bridge.h> +#include <widgets/misc/e-alert-bar.h> #include <widgets/misc/e-import-assistant.h> #include <widgets/misc/e-menu-tool-button.h> #include <widgets/misc/e-online-button.h> @@ -87,6 +94,7 @@ struct _EShellWindowPrivate { /*** Widgetry ***/ + GtkWidget *alert_bar; GtkWidget *content_pane; GtkWidget *content_notebook; GtkWidget *sidebar_notebook; diff --git a/shell/e-shell-window.c b/shell/e-shell-window.c index 7704fb5e71..4f4e5c1b82 100644 --- a/shell/e-shell-window.c +++ b/shell/e-shell-window.c @@ -27,15 +27,10 @@ #include "e-shell-window-private.h" -#include <gconf/gconf-client.h> - -#include <e-util/e-extensible.h> -#include <e-util/e-plugin-ui.h> -#include <e-util/e-util-private.h> - enum { PROP_0, PROP_ACTIVE_VIEW, + PROP_ALERT_BAR, PROP_FOCUS_TRACKER, PROP_GEOMETRY, PROP_SAFE_MODE, @@ -54,11 +49,17 @@ enum { static gulong signals[LAST_SIGNAL]; +/* Forward Declarations */ +static void e_shell_window_alert_sink_init + (EAlertSinkInterface *interface); + G_DEFINE_TYPE_WITH_CODE ( EShellWindow, e_shell_window, GTK_TYPE_WINDOW, G_IMPLEMENT_INTERFACE ( + E_TYPE_ALERT_SINK, e_shell_window_alert_sink_init) + G_IMPLEMENT_INTERFACE ( E_TYPE_EXTENSIBLE, NULL)) static void @@ -254,6 +255,12 @@ shell_window_get_property (GObject *object, E_SHELL_WINDOW (object))); return; + case PROP_ALERT_BAR: + g_value_set_object ( + value, e_shell_window_get_alert_bar ( + E_SHELL_WINDOW (object))); + return; + case PROP_FOCUS_TRACKER: g_value_set_object ( value, e_shell_window_get_focus_tracker ( @@ -492,19 +499,29 @@ shell_window_construct_sidebar (EShellWindow *shell_window) static GtkWidget * shell_window_construct_content (EShellWindow *shell_window) { - GtkWidget *notebook; + GtkWidget *box; + GtkWidget *widget; - notebook = gtk_notebook_new (); - gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE); - gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE); - shell_window->priv->content_notebook = g_object_ref_sink (notebook); - gtk_widget_show (notebook); + box = gtk_vbox_new (FALSE, 0); + gtk_widget_show (box); + + widget = e_alert_bar_new (); + gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0); + shell_window->priv->alert_bar = g_object_ref (widget); + /* EAlertBar controls its own visibility. */ + + widget = gtk_notebook_new (); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE); + gtk_notebook_set_show_border (GTK_NOTEBOOK (widget), FALSE); + gtk_box_pack_start (GTK_BOX (box), widget, TRUE, TRUE, 0); + shell_window->priv->content_notebook = g_object_ref (widget); + gtk_widget_show (widget); g_signal_connect ( shell_window, "notify::active-view", - G_CALLBACK (shell_window_set_notebook_page), notebook); + G_CALLBACK (shell_window_set_notebook_page), widget); - return notebook; + return box; } static GtkWidget * @@ -676,6 +693,34 @@ shell_window_realize (GtkWidget *widget) } static void +shell_window_submit_alert (EAlertSink *alert_sink, + EAlert *alert) +{ + EShellWindow *shell_window; + GtkWidget *alert_bar; + GtkWidget *dialog; + + shell_window = E_SHELL_WINDOW (alert_sink); + alert_bar = e_shell_window_get_alert_bar (shell_window); + + switch (e_alert_get_message_type (alert)) { + case GTK_MESSAGE_INFO: + case GTK_MESSAGE_WARNING: + case GTK_MESSAGE_ERROR: + e_alert_bar_add_alert ( + E_ALERT_BAR (alert_bar), alert); + break; + + default: + dialog = e_alert_dialog_new ( + GTK_WINDOW (shell_window), alert); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + break; + } +} + +static void e_shell_window_class_init (EShellWindowClass *class) { GObjectClass *object_class; @@ -716,6 +761,21 @@ e_shell_window_class_init (EShellWindowClass *class) G_PARAM_READWRITE)); /** + * EShellWindow:alert-bar + * + * Displays informational and error messages. + **/ + g_object_class_install_property ( + object_class, + PROP_ALERT_BAR, + g_param_spec_object ( + "alert-bar", + "Alert Bar", + "Displays informational and error messages", + E_TYPE_ALERT_BAR, + G_PARAM_READABLE)); + + /** * EShellWindow:focus-tracker * * The shell window's #EFocusTracker. @@ -876,6 +936,12 @@ e_shell_window_class_init (EShellWindowClass *class) } static void +e_shell_window_alert_sink_init (EAlertSinkInterface *interface) +{ + interface->submit_alert = shell_window_submit_alert; +} + +static void e_shell_window_init (EShellWindow *shell_window) { shell_window->priv = E_SHELL_WINDOW_GET_PRIVATE (shell_window); @@ -1036,6 +1102,22 @@ e_shell_window_get_shell_view_action (EShellWindow *shell_window, } /** + * e_shell_window_get_alert_bar: + * @shell_window: an #EShellWindow + * + * Returns the #EAlertBar used to display informational and error messages. + * + * Returns: the #EAlertBar for @shell_window + **/ +GtkWidget * +e_shell_window_get_alert_bar (EShellWindow *shell_window) +{ + g_return_val_if_fail (E_IS_SHELL_WINDOW (shell_window), NULL); + + return shell_window->priv->alert_bar; +} + +/** * e_shell_window_get_focus_tracker: * @shell_window: an #EShellWindow * diff --git a/shell/e-shell-window.h b/shell/e-shell-window.h index 8e29093ed7..25e039a0ab 100644 --- a/shell/e-shell-window.h +++ b/shell/e-shell-window.h @@ -96,6 +96,7 @@ struct _EShellView * GtkAction * e_shell_window_get_shell_view_action (EShellWindow *shell_window, const gchar *view_name); +GtkWidget * e_shell_window_get_alert_bar (EShellWindow *shell_window); EFocusTracker * e_shell_window_get_focus_tracker (EShellWindow *shell_window); GtkUIManager * e_shell_window_get_ui_manager (EShellWindow *shell_window); diff --git a/widgets/misc/e-alert-bar.c b/widgets/misc/e-alert-bar.c index b0509785f4..6e81bece1f 100644 --- a/widgets/misc/e-alert-bar.c +++ b/widgets/misc/e-alert-bar.c @@ -21,11 +21,14 @@ #include <config.h> #include <glib/gi18n-lib.h> +#include "e-util/e-alert-action.h" + #define E_ALERT_BAR_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_ALERT_BAR, EAlertBarPrivate)) -#define ICON_SIZE GTK_ICON_SIZE_DIALOG +/* GTK_ICON_SIZE_DIALOG is a tad too big. */ +#define ICON_SIZE GTK_ICON_SIZE_DND struct _EAlertBarPrivate { GQueue alerts; @@ -46,8 +49,8 @@ alert_bar_show_alert (EAlertBar *alert_bar) GtkLabel *label; GtkInfoBar *info_bar; GtkWidget *action_area; - EAlertButton *buttons; EAlert *alert; + GList *actions; GList *children; GtkMessageType message_type; const gchar *stock_id; @@ -69,22 +72,27 @@ alert_bar_show_alert (EAlertBar *alert_bar) } /* Add new buttons. */ - buttons = e_alert_peek_buttons (alert); - if (buttons == NULL) { - gtk_info_bar_add_button ( - info_bar, _("_Dismiss"), GTK_RESPONSE_CLOSE); - } else while (buttons != NULL) { - const gchar *button_text; - - if (buttons->stock != NULL) - button_text = buttons->stock; - else - button_text = buttons->label; - - gtk_info_bar_add_button ( - info_bar, button_text, buttons->response); - - buttons = buttons->next; + 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 gtk_info_bar_response(), so + * we can add buttons directly to the action + * area without knowning 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, FALSE, 0); + + actions = g_list_next (actions); } response_id = e_alert_get_default_response (alert); @@ -109,51 +117,65 @@ alert_bar_show_alert (EAlertBar *alert_bar) } static void -alert_bar_dispose (GObject *object) +alert_bar_response_cb (EAlert *alert, + gint response_id, + EAlertBar *alert_bar) { - EAlertBarPrivate *priv; - - priv = E_ALERT_BAR_GET_PRIVATE (object); - - while (!g_queue_is_empty (&priv->alerts)) - g_object_unref (g_queue_pop_head (&priv->alerts)); + GQueue *queue; + EAlert *head; + GList *link; + gboolean was_head; + + queue = &alert_bar->priv->alerts; + head = g_queue_peek_head (queue); + was_head = (alert == head); + + g_signal_handlers_disconnect_by_func ( + alert, alert_bar_response_cb, alert_bar); + + /* XXX Would be easier if g_queue_remove() returned a boolean. */ + link = g_queue_find (queue, alert); + if (link != NULL) { + g_queue_delete_link (queue, link); + g_object_unref (alert); + } - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (e_alert_bar_parent_class)->dispose (object); + if (g_queue_is_empty (queue)) + gtk_widget_hide (GTK_WIDGET (alert_bar)); + else if (was_head) { + GtkInfoBar *info_bar = GTK_INFO_BAR (alert_bar); + gtk_info_bar_response (info_bar, response_id); + alert_bar_show_alert (alert_bar); + } } static void -alert_bar_response (GtkInfoBar *info_bar, - gint response_id) +alert_bar_dispose (GObject *object) { - EAlertBar *alert_bar; - EAlert *alert; + EAlertBarPrivate *priv; - alert_bar = E_ALERT_BAR (info_bar); + priv = E_ALERT_BAR_GET_PRIVATE (object); - alert = g_queue_pop_head (&alert_bar->priv->alerts); - e_alert_response (alert, response_id); - g_object_unref (alert); + while (!g_queue_is_empty (&priv->alerts)) { + EAlert *alert = g_queue_pop_head (&priv->alerts); + g_signal_handlers_disconnect_by_func ( + alert, alert_bar_response_cb, object); + g_object_unref (alert); + } - if (!g_queue_is_empty (&alert_bar->priv->alerts)) - alert_bar_show_alert (alert_bar); - else - gtk_widget_hide (GTK_WIDGET (alert_bar)); + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_alert_bar_parent_class)->dispose (object); } static void e_alert_bar_class_init (EAlertBarClass *class) { GObjectClass *object_class; - GtkInfoBarClass *info_bar_class; g_type_class_add_private (class, sizeof (EAlertBarPrivate)); object_class = G_OBJECT_CLASS (class); object_class->dispose = alert_bar_dispose; - - info_bar_class = GTK_INFO_BAR_CLASS (class); - info_bar_class->response = alert_bar_response; } static void @@ -230,6 +252,10 @@ e_alert_bar_add_alert (EAlertBar *alert_bar, g_return_if_fail (E_IS_ALERT_BAR (alert_bar)); g_return_if_fail (E_IS_ALERT (alert)); + g_signal_connect ( + alert, "response", + G_CALLBACK (alert_bar_response_cb), alert_bar); + g_queue_push_head (&alert_bar->priv->alerts, g_object_ref (alert)); alert_bar_show_alert (alert_bar); |