diff options
author | Danielle Madeley <danielle.madeley@collabora.co.uk> | 2011-03-14 09:58:00 +0800 |
---|---|---|
committer | Danielle Madeley <danielle.madeley@collabora.co.uk> | 2011-03-14 09:58:00 +0800 |
commit | 8e6a977d36aea1eb4b429ddee84cb9b4bc4142cd (patch) | |
tree | 5c1858c474144e0cd511fc6891e9d21ef978c5e3 /libempathy-gtk | |
parent | 2e3e5e8e732fbc2e4d3ed61892aefa09efabbfc2 (diff) | |
parent | c0eca0414ca529da3b083c099ec3031436420b00 (diff) | |
download | gsoc2013-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'
Diffstat (limited to 'libempathy-gtk')
-rw-r--r-- | libempathy-gtk/Makefile.am | 3 | ||||
-rw-r--r-- | libempathy-gtk/empathy-chat.c | 3 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-blocking-dialog.c | 791 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-blocking-dialog.h | 60 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-blocking-dialog.ui | 149 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-dialogs.c | 103 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-dialogs.h | 3 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-dialogs.ui | 53 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-menu.c | 94 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-menu.h | 3 | ||||
-rw-r--r-- | libempathy-gtk/empathy-individual-dialogs.c | 139 | ||||
-rw-r--r-- | libempathy-gtk/empathy-individual-dialogs.h | 3 | ||||
-rw-r--r-- | libempathy-gtk/empathy-individual-view.c | 45 |
13 files changed, 1426 insertions, 23 deletions
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); } } |