aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--e-util/e-client-selector.c151
1 files changed, 150 insertions, 1 deletions
diff --git a/e-util/e-client-selector.c b/e-util/e-client-selector.c
index e164c80249..32a53e428d 100644
--- a/e-util/e-client-selector.c
+++ b/e-util/e-client-selector.c
@@ -32,6 +32,8 @@
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_CLIENT_SELECTOR, EClientSelectorPrivate))
+typedef struct _AsyncContext AsyncContext;
+
struct _EClientSelectorPrivate {
EClientCache *client_cache;
gulong backend_died_handler_id;
@@ -39,6 +41,11 @@ struct _EClientSelectorPrivate {
gulong client_notify_online_handler_id;
};
+struct _AsyncContext {
+ EClientSelector *selector;
+ ESource *source;
+};
+
enum {
PROP_0,
PROP_CLIENT_CACHE
@@ -50,6 +57,15 @@ G_DEFINE_TYPE (
E_TYPE_SOURCE_SELECTOR)
static void
+async_context_free (AsyncContext *async_context)
+{
+ g_clear_object (&async_context->selector);
+ g_clear_object (&async_context->source);
+
+ g_slice_free (AsyncContext, async_context);
+}
+
+static void
client_selector_update_status_icon_cb (GtkTreeViewColumn *column,
GtkCellRenderer *renderer,
GtkTreeModel *model,
@@ -85,8 +101,17 @@ client_selector_update_status_icon_cb (GtkTreeViewColumn *column,
dead_backend = e_client_selector_is_backend_dead (
E_CLIENT_SELECTOR (tree_view), source);
- if (dead_backend)
+ if (dead_backend) {
icon_name = "network-error-symbolic";
+ } else {
+ gpointer data;
+
+ /* See client_selector_can_reach_cb() */
+ data = g_object_get_data (
+ G_OBJECT (source),
+ "initial-icon-name");
+ icon_name = (const gchar *) data;
+ }
g_object_unref (source);
}
@@ -141,6 +166,50 @@ client_selector_client_notify_cb (EClientCache *client_cache,
}
static void
+client_selector_can_reach_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ EClient *client;
+ AsyncContext *async_context;
+ gboolean reachable;
+
+ async_context = (AsyncContext *) user_data;
+
+ /* We don't care about errors here. This is just to show some
+ * initial icon next to the ESource before creating an EClient. */
+ reachable = g_network_monitor_can_reach_finish (
+ G_NETWORK_MONITOR (source_object), result, NULL);
+
+ client = e_client_selector_ref_cached_client (
+ async_context->selector, async_context->source);
+
+ /* EClient's online state is authoritative.
+ * Defer to it if an instance already exists. */
+ if (client == NULL) {
+ const gchar *icon_name;
+
+ if (reachable)
+ icon_name = "network-idle-symbolic";
+ else
+ icon_name = "network-offline-symbolic";
+
+ /* XXX Hackish way to stash the initial icon name. */
+ g_object_set_data (
+ G_OBJECT (async_context->source),
+ "initial-icon-name", (gpointer) icon_name);
+
+ e_source_selector_update_row (
+ E_SOURCE_SELECTOR (async_context->selector),
+ async_context->source);
+ }
+
+ g_clear_object (&client);
+
+ async_context_free (async_context);
+}
+
+static void
client_selector_set_client_cache (EClientSelector *selector,
EClientCache *client_cache)
{
@@ -224,9 +293,13 @@ client_selector_constructed (GObject *object)
{
EClientSelector *selector;
EClientCache *client_cache;
+ ESourceRegistry *registry;
GtkTreeView *tree_view;
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
+ GNetworkMonitor *network_monitor;
+ const gchar *extension_name;
+ GList *list, *link;
gulong handler_id;
selector = E_CLIENT_SELECTOR (object);
@@ -270,6 +343,82 @@ client_selector_constructed (GObject *object)
selector->priv->client_notify_online_handler_id = handler_id;
g_object_unref (client_cache);
+
+ /* Have GNetworkMonitor make an initial guess at the online
+ * state of backends by evaluating the reachability of their
+ * host name. This will show an initial status icon for all
+ * displayed ESources without actually opening a connection,
+ * since some backends are expensive to start unnecessarily.
+ *
+ * XXX It occurred to me after writing this that it would be
+ * better for ESourceSelector to evaluate reachability of
+ * ESource host names, and keep it up-to-date in response
+ * to network changes. It could automatically trigger a
+ * GtkTreeModel::row-changed signal when it has a new host
+ * reachability result, and provide that result via some
+ * e_source_selector_get_host_reachable() for us to fall
+ * back on if no EClient instance is available.
+ *
+ * But the approach below is good enough for now.
+ */
+
+ network_monitor = g_network_monitor_get_default ();
+
+ registry = e_source_selector_get_registry (
+ E_SOURCE_SELECTOR (selector));
+ extension_name = e_source_selector_get_extension_name (
+ E_SOURCE_SELECTOR (selector));
+
+ list = e_source_registry_list_sources (registry, extension_name);
+
+ extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ ESource *source = E_SOURCE (link->data);
+ ESource *auth_source;
+ ESourceAuthentication *auth_extension;
+ GSocketConnectable *socket_connectable;
+ const gchar *host;
+ guint16 port;
+
+ auth_source = e_source_registry_find_extension (
+ registry, source, extension_name);
+
+ if (auth_source == NULL)
+ continue;
+
+ auth_extension = e_source_get_extension (
+ auth_source, extension_name);
+
+ host = e_source_authentication_get_host (auth_extension);
+ port = e_source_authentication_get_port (auth_extension);
+
+ socket_connectable = g_network_address_new (host, port);
+
+ /* XXX GNetworkAddress will happily take a NULL host
+ * but then crash while enumerating the address,
+ * so watch out for that. */
+ if (host == NULL)
+ g_clear_object (&socket_connectable);
+
+ if (socket_connectable != NULL) {
+ AsyncContext *async_context;
+
+ async_context = g_slice_new0 (AsyncContext);
+ async_context->selector = g_object_ref (selector);
+ async_context->source = g_object_ref (source);
+
+ g_network_monitor_can_reach_async (
+ network_monitor, socket_connectable, NULL,
+ client_selector_can_reach_cb, async_context);
+
+ g_object_unref (socket_connectable);
+ }
+
+ g_object_unref (auth_source);
+ }
+
+ g_list_free_full (list, (GDestroyNotify) g_object_unref);
}
static void