/*
* e-activity-bar.c
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* 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 General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <glib/gi18n-lib.h>
#include <libedataserver/libedataserver.h>
#include "e-dialog-widgets.h"
#include "e-misc-utils.h"
#include "e-spinner.h"
#include "e-activity-bar.h"
#define E_ACTIVITY_BAR_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_ACTIVITY_BAR, EActivityBarPrivate))
#define FEEDBACK_PERIOD 1 /* seconds */
#define COMPLETED_ICON_NAME "emblem-default"
struct _EActivityBarPrivate {
EActivity *activity; /* weak reference */
GtkWidget *image; /* not referenced */
GtkWidget *label; /* not referenced */
GtkWidget *cancel; /* not referenced */
GtkWidget *spinner; /* not referenced */
/* If the user clicks the Cancel button, keep the cancelled
* EActivity object alive for a short duration so the user
* gets some visual feedback that cancellation worked. */
guint timeout_id;
};
enum {
PROP_0,
PROP_ACTIVITY
};
G_DEFINE_TYPE (
EActivityBar,
e_activity_bar,
GTK_TYPE_INFO_BAR)
typedef struct _EActivityBarTimeoutData {
EActivityBar *bar;
EActivity *activity;
} EActivityBarTimeoutData;
static void
activity_bar_timeout_data_free (gpointer ptr)
{
EActivityBarTimeoutData *data = ptr;
if (data) {
g_object_unref (data->activity);
g_free (data);
}
}
static gboolean
activity_bar_timeout_reached (gpointer user_data)
{
EActivityBarTimeoutData *data = user_data;
g_return_val_if_fail (data != NULL, FALSE);
g_return_val_if_fail (E_IS_ACTIVITY_BAR (data->bar), FALSE);
if (!g_source_is_destroyed (g_main_current_source ()) &&
g_source_get_id (g_main_current_source ()) == data->bar->priv->timeout_id)
data->bar->priv->timeout_id = 0;
return FALSE;
}
static void
activity_bar_feedback (EActivityBar *bar)
{
EActivity *activity;
EActivityState state;
EActivityBarTimeoutData *data;
activity = e_activity_bar_get_activity (bar);
g_return_if_fail (E_IS_ACTIVITY (activity));
state = e_activity_get_state (activity);
if (state != E_ACTIVITY_CANCELLED && state != E_ACTIVITY_COMPLETED)
return;
if (bar->priv->timeout_id > 0)
g_source_remove (bar->priv->timeout_id);
data = g_new0 (EActivityBarTimeoutData, 1);
data->bar = bar;
data->activity = g_object_ref (activity);
/* Hold a reference on the EActivity for a short
* period so the activity bar stays visible. */
bar->priv->timeout_id = e_named_timeout_add_seconds_full (
G_PRIORITY_LOW, FEEDBACK_PERIOD, activity_bar_timeout_reached,
data, activity_bar_timeout_data_free);
}
static void
activity_bar_update (EActivityBar *bar)
{
EActivity *activity;
EActivityState state;
GCancellable *cancellable;
const gchar *icon_name;
gboolean sensitive;
gboolean visible;
gchar *description;
activity = e_activity_bar_get_activity (bar);
if (activity == NULL) {
gtk_widget_hide (GTK_WIDGET (bar));
return;
}
cancellable = e_activity_get_cancellable (activity);
icon_name = e_activity_get_icon_name (activity);
state = e_activity_get_state (activity);
description = e_activity_describe (activity);
gtk_label_set_text (GTK_LABEL (bar->priv->label), description);
if (state == E_ACTIVITY_CANCELLED) {
PangoAttribute *attr;
PangoAttrList *attr_list;
attr_list = pango_attr_list_new ();
attr = pango_attr_strikethrough_new (TRUE);
pango_attr_list_insert (attr_list, attr);
gtk_label_set_attributes (
GTK_LABEL (bar->priv->label), attr_list);
pango_attr_list_unref (attr_list);
} else
gtk_label_set_attributes (
GTK_LABEL (bar->priv->label), NULL);
if (state == E_ACTIVITY_COMPLETED)
icon_name = COMPLETED_ICON_NAME;
if (state == E_ACTIVITY_CANCELLED) {
gtk_image_set_from_icon_name (
GTK_IMAGE (bar->priv->image),
"process-stop", GTK_ICON_SIZE_BUTTON);
gtk_widget_show (bar->priv->image);
} else if (icon_name != NULL) {
gtk_image_set_from_icon_name (
GTK_IMAGE (bar->priv->image),
icon_name, GTK_ICON_SIZE_BUTTON);
gtk_widget_show (bar->priv->image);
} else {
gtk_widget_hide (bar->priv->image);
}
visible = (cancellable != NULL);
gtk_widget_set_visible (bar->priv->cancel, visible);
sensitive = (state == E_ACTIVITY_RUNNING);
gtk_widget_set_sensitive (bar->priv->cancel, sensitive);
visible = (description != NULL && *description != '\0');
gtk_widget_set_visible (GTK_WIDGET (bar), visible);
g_free (description);
}
static void
activity_bar_cancel (EActivityBar *bar)
{
EActivity *activity;
activity = e_activity_bar_get_activity (bar);
g_return_if_fail (E_IS_ACTIVITY (activity));
e_activity_cancel (activity);
activity_bar_update (bar);
}
static void
activity_bar_weak_notify_cb (EActivityBar *bar,
GObject *where_the_object_was)
{
g_return_if_fail (E_IS_ACTIVITY_BAR (bar));
bar->priv->activity = NULL;
e_activity_bar_set_activity (bar, NULL);
}
static void
activity_bar_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_ACTIVITY:
e_activity_bar_set_activity (
E_ACTIVITY_BAR (object),
g_value_get_object (value));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
activity_bar_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_ACTIVITY:
g_value_set_object (
value, e_activity_bar_get_activity (
E_ACTIVITY_BAR (object)));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
activity_bar_dispose (GObject *object)
{
EActivityBarPrivate *priv;
priv = E_ACTIVITY_BAR_GET_PRIVATE (object);
if (priv->timeout_id > 0) {
g_source_remove (priv->timeout_id);
priv->timeout_id = 0;
}
if (priv->activity != NULL) {
g_signal_handlers_disconnect_matched (
priv->activity, G_SIGNAL_MATCH_DATA,
0, 0, NULL, NULL, object);
g_object_weak_unref (
G_OBJECT (priv->activity), (GWeakNotify)
activity_bar_weak_notify_cb, object);
priv->activity = NULL;
}
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_activity_bar_parent_class)->dispose (object);
}
static void
e_activity_bar_class_init (EActivityBarClass *class)
{
GObjectClass *object_class;
g_type_class_add_private (class, sizeof (EActivityBarPrivate));
object_class = G_OBJECT_CLASS (class);
object_class->set_property = activity_bar_set_property;
object_class->get_property = activity_bar_get_property;
object_class->dispose = activity_bar_dispose;
g_object_class_install_property (
object_class,
PROP_ACTIVITY,
g_param_spec_object (
"activity",
NULL,
NULL,
E_TYPE_ACTIVITY,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
}
static void
e_activity_bar_init (EActivityBar *bar)
{
GtkWidget *container;
GtkWidget *widget;
bar->priv = E_ACTIVITY_BAR_GET_PRIVATE (bar);
container = gtk_info_bar_get_content_area (GTK_INFO_BAR (bar));
widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
gtk_container_add (GTK_CONTAINER (container), widget);
gtk_widget_show (widget);
container = widget;
widget = gtk_image_new ();
gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
bar->priv->image = widget;
widget = e_spinner_new ();
e_spinner_start (E_SPINNER (widget));
gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
bar->priv->spinner = widget;
/* The spinner is only visible when the image is not. */
g_object_bind_property (
bar->priv->image, "visible",
bar->priv->spinner, "visible",
G_BINDING_BIDIRECTIONAL |
G_BINDING_SYNC_CREATE |
G_BINDING_INVERT_BOOLEAN);
widget = gtk_label_new (NULL);
gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
bar->priv->label = widget;
gtk_widget_show (widget);
/* This is only shown if the EActivity has a GCancellable. */
widget = e_dialog_button_new_with_icon ("process-stop", _("_Cancel"));
gtk_info_bar_add_action_widget (
GTK_INFO_BAR (bar), widget, GTK_RESPONSE_CANCEL);
bar->priv->cancel = widget;
gtk_widget_hide (widget);
g_signal_connect_swapped (
widget, "clicked",
G_CALLBACK (activity_bar_cancel), bar);
}
GtkWidget *
e_activity_bar_new (void)
{
return g_object_new (E_TYPE_ACTIVITY_BAR, NULL);
}
EActivity *
e_activity_bar_get_activity (EActivityBar *bar)
{
g_return_val_if_fail (E_IS_ACTIVITY_BAR (bar), NULL);
return bar->priv->activity;
}
void
e_activity_bar_set_activity (EActivityBar *bar,
EActivity *activity)
{
g_return_if_fail (E_IS_ACTIVITY_BAR (bar));
if (activity != NULL)
g_return_if_fail (E_IS_ACTIVITY (activity));
if (bar->priv->timeout_id > 0) {
g_source_remove (bar->priv->timeout_id);
bar->priv->timeout_id = 0;
}
if (bar->priv->activity != NULL) {
g_signal_handlers_disconnect_matched (
bar->priv->activity, G_SIGNAL_MATCH_DATA,
0, 0, NULL, NULL, bar);
g_object_weak_unref (
G_OBJECT (bar->priv->activity),
(GWeakNotify) activity_bar_weak_notify_cb, bar);
}
bar->priv->activity = activity;
if (activity != NULL) {
g_object_weak_ref (
G_OBJECT (activity), (GWeakNotify)
activity_bar_weak_notify_cb, bar);
g_signal_connect_swapped (
activity, "notify::state",
G_CALLBACK (activity_bar_feedback), bar);
g_signal_connect_swapped (
activity, "notify",
G_CALLBACK (activity_bar_update), bar);
}
activity_bar_update (bar);
g_object_notify (G_OBJECT (bar), "activity");
}