[evolution] EMFolderTree: Show connection status icons.
- From: Matthew Barnes <mbarnes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution] EMFolderTree: Show connection status icons.
- Date: Wed, 20 Nov 2013 17:32:04 +0000 (UTC)
commit 9bb9e9d47844ff9102bd114c6636783a5d41d625
Author: Matthew Barnes <mbarnes redhat com>
Date: Wed Nov 20 12:07:43 2013 -0500
EMFolderTree: Show connection status icons.
Each network service row in the folder tree now shows an icon which
follows the state of the service's connection status and remote host
reachability.
mail/em-folder-tree-model.c | 201 ++++++++++++++++++++++++++++++++++++++++++-
mail/em-folder-tree-model.h | 7 ++
mail/em-folder-tree.c | 23 +++++
3 files changed, 228 insertions(+), 3 deletions(-)
---
diff --git a/mail/em-folder-tree-model.c b/mail/em-folder-tree-model.c
index 3f7c087..c8ab161 100644
--- a/mail/em-folder-tree-model.c
+++ b/mail/em-folder-tree-model.c
@@ -46,6 +46,10 @@
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), EM_TYPE_FOLDER_TREE_MODEL, EMFolderTreeModelPrivate))
+/* See GtkCellRendererSpinner:pulse property.
+ * Animation cycles over 12 frames in 750 ms. */
+#define SPINNER_PULSE_INTERVAL (750 / 12)
+
typedef struct _StoreInfo StoreInfo;
struct _EMFolderTreeModelPrivate {
@@ -78,6 +82,15 @@ struct _StoreInfo {
gulong folder_info_stale_handler_id;
gulong folder_subscribed_handler_id;
gulong folder_unsubscribed_handler_id;
+ gulong connection_status_handler_id;
+ gulong host_reachable_handler_id;
+
+ /* For comparison with the current status. */
+ CamelServiceConnectionStatus last_status;
+
+ /* Spinner renderers have to be animated manually. */
+ guint spinner_pulse_value;
+ guint spinner_pulse_timeout_id;
};
enum {
@@ -117,6 +130,10 @@ static void folder_tree_model_folder_unsubscribed_cb
(CamelStore *store,
CamelFolderInfo *fi,
GWeakRef *model_weak_ref);
+static void folder_tree_model_status_notify_cb
+ (CamelStore *store,
+ GParamSpec *pspec,
+ GWeakRef *model_weak_ref);
static guint signals[LAST_SIGNAL];
@@ -126,6 +143,7 @@ static StoreInfo *
store_info_new (EMFolderTreeModel *model,
CamelStore *store)
{
+ CamelService *service;
StoreInfo *si;
gulong handler_id;
@@ -183,6 +201,25 @@ store_info_new (EMFolderTreeModel *model,
si->folder_unsubscribed_handler_id = handler_id;
}
+ if (CAMEL_IS_NETWORK_SERVICE (store)) {
+ handler_id = g_signal_connect_data (
+ store, "notify::connection-status",
+ G_CALLBACK (folder_tree_model_status_notify_cb),
+ e_weak_ref_new (model),
+ (GClosureNotify) e_weak_ref_free, 0);
+ si->connection_status_handler_id = handler_id;
+
+ handler_id = g_signal_connect_data (
+ store, "notify::host-reachable",
+ G_CALLBACK (folder_tree_model_status_notify_cb),
+ e_weak_ref_new (model),
+ (GClosureNotify) e_weak_ref_free, 0);
+ si->host_reachable_handler_id = handler_id;
+ }
+
+ service = CAMEL_SERVICE (store);
+ si->last_status = camel_service_get_connection_status (service);
+
return si;
}
@@ -234,6 +271,19 @@ store_info_unref (StoreInfo *si)
si->store,
si->folder_unsubscribed_handler_id);
+ if (si->connection_status_handler_id > 0)
+ g_signal_handler_disconnect (
+ si->store,
+ si->connection_status_handler_id);
+
+ if (si->host_reachable_handler_id > 0)
+ g_signal_handler_disconnect (
+ si->store,
+ si->host_reachable_handler_id);
+
+ if (si->spinner_pulse_timeout_id > 0)
+ g_source_remove (si->spinner_pulse_timeout_id);
+
g_object_unref (si->store);
gtk_tree_row_reference_free (si->row);
g_hash_table_destroy (si->full_hash);
@@ -449,6 +499,36 @@ folder_tree_model_services_reordered (EMailAccountStore *account_store,
folder_tree_model_sort, NULL, NULL);
}
+static gboolean
+folder_tree_model_spinner_pulse_cb (gpointer user_data)
+{
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ StoreInfo *si;
+
+ si = (StoreInfo *) user_data;
+
+ if (!gtk_tree_row_reference_valid (si->row))
+ return G_SOURCE_REMOVE;
+
+ path = gtk_tree_row_reference_get_path (si->row);
+ model = gtk_tree_row_reference_get_model (si->row);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
+
+ gtk_tree_store_set (
+ GTK_TREE_STORE (model), &iter,
+ COL_STATUS_SPINNER_PULSE,
+ si->spinner_pulse_value++,
+ -1);
+
+ if (si->spinner_pulse_value == G_MAXUINT)
+ si->spinner_pulse_value = 0;
+
+ return G_SOURCE_CONTINUE;
+}
+
static void
folder_tree_model_selection_finalized_cb (EMFolderTreeModel *model)
{
@@ -565,7 +645,10 @@ folder_tree_model_constructed (GObject *object)
G_TYPE_BOOLEAN, /* has not-yet-loaded subfolders */
G_TYPE_UINT, /* last known unread count */
G_TYPE_BOOLEAN, /* folder is a draft folder */
- G_TYPE_UINT /* user's sortorder */
+ G_TYPE_ICON, /* status GIcon */
+ G_TYPE_BOOLEAN, /* status icon visible */
+ G_TYPE_UINT, /* status spinner pulse */
+ G_TYPE_BOOLEAN, /* status spinner visible */
};
gtk_tree_store_set_column_types (
@@ -1360,6 +1443,115 @@ exit:
g_object_unref (model);
}
+static void
+folder_tree_model_update_status_icon (StoreInfo *si)
+{
+ CamelService *service;
+ CamelServiceConnectionStatus status;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ GIcon *icon = NULL;
+ const gchar *icon_name;
+ gboolean was_connecting;
+ gboolean host_reachable;
+
+ g_return_if_fail (si != NULL);
+
+ if (!gtk_tree_row_reference_valid (si->row))
+ return;
+
+ service = CAMEL_SERVICE (si->store);
+ status = camel_service_get_connection_status (service);
+ was_connecting = (si->last_status == CAMEL_SERVICE_CONNECTING);
+ si->last_status = status;
+
+ host_reachable = camel_network_service_get_host_reachable (
+ CAMEL_NETWORK_SERVICE (service));
+
+ switch (status) {
+ case CAMEL_SERVICE_DISCONNECTED:
+ if (!host_reachable)
+ icon_name = "network-no-route-symbolic";
+ else if (was_connecting)
+ icon_name = "network-error-symbolic";
+ else
+ icon_name = "network-offline-symbolic";
+ break;
+
+ case CAMEL_SERVICE_CONNECTING:
+ icon_name = NULL;
+ break;
+
+ case CAMEL_SERVICE_CONNECTED:
+ icon_name = "network-idle-symbolic";
+ break;
+
+ case CAMEL_SERVICE_DISCONNECTING:
+ icon_name = NULL;
+ break;
+ }
+
+ if (icon_name == NULL && si->spinner_pulse_timeout_id == 0) {
+ si->spinner_pulse_timeout_id = g_timeout_add_full (
+ G_PRIORITY_DEFAULT,
+ SPINNER_PULSE_INTERVAL,
+ folder_tree_model_spinner_pulse_cb,
+ store_info_ref (si),
+ (GDestroyNotify) store_info_unref);
+ }
+
+ if (icon_name != NULL && si->spinner_pulse_timeout_id > 0) {
+ g_source_remove (si->spinner_pulse_timeout_id);
+ si->spinner_pulse_timeout_id = 0;
+ }
+
+ path = gtk_tree_row_reference_get_path (si->row);
+ model = gtk_tree_row_reference_get_model (si->row);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
+
+ if (icon_name != NULL) {
+ /* Use fallbacks if symbolic icons are not available. */
+ icon = g_themed_icon_new_with_default_fallbacks (icon_name);
+ }
+
+ gtk_tree_store_set (
+ GTK_TREE_STORE (model), &iter,
+ COL_STATUS_ICON, icon,
+ COL_STATUS_ICON_VISIBLE, (icon_name != NULL),
+ COL_STATUS_SPINNER_VISIBLE, (icon_name == NULL),
+ -1);
+
+ g_clear_object (&icon);
+
+}
+
+static void
+folder_tree_model_status_notify_cb (CamelStore *store,
+ GParamSpec *pspec,
+ GWeakRef *model_weak_ref)
+{
+ EMFolderTreeModel *model;
+ StoreInfo *si;
+
+ /* Even though this is a GObject::notify signal, CamelService
+ * always emits it from its GMainContext on the "main" thread,
+ * so it's safe to modify the GtkTreeStore from here. */
+
+ model = g_weak_ref_get (model_weak_ref);
+ g_return_if_fail (model != NULL);
+
+ si = folder_tree_model_store_index_lookup (model, store);
+
+ if (si != NULL) {
+ folder_tree_model_update_status_icon (si);
+ store_info_unref (si);
+ }
+
+ g_object_unref (model);
+}
+
void
em_folder_tree_model_add_store (EMFolderTreeModel *model,
CamelStore *store)
@@ -1427,8 +1619,6 @@ em_folder_tree_model_add_store (EMFolderTreeModel *model,
folder_tree_model_store_index_insert (model, si);
- store_info_unref (si);
-
/* Each store has folders, but we don't load them until
* the user demands them. */
root = iter;
@@ -1446,8 +1636,13 @@ em_folder_tree_model_add_store (EMFolderTreeModel *model,
COL_BOOL_IS_DRAFT, FALSE,
-1);
+ if (CAMEL_IS_NETWORK_SERVICE (store))
+ folder_tree_model_update_status_icon (si);
+
g_signal_emit (model, signals[LOADED_ROW], 0, path, &root);
gtk_tree_path_free (path);
+
+ store_info_unref (si);
}
void
diff --git a/mail/em-folder-tree-model.h b/mail/em-folder-tree-model.h
index 85fe1e7..5ec4a3b 100644
--- a/mail/em-folder-tree-model.h
+++ b/mail/em-folder-tree-model.h
@@ -69,6 +69,13 @@ enum {
* been added to the tree */
COL_UINT_UNREAD_LAST_SEL, /* last known unread count */
COL_BOOL_IS_DRAFT, /* %TRUE for a draft folder */
+
+ /* Status icon/spinner, only for top-level store rows. */
+ COL_STATUS_ICON,
+ COL_STATUS_ICON_VISIBLE,
+ COL_STATUS_SPINNER_PULSE,
+ COL_STATUS_SPINNER_VISIBLE,
+
NUM_COLUMNS
};
diff --git a/mail/em-folder-tree.c b/mail/em-folder-tree.c
index f7e7afd..57f9ef6 100644
--- a/mail/em-folder-tree.c
+++ b/mail/em-folder-tree.c
@@ -1195,6 +1195,8 @@ folder_tree_constructed (GObject *object)
priv->selection_changed_handler_id = handler_id;
column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_sizing (
+ column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
gtk_tree_view_append_column (tree_view, column);
renderer = gtk_cell_renderer_pixbuf_new ();
@@ -1221,6 +1223,27 @@ folder_tree_constructed (GObject *object)
renderer, "edited",
G_CALLBACK (folder_tree_cell_edited_cb), object);
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_append_column (tree_view, column);
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ g_object_set (renderer, "xalign", 1.0, NULL);
+ gtk_tree_view_column_pack_end (column, renderer, FALSE);
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "gicon", COL_STATUS_ICON);
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "visible", COL_STATUS_ICON_VISIBLE);
+
+ renderer = gtk_cell_renderer_spinner_new ();
+ g_object_set (renderer, "xalign", 1.0, NULL);
+ gtk_tree_view_column_pack_end (column, renderer, FALSE);
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "active", COL_BOOL_IS_STORE);
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "pulse", COL_STATUS_SPINNER_PULSE);
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "visible", COL_STATUS_SPINNER_VISIBLE);
+
gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
gtk_tree_selection_set_select_function (
selection, (GtkTreeSelectionFunc)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]