/* * empathy-connection-aggregator.c - Source for EmpathyConnectionAggregator * Copyright (C) 2010 Collabora Ltd. * @author Cosimo Cecchi * * This library 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; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "empathy-connection-aggregator.h" #define DEBUG_FLAG EMPATHY_DEBUG_OTHER #include "empathy-debug.h" G_DEFINE_TYPE (EmpathyConnectionAggregator, empathy_connection_aggregator, G_TYPE_OBJECT); enum { EVENT_CONTACT_LIST_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; struct _EmpathyConnectionAggregatorPriv { TpAccountManager *mgr; /* List of owned TpConnection */ GList *conns; }; static void empathy_connection_aggregator_dispose (GObject *object) { EmpathyConnectionAggregator *self = (EmpathyConnectionAggregator *) object; g_clear_object (&self->priv->mgr); g_list_free_full (self->priv->conns, g_object_unref); self->priv->conns = NULL; G_OBJECT_CLASS (empathy_connection_aggregator_parent_class)->dispose (object); } static void empathy_connection_aggregator_class_init ( EmpathyConnectionAggregatorClass *klass) { GObjectClass *oclass = G_OBJECT_CLASS (klass); oclass->dispose = empathy_connection_aggregator_dispose; signals[EVENT_CONTACT_LIST_CHANGED] = g_signal_new ("contact-list-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_PTR_ARRAY, G_TYPE_PTR_ARRAY); g_type_class_add_private (klass, sizeof (EmpathyConnectionAggregatorPriv)); } static void contact_list_changed_cb (TpConnection *conn, GPtrArray *added, GPtrArray *removed, EmpathyConnectionAggregator *self) { g_signal_emit (self, signals[EVENT_CONTACT_LIST_CHANGED], 0, added, removed); } static void conn_invalidated_cb (TpConnection *conn, guint domain, gint code, gchar *message, EmpathyConnectionAggregator *self) { self->priv->conns = g_list_remove (self->priv->conns, conn); g_object_unref (conn); } static void check_connection (EmpathyConnectionAggregator *self, TpConnection *conn) { GPtrArray *contacts; if (g_list_find (self->priv->conns, conn) != NULL) return; self->priv->conns = g_list_prepend (self->priv->conns, g_object_ref (conn)); tp_g_signal_connect_object (conn, "contact-list-changed", G_CALLBACK (contact_list_changed_cb), self, 0); contacts = tp_connection_dup_contact_list (conn); if (contacts != NULL) { GPtrArray *empty; empty = g_ptr_array_new (); contact_list_changed_cb (conn, contacts, empty, self); g_ptr_array_unref (empty); } g_ptr_array_unref (contacts); tp_g_signal_connect_object (conn, "invalidated", G_CALLBACK (conn_invalidated_cb), self, 0); } static void check_account (EmpathyConnectionAggregator *self, TpAccount *account) { TpConnection *conn; conn = tp_account_get_connection (account); if (conn != NULL) check_connection (self, conn); } static void account_conn_changed_cb (TpAccount *account, GParamSpec *spec, EmpathyConnectionAggregator *self) { check_account (self, account); } static void add_account (EmpathyConnectionAggregator *self, TpAccount *account) { check_account (self, account); tp_g_signal_connect_object (account, "notify::connection", G_CALLBACK (account_conn_changed_cb), self, 0); } static void account_validity_changed_cb (TpAccountManager *manager, TpAccount *account, gboolean valid, EmpathyConnectionAggregator *self) { if (valid) add_account (self, account); } static void am_prepare_cb (GObject *source, GAsyncResult *result, gpointer user_data) { EmpathyConnectionAggregator *self = EMPATHY_CONNECTION_AGGREGATOR (user_data); GError *error = NULL; GList *accounts, *l; if (!tp_proxy_prepare_finish (source, result, &error)) { DEBUG ("Failed to prepare account manager: %s", error->message); g_error_free (error); goto out; } accounts = tp_account_manager_dup_valid_accounts (self->priv->mgr); for (l = accounts; l != NULL; l = g_list_next (l)) { TpAccount *account = l->data; add_account (self, account); } tp_g_signal_connect_object (self->priv->mgr, "account-validity-changed", G_CALLBACK (account_validity_changed_cb), self, 0); g_list_free_full (accounts, g_object_unref); out: g_object_unref (self); } static void empathy_connection_aggregator_init (EmpathyConnectionAggregator *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EMPATHY_TYPE_CONNECTION_AGGREGATOR, EmpathyConnectionAggregatorPriv); self->priv->mgr = tp_account_manager_dup (); tp_proxy_prepare_async (self->priv->mgr, NULL, am_prepare_cb, g_object_ref (self)); } EmpathyConnectionAggregator * empathy_connection_aggregator_dup_singleton (void) { static EmpathyConnectionAggregator *aggregator = NULL; if (G_LIKELY (aggregator != NULL)) return g_object_ref (aggregator); aggregator = g_object_new (EMPATHY_TYPE_CONNECTION_AGGREGATOR, NULL); g_object_add_weak_pointer (G_OBJECT (aggregator), (gpointer *) &aggregator); return aggregator; } /* (transfer container) */ GList * empathy_connection_aggregator_get_all_groups (EmpathyConnectionAggregator *self) { GList *keys, *l; GHashTable *set; set = g_hash_table_new (g_str_hash, g_str_equal); for (l = self->priv->conns; l != NULL; l = g_list_next (l)) { TpConnection *conn = l->data; const gchar * const *groups; guint i; groups = tp_connection_get_contact_groups (conn); if (groups == NULL) continue; for (i = 0; groups[i] != NULL; i++) g_hash_table_insert (set, (gchar *) groups[i], GUINT_TO_POINTER (TRUE)); } keys = g_hash_table_get_keys (set); g_hash_table_unref (set); return keys; } GPtrArray * empathy_connection_aggregator_dup_all_contacts ( EmpathyConnectionAggregator *self) { GPtrArray *result; GList *l; result = g_ptr_array_new_with_free_func (g_object_unref); for (l = self->priv->conns; l != NULL; l = g_list_next (l)) { TpConnection *conn = l->data; GPtrArray *contacts; contacts = tp_connection_dup_contact_list (conn); if (contacts == NULL) continue; tp_g_ptr_array_extend (result, contacts); /* tp_g_ptr_array_extend() doesn't give us an extra ref */ g_ptr_array_foreach (contacts, (GFunc) g_object_ref, NULL); g_ptr_array_unref (contacts); } return result; } static void rename_group_cb (GObject *source, GAsyncResult *result, gpointer user_data) { GError *error = NULL; if (!tp_connection_rename_group_finish (TP_CONNECTION (source), result, &error)) { DEBUG ("Failed to rename group on %s: %s", tp_proxy_get_object_path (source), error->message); g_error_free (error); } } void empathy_connection_aggregator_rename_group (EmpathyConnectionAggregator *self, const gchar *old_name, const gchar *new_name) { GList *l; for (l = self->priv->conns; l != NULL; l = g_list_next (l)) { TpConnection *conn = l->data; const gchar * const *groups; groups = tp_connection_get_contact_groups (conn); if (!tp_strv_contains (groups, old_name)) continue; DEBUG ("Rename group '%s' to '%s' on %s", old_name, new_name, tp_proxy_get_object_path (conn)); tp_connection_rename_group_async (conn, old_name, new_name, rename_group_cb, NULL); } }