aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanielle Madeley <danielle.madeley@collabora.co.uk>2011-03-14 09:58:00 +0800
committerDanielle Madeley <danielle.madeley@collabora.co.uk>2011-03-14 09:58:00 +0800
commit8e6a977d36aea1eb4b429ddee84cb9b4bc4142cd (patch)
tree5c1858c474144e0cd511fc6891e9d21ef978c5e3
parent2e3e5e8e732fbc2e4d3ed61892aefa09efabbfc2 (diff)
parentc0eca0414ca529da3b083c099ec3031436420b00 (diff)
downloadgsoc2013-empathy-8e6a977d36aea1eb4b429ddee84cb9b4bc4142cd.tar
gsoc2013-empathy-8e6a977d36aea1eb4b429ddee84cb9b4bc4142cd.tar.gz
gsoc2013-empathy-8e6a977d36aea1eb4b429ddee84cb9b4bc4142cd.tar.bz2
gsoc2013-empathy-8e6a977d36aea1eb4b429ddee84cb9b4bc4142cd.tar.lz
gsoc2013-empathy-8e6a977d36aea1eb4b429ddee84cb9b4bc4142cd.tar.xz
gsoc2013-empathy-8e6a977d36aea1eb4b429ddee84cb9b4bc4142cd.tar.zst
gsoc2013-empathy-8e6a977d36aea1eb4b429ddee84cb9b4bc4142cd.zip
Merge branch 'glassrose-contact-blocking-rebase'
-rw-r--r--extensions/Connection_Interface_Contact_Blocking.xml172
-rw-r--r--extensions/Makefile.am1
-rw-r--r--extensions/misc.xml1
-rw-r--r--libempathy-gtk/Makefile.am3
-rw-r--r--libempathy-gtk/empathy-chat.c3
-rw-r--r--libempathy-gtk/empathy-contact-blocking-dialog.c791
-rw-r--r--libempathy-gtk/empathy-contact-blocking-dialog.h60
-rw-r--r--libempathy-gtk/empathy-contact-blocking-dialog.ui149
-rw-r--r--libempathy-gtk/empathy-contact-dialogs.c103
-rw-r--r--libempathy-gtk/empathy-contact-dialogs.h3
-rw-r--r--libempathy-gtk/empathy-contact-dialogs.ui53
-rw-r--r--libempathy-gtk/empathy-contact-menu.c94
-rw-r--r--libempathy-gtk/empathy-contact-menu.h3
-rw-r--r--libempathy-gtk/empathy-individual-dialogs.c139
-rw-r--r--libempathy-gtk/empathy-individual-dialogs.h3
-rw-r--r--libempathy-gtk/empathy-individual-view.c45
-rw-r--r--libempathy/empathy-contact-list.c24
-rw-r--r--libempathy/empathy-contact-list.h15
-rw-r--r--libempathy/empathy-contact-manager.c43
-rw-r--r--libempathy/empathy-individual-manager.c85
-rw-r--r--libempathy/empathy-individual-manager.h9
-rw-r--r--libempathy/empathy-tp-contact-list.c110
-rw-r--r--libempathy/empathy-tp-contact-list.h1
-rw-r--r--src/empathy-chat-window.c7
-rw-r--r--src/empathy-main-window.c17
-rw-r--r--src/empathy-main-window.ui7
-rw-r--r--tests/interactive/.gitignore1
-rw-r--r--tests/interactive/Makefile.am2
-rw-r--r--tests/interactive/test-empathy-contact-blocking-dialog.c55
29 files changed, 1967 insertions, 32 deletions
diff --git a/extensions/Connection_Interface_Contact_Blocking.xml b/extensions/Connection_Interface_Contact_Blocking.xml
new file mode 100644
index 000000000..543d43a5c
--- /dev/null
+++ b/extensions/Connection_Interface_Contact_Blocking.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" ?>
+<node name="/Connection_Interface_Contact_Blocking" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <tp:copyright>Copyright © 2009-2010 Collabora Ltd.</tp:copyright>
+ <tp:copyright>Copyright © 2009 Nokia Corporation</tp:copyright>
+ <tp:license xmlns="http://www.w3.org/1999/xhtml">
+ <p>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.</p>
+
+ <p>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.</p>
+
+ <p>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 Street, Fifth Floor, Boston, MA 02110-1301,
+ USA.</p>
+ </tp:license>
+ <interface name="org.freedesktop.Telepathy.Connection.Interface.ContactBlocking.DRAFT"
+ tp:causes-havoc="experimental">
+ <tp:requires interface="org.freedesktop.Telepathy.Connection"/>
+
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>An interface for connections where contacts can be blocked from
+ communicating with this user and receiving this user's presence.</p>
+
+ <p>When this interface becomes stable, it will replace the <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Channel.Type"
+ >ContactList</tp:dbus-ref> channel with TargetHandleType
+ Handle_Type_List and TargetID 'deny'.</p>
+ </tp:docstring>
+
+ <method name="BlockContacts" tp:name-for-bindings="Block_Contacts">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Direct the server to block some contacts. The precise effect is
+ protocol-dependent, but SHOULD include ignoring all current and
+ subsequent communications from the given contacts, avoiding sending
+ presence to them in future, and if they were already receiving the
+ local user's presence, behaving as if the local user went
+ offline.</p>
+
+ <p><em>FIXME: do we need to allow this on protocols where it won't
+ persist, or where we can't edit the block lists?</em></p>
+ </tp:docstring>
+
+ <arg name="Contacts" type="au" direction="in" tp:type="Contact_Handle[]">
+ <tp:docstring>Some contacts to block.</tp:docstring>
+ </arg>
+
+ <arg name="Report_Abusive" type="b" direction="in">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>In addition to blocking, report these contacts as abusive to the
+ server administrators.</p>
+
+ <p>Clients can determine whether this capability is available by
+ checking the
+ <tp:member-ref>ContactBlockingCapabilities</tp:member-ref>
+ property. If the capability is not present and this argument is
+ true, the error <tp:error-ref>NotCapable</tp:error-ref> SHOULD
+ be raised.</p>
+
+ <tp:rationale>
+ Some protocol libraries, in their conformance requirements,
+ require the ability to report blocked contacts as abusive.
+ </tp:rationale>
+ </tp:docstring>
+ </arg>
+
+ <tp:possible-errors>
+ <tp:error name="org.freedesktop.Telepathy.Error.Disconnected"/>
+ <tp:error name="org.freedesktop.Telepathy.Error.InvalidHandle"/>
+ <tp:error name="org.freedesktop.Telepathy.Error.NetworkError"/>
+ <tp:error name="org.freedesktop.Telepathy.Error.NotCapable"/>
+ </tp:possible-errors>
+ </method>
+
+ <method name="UnblockContacts" tp:name-for-bindings="Unblock_Contacts">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Reverse the effects of a previous call to
+ <tp:member-ref>BlockContacts</tp:member-ref>.</p>
+ </tp:docstring>
+
+ <arg name="Contacts" type="au" direction="in" tp:type="Contact_Handle[]">
+ <tp:docstring>Some contacts to unblock.</tp:docstring>
+ </arg>
+
+ <tp:possible-errors>
+ <tp:error name="org.freedesktop.Telepathy.Error.Disconnected"/>
+ <tp:error name="org.freedesktop.Telepathy.Error.InvalidHandle"/>
+ <tp:error name="org.freedesktop.Telepathy.Error.NetworkError"/>
+ </tp:possible-errors>
+ </method>
+
+ <method name="RequestBlockedContacts"
+ tp:name-for-bindings="Request_Blocked_Contacts">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>List the contacts that are blocked.</p>
+
+ <p>Clients SHOULD allow a relatively long timeout for calls to this
+ method, since on some protocols contact blocking is part of the
+ contact list, which can take a significant time to retrieve.</p>
+ </tp:docstring>
+
+ <arg name="Contacts" type="au" direction="out" tp:type="Contact_Handle[]">
+ <tp:docstring>The list of blocked contacts.</tp:docstring>
+ </arg>
+
+ <tp:possible-errors>
+ <tp:error name="org.freedesktop.Telepathy.Error.Disconnected"/>
+ <tp:error name="org.freedesktop.Telepathy.Error.InvalidHandle"/>
+ <tp:error name="org.freedesktop.Telepathy.Error.NetworkError"/>
+ </tp:possible-errors>
+ </method>
+
+ <signal name="BlockedContactsChanged"
+ tp:name-for-bindings="Blocked_Contacts_Changed">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Emitted when the list of blocked contacts is first retrieved
+ (before returning from any pending calls to
+ <tp:member-ref>RequestBlockedContacts</tp:member-ref>), and
+ whenever the list of blocked contacts subsequently changes.</p>
+ </tp:docstring>
+
+ <arg name="BlockedContacts" type="au" tp:type="Contact_Handle[]">
+ <tp:docstring>Contacts added to the result of
+ <tp:member-ref>RequestBlockedContacts</tp:member-ref>.</tp:docstring>
+ </arg>
+
+ <arg name="UnblockedContacts" type="au" tp:type="Contact_Handle[]">
+ <tp:docstring>Contacts removed from the result of
+ <tp:member-ref>RequestBlockedContacts</tp:member-ref>.</tp:docstring>
+ </arg>
+ </signal>
+
+ <tp:contact-attribute name="blocked" type="b">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>True if the contact would be in the result of
+ <tp:member-ref>RequestBlockedContacts</tp:member-ref>;
+ False or omitted if the contact is not blocked, or if it is
+ unknown whether the contact is blocked.</p>
+ </tp:docstring>
+ </tp:contact-attribute>
+
+ <property name="ContactBlockingCapabilities"
+ tp:name-for-bindings="Contact_Blocking_Capabilities"
+ tp:type="Contact_Blocking_Capabilities" type="u" access="read"
+ tp:immutable="yes">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Additional capabilities for contact blocking (i.e. whether we can
+ report abusive contacts).</p>
+
+ <p><b>Note:</b> there is no capability for supporting blocking itself,
+ the presence of this interface indicates that contact blocking is
+ supported.</p>
+ </tp:docstring>
+ </property>
+
+ <tp:flags name="Contact_Blocking_Capabilities" type="u"
+ value-prefix="Contact_Blocking_Capability">
+ <tp:flag suffix="Can_Report_Abusive" value="1">
+ <tp:docstring>
+ This protocol is capable of reporting abusive contacts to the server
+ administrators.
+ </tp:docstring>
+ </tp:flag>
+ </tp:flags>
+
+ </interface>
+</node>
+<!-- vim:set sw=2 sts=2 et ft=xml: -->
diff --git a/extensions/Makefile.am b/extensions/Makefile.am
index d0f104821..b3147f301 100644
--- a/extensions/Makefile.am
+++ b/extensions/Makefile.am
@@ -18,6 +18,7 @@ EXTRA_DIST = \
Authentication_TLS_Certificate.xml \
Channel_Interface_Credentials_Storage.xml \
Channel_Type_Server_TLS_Connection.xml \
+ Connection_Interface_Contact_Blocking.xml \
$(NULL)
noinst_LTLIBRARIES = libemp-extensions.la
diff --git a/extensions/misc.xml b/extensions/misc.xml
index b1f6e88eb..9b153f11a 100644
--- a/extensions/misc.xml
+++ b/extensions/misc.xml
@@ -10,5 +10,6 @@
<xi:include href="Authentication_TLS_Certificate.xml" />
<xi:include href="Channel_Interface_Credentials_Storage.xml" />
<xi:include href="Channel_Type_Server_TLS_Connection.xml" />
+<xi:include href="Connection_Interface_Contact_Blocking.xml" />
</tp:spec>
diff --git a/libempathy-gtk/Makefile.am b/libempathy-gtk/Makefile.am
index 47b416fa0..18f9a1cdc 100644
--- a/libempathy-gtk/Makefile.am
+++ b/libempathy-gtk/Makefile.am
@@ -39,6 +39,7 @@ libempathy_gtk_handwritten_source = \
empathy-chat-text-view.c \
empathy-chat-view.c \
empathy-chat.c \
+ empathy-contact-blocking-dialog.c \
empathy-contact-dialogs.c \
empathy-contact-list-store.c \
empathy-contact-list-view.c \
@@ -98,6 +99,7 @@ libempathy_gtk_headers = \
empathy-chat-text-view.h \
empathy-chat-view.h \
empathy-chat.h \
+ empathy-contact-blocking-dialog.h \
empathy-contact-dialogs.h \
empathy-contact-list-store.h \
empathy-contact-list-view.h \
@@ -177,6 +179,7 @@ uidir = $(datadir)/empathy
ui_DATA = \
empathy-contact-widget.ui \
empathy-contact-dialogs.ui \
+ empathy-contact-blocking-dialog.ui \
empathy-account-widget-generic.ui \
empathy-account-widget-jabber.ui \
empathy-account-widget-msn.ui \
diff --git a/libempathy-gtk/empathy-chat.c b/libempathy-gtk/empathy-chat.c
index 72e906079..b696963ac 100644
--- a/libempathy-gtk/empathy-chat.c
+++ b/libempathy-gtk/empathy-chat.c
@@ -3590,7 +3590,8 @@ empathy_chat_get_contact_menu (EmpathyChat *chat)
menu = empathy_contact_menu_new (priv->remote_contact,
EMPATHY_CONTACT_FEATURE_CALL |
EMPATHY_CONTACT_FEATURE_LOG |
- EMPATHY_CONTACT_FEATURE_INFO);
+ EMPATHY_CONTACT_FEATURE_INFO |
+ EMPATHY_CONTACT_FEATURE_BLOCK);
}
return menu;
diff --git a/libempathy-gtk/empathy-contact-blocking-dialog.c b/libempathy-gtk/empathy-contact-blocking-dialog.c
new file mode 100644
index 000000000..a46820c5d
--- /dev/null
+++ b/libempathy-gtk/empathy-contact-blocking-dialog.c
@@ -0,0 +1,791 @@
+/*
+ * empathy-contact-blocking-dialog.c
+ *
+ * EmpathyContactBlockingDialog
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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
+ *
+ * Authors: Danielle Madeley <danielle.madeley@collabora.co.uk>
+ */
+
+#include <glib/gi18n.h>
+
+#include <libempathy/empathy-utils.h>
+
+#include <libempathy/empathy-contact-manager.h>
+#include <libempathy/empathy-tp-contact-list.h>
+
+#include <libempathy-gtk/empathy-account-chooser.h>
+#include <libempathy-gtk/empathy-ui-utils.h>
+
+#include "empathy-contact-blocking-dialog.h"
+
+#define DEBUG_FLAG EMPATHY_DEBUG_OTHER
+#include <libempathy/empathy-debug.h>
+
+#define GET_PRIVATE(o) (EMPATHY_CONTACT_BLOCKING_DIALOG (o)->priv)
+#define DECLARE_CALLBACK(func) \
+ static void func (GObject *, GAsyncResult *, gpointer);
+
+G_DEFINE_TYPE (EmpathyContactBlockingDialog, empathy_contact_blocking_dialog,
+ GTK_TYPE_DIALOG);
+
+struct _EmpathyContactBlockingDialogPrivate
+{
+ /* a map of all active connections to their 'deny' channel */
+ GHashTable *channels; /* reffed TpConnection* -> reffed TpChannel* */
+
+ guint block_account_changed;
+
+ GtkListStore *blocked_contacts;
+ GtkListStore *completion_contacts;
+ GtkTreeSelection *selection;
+
+ GtkWidget *account_chooser;
+ GtkWidget *add_button;
+ GtkWidget *add_contact_entry;
+ GtkWidget *info_bar;
+ GtkWidget *info_bar_label;
+ GtkWidget *remove_button;
+};
+
+enum /* blocked-contacts columns */
+{
+ COL_IDENTIFIER,
+ COL_HANDLE,
+ N_COLUMNS
+};
+
+static const char *
+get_pretty_conn_name (TpConnection *conn)
+{
+ return tp_proxy_get_object_path (conn) + strlen (TP_CONN_OBJECT_PATH_BASE);
+}
+
+static void
+contact_blocking_dialog_filter_account_chooser (TpAccount *account,
+ EmpathyAccountChooserFilterResultCallback callback,
+ gpointer callback_data,
+ gpointer user_data)
+{
+ EmpathyContactBlockingDialog *self = user_data;
+ TpConnection *conn = tp_account_get_connection (account);
+ gboolean enable;
+
+ enable =
+ conn != NULL &&
+ g_hash_table_lookup (self->priv->channels, conn) != NULL;
+
+ callback (enable, callback_data);
+}
+
+static void contact_blocking_dialog_account_changed (GtkWidget *,
+ EmpathyContactBlockingDialog *);
+
+static void
+contact_blocking_dialog_refilter_account_chooser (
+ EmpathyContactBlockingDialog *self)
+{
+ EmpathyAccountChooser *chooser =
+ EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser);
+ TpConnection *conn;
+ gboolean enabled;
+
+ DEBUG ("Refiltering account chooser");
+
+ /* set the filter to refilter the account chooser */
+ self->priv->block_account_changed++;
+ empathy_account_chooser_set_filter (chooser,
+ contact_blocking_dialog_filter_account_chooser, self);
+ self->priv->block_account_changed--;
+
+ conn = empathy_account_chooser_get_connection (chooser);
+ enabled = (empathy_account_chooser_get_account (chooser) != NULL &&
+ conn != NULL &&
+ g_hash_table_lookup (self->priv->channels, conn) != NULL);
+
+ if (!enabled)
+ DEBUG ("No account selected");
+
+ gtk_widget_set_sensitive (self->priv->add_button, enabled);
+ gtk_widget_set_sensitive (self->priv->add_contact_entry, enabled);
+
+ contact_blocking_dialog_account_changed (self->priv->account_chooser, self);
+}
+
+static void contact_blocking_dialog_inspected_handles (TpConnection *,
+ const char **, const GError *, gpointer, GObject *);
+
+static void
+contact_blocking_dialog_add_contacts_to_list (
+ EmpathyContactBlockingDialog *self,
+ TpConnection *conn,
+ GArray *handles)
+{
+ if (handles->len > 0)
+ tp_cli_connection_call_inspect_handles (conn, -1,
+ TP_HANDLE_TYPE_CONTACT, handles,
+ contact_blocking_dialog_inspected_handles,
+ g_boxed_copy (DBUS_TYPE_G_UINT_ARRAY, handles),
+ (GDestroyNotify) g_array_unref, G_OBJECT (self));
+}
+
+static void
+contact_blocking_dialog_inspected_handles (TpConnection *conn,
+ const char **identifiers,
+ const GError *in_error,
+ gpointer user_data,
+ GObject *self)
+{
+ EmpathyContactBlockingDialogPrivate *priv = GET_PRIVATE (self);
+ GArray *handles = user_data;
+ guint i;
+
+ if (in_error != NULL)
+ {
+ DEBUG ("Failed to inspect handles: %s", in_error->message);
+ return;
+ }
+
+ DEBUG ("Adding %u identifiers", handles->len);
+
+ for (i = 0; i < handles->len; i++)
+ {
+ const char *identifier = identifiers[i];
+ TpHandle handle = g_array_index (handles, TpHandle, i);
+
+ gtk_list_store_insert_with_values (priv->blocked_contacts, NULL, -1,
+ COL_IDENTIFIER, identifier,
+ COL_HANDLE, handle,
+ -1);
+ }
+}
+
+DECLARE_CALLBACK (contact_blocking_dialog_connection_prepared);
+
+static void
+contact_blocking_dialog_connection_status_changed (TpAccount *account,
+ guint old_status,
+ guint new_status,
+ guint reason,
+ const char *dbus_reason,
+ GHashTable *details,
+ EmpathyContactBlockingDialog *self)
+{
+ TpConnection *conn = tp_account_get_connection (account);
+
+ switch (new_status)
+ {
+ case TP_CONNECTION_STATUS_DISCONNECTED:
+ DEBUG ("Connection %s invalidated", get_pretty_conn_name (conn));
+
+ /* remove the channel from the hash table */
+ g_hash_table_remove (self->priv->channels, conn);
+ contact_blocking_dialog_refilter_account_chooser (self);
+ break;
+
+ case TP_CONNECTION_STATUS_CONNECTING:
+ break;
+
+ case TP_CONNECTION_STATUS_CONNECTED:
+ DEBUG ("Connection %s reconnected", get_pretty_conn_name (conn));
+
+ tp_proxy_prepare_async (conn, NULL,
+ contact_blocking_dialog_connection_prepared, self);
+ }
+}
+
+static void
+contact_blocking_dialog_deny_channel_members_changed (TpChannel *channel,
+ const char *message,
+ GArray *added,
+ GArray *removed,
+ GArray *local_pending,
+ GArray *remote_pending,
+ TpHandle actor,
+ guint reason,
+ EmpathyContactBlockingDialog *self)
+{
+ TpConnection *conn = tp_channel_borrow_connection (channel);
+ GtkTreeModel *model = GTK_TREE_MODEL (self->priv->blocked_contacts);
+ GtkTreeIter iter;
+ TpIntset *removed_set;
+ gboolean valid;
+
+ /* we only care about changes to the selected connection */
+ /* FIXME: can we compare proxy pointers directly? */
+ if (tp_strdiff (
+ tp_proxy_get_object_path (tp_channel_borrow_connection (channel)),
+ tp_proxy_get_object_path (empathy_account_chooser_get_connection (
+ EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser)))))
+ return;
+
+ DEBUG ("deny list changed: %u added, %u removed", added->len, removed->len);
+
+ /* add contacts */
+ contact_blocking_dialog_add_contacts_to_list (self, conn, added);
+
+ /* remove contacts */
+ removed_set = tp_intset_from_array (removed);
+
+ valid = gtk_tree_model_get_iter_first (model, &iter);
+ while (valid)
+ {
+ TpHandle handle;
+
+ gtk_tree_model_get (model, &iter,
+ COL_HANDLE, &handle,
+ -1);
+
+ if (tp_intset_is_member (removed_set, handle))
+ valid = gtk_list_store_remove (self->priv->blocked_contacts, &iter);
+ else
+ valid = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ tp_intset_destroy (removed_set);
+}
+
+DECLARE_CALLBACK (contact_blocking_dialog_connection_prepared);
+
+static void
+contact_blocking_dialog_am_prepared (GObject *am,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ EmpathyContactBlockingDialog *self = user_data;
+ GList *accounts, *ptr;
+ GError *error = NULL;
+
+ if (!tp_proxy_prepare_finish (am, result, &error))
+ {
+ g_critical ("Could not prepare Account Manager: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ accounts = tp_account_manager_get_valid_accounts (TP_ACCOUNT_MANAGER (am));
+
+ for (ptr = accounts; ptr != NULL; ptr = ptr->next)
+ {
+ TpAccount *account = ptr->data;
+ TpConnection *conn;
+
+ tp_g_signal_connect_object (account, "status-changed",
+ G_CALLBACK (contact_blocking_dialog_connection_status_changed),
+ self, 0);
+
+ conn = tp_account_get_connection (TP_ACCOUNT (account));
+
+ if (conn != NULL)
+ {
+ tp_proxy_prepare_async (conn, NULL,
+ contact_blocking_dialog_connection_prepared, self);
+ }
+ }
+
+ g_list_free (accounts);
+}
+
+static void contact_blocking_dialog_got_deny_channel (TpConnection *,
+ gboolean, const char *, GHashTable *, const GError *, gpointer, GObject *);
+
+static void
+contact_blocking_dialog_connection_prepared (GObject *conn,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ EmpathyContactBlockingDialog *self = user_data;
+ GHashTable *request;
+ GError *error = NULL;
+
+ if (!tp_proxy_prepare_finish (conn, result, &error))
+ {
+ DEBUG ("Failed to prepare connection: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ /* request the deny channel */
+ request = tp_asv_new (
+ TP_PROP_CHANNEL_CHANNEL_TYPE,
+ G_TYPE_STRING,
+ TP_IFACE_CHANNEL_TYPE_CONTACT_LIST,
+
+ TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
+ G_TYPE_UINT,
+ TP_HANDLE_TYPE_LIST,
+
+ TP_PROP_CHANNEL_TARGET_ID,
+ G_TYPE_STRING,
+ "deny",
+
+ NULL);
+
+ tp_cli_connection_interface_requests_call_ensure_channel (
+ TP_CONNECTION (conn), -1, request,
+ contact_blocking_dialog_got_deny_channel, NULL, NULL, G_OBJECT (self));
+
+ g_hash_table_destroy (request);
+}
+
+DECLARE_CALLBACK (contact_blocking_dialog_deny_channel_prepared);
+
+static void
+contact_blocking_dialog_got_deny_channel (TpConnection *conn,
+ gboolean yours,
+ const char *channel_path,
+ GHashTable *props,
+ const GError *in_error,
+ gpointer user_data,
+ GObject *self)
+{
+ TpChannel *channel;
+ GError *error = NULL;
+
+ const GQuark features[] = {
+ TP_CHANNEL_FEATURE_CORE,
+ TP_CHANNEL_FEATURE_GROUP,
+ 0 };
+
+ if (in_error != NULL)
+ {
+ DEBUG ("Failed to get 'deny' channel: %s", in_error->message);
+ return;
+ }
+
+ channel = tp_channel_new_from_properties (conn, channel_path, props, &error);
+
+ if (error != NULL)
+ {
+ DEBUG ("Failed to create channel proxy: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ tp_proxy_prepare_async (channel, features,
+ contact_blocking_dialog_deny_channel_prepared, self);
+}
+
+static void
+contact_blocking_dialog_deny_channel_prepared (GObject *channel,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ EmpathyContactBlockingDialog *self = user_data;
+ TpConnection *conn;
+ GError *error = NULL;
+
+ if (!tp_proxy_prepare_finish (channel, result, &error))
+ {
+ DEBUG ("Failed to prepare channel: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ conn = tp_channel_borrow_connection (TP_CHANNEL (channel));
+
+ DEBUG ("Channel prepared for connection %s", get_pretty_conn_name (conn));
+
+ g_hash_table_insert (self->priv->channels,
+ g_object_ref (conn), channel);
+ contact_blocking_dialog_refilter_account_chooser (self);
+
+ tp_g_signal_connect_object (channel, "group-members-changed",
+ G_CALLBACK (contact_blocking_dialog_deny_channel_members_changed),
+ self, 0);
+}
+
+static void
+contact_blocking_dialog_set_error (EmpathyContactBlockingDialog *self,
+ const GError *error)
+{
+ const char *msg = NULL;
+
+ if (error->domain == TP_ERRORS)
+ {
+ if (error->code == TP_ERROR_INVALID_HANDLE)
+ msg = _("Unknown or invalid identifier");
+ else if (error->code == TP_ERROR_NOT_AVAILABLE)
+ msg = _("Contact blocking temporarily unavailable");
+ else if (error->code == TP_ERROR_NOT_CAPABLE)
+ msg = _("Contact blocking unavailable");
+ else if (error->code == TP_ERROR_PERMISSION_DENIED)
+ msg = _("Permission Denied");
+ }
+
+ if (msg == NULL)
+ msg = _("Could not block contact");
+
+ gtk_label_set_text (GTK_LABEL (self->priv->info_bar_label), msg);
+ gtk_widget_show (self->priv->info_bar);
+}
+
+static void contact_blocking_dialog_add_contact_got_handle (TpConnection *,
+ const GArray *, const GError *, gpointer, GObject *);
+
+static void
+contact_blocking_dialog_add_contact (GtkWidget *widget,
+ EmpathyContactBlockingDialog *self)
+{
+ TpConnection *conn = empathy_account_chooser_get_connection (
+ EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser));
+ const char *identifiers[2] = { NULL, };
+
+ identifiers[0] = gtk_entry_get_text (
+ GTK_ENTRY (self->priv->add_contact_entry));
+
+ DEBUG ("Looking up handle for '%s'", identifiers[0]);
+
+ tp_cli_connection_call_request_handles (conn, -1,
+ TP_HANDLE_TYPE_CONTACT, identifiers,
+ contact_blocking_dialog_add_contact_got_handle,
+ NULL, NULL, G_OBJECT (self));
+
+ gtk_entry_set_text (GTK_ENTRY (self->priv->add_contact_entry), "");
+ gtk_widget_hide (self->priv->info_bar);
+}
+
+static void
+contact_blocking_dialog_added_contact (TpChannel *, const GError *,
+ gpointer, GObject *);
+
+static void
+contact_blocking_dialog_add_contact_got_handle (TpConnection *conn,
+ const GArray *handles,
+ const GError *in_error,
+ gpointer user_data,
+ GObject *self)
+{
+ EmpathyContactBlockingDialogPrivate *priv = GET_PRIVATE (self);
+ TpChannel *channel = g_hash_table_lookup (priv->channels, conn);
+
+ if (in_error != NULL)
+ {
+ DEBUG ("Error getting handle: %s", in_error->message);
+
+ contact_blocking_dialog_set_error (
+ EMPATHY_CONTACT_BLOCKING_DIALOG (self), in_error);
+
+ return;
+ }
+
+ g_return_if_fail (handles->len == 1);
+
+ DEBUG ("Adding handle %u to deny channel",
+ g_array_index (handles, TpHandle, 0));
+
+ tp_cli_channel_interface_group_call_add_members (channel, -1,
+ handles, "",
+ contact_blocking_dialog_added_contact, NULL, NULL, self);
+}
+
+static void
+contact_blocking_dialog_added_contact (TpChannel *channel,
+ const GError *in_error,
+ gpointer user_data,
+ GObject *self)
+{
+ if (in_error != NULL)
+ {
+ DEBUG ("Error adding contact to deny list: %s", in_error->message);
+
+ contact_blocking_dialog_set_error (
+ EMPATHY_CONTACT_BLOCKING_DIALOG (self), in_error);
+
+ return;
+ }
+
+ DEBUG ("Contact added");
+}
+
+static void
+contact_blocking_dialog_removed_contacts (TpChannel *,
+ const GError *, gpointer, GObject *);
+
+static void
+contact_blocking_dialog_remove_contacts (GtkWidget *button,
+ EmpathyContactBlockingDialog *self)
+{
+ TpConnection *conn = empathy_account_chooser_get_connection (
+ EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser));
+ TpChannel *channel = g_hash_table_lookup (self->priv->channels, conn);
+ GtkTreeModel *model;
+ GList *rows, *ptr;
+ GArray *handles = g_array_new (FALSE, FALSE, sizeof (TpHandle));
+
+ rows = gtk_tree_selection_get_selected_rows (self->priv->selection, &model);
+
+ for (ptr = rows; ptr != NULL; ptr = ptr->next)
+ {
+ GtkTreePath *path = ptr->data;
+ GtkTreeIter iter;
+ TpHandle handle;
+
+ if (!gtk_tree_model_get_iter (model, &iter, path))
+ continue;
+
+ gtk_tree_model_get (model, &iter,
+ COL_HANDLE, &handle,
+ -1);
+
+ g_array_append_val (handles, handle);
+ gtk_tree_path_free (path);
+ }
+
+ g_list_free (rows);
+
+ if (handles->len > 0)
+ {
+ DEBUG ("Removing %u handles", handles->len);
+
+ tp_cli_channel_interface_group_call_remove_members (channel, -1,
+ handles, "",
+ contact_blocking_dialog_removed_contacts,
+ NULL, NULL, G_OBJECT (self));
+ }
+
+ g_array_unref (handles);
+}
+
+static void
+contact_blocking_dialog_removed_contacts (TpChannel *channel,
+ const GError *in_error,
+ gpointer user_data,
+ GObject *self)
+{
+ if (in_error != NULL)
+ {
+ DEBUG ("Error removing contacts from deny list: %s", in_error->message);
+
+ contact_blocking_dialog_set_error (
+ EMPATHY_CONTACT_BLOCKING_DIALOG (self), in_error);
+
+ return;
+ }
+
+ DEBUG ("Contacts removed");
+}
+
+static void
+contact_blocking_dialog_account_changed (GtkWidget *account_chooser,
+ EmpathyContactBlockingDialog *self)
+{
+ TpConnection *conn = empathy_account_chooser_get_connection (
+ EMPATHY_ACCOUNT_CHOOSER (account_chooser));
+ TpChannel *channel;
+ GArray *blocked;
+ EmpathyContactManager *contact_manager;
+ EmpathyTpContactList *contact_list;
+ GList *members, *ptr;
+
+ if (self->priv->block_account_changed > 0)
+ return;
+
+ /* clear the lists of contacts */
+ gtk_list_store_clear (self->priv->blocked_contacts);
+ gtk_list_store_clear (self->priv->completion_contacts);
+
+ if (conn == NULL)
+ return;
+
+ DEBUG ("Account changed: %s", get_pretty_conn_name (conn));
+
+ /* load the deny list */
+ channel = g_hash_table_lookup (self->priv->channels, conn);
+
+ if (channel == NULL)
+ return;
+
+ g_return_if_fail (TP_IS_CHANNEL (channel));
+
+ blocked = tp_intset_to_array (tp_channel_group_get_members (channel));
+
+ DEBUG ("%u contacts on blocked list", blocked->len);
+
+ contact_blocking_dialog_add_contacts_to_list (self, conn, blocked);
+ g_array_unref (blocked);
+
+ /* load the completion list */
+ g_return_if_fail (empathy_contact_manager_initialized ());
+
+ DEBUG ("Loading contacts");
+
+ contact_manager = empathy_contact_manager_dup_singleton ();
+ contact_list = empathy_contact_manager_get_list (contact_manager, conn);
+ members = empathy_contact_list_get_members (
+ EMPATHY_CONTACT_LIST (contact_list));
+
+ for (ptr = members; ptr != NULL; ptr = ptr->next)
+ {
+ EmpathyContact *contact = ptr->data;
+
+ gtk_list_store_insert_with_values (self->priv->completion_contacts,
+ NULL, -1,
+ COL_IDENTIFIER, empathy_contact_get_id (contact),
+ -1);
+
+ g_object_unref (contact);
+ }
+
+ g_list_free (members);
+ g_object_unref (contact_manager);
+}
+
+static void
+contact_blocking_dialog_view_selection_changed (GtkTreeSelection *selection,
+ EmpathyContactBlockingDialog *self)
+{
+ GList *rows = gtk_tree_selection_get_selected_rows (selection, NULL);
+
+ /* update the sensitivity of the remove button */
+ gtk_widget_set_sensitive (self->priv->remove_button, rows != NULL);
+
+ g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (rows);
+}
+
+static void
+contact_blocking_dialog_dispose (GObject *self)
+{
+ EmpathyContactBlockingDialogPrivate *priv = GET_PRIVATE (self);
+
+ tp_clear_pointer (&priv->channels, g_hash_table_destroy);
+
+ G_OBJECT_CLASS (empathy_contact_blocking_dialog_parent_class)->dispose (self);
+}
+
+static void
+empathy_contact_blocking_dialog_class_init (
+ EmpathyContactBlockingDialogClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->dispose = contact_blocking_dialog_dispose;
+
+ g_type_class_add_private (gobject_class,
+ sizeof (EmpathyContactBlockingDialogPrivate));
+}
+
+static void
+empathy_contact_blocking_dialog_init (EmpathyContactBlockingDialog *self)
+{
+ GtkBuilder *gui;
+ char *filename;
+ GtkWidget *contents;
+ GtkWidget *account_hbox, *blocked_contacts_view;
+ GtkEntryCompletion *completion;
+ TpAccountManager *am;
+
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ EMPATHY_TYPE_CONTACT_BLOCKING_DIALOG,
+ EmpathyContactBlockingDialogPrivate);
+
+ self->priv->channels = g_hash_table_new_full (NULL, NULL,
+ g_object_unref, g_object_unref);
+
+ gtk_window_set_title (GTK_WINDOW (self), _("Edit Blocked Contacts"));
+ gtk_dialog_add_button (GTK_DIALOG (self),
+ GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
+
+ filename = empathy_file_lookup ("empathy-contact-blocking-dialog.ui",
+ "libempathy-gtk");
+
+ gui = empathy_builder_get_file (filename,
+ "contents", &contents,
+ "account-hbox", &account_hbox,
+ "add-button", &self->priv->add_button,
+ "add-contact-entry", &self->priv->add_contact_entry,
+ "blocked-contacts", &self->priv->blocked_contacts,
+ "blocked-contacts-view", &blocked_contacts_view,
+ "remove-button", &self->priv->remove_button,
+ NULL);
+
+ empathy_builder_connect (gui, self,
+ "add-button", "clicked", contact_blocking_dialog_add_contact,
+ "add-contact-entry", "activate", contact_blocking_dialog_add_contact,
+ "remove-button", "clicked", contact_blocking_dialog_remove_contacts,
+ NULL);
+
+ /* add the contents to the dialog */
+ gtk_container_add (
+ GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (self))),
+ contents);
+ gtk_widget_show (contents);
+
+ /* set up the tree selection */
+ self->priv->selection = gtk_tree_view_get_selection (
+ GTK_TREE_VIEW (blocked_contacts_view));
+ gtk_tree_selection_set_mode (self->priv->selection, GTK_SELECTION_MULTIPLE);
+ g_signal_connect (self->priv->selection, "changed",
+ G_CALLBACK (contact_blocking_dialog_view_selection_changed), self);
+
+ /* build the contact entry */
+ self->priv->completion_contacts = gtk_list_store_new (1, G_TYPE_STRING);
+ completion = gtk_entry_completion_new ();
+ gtk_entry_completion_set_model (completion,
+ GTK_TREE_MODEL (self->priv->completion_contacts));
+ gtk_entry_completion_set_text_column (completion, COL_IDENTIFIER);
+ gtk_entry_set_completion (GTK_ENTRY (self->priv->add_contact_entry),
+ completion);
+ g_object_unref (completion);
+ g_object_unref (self->priv->completion_contacts);
+
+ /* add the account chooser */
+ self->priv->account_chooser = empathy_account_chooser_new ();
+ contact_blocking_dialog_refilter_account_chooser (self);
+ g_signal_connect (self->priv->account_chooser, "changed",
+ G_CALLBACK (contact_blocking_dialog_account_changed), self);
+
+ gtk_box_pack_start (GTK_BOX (account_hbox), self->priv->account_chooser,
+ TRUE, TRUE, 0);
+ gtk_widget_show (self->priv->account_chooser);
+
+ /* add an error warning info bar */
+ self->priv->info_bar = gtk_info_bar_new ();
+ gtk_box_pack_start (GTK_BOX (contents), self->priv->info_bar, FALSE, TRUE, 0);
+ gtk_info_bar_set_message_type (GTK_INFO_BAR (self->priv->info_bar),
+ GTK_MESSAGE_ERROR);
+
+ self->priv->info_bar_label = gtk_label_new ("");
+ gtk_container_add (GTK_CONTAINER (
+ gtk_info_bar_get_content_area (GTK_INFO_BAR (self->priv->info_bar))),
+ self->priv->info_bar_label);
+ gtk_widget_show (self->priv->info_bar_label);
+
+ /* prepare the account manager */
+ am = tp_account_manager_dup ();
+ tp_proxy_prepare_async (am, NULL, contact_blocking_dialog_am_prepared, self);
+ g_object_unref (am);
+
+ g_free (filename);
+ g_object_unref (gui);
+}
+
+GtkWidget *
+empathy_contact_blocking_dialog_new (GtkWindow *parent)
+{
+ GtkWidget *self = g_object_new (EMPATHY_TYPE_CONTACT_BLOCKING_DIALOG,
+ NULL);
+
+ if (parent != NULL)
+ {
+ gtk_window_set_transient_for (GTK_WINDOW (self), parent);
+ }
+
+ return self;
+}
diff --git a/libempathy-gtk/empathy-contact-blocking-dialog.h b/libempathy-gtk/empathy-contact-blocking-dialog.h
new file mode 100644
index 000000000..96451c6c3
--- /dev/null
+++ b/libempathy-gtk/empathy-contact-blocking-dialog.h
@@ -0,0 +1,60 @@
+/*
+ * empathy-contact-blocking-dialog.h
+ *
+ * EmpathyContactBlockingDialog
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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
+ *
+ * Authors: Danielle Madeley <danielle.madeley@collabora.co.uk>
+ */
+
+#ifndef __EMPATHY_CONTACT_BLOCKING_DIALOG_H__
+#define __EMPATHY_CONTACT_BLOCKING_DIALOG_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EMPATHY_TYPE_CONTACT_BLOCKING_DIALOG (empathy_contact_blocking_dialog_get_type ())
+#define EMPATHY_CONTACT_BLOCKING_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EMPATHY_TYPE_CONTACT_BLOCKING_DIALOG, EmpathyContactBlockingDialog))
+#define EMPATHY_CONTACT_BLOCKING_DIALOG_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EMPATHY_TYPE_CONTACT_BLOCKING_DIALOG, EmpathyContactBlockingDialogClass))
+#define EMPATHY_IS_CONTACT_BLOCKING_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EMPATHY_TYPE_CONTACT_BLOCKING_DIALOG))
+#define EMPATHY_IS_CONTACT_BLOCKING_DIALOG_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EMPATHY_TYPE_CONTACT_BLOCKING_DIALOG))
+#define EMPATHY_CONTACT_BLOCKING_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_CONTACT_BLOCKING_DIALOG, EmpathyContactBlockingDialogClass))
+
+typedef struct _EmpathyContactBlockingDialog EmpathyContactBlockingDialog;
+typedef struct _EmpathyContactBlockingDialogClass EmpathyContactBlockingDialogClass;
+typedef struct _EmpathyContactBlockingDialogPrivate EmpathyContactBlockingDialogPrivate;
+
+struct _EmpathyContactBlockingDialog
+{
+ GtkDialog parent;
+ EmpathyContactBlockingDialogPrivate *priv;
+};
+
+struct _EmpathyContactBlockingDialogClass
+{
+ GtkDialogClass parent_class;
+};
+
+GType empathy_contact_blocking_dialog_get_type (void);
+
+GtkWidget *empathy_contact_blocking_dialog_new (GtkWindow *parent);
+
+G_END_DECLS
+
+#endif
diff --git a/libempathy-gtk/empathy-contact-blocking-dialog.ui b/libempathy-gtk/empathy-contact-blocking-dialog.ui
new file mode 100644
index 000000000..b2ea89b81
--- /dev/null
+++ b/libempathy-gtk/empathy-contact-blocking-dialog.ui
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkListStore" id="blocked-contacts">
+ <columns>
+ <!-- column-name identifier -->
+ <column type="gchararray"/>
+ <!-- column-name handle -->
+ <column type="guint"/>
+ </columns>
+ </object>
+ <object class="GtkVBox" id="contents">
+ <property name="visible">True</property>
+ <property name="border_width">6</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="account-hbox">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Account:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">etched-in</property>
+ <child>
+ <object class="GtkTreeView" id="blocked-contacts-view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">blocked-contacts</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+ <property name="title">Blocked Contacts</property>
+ <property name="sort_indicator">True</property>
+ <property name="sort_column_id">0</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVButtonBox" id="vbuttonbox1">
+ <property name="visible">True</property>
+ <property name="layout_style">start</property>
+ <child>
+ <object class="GtkButton" id="remove-button">
+ <property name="label">gtk-remove</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="add-contact-hbox">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkEntry" id="add-contact-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="add-button">
+ <property name="label">gtk-add</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkSizeGroup" id="sizegroup1">
+ <widgets>
+ <widget name="add-button"/>
+ <widget name="remove-button"/>
+ </widgets>
+ </object>
+</interface>
diff --git a/libempathy-gtk/empathy-contact-dialogs.c b/libempathy-gtk/empathy-contact-dialogs.c
index fb67c41cc..d16062eff 100644
--- a/libempathy-gtk/empathy-contact-dialogs.c
+++ b/libempathy-gtk/empathy-contact-dialogs.c
@@ -17,6 +17,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: Xavier Claessens <xclaesse@gmail.com>
+ * Danielle Madeley <danielle.madeley@collabora.co.uk>
*/
#include <config.h>
@@ -83,9 +84,29 @@ subscription_dialog_response_cb (GtkDialog *dialog,
empathy_contact_list_remove (EMPATHY_CONTACT_LIST (manager),
contact, "");
}
+ else if (response == GTK_RESPONSE_REJECT) {
+ gboolean abusive;
+
+ /* confirm the blocking */
+ if (empathy_block_contact_dialog_show (GTK_WINDOW (dialog),
+ contact, &abusive)) {
+ empathy_contact_list_remove (
+ EMPATHY_CONTACT_LIST (manager),
+ contact, "");
+ empathy_contact_list_set_blocked (
+ EMPATHY_CONTACT_LIST (manager),
+ contact, TRUE, abusive);
+ } else {
+ /* if they don't confirm, return back to the
+ * first dialog */
+ goto finally;
+ }
+ }
subscription_dialogs = g_list_remove (subscription_dialogs, dialog);
gtk_widget_destroy (GTK_WIDGET (dialog));
+
+finally:
g_object_unref (manager);
}
@@ -99,8 +120,13 @@ empathy_subscription_dialog_show (EmpathyContact *contact,
GtkWidget *hbox_subscription;
GtkWidget *vbox;
GtkWidget *contact_widget;
+ GtkWidget *block_user_button;
GList *l;
gchar *filename;
+ EmpathyContactManager *manager;
+ EmpathyContactListFlags flags;
+
+ manager = empathy_contact_manager_dup_singleton ();
g_return_if_fail (EMPATHY_IS_CONTACT (contact));
@@ -117,6 +143,7 @@ empathy_subscription_dialog_show (EmpathyContact *contact,
gui = empathy_builder_get_file (filename,
"subscription_request_dialog", &dialog,
"hbox_subscription", &hbox_subscription,
+ "block-user-button", &block_user_button,
NULL);
g_free (filename);
g_object_unref (gui);
@@ -161,10 +188,17 @@ empathy_subscription_dialog_show (EmpathyContact *contact,
G_CALLBACK (subscription_dialog_response_cb),
contact_widget);
+ flags = empathy_contact_manager_get_flags_for_connection (manager,
+ empathy_contact_get_connection (contact));
+
+ if (flags & EMPATHY_CONTACT_LIST_CAN_BLOCK)
+ gtk_widget_show (block_user_button);
+
if (parent) {
gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
}
+ g_object_unref (manager);
gtk_widget_show (dialog);
}
@@ -468,3 +502,72 @@ empathy_new_contact_dialog_show_with_contact (GtkWindow *parent,
gtk_widget_show (dialog);
}
+/**
+ * empathy_block_contact_dialog_show:
+ * @parent: the parent of this dialog (or %NULL)
+ * @contact: the contact for this dialog
+ * @abusive: a pointer to store the value of the abusive contact check box
+ * (or %NULL)
+ *
+ * Returns: %TRUE if the user wishes to block the contact
+ */
+gboolean
+empathy_block_contact_dialog_show (GtkWindow *parent,
+ EmpathyContact *contact,
+ gboolean *abusive)
+{
+ EmpathyContactManager *manager;
+ EmpathyContactListFlags flags;
+ GtkWidget *dialog;
+ GtkWidget *abusive_check = NULL;
+ int res;
+
+ manager = empathy_contact_manager_dup_singleton ();
+ flags = empathy_contact_manager_get_flags_for_connection (manager,
+ empathy_contact_get_connection (contact));
+
+ dialog = gtk_message_dialog_new (parent,
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
+ _("Block %s?"),
+ empathy_contact_get_alias (contact));
+
+ gtk_message_dialog_format_secondary_text (
+ GTK_MESSAGE_DIALOG (dialog),
+ _("Are you sure you want to block '%s' from "
+ "contacting you again?"),
+ empathy_contact_get_alias (contact));
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ _("_Block"), GTK_RESPONSE_REJECT,
+ NULL);
+
+ /* ask the user if they want to also report the contact as abusive */
+ if (flags & EMPATHY_CONTACT_LIST_CAN_REPORT_ABUSIVE) {
+ GtkWidget *vbox;
+
+ vbox = gtk_message_dialog_get_message_area (
+ GTK_MESSAGE_DIALOG (dialog));
+ abusive_check = gtk_check_button_new_with_mnemonic (
+ _("_Report this contact as abusive"));
+
+ gtk_box_pack_start (GTK_BOX (vbox), abusive_check,
+ FALSE, TRUE, 0);
+ gtk_widget_show (abusive_check);
+ }
+
+ res = gtk_dialog_run (GTK_DIALOG (dialog));
+ if (abusive != NULL) {
+ if (abusive_check != NULL) {
+ *abusive = gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (abusive_check));
+ } else {
+ *abusive = FALSE;
+ }
+ }
+
+ gtk_widget_destroy (dialog);
+ g_object_unref (manager);
+
+ return res == GTK_RESPONSE_REJECT;
+}
diff --git a/libempathy-gtk/empathy-contact-dialogs.h b/libempathy-gtk/empathy-contact-dialogs.h
index 8c3ffc6c1..7dc1db10f 100644
--- a/libempathy-gtk/empathy-contact-dialogs.h
+++ b/libempathy-gtk/empathy-contact-dialogs.h
@@ -39,6 +39,9 @@ void empathy_contact_personal_dialog_show (GtkWindow *parent);
void empathy_new_contact_dialog_show (GtkWindow *parent);
void empathy_new_contact_dialog_show_with_contact (GtkWindow *parent,
EmpathyContact *contact);
+gboolean empathy_block_contact_dialog_show (GtkWindow *parent,
+ EmpathyContact *contact,
+ gboolean *abusive);
G_END_DECLS
diff --git a/libempathy-gtk/empathy-contact-dialogs.ui b/libempathy-gtk/empathy-contact-dialogs.ui
index c18bfabb4..ecafddb69 100644
--- a/libempathy-gtk/empathy-contact-dialogs.ui
+++ b/libempathy-gtk/empathy-contact-dialogs.ui
@@ -1,13 +1,14 @@
-<?xml version="1.0"?>
-<!--*- mode: xml -*-->
+<?xml version="1.0" encoding="UTF-8"?>
<interface>
+ <!-- interface-requires gtk+ 2.12 -->
+ <!-- interface-naming-policy toplevel-contextual -->
<object class="GtkDialog" id="subscription_request_dialog">
<property name="border_width">5</property>
<property name="title" translatable="yes">Subscription Request</property>
<property name="role">subscription_request</property>
<property name="resizable">False</property>
- <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
- <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="type_hint">dialog</property>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox4">
<property name="visible">True</property>
@@ -22,11 +23,12 @@
<property name="visible">True</property>
<property name="yalign">0</property>
<property name="stock">gtk-dialog-question</property>
- <property name="icon_size">6</property>
+ <property name="icon-size">6</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
+ <property name="position">0</property>
</packing>
</child>
<child>
@@ -40,50 +42,77 @@
<child internal-child="action_area">
<object class="GtkHButtonBox" id="dialog-action_area4">
<property name="visible">True</property>
- <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="block-user-button">
+ <property name="label" translatable="yes">_Block User</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
<child>
<object class="GtkButton" id="button19">
+ <property name="label" translatable="yes">Decide _Later</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
- <property name="label" translatable="yes">Decide _Later</property>
+ <property name="receives_default">False</property>
<property name="use_underline">True</property>
</object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
</child>
<child>
<object class="GtkButton" id="button20">
+ <property name="label">gtk-no</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
- <property name="label">gtk-no</property>
+ <property name="receives_default">False</property>
<property name="use_stock">True</property>
</object>
<packing>
- <property name="position">1</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button21">
+ <property name="label">gtk-yes</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
- <property name="label">gtk-yes</property>
+ <property name="receives_default">False</property>
<property name="use_stock">True</property>
</object>
<packing>
- <property name="position">2</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
- <property name="pack_type">GTK_PACK_END</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
</packing>
</child>
</object>
</child>
<action-widgets>
+ <action-widget response="-2">block-user-button</action-widget>
<action-widget response="-6">button19</action-widget>
<action-widget response="-9">button20</action-widget>
<action-widget response="-8">button21</action-widget>
diff --git a/libempathy-gtk/empathy-contact-menu.c b/libempathy-gtk/empathy-contact-menu.c
index 3bf415746..9f36cf552 100644
--- a/libempathy-gtk/empathy-contact-menu.c
+++ b/libempathy-gtk/empathy-contact-menu.c
@@ -40,6 +40,8 @@
#include "empathy-ui-utils.h"
#include "empathy-share-my-desktop.h"
+static GtkWidget *empathy_contact_block_menu_item_new (EmpathyContact *);
+
GtkWidget *
empathy_contact_menu_new (EmpathyContact *contact,
EmpathyContactFeatureFlags features)
@@ -139,6 +141,19 @@ empathy_contact_menu_new (EmpathyContact *contact,
gtk_widget_show (item);
}
+ /* Separator & Block */
+ if (features & EMPATHY_CONTACT_FEATURE_BLOCK &&
+ (item = empathy_contact_block_menu_item_new (contact)) != NULL) {
+ GtkWidget *sep;
+
+ sep = gtk_separator_menu_item_new ();
+ gtk_menu_shell_append (shell, sep);
+ gtk_widget_show (sep);
+
+ gtk_menu_shell_append (shell, item);
+ gtk_widget_show (item);
+ }
+
return menu;
}
@@ -214,6 +229,85 @@ empathy_contact_add_menu_item_new (EmpathyContact *contact)
}
static void
+empathy_contact_block_menu_item_toggled (GtkCheckMenuItem *item,
+ EmpathyContact *contact)
+{
+ static guint block_signal = 0;
+ EmpathyContactManager *manager;
+ gboolean blocked, abusive;
+
+ if (block_signal > 0)
+ return;
+
+ blocked = gtk_check_menu_item_get_active (item);
+
+ if (blocked) {
+ /* confirm the user really wishes to block the contact */
+ GtkWidget *parent;
+
+ /* gtk_menu_get_attach_widget() doesn't behave properly here
+ * for some reason */
+ parent = g_object_get_data (
+ G_OBJECT (gtk_widget_get_parent (GTK_WIDGET (item))),
+ "window");
+
+ if (!empathy_block_contact_dialog_show (GTK_WINDOW (parent),
+ contact, &abusive))
+ return;
+ }
+
+ manager = empathy_contact_manager_dup_singleton ();
+ empathy_contact_list_set_blocked (EMPATHY_CONTACT_LIST (manager),
+ contact, blocked, abusive);
+ g_object_unref (manager);
+
+ /* update the toggle with the blocked status */
+ block_signal++;
+ gtk_check_menu_item_set_active (item, blocked);
+ block_signal--;
+}
+
+static GtkWidget *
+empathy_contact_block_menu_item_new (EmpathyContact *contact)
+{
+ GtkWidget *item;
+ EmpathyContactManager *manager;
+ TpConnection *connection;
+ EmpathyContactListFlags flags;
+ gboolean blocked;
+
+ g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
+
+ manager = empathy_contact_manager_dup_singleton ();
+
+ if (!empathy_contact_manager_initialized ()) {
+ return NULL;
+ }
+
+ connection = empathy_contact_get_connection (contact);
+
+ flags = empathy_contact_manager_get_flags_for_connection (manager,
+ connection);
+
+ if (!(flags & EMPATHY_CONTACT_LIST_CAN_BLOCK)) {
+ return NULL;
+ }
+
+ item = gtk_check_menu_item_new_with_mnemonic (_("_Block Contact"));
+ blocked = empathy_contact_list_get_blocked (
+ EMPATHY_CONTACT_LIST (manager),
+ contact);
+
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), blocked);
+
+ g_signal_connect (item, "toggled",
+ G_CALLBACK (empathy_contact_block_menu_item_toggled),
+ contact);
+
+ return item;
+}
+
+static void
empathy_contact_chat_menu_item_activated (GtkMenuItem *item,
EmpathyContact *contact)
{
diff --git a/libempathy-gtk/empathy-contact-menu.h b/libempathy-gtk/empathy-contact-menu.h
index 2e0247420..35d6479e6 100644
--- a/libempathy-gtk/empathy-contact-menu.h
+++ b/libempathy-gtk/empathy-contact-menu.h
@@ -37,7 +37,8 @@ typedef enum {
EMPATHY_CONTACT_FEATURE_INFO = 1 << 4,
EMPATHY_CONTACT_FEATURE_FAVOURITE = 1 << 5,
EMPATHY_CONTACT_FEATURE_FT = 1 << 6,
- EMPATHY_CONTACT_FEATURE_ALL = (1 << 7) - 1,
+ EMPATHY_CONTACT_FEATURE_BLOCK = 1 << 7,
+ EMPATHY_CONTACT_FEATURE_ALL = (1 << 8) - 1,
} EmpathyContactFeatureFlags;
GtkWidget * empathy_contact_menu_new (EmpathyContact *contact,
diff --git a/libempathy-gtk/empathy-individual-dialogs.c b/libempathy-gtk/empathy-individual-dialogs.c
index 2c9801059..67ec22164 100644
--- a/libempathy-gtk/empathy-individual-dialogs.c
+++ b/libempathy-gtk/empathy-individual-dialogs.c
@@ -29,14 +29,18 @@
#include <telepathy-glib/util.h>
#include <folks/folks.h>
+#include <folks/folks-telepathy.h>
#include <libempathy/empathy-individual-manager.h>
#include <libempathy/empathy-utils.h>
+#include <libempathy/empathy-contact-manager.h>
#include "empathy-individual-dialogs.h"
#include "empathy-contact-widget.h"
#include "empathy-ui-utils.h"
+#define BULLET_POINT "\342\200\242"
+
static GtkWidget *new_individual_dialog = NULL;
/*
@@ -156,3 +160,138 @@ empathy_new_individual_dialog_show_with_individual (GtkWindow *parent,
tp_clear_object (&contact);
}
+
+static char *
+contact_pretty_name (TpContact *contact)
+{
+ const char *alias = tp_contact_get_alias (contact);
+ const char *identifier = tp_contact_get_identifier (contact);
+
+ if (tp_strdiff (alias, identifier))
+ return g_strdup_printf ("%s (%s)", alias, identifier);
+ else
+ return g_strdup (alias);
+}
+
+/*
+ * Block contact dialog
+ */
+gboolean
+empathy_block_individual_dialog_show (GtkWindow *parent,
+ FolksIndividual *individual,
+ gboolean *abusive)
+{
+ EmpathyContactManager *contact_manager =
+ empathy_contact_manager_dup_singleton ();
+ GtkWidget *dialog;
+ GtkWidget *abusive_check = NULL;
+ GList *personas, *l;
+ GString *text = g_string_new ("");
+ GString *blocked_str = g_string_new ("");
+ GString *notblocked_str = g_string_new ("");
+ guint npersonas_blocked = 0, npersonas_notblocked = 0;
+ gboolean can_report_abuse = FALSE;
+ int res;
+
+ dialog = gtk_message_dialog_new (parent,
+ GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
+ _("Block %s?"),
+ folks_aliasable_get_alias (FOLKS_ALIASABLE (individual)));
+
+ /* build a list of personas that support blocking */
+ personas = folks_individual_get_personas (individual);
+
+ for (l = personas; l != NULL; l = l->next)
+ {
+ TpfPersona *persona = l->data;
+ TpContact *contact;
+ EmpathyContactListFlags flags;
+ GString *s;
+ char *str;
+
+ if (!TPF_IS_PERSONA (persona))
+ continue;
+
+ contact = tpf_persona_get_contact (persona);
+ flags = empathy_contact_manager_get_flags_for_connection (
+ contact_manager, tp_contact_get_connection (contact));
+
+ if (flags & EMPATHY_CONTACT_LIST_CAN_BLOCK)
+ {
+ s = blocked_str;
+ npersonas_blocked++;
+ }
+ else
+ {
+ s = notblocked_str;
+ npersonas_notblocked++;
+ }
+
+ if (flags & EMPATHY_CONTACT_LIST_CAN_REPORT_ABUSIVE)
+ can_report_abuse = TRUE;
+
+ str = contact_pretty_name (contact);
+ g_string_append_printf (s, "\n " BULLET_POINT " %s", str);
+ g_free (str);
+ }
+
+ g_string_append_printf (text,
+ _("Are you sure you want to block '%s' from contacting you again?"),
+ folks_aliasable_get_alias (FOLKS_ALIASABLE (individual)));
+
+ if (npersonas_blocked > 0)
+ g_string_append_printf (text, "\n\n%s\n%s",
+ ngettext ("The following identity will be blocked:",
+ "The following identities will be blocked:",
+ npersonas_blocked),
+ blocked_str->str);
+
+ if (npersonas_notblocked > 0)
+ g_string_append_printf (text, "\n\n%s\n%s",
+ ngettext ("The following identity can not be blocked:",
+ "The following identities can not be blocked:",
+ npersonas_notblocked),
+ notblocked_str->str);
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", text->str);
+
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ _("_Block"), GTK_RESPONSE_REJECT,
+ NULL);
+
+ if (can_report_abuse)
+ {
+ GtkWidget *vbox;
+
+ vbox = gtk_message_dialog_get_message_area (GTK_MESSAGE_DIALOG (dialog));
+ abusive_check = gtk_check_button_new_with_mnemonic (
+ ngettext ("_Report this contact as abusive",
+ "_Report these contacts as abusive",
+ npersonas_blocked));
+
+ gtk_box_pack_start (GTK_BOX (vbox), abusive_check, FALSE, TRUE, 0);
+ gtk_widget_show (abusive_check);
+ }
+
+ g_object_unref (contact_manager);
+ g_string_free (text, TRUE);
+ g_string_free (blocked_str, TRUE);
+ g_string_free (notblocked_str, TRUE);
+
+ res = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if (abusive != NULL)
+ {
+ if (abusive_check != NULL)
+ *abusive = gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (abusive_check));
+ else
+ *abusive = FALSE;
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return res == GTK_RESPONSE_REJECT;
+}
diff --git a/libempathy-gtk/empathy-individual-dialogs.h b/libempathy-gtk/empathy-individual-dialogs.h
index b34a7ab18..1444d5ac8 100644
--- a/libempathy-gtk/empathy-individual-dialogs.h
+++ b/libempathy-gtk/empathy-individual-dialogs.h
@@ -32,6 +32,9 @@ G_BEGIN_DECLS
void empathy_new_individual_dialog_show (GtkWindow *parent);
void empathy_new_individual_dialog_show_with_individual (GtkWindow *parent,
FolksIndividual *individual);
+gboolean empathy_block_individual_dialog_show (GtkWindow *parent,
+ FolksIndividual *individual,
+ gboolean *abusive);
G_END_DECLS
diff --git a/libempathy-gtk/empathy-individual-view.c b/libempathy-gtk/empathy-individual-view.c
index 9be022139..172b1facd 100644
--- a/libempathy-gtk/empathy-individual-view.c
+++ b/libempathy-gtk/empathy-individual-view.c
@@ -47,6 +47,7 @@
#include "empathy-individual-menu.h"
#include "empathy-individual-store.h"
#include "empathy-contact-dialogs.h"
+#include "empathy-individual-dialogs.h"
#include "empathy-images.h"
#include "empathy-linking-dialog.h"
#include "empathy-cell-renderer-expander.h"
@@ -2269,16 +2270,22 @@ empathy_individual_view_dup_selected_group (EmpathyIndividualView *view,
return name;
}
-static gboolean
+static int
individual_view_remove_dialog_show (GtkWindow *parent,
const gchar *message,
- const gchar *secondary_text)
+ const gchar *secondary_text,
+ gboolean block_button)
{
GtkWidget *dialog;
gboolean res;
dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL,
GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s", message);
+
+ if (block_button)
+ gtk_dialog_add_button (GTK_DIALOG (dialog),
+ _("Delete and Block"), GTK_RESPONSE_REJECT);
+
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
GTK_STOCK_DELETE, GTK_RESPONSE_YES, NULL);
@@ -2290,7 +2297,7 @@ individual_view_remove_dialog_show (GtkWindow *parent,
res = gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
- return (res == GTK_RESPONSE_YES);
+ return res;
}
static void
@@ -2310,7 +2317,7 @@ individual_view_group_remove_activate_cb (GtkMenuItem *menuitem,
group);
parent = empathy_get_toplevel_window (GTK_WIDGET (view));
if (individual_view_remove_dialog_show (parent, _("Removing group"),
- text))
+ text, FALSE) == GTK_RESPONSE_YES)
{
EmpathyIndividualManager *manager =
empathy_individual_manager_dup_singleton ();
@@ -2389,10 +2396,13 @@ individual_view_remove_activate_cb (GtkMenuItem *menuitem,
if (individual != NULL)
{
+ EmpathyIndividualManager *manager;
gchar *text;
GtkWindow *parent;
GList *l, *personas;
guint persona_count = 0;
+ gboolean can_block;
+ int res;
personas = folks_individual_get_personas (individual);
@@ -2428,20 +2438,37 @@ individual_view_remove_activate_cb (GtkMenuItem *menuitem,
folks_aliasable_get_alias (FOLKS_ALIASABLE (individual)));
}
+
+ manager = empathy_individual_manager_dup_singleton ();
+ can_block = empathy_individual_manager_supports_blocking (manager,
+ individual);
parent = empathy_get_toplevel_window (GTK_WIDGET (view));
+ res = individual_view_remove_dialog_show (parent, _("Removing contact"),
+ text, can_block);
- if (individual_view_remove_dialog_show (parent, _("Removing contact"),
- text))
+ if (res == GTK_RESPONSE_YES || res == GTK_RESPONSE_REJECT)
{
- EmpathyIndividualManager *manager;
+ gboolean abusive;
+
+ if (res == GTK_RESPONSE_REJECT &&
+ empathy_block_individual_dialog_show (parent, individual,
+ &abusive))
+ {
+ empathy_individual_manager_set_blocked (manager, individual,
+ TRUE, abusive);
+ }
+ else
+ {
+ goto finally;
+ }
- manager = empathy_individual_manager_dup_singleton ();
empathy_individual_manager_remove (manager, individual, "");
- g_object_unref (G_OBJECT (manager));
}
+finally:
g_free (text);
g_object_unref (individual);
+ g_object_unref (manager);
}
}
diff --git a/libempathy/empathy-contact-list.c b/libempathy/empathy-contact-list.c
index 631bb4a37..d28866735 100644
--- a/libempathy/empathy-contact-list.c
+++ b/libempathy/empathy-contact-list.c
@@ -278,3 +278,27 @@ empathy_contact_list_remove_from_favourites (EmpathyContactList *list,
contact);
}
}
+
+void
+empathy_contact_list_set_blocked (EmpathyContactList *list,
+ EmpathyContact *contact,
+ gboolean blocked,
+ gboolean abusive)
+{
+ EmpathyContactListIface *iface = EMPATHY_CONTACT_LIST_GET_IFACE (list);
+
+ if (iface->set_blocked != NULL)
+ iface->set_blocked (list, contact, blocked, abusive);
+}
+
+gboolean
+empathy_contact_list_get_blocked (EmpathyContactList *list,
+ EmpathyContact *contact)
+{
+ EmpathyContactListIface *iface = EMPATHY_CONTACT_LIST_GET_IFACE (list);
+
+ if (iface->get_blocked != NULL)
+ return iface->get_blocked (list, contact);
+ else
+ return FALSE;
+}
diff --git a/libempathy/empathy-contact-list.h b/libempathy/empathy-contact-list.h
index 3817af876..cf523bf2a 100644
--- a/libempathy/empathy-contact-list.h
+++ b/libempathy/empathy-contact-list.h
@@ -39,6 +39,8 @@ typedef enum {
EMPATHY_CONTACT_LIST_CAN_REMOVE = 1 << 1,
EMPATHY_CONTACT_LIST_CAN_ALIAS = 1 << 2,
EMPATHY_CONTACT_LIST_CAN_GROUP = 1 << 3,
+ EMPATHY_CONTACT_LIST_CAN_BLOCK = 1 << 4,
+ EMPATHY_CONTACT_LIST_CAN_REPORT_ABUSIVE = 1 << 5,
} EmpathyContactListFlags;
typedef struct _EmpathyContactListIface EmpathyContactListIface;
@@ -77,6 +79,12 @@ struct _EmpathyContactListIface {
EmpathyContact *contact);
void (*remove_favourite) (EmpathyContactList *list,
EmpathyContact *contact);
+ void (*set_blocked) (EmpathyContactList *list,
+ EmpathyContact *contact,
+ gboolean blocked,
+ gboolean abusive);
+ gboolean (*get_blocked) (EmpathyContactList *list,
+ EmpathyContact *contact);
};
GType empathy_contact_list_get_type (void) G_GNUC_CONST;
@@ -116,6 +124,13 @@ void empathy_contact_list_remove_from_favourites
(EmpathyContactList *list,
EmpathyContact *contact);
+void empathy_contact_list_set_blocked (EmpathyContactList *list,
+ EmpathyContact *contact,
+ gboolean blocked,
+ gboolean abusive);
+gboolean empathy_contact_list_get_blocked (EmpathyContactList *list,
+ EmpathyContact *contact);
+
G_END_DECLS
diff --git a/libempathy/empathy-contact-manager.c b/libempathy/empathy-contact-manager.c
index a900fa610..b00f82477 100644
--- a/libempathy/empathy-contact-manager.c
+++ b/libempathy/empathy-contact-manager.c
@@ -865,6 +865,47 @@ contact_manager_remove_group (EmpathyContactList *manager,
}
static void
+contact_manager_set_blocked (EmpathyContactList *manager,
+ EmpathyContact *contact,
+ gboolean blocked,
+ gboolean abusive)
+{
+ EmpathyContactManagerPriv *priv = GET_PRIV (manager);
+ EmpathyContactList *list;
+ TpConnection *connection;
+
+ g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
+
+ connection = empathy_contact_get_connection (contact);
+ list = g_hash_table_lookup (priv->lists, connection);
+
+ if (list != NULL) {
+ empathy_contact_list_set_blocked (list, contact,
+ blocked, abusive);
+ }
+}
+
+static gboolean
+contact_manager_get_blocked (EmpathyContactList *manager,
+ EmpathyContact *contact)
+{
+ EmpathyContactManagerPriv *priv = GET_PRIV (manager);
+ EmpathyContactList *list;
+ TpConnection *connection;
+
+ g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), FALSE);
+
+ connection = empathy_contact_get_connection (contact);
+ list = g_hash_table_lookup (priv->lists, connection);
+
+ if (list != NULL) {
+ return empathy_contact_list_get_blocked (list, contact);
+ } else {
+ return FALSE;
+ }
+}
+
+static void
contact_manager_iface_init (EmpathyContactListIface *iface)
{
iface->add = contact_manager_add;
@@ -880,6 +921,8 @@ contact_manager_iface_init (EmpathyContactListIface *iface)
iface->is_favourite = contact_manager_is_favourite;
iface->remove_favourite = contact_manager_remove_favourite;
iface->add_favourite = contact_manager_add_favourite;
+ iface->set_blocked = contact_manager_set_blocked;
+ iface->get_blocked = contact_manager_get_blocked;
}
EmpathyContactListFlags
diff --git a/libempathy/empathy-individual-manager.c b/libempathy/empathy-individual-manager.c
index 894ae648b..137d00f1f 100644
--- a/libempathy/empathy-individual-manager.c
+++ b/libempathy/empathy-individual-manager.c
@@ -30,12 +30,14 @@
#include <telepathy-glib/util.h>
#include <folks/folks.h>
+#include <folks/folks-telepathy.h>
#include <extensions/extensions.h>
#include "empathy-individual-manager.h"
#include "empathy-marshal.h"
#include "empathy-utils.h"
+#include "empathy-contact-manager.h"
#define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
#include "empathy-debug.h"
@@ -483,6 +485,89 @@ empathy_individual_manager_remove (EmpathyIndividualManager *self,
aggregator_remove_individual_cb, self);
}
+/* FIXME: The parameter @self is not required and the method can be placed in
+ * utilities. I left it as it is to stay coherent with empathy-2.34 */
+/**
+ * empathy_individual_manager_supports_blocking
+ * @self: the #EmpathyIndividualManager
+ * @individual: an individual to check
+ *
+ * Indicates whether any personas of an @individual can be blocked.
+ *
+ * Returns: %TRUE if any persona supports contact blocking
+ */
+gboolean
+empathy_individual_manager_supports_blocking (EmpathyIndividualManager *self,
+ FolksIndividual *individual)
+{
+ EmpathyIndividualManagerPriv *priv;
+ GList *personas, *l;
+
+ g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_MANAGER (self), FALSE);
+
+ priv = GET_PRIV (self);
+
+ personas = folks_individual_get_personas (individual);
+
+ for (l = personas; l != NULL; l = l->next)
+ {
+ TpfPersona *persona = l->data;
+ TpConnection *conn;
+ EmpathyContactManager *manager;
+
+ if (!TPF_IS_PERSONA (persona))
+ continue;
+
+ conn = tp_contact_get_connection (tpf_persona_get_contact (persona));
+ manager = empathy_contact_manager_dup_singleton ();
+
+ if (empathy_contact_manager_get_flags_for_connection (manager, conn) &
+ EMPATHY_CONTACT_LIST_CAN_BLOCK)
+ return TRUE;
+
+ g_object_unref (manager);
+ }
+
+ return FALSE;
+}
+
+void
+empathy_individual_manager_set_blocked (EmpathyIndividualManager *self,
+ FolksIndividual *individual,
+ gboolean blocked,
+ gboolean abusive)
+{
+ EmpathyIndividualManagerPriv *priv;
+ GList *personas, *l;
+
+ g_return_if_fail (EMPATHY_IS_INDIVIDUAL_MANAGER (self));
+
+ priv = GET_PRIV (self);
+
+ personas = folks_individual_get_personas (individual);
+
+ for (l = personas; l != NULL; l = l->next)
+ {
+ TpfPersona *persona = l->data;
+ EmpathyContact *contact;
+ EmpathyContactManager *manager;
+
+ if (!TPF_IS_PERSONA (persona))
+ continue;
+
+ contact = empathy_contact_dup_from_tp_contact (
+ tpf_persona_get_contact (persona));
+ empathy_contact_set_persona (contact, FOLKS_PERSONA (persona));
+ manager = empathy_contact_manager_dup_singleton ();
+ empathy_contact_list_set_blocked (
+ EMPATHY_CONTACT_LIST (manager),
+ contact, blocked, abusive);
+
+ g_object_unref (manager);
+ g_object_unref (contact);
+ }
+}
+
static void
groups_change_group_cb (GObject *source,
GAsyncResult *result,
diff --git a/libempathy/empathy-individual-manager.h b/libempathy/empathy-individual-manager.h
index f2f5f5b56..1fec67d91 100644
--- a/libempathy/empathy-individual-manager.h
+++ b/libempathy/empathy-individual-manager.h
@@ -81,5 +81,14 @@ void empathy_individual_manager_unlink_individual (
EmpathyIndividualManager *self,
FolksIndividual *individual);
+gboolean empathy_individual_manager_supports_blocking (
+ EmpathyIndividualManager *self,
+ FolksIndividual *individual);
+
+void empathy_individual_manager_set_blocked (EmpathyIndividualManager *self,
+ FolksIndividual *individual,
+ gboolean blocked,
+ gboolean abusive);
+
G_END_DECLS
#endif /* __EMPATHY_INDIVIDUAL_MANAGER_H__ */
diff --git a/libempathy/empathy-tp-contact-list.c b/libempathy/empathy-tp-contact-list.c
index 263c379f6..90932a20a 100644
--- a/libempathy/empathy-tp-contact-list.c
+++ b/libempathy/empathy-tp-contact-list.c
@@ -31,6 +31,8 @@
#include <telepathy-glib/dbus.h>
#include <telepathy-glib/interfaces.h>
+#include <extensions/extensions.h>
+
#include "empathy-tp-contact-list.h"
#include "empathy-tp-contact-factory.h"
#include "empathy-contact-list.h"
@@ -46,6 +48,7 @@ typedef struct {
TpChannel *publish;
TpChannel *subscribe;
TpChannel *stored;
+ TpChannel *deny;
/* contact handle (TpHandle) => reffed (EmpathyContact *)
*
* Union of:
@@ -722,6 +725,10 @@ tp_contact_list_finalize (GObject *object)
g_object_unref (priv->stored);
}
+ if (priv->deny) {
+ g_object_unref (priv->deny);
+ }
+
if (priv->connection) {
g_object_unref (priv->connection);
}
@@ -773,6 +780,11 @@ got_list_channel (EmpathyTpContactList *list,
g_signal_connect (priv->subscribe, "group-members-changed",
G_CALLBACK (tp_contact_list_subscribe_group_members_changed_cb),
list);
+ } else if (!tp_strdiff (id, "deny")) {
+ if (priv->deny != NULL)
+ return;
+ DEBUG ("Got 'deny' channel");
+ priv->deny = g_object_ref (channel);
}
}
@@ -799,6 +811,27 @@ list_ensure_channel_cb (TpConnection *conn,
}
static void
+list_get_contact_blocking_capabilities_cb (TpProxy *conn,
+ const GValue *value,
+ const GError *in_error,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ EmpathyTpContactList *list = EMPATHY_TP_CONTACT_LIST (weak_object);
+ EmpathyTpContactListPriv *priv = GET_PRIV (list);
+ EmpContactBlockingCapabilities caps;
+
+ g_return_if_fail (G_VALUE_HOLDS_UINT (value));
+
+ caps = g_value_get_uint (value);
+
+ if (caps & EMP_CONTACT_BLOCKING_CAPABILITY_CAN_REPORT_ABUSIVE) {
+ DEBUG ("Connection can report abusive contacts");
+ priv->flags |= EMPATHY_CONTACT_LIST_CAN_REPORT_ABUSIVE;
+ }
+}
+
+static void
iterate_on_channels (EmpathyTpContactList *list,
const GPtrArray *channels)
{
@@ -881,8 +914,8 @@ conn_ready_cb (TpConnection *connection,
NULL, NULL, G_OBJECT (list));
request = tp_asv_new (
- TP_IFACE_CHANNEL ".ChannelType", G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST,
- TP_IFACE_CHANNEL ".TargetHandleType", G_TYPE_UINT, TP_HANDLE_TYPE_LIST,
+ TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST,
+ TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_LIST,
NULL);
/* Watch the NewChannels signal so if ensuring list channels fails (for
@@ -892,21 +925,39 @@ conn_ready_cb (TpConnection *connection,
priv->connection, new_channels_cb, NULL, NULL, G_OBJECT (list), NULL);
/* Request the 'stored' list. */
- tp_asv_set_static_string (request, TP_IFACE_CHANNEL ".TargetID", "stored");
+ tp_asv_set_static_string (request, TP_PROP_CHANNEL_TARGET_ID, "stored");
tp_cli_connection_interface_requests_call_ensure_channel (priv->connection,
G_MAXINT, request, list_ensure_channel_cb, list, NULL, G_OBJECT (list));
/* Request the 'publish' list. */
- tp_asv_set_static_string (request, TP_IFACE_CHANNEL ".TargetID", "publish");
+ tp_asv_set_static_string (request, TP_PROP_CHANNEL_TARGET_ID, "publish");
tp_cli_connection_interface_requests_call_ensure_channel (priv->connection,
G_MAXINT, request, list_ensure_channel_cb, list, NULL, G_OBJECT (list));
/* Request the 'subscribe' list. */
- tp_asv_set_static_string (request, TP_IFACE_CHANNEL ".TargetID", "subscribe");
+ tp_asv_set_static_string (request, TP_PROP_CHANNEL_TARGET_ID, "subscribe");
+ tp_cli_connection_interface_requests_call_ensure_channel (priv->connection,
+ G_MAXINT, request, list_ensure_channel_cb, list, NULL, G_OBJECT (list));
+
+ /* Request the 'deny' list */
+ tp_asv_set_static_string (request, TP_PROP_CHANNEL_TARGET_ID, "deny");
tp_cli_connection_interface_requests_call_ensure_channel (priv->connection,
G_MAXINT, request, list_ensure_channel_cb, list, NULL, G_OBJECT (list));
g_hash_table_unref (request);
+
+ /* Find out if we support reporting abusive contacts --
+ * this is done via the new Conn.I.ContactBlocking interface */
+ if (tp_proxy_has_interface_by_id (priv->connection,
+ EMP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_BLOCKING)) {
+ DEBUG ("Have Conn.I.ContactBlocking");
+
+ tp_cli_dbus_properties_call_get (priv->connection, -1,
+ EMP_IFACE_CONNECTION_INTERFACE_CONTACT_BLOCKING,
+ "ContactBlockingCapabilities",
+ list_get_contact_blocking_capabilities_cb,
+ NULL, NULL, G_OBJECT (list));
+ }
out:
g_object_unref (list);
}
@@ -1289,10 +1340,56 @@ tp_contact_list_get_flags (EmpathyContactList *list)
}
}
+ if (priv->deny != NULL)
+ flags |= EMPATHY_CONTACT_LIST_CAN_BLOCK;
+
return flags;
}
static void
+tp_contact_list_set_blocked (EmpathyContactList *list,
+ EmpathyContact *contact,
+ gboolean blocked,
+ gboolean abusive)
+{
+ EmpathyTpContactListPriv *priv = GET_PRIV (list);
+ TpHandle handle = empathy_contact_get_handle (contact);
+ GArray handles = { (char *) &handle, 1 };
+
+ g_return_if_fail (TP_IS_CHANNEL (priv->deny));
+
+ if (blocked && abusive) {
+ /* we have to do this via the new interface */
+ g_return_if_fail (priv->flags &
+ EMPATHY_CONTACT_LIST_CAN_REPORT_ABUSIVE);
+
+ emp_cli_connection_interface_contact_blocking_call_block_contacts (
+ TP_PROXY (priv->connection), -1,
+ &handles, TRUE, NULL, NULL, NULL, NULL);
+ } else if (blocked) {
+ tp_cli_channel_interface_group_call_add_members (
+ priv->deny, -1,
+ &handles, NULL, NULL, NULL, NULL, NULL);
+ } else {
+ tp_cli_channel_interface_group_call_remove_members (
+ priv->deny, -1,
+ &handles, NULL, NULL, NULL, NULL, NULL);
+ }
+}
+
+static gboolean
+tp_contact_list_get_blocked (EmpathyContactList *list,
+ EmpathyContact *contact)
+{
+ EmpathyTpContactListPriv *priv = GET_PRIV (list);
+
+ g_return_val_if_fail (TP_IS_CHANNEL (priv->deny), FALSE);
+
+ return tp_intset_is_member (tp_channel_group_get_members (priv->deny),
+ empathy_contact_get_handle (contact));
+}
+
+static void
tp_contact_list_iface_init (EmpathyContactListIface *iface)
{
iface->add = tp_contact_list_add;
@@ -1306,6 +1403,8 @@ tp_contact_list_iface_init (EmpathyContactListIface *iface)
iface->rename_group = tp_contact_list_rename_group;
iface->remove_group = tp_contact_list_remove_group;
iface->get_flags = tp_contact_list_get_flags;
+ iface->set_blocked = tp_contact_list_set_blocked;
+ iface->get_blocked = tp_contact_list_get_blocked;
}
void
@@ -1334,4 +1433,3 @@ empathy_tp_contact_list_remove_all (EmpathyTpContactList *list)
}
g_hash_table_remove_all (priv->pendings);
}
-
diff --git a/libempathy/empathy-tp-contact-list.h b/libempathy/empathy-tp-contact-list.h
index 071fc0b91..9a555bc7a 100644
--- a/libempathy/empathy-tp-contact-list.h
+++ b/libempathy/empathy-tp-contact-list.h
@@ -26,6 +26,7 @@
#include <glib.h>
#include <telepathy-glib/connection.h>
+#include <libempathy/empathy-contact.h>
G_BEGIN_DECLS
diff --git a/src/empathy-chat-window.c b/src/empathy-chat-window.c
index 799a8254d..23c93a15b 100644
--- a/src/empathy-chat-window.c
+++ b/src/empathy-chat-window.c
@@ -417,12 +417,15 @@ chat_window_contact_menu_update (EmpathyChatWindowPriv *priv,
if (orig_submenu == NULL || !gtk_widget_get_visible (orig_submenu)) {
submenu = empathy_chat_get_contact_menu (priv->current_chat);
+
if (submenu != NULL) {
+ /* gtk_menu_attach_to_widget() doesn't behave nicely here */
+ g_object_set_data (G_OBJECT (submenu), "window", priv->dialog);
+
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
gtk_widget_show (menu);
gtk_widget_set_sensitive (menu, TRUE);
- }
- else {
+ } else {
gtk_widget_set_sensitive (menu, FALSE);
}
} else {
diff --git a/src/empathy-main-window.c b/src/empathy-main-window.c
index 8f08f74be..12b99ba5f 100644
--- a/src/empathy-main-window.c
+++ b/src/empathy-main-window.c
@@ -50,6 +50,7 @@
#include <libempathy-gtk/empathy-contact-list-store.h>
#include <libempathy-gtk/empathy-contact-list-view.h>
#include <libempathy-gtk/empathy-live-search.h>
+#include <libempathy-gtk/empathy-contact-blocking-dialog.h>
#include <libempathy-gtk/empathy-contact-search-dialog.h>
#include <libempathy-gtk/empathy-geometry.h>
#include <libempathy-gtk/empathy-gtk-enum-types.h>
@@ -1464,6 +1465,18 @@ main_window_edit_personal_information_cb (GtkAction *action,
}
static void
+main_window_edit_blocked_contacts_cb (GtkAction *action,
+ EmpathyMainWindow *window)
+{
+ GtkWidget *dialog;
+
+ dialog = empathy_contact_blocking_dialog_new (GTK_WINDOW (window));
+ gtk_widget_show (dialog);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (gtk_widget_destroy), NULL);
+}
+
+static void
main_window_edit_preferences_cb (GtkAction *action,
EmpathyMainWindow *window)
{
@@ -1622,7 +1635,8 @@ main_window_connection_items_setup (EmpathyMainWindow *window,
"chat_new_message",
"chat_new_call",
"chat_add_contact",
- "edit_personal_information"
+ "edit_personal_information",
+ "edit_blocked_contacts"
};
for (i = 0, list = NULL; i < G_N_ELEMENTS (actions_connected); i++) {
@@ -1801,6 +1815,7 @@ empathy_main_window_init (EmpathyMainWindow *window)
"edit", "activate", main_window_edit_cb,
"edit_accounts", "activate", main_window_edit_accounts_cb,
"edit_personal_information", "activate", main_window_edit_personal_information_cb,
+ "edit_blocked_contacts", "activate", main_window_edit_blocked_contacts_cb,
"edit_preferences", "activate", main_window_edit_preferences_cb,
"edit_search_contacts", "activate", main_window_edit_search_contacts_cb,
"help_about", "activate", main_window_help_about_cb,
diff --git a/src/empathy-main-window.ui b/src/empathy-main-window.ui
index 0cfd6bee4..217b276f2 100644
--- a/src/empathy-main-window.ui
+++ b/src/empathy-main-window.ui
@@ -107,6 +107,12 @@
</object>
</child>
<child>
+ <object class="GtkAction" id="edit_blocked_contacts">
+ <property name="name">edit_blocked_contacts</property>
+ <property name="label" translatable="yes">_Blocked Contacts</property>
+ </object>
+ </child>
+ <child>
<object class="GtkAction" id="edit_preferences">
<property name="stock_id">gtk-preferences</property>
<property name="name">edit_preferences</property>
@@ -249,6 +255,7 @@
<menuitem action="edit_accounts"/>
<menuitem action="edit_personal_information"/>
<menuitem action="edit_search_contacts"/>
+ <menuitem action="edit_blocked_contacts"/>
<separator/>
<menuitem action="edit_preferences"/>
</menu>
diff --git a/tests/interactive/.gitignore b/tests/interactive/.gitignore
index ebaa4d1e9..ea3271654 100644
--- a/tests/interactive/.gitignore
+++ b/tests/interactive/.gitignore
@@ -4,6 +4,7 @@ contact-run-until-ready
contact-run-until-ready-2
empetit
test-empathy-account-assistant
+test-empathy-contact-blocking-dialog
test-empathy-presence-chooser
test-empathy-status-preset-dialog
test-empathy-protocol-chooser
diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am
index 24e674c6a..bf2e83929 100644
--- a/tests/interactive/Makefile.am
+++ b/tests/interactive/Makefile.am
@@ -18,6 +18,7 @@ noinst_PROGRAMS = \
empathy-logs \
empetit \
test-empathy-account-assistant \
+ test-empathy-contact-blocking-dialog \
test-empathy-presence-chooser \
test-empathy-status-preset-dialog \
test-empathy-protocol-chooser \
@@ -26,6 +27,7 @@ noinst_PROGRAMS = \
contact_manager_SOURCES = contact-manager.c
empathy_logs_SOURCES = empathy-logs.c
empetit_SOURCES = empetit.c
+test_empathy_contact_blocking_dialog_SOURCES = test-empathy-contact-blocking-dialog.c
test_empathy_presence_chooser_SOURCES = test-empathy-presence-chooser.c
test_empathy_status_preset_dialog_SOURCES = test-empathy-status-preset-dialog.c
test_empathy_protocol_chooser_SOURCES = test-empathy-protocol-chooser.c
diff --git a/tests/interactive/test-empathy-contact-blocking-dialog.c b/tests/interactive/test-empathy-contact-blocking-dialog.c
new file mode 100644
index 000000000..6c5615ec3
--- /dev/null
+++ b/tests/interactive/test-empathy-contact-blocking-dialog.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * 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 General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Danielle Madeley <danielle.madeley@collabora.co.uk>
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+
+#include <libempathy/empathy-contact-manager.h>
+
+#include <libempathy-gtk/empathy-ui-utils.h>
+#include <libempathy-gtk/empathy-contact-blocking-dialog.h>
+
+int
+main (int argc,
+ char **argv)
+ {
+ EmpathyContactManager *manager;
+ GtkWidget *dialog;
+
+ gtk_init (&argc, &argv);
+ empathy_gtk_init ();
+
+ manager = empathy_contact_manager_dup_singleton ();
+ dialog = empathy_contact_blocking_dialog_new (NULL);
+
+ g_signal_connect_swapped (dialog, "response",
+ G_CALLBACK (gtk_main_quit), NULL);
+
+ gtk_widget_show (dialog);
+
+ gtk_main ();
+
+ gtk_widget_destroy (dialog);
+ g_object_unref (manager);
+
+ return 0;
+ }