2 * empathy-contact-blocking-dialog.c
4 * EmpathyContactBlockingDialog
6 * Copyright (C) 2011 Collabora Ltd.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 * Authors: Danielle Madeley <danielle.madeley@collabora.co.uk>
26 #include <glib/gi18n-lib.h>
28 #include <libempathy/empathy-utils.h>
30 #include <libempathy-gtk/empathy-account-chooser.h>
31 #include <libempathy-gtk/empathy-ui-utils.h>
33 #include "empathy-contact-blocking-dialog.h"
35 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
36 #include <libempathy/empathy-debug.h>
38 #define GET_PRIVATE(o) (EMPATHY_CONTACT_BLOCKING_DIALOG (o)->priv)
39 #define DECLARE_CALLBACK(func) \
40 static void func (GObject *, GAsyncResult *, gpointer);
42 G_DEFINE_TYPE (EmpathyContactBlockingDialog
, empathy_contact_blocking_dialog
,
45 struct _EmpathyContactBlockingDialogPrivate
47 guint block_account_changed
;
49 GtkListStore
*blocked_contacts
;
50 GtkListStore
*completion_contacts
;
51 GtkTreeSelection
*selection
;
53 GtkWidget
*account_chooser
;
54 GtkWidget
*add_button
;
55 GtkWidget
*add_contact_entry
;
57 GtkWidget
*info_bar_label
;
58 GtkWidget
*remove_button
;
60 TpConnection
*current_conn
;
63 enum /* blocked-contacts columns */
65 COL_BLOCKED_IDENTIFIER
,
70 enum /* completion_contacts columns */
72 COL_COMPLETION_IDENTIFIER
,
78 get_pretty_conn_name (TpConnection
*conn
)
80 return tp_proxy_get_object_path (conn
) + strlen (TP_CONN_OBJECT_PATH_BASE
);
84 contact_blocking_dialog_filter_account_chooser (TpAccount
*account
,
85 EmpathyAccountChooserFilterResultCallback callback
,
86 gpointer callback_data
,
89 TpConnection
*conn
= tp_account_get_connection (account
);
94 tp_proxy_has_interface_by_id (conn
,
95 TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_BLOCKING
);
97 callback (enable
, callback_data
);
100 static void contact_blocking_dialog_account_changed (GtkWidget
*,
101 EmpathyContactBlockingDialog
*);
104 contact_blocking_dialog_refilter_account_chooser (
105 EmpathyContactBlockingDialog
*self
)
107 EmpathyAccountChooser
*chooser
=
108 EMPATHY_ACCOUNT_CHOOSER (self
->priv
->account_chooser
);
112 DEBUG ("Refiltering account chooser");
114 /* set the filter to refilter the account chooser */
115 self
->priv
->block_account_changed
++;
116 empathy_account_chooser_set_filter (chooser
,
117 contact_blocking_dialog_filter_account_chooser
, self
);
118 self
->priv
->block_account_changed
--;
120 conn
= empathy_account_chooser_get_connection (chooser
);
121 enabled
= (empathy_account_chooser_get_account (chooser
) != NULL
&&
123 tp_proxy_has_interface_by_id (conn
,
124 TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_BLOCKING
));
127 DEBUG ("No account selected");
129 gtk_widget_set_sensitive (self
->priv
->add_button
, enabled
);
130 gtk_widget_set_sensitive (self
->priv
->add_contact_entry
, enabled
);
132 contact_blocking_dialog_account_changed (self
->priv
->account_chooser
, self
);
136 contact_blocking_dialog_add_blocked (
137 EmpathyContactBlockingDialog
*self
,
140 EmpathyContactBlockingDialogPrivate
*priv
= GET_PRIVATE (self
);
146 for (i
= 0; i
< blocked
->len
; i
++)
148 TpContact
*contact
= g_ptr_array_index (blocked
, i
);
150 gtk_list_store_insert_with_values (priv
->blocked_contacts
, NULL
, -1,
151 COL_BLOCKED_IDENTIFIER
, tp_contact_get_identifier (contact
),
152 COL_BLOCKED_CONTACT
, contact
,
158 blocked_contacts_changed_cb (TpConnection
*conn
,
161 EmpathyContactBlockingDialog
*self
)
163 GtkTreeModel
*model
= GTK_TREE_MODEL (self
->priv
->blocked_contacts
);
167 DEBUG ("blocked contacts changed on %s: %u added, %u removed",
168 get_pretty_conn_name (conn
), added
->len
, removed
->len
);
171 contact_blocking_dialog_add_blocked (self
, added
);
173 /* remove contacts */
174 valid
= gtk_tree_model_get_iter_first (model
, &iter
);
179 gtk_tree_model_get (model
, &iter
,
180 COL_BLOCKED_CONTACT
, &contact
,
183 if (tp_g_ptr_array_contains (removed
, contact
))
184 valid
= gtk_list_store_remove (self
->priv
->blocked_contacts
, &iter
);
186 valid
= gtk_tree_model_iter_next (model
, &iter
);
188 g_object_unref (contact
);
193 contact_blocking_dialog_connection_status_changed (TpAccount
*account
,
197 const char *dbus_reason
,
199 EmpathyContactBlockingDialog
*self
)
201 TpConnection
*conn
= tp_account_get_connection (account
);
205 case TP_CONNECTION_STATUS_DISCONNECTED
:
206 DEBUG ("Connection %s invalidated", get_pretty_conn_name (conn
));
208 contact_blocking_dialog_refilter_account_chooser (self
);
211 case TP_CONNECTION_STATUS_CONNECTING
:
214 case TP_CONNECTION_STATUS_CONNECTED
:
215 DEBUG ("Connection %s reconnected", get_pretty_conn_name (conn
));
217 contact_blocking_dialog_refilter_account_chooser (self
);
222 contact_blocking_dialog_am_prepared (GObject
*am
,
223 GAsyncResult
*result
,
226 EmpathyContactBlockingDialog
*self
= user_data
;
227 GList
*accounts
, *ptr
;
228 GError
*error
= NULL
;
230 if (!tp_proxy_prepare_finish (am
, result
, &error
))
232 g_critical ("Could not prepare Account Manager: %s", error
->message
);
233 g_error_free (error
);
237 accounts
= tp_account_manager_dup_valid_accounts (TP_ACCOUNT_MANAGER (am
));
239 for (ptr
= accounts
; ptr
!= NULL
; ptr
= ptr
->next
)
241 TpAccount
*account
= ptr
->data
;
243 tp_g_signal_connect_object (account
, "status-changed",
244 G_CALLBACK (contact_blocking_dialog_connection_status_changed
),
247 contact_blocking_dialog_refilter_account_chooser (self
);
250 g_list_free_full (accounts
, g_object_unref
);
254 contact_blocking_dialog_set_error (EmpathyContactBlockingDialog
*self
,
257 const char *msg
= NULL
;
259 if (error
->domain
== TP_ERROR
)
261 if (error
->code
== TP_ERROR_INVALID_HANDLE
)
262 msg
= _("Unknown or invalid identifier");
263 else if (error
->code
== TP_ERROR_NOT_AVAILABLE
)
264 msg
= _("Contact blocking temporarily unavailable");
265 else if (error
->code
== TP_ERROR_NOT_CAPABLE
)
266 msg
= _("Contact blocking unavailable");
267 else if (error
->code
== TP_ERROR_PERMISSION_DENIED
)
268 msg
= _("Permission Denied");
272 msg
= _("Could not block contact");
274 gtk_label_set_text (GTK_LABEL (self
->priv
->info_bar_label
), msg
);
275 gtk_widget_show (self
->priv
->info_bar
);
279 block_cb (GObject
*source
,
280 GAsyncResult
*result
,
283 EmpathyContactBlockingDialog
*self
= user_data
;
284 GError
*error
= NULL
;
286 if (!tp_contact_block_finish (TP_CONTACT (source
), result
,
289 DEBUG ("Error blocking contacts: %s", error
->message
);
291 contact_blocking_dialog_set_error (
292 EMPATHY_CONTACT_BLOCKING_DIALOG (self
), error
);
294 g_error_free (error
);
298 DEBUG ("Contact blocked");
302 block_contact_got_contact (GObject
*source
,
303 GAsyncResult
*result
,
306 EmpathyContactBlockingDialog
*self
;
307 TpConnection
*conn
= TP_CONNECTION (source
);
308 TpWeakRef
*wr
= user_data
;
310 GError
*error
= NULL
;
312 self
= tp_weak_ref_dup_object (wr
);
316 contact
= tp_connection_dup_contact_by_id_finish (conn
, result
, &error
);
319 DEBUG ("Error getting contact on %s: %s",
320 get_pretty_conn_name (conn
), error
->message
);
322 contact_blocking_dialog_set_error (
323 EMPATHY_CONTACT_BLOCKING_DIALOG (self
), error
);
325 g_error_free (error
);
329 tp_contact_block_async (contact
, FALSE
, block_cb
, self
);
330 g_object_unref (contact
);
333 g_clear_object (&self
);
334 tp_weak_ref_destroy (wr
);
338 contact_blocking_dialog_add_contact (GtkWidget
*widget
,
339 EmpathyContactBlockingDialog
*self
)
341 TpConnection
*conn
= empathy_account_chooser_get_connection (
342 EMPATHY_ACCOUNT_CHOOSER (self
->priv
->account_chooser
));
343 const char *identifier
;
345 identifier
= gtk_entry_get_text (
346 GTK_ENTRY (self
->priv
->add_contact_entry
));
348 DEBUG ("Looking up handle for '%s' on %s",
349 identifier
, get_pretty_conn_name (conn
));
351 tp_connection_dup_contact_by_id_async (conn
, identifier
,
352 0, NULL
, block_contact_got_contact
,
353 tp_weak_ref_new (self
, NULL
, NULL
));
355 gtk_entry_set_text (GTK_ENTRY (self
->priv
->add_contact_entry
), "");
356 gtk_widget_hide (self
->priv
->info_bar
);
360 unblock_cb (GObject
*source
,
361 GAsyncResult
*result
,
364 EmpathyContactBlockingDialog
*self
= user_data
;
365 GError
*error
= NULL
;
367 if (!tp_connection_unblock_contacts_finish (TP_CONNECTION (source
), result
,
370 DEBUG ("Error unblocking contacts: %s", error
->message
);
372 contact_blocking_dialog_set_error (
373 EMPATHY_CONTACT_BLOCKING_DIALOG (self
), error
);
375 g_error_free (error
);
379 DEBUG ("Contacts unblocked");
383 contact_blocking_dialog_remove_contacts (GtkWidget
*button
,
384 EmpathyContactBlockingDialog
*self
)
386 TpConnection
*conn
= empathy_account_chooser_get_connection (
387 EMPATHY_ACCOUNT_CHOOSER (self
->priv
->account_chooser
));
392 rows
= gtk_tree_selection_get_selected_rows (self
->priv
->selection
, &model
);
394 contacts
= g_ptr_array_new_with_free_func (g_object_unref
);
396 for (ptr
= rows
; ptr
!= NULL
; ptr
= ptr
->next
)
398 GtkTreePath
*path
= ptr
->data
;
402 if (!gtk_tree_model_get_iter (model
, &iter
, path
))
405 gtk_tree_model_get (model
, &iter
,
406 COL_BLOCKED_CONTACT
, &contact
,
409 g_ptr_array_add (contacts
, contact
);
411 gtk_tree_path_free (path
);
416 if (contacts
->len
> 0)
418 DEBUG ("Unblocking %u contacts", contacts
->len
);
420 tp_connection_unblock_contacts_async (conn
, contacts
->len
,
421 (TpContact
* const *) contacts
->pdata
, unblock_cb
, self
);
424 g_ptr_array_unref (contacts
);
428 contact_blocking_dialog_account_changed (GtkWidget
*account_chooser
,
429 EmpathyContactBlockingDialog
*self
)
431 TpConnection
*conn
= empathy_account_chooser_get_connection (
432 EMPATHY_ACCOUNT_CHOOSER (account_chooser
));
437 if (self
->priv
->block_account_changed
> 0)
440 if (conn
== self
->priv
->current_conn
)
443 /* clear the lists of contacts */
444 gtk_list_store_clear (self
->priv
->blocked_contacts
);
445 gtk_list_store_clear (self
->priv
->completion_contacts
);
447 if (self
->priv
->current_conn
!= NULL
)
449 g_signal_handlers_disconnect_by_func (self
->priv
->current_conn
,
450 blocked_contacts_changed_cb
, self
);
452 g_clear_object (&self
->priv
->current_conn
);
458 DEBUG ("Account changed: %s", get_pretty_conn_name (conn
));
460 self
->priv
->current_conn
= g_object_ref (conn
);
462 tp_g_signal_connect_object (conn
, "blocked-contacts-changed",
463 G_CALLBACK (blocked_contacts_changed_cb
), self
, 0);
465 blocked
= tp_connection_get_blocked_contacts (conn
);
467 DEBUG ("%u contacts blocked on %s",
468 blocked
!= NULL
? blocked
->len
: 0, get_pretty_conn_name (conn
));
470 contact_blocking_dialog_add_blocked (self
, blocked
);
472 DEBUG ("Loading contacts");
474 members
= tp_connection_dup_contact_list (conn
);
476 for (i
= 0; i
< members
->len
; i
++)
478 TpContact
*contact
= g_ptr_array_index (members
, i
);
481 tmpstr
= g_strdup_printf ("%s (%s)",
482 tp_contact_get_alias (contact
),
483 tp_contact_get_identifier (contact
));
485 gtk_list_store_insert_with_values (self
->priv
->completion_contacts
,
487 COL_COMPLETION_IDENTIFIER
, tp_contact_get_identifier (contact
),
488 COL_COMPLETION_TEXT
, tmpstr
,
494 g_ptr_array_unref (members
);
498 contact_blocking_dialog_view_selection_changed (GtkTreeSelection
*selection
,
499 EmpathyContactBlockingDialog
*self
)
501 GList
*rows
= gtk_tree_selection_get_selected_rows (selection
, NULL
);
503 /* update the sensitivity of the remove button */
504 gtk_widget_set_sensitive (self
->priv
->remove_button
, rows
!= NULL
);
506 g_list_foreach (rows
, (GFunc
) gtk_tree_path_free
, NULL
);
511 contact_selector_dialog_match_func (GtkEntryCompletion
*completion
,
520 model
= gtk_entry_completion_get_model (completion
);
521 if (model
== NULL
|| iter
== NULL
)
524 gtk_tree_model_get (model
, iter
, COL_COMPLETION_TEXT
, &str
, -1);
525 lower
= g_utf8_strdown (str
, -1);
526 if (strstr (lower
, key
))
528 DEBUG ("Key %s is matching name **%s**", key
, str
);
535 gtk_tree_model_get (model
, iter
, COL_COMPLETION_IDENTIFIER
, &str
, -1);
536 lower
= g_utf8_strdown (str
, -1);
537 if (strstr (lower
, key
))
539 DEBUG ("Key %s is matching ID **%s**", key
, str
);
552 contact_selector_dialog_match_selected_cb (GtkEntryCompletion
*widget
,
555 EmpathyContactBlockingDialog
*self
)
559 if (iter
== NULL
|| model
== NULL
)
562 gtk_tree_model_get (model
, iter
, COL_COMPLETION_IDENTIFIER
, &id
, -1);
563 gtk_entry_set_text (GTK_ENTRY (self
->priv
->add_contact_entry
), id
);
565 DEBUG ("Got selected match **%s**", id
);
573 contact_blocking_dialog_dispose (GObject
*self
)
575 EmpathyContactBlockingDialogPrivate
*priv
= GET_PRIVATE (self
);
577 g_clear_object (&priv
->current_conn
);
579 G_OBJECT_CLASS (empathy_contact_blocking_dialog_parent_class
)->dispose (self
);
583 empathy_contact_blocking_dialog_class_init (
584 EmpathyContactBlockingDialogClass
*klass
)
586 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
588 gobject_class
->dispose
= contact_blocking_dialog_dispose
;
590 g_type_class_add_private (gobject_class
,
591 sizeof (EmpathyContactBlockingDialogPrivate
));
595 empathy_contact_blocking_dialog_init (EmpathyContactBlockingDialog
*self
)
600 GtkWidget
*account_hbox
, *blocked_contacts_view
, *blocked_contacts_sw
,
602 GtkEntryCompletion
*completion
;
603 TpAccountManager
*am
;
604 GtkStyleContext
*context
;
605 TpSimpleClientFactory
*factory
;
607 self
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
608 EMPATHY_TYPE_CONTACT_BLOCKING_DIALOG
,
609 EmpathyContactBlockingDialogPrivate
);
611 gtk_window_set_title (GTK_WINDOW (self
), _("Edit Blocked Contacts"));
612 gtk_dialog_add_button (GTK_DIALOG (self
),
613 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
);
615 filename
= empathy_file_lookup ("empathy-contact-blocking-dialog.ui",
618 gui
= empathy_builder_get_file (filename
,
619 "contents", &contents
,
620 "account-hbox", &account_hbox
,
621 "add-button", &self
->priv
->add_button
,
622 "add-contact-entry", &self
->priv
->add_contact_entry
,
623 "blocked-contacts", &self
->priv
->blocked_contacts
,
624 "blocked-contacts-sw", &blocked_contacts_sw
,
625 "blocked-contacts-view", &blocked_contacts_view
,
626 "remove-button", &self
->priv
->remove_button
,
627 "remove-toolbar", &remove_toolbar
,
630 empathy_builder_connect (gui
, self
,
631 "add-button", "clicked", contact_blocking_dialog_add_contact
,
632 "add-contact-entry", "activate", contact_blocking_dialog_add_contact
,
633 "remove-button", "clicked", contact_blocking_dialog_remove_contacts
,
636 /* join the remove toolbar to the treeview */
637 context
= gtk_widget_get_style_context (blocked_contacts_sw
);
638 gtk_style_context_set_junction_sides (context
, GTK_JUNCTION_BOTTOM
);
639 context
= gtk_widget_get_style_context (remove_toolbar
);
640 gtk_style_context_set_junction_sides (context
, GTK_JUNCTION_TOP
);
642 /* add the contents to the dialog */
644 GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (self
))),
646 gtk_widget_show (contents
);
648 /* set up the tree selection */
649 self
->priv
->selection
= gtk_tree_view_get_selection (
650 GTK_TREE_VIEW (blocked_contacts_view
));
651 gtk_tree_selection_set_mode (self
->priv
->selection
, GTK_SELECTION_MULTIPLE
);
652 g_signal_connect (self
->priv
->selection
, "changed",
653 G_CALLBACK (contact_blocking_dialog_view_selection_changed
), self
);
655 /* build the contact entry */
656 self
->priv
->completion_contacts
= gtk_list_store_new (N_COMPLETION_COLUMNS
,
657 G_TYPE_STRING
, /* id */
658 G_TYPE_STRING
, /* text */
659 TP_TYPE_CONTACT
); /* contact */
661 completion
= gtk_entry_completion_new ();
662 gtk_entry_completion_set_model (completion
,
663 GTK_TREE_MODEL (self
->priv
->completion_contacts
));
664 gtk_entry_completion_set_text_column (completion
, COL_COMPLETION_TEXT
);
665 gtk_entry_completion_set_match_func (completion
,
666 contact_selector_dialog_match_func
,
668 g_signal_connect (completion
, "match-selected",
669 G_CALLBACK (contact_selector_dialog_match_selected_cb
),
671 gtk_entry_set_completion (GTK_ENTRY (self
->priv
->add_contact_entry
),
673 g_object_unref (completion
);
674 g_object_unref (self
->priv
->completion_contacts
);
676 /* add the account chooser */
677 self
->priv
->account_chooser
= empathy_account_chooser_new ();
678 contact_blocking_dialog_refilter_account_chooser (self
);
679 g_signal_connect (self
->priv
->account_chooser
, "changed",
680 G_CALLBACK (contact_blocking_dialog_account_changed
), self
);
682 gtk_box_pack_start (GTK_BOX (account_hbox
), self
->priv
->account_chooser
,
684 gtk_widget_show (self
->priv
->account_chooser
);
686 /* add an error warning info bar */
687 self
->priv
->info_bar
= gtk_info_bar_new ();
688 gtk_box_pack_start (GTK_BOX (contents
), self
->priv
->info_bar
, FALSE
, TRUE
, 0);
689 gtk_info_bar_set_message_type (GTK_INFO_BAR (self
->priv
->info_bar
),
692 self
->priv
->info_bar_label
= gtk_label_new ("");
693 gtk_container_add (GTK_CONTAINER (
694 gtk_info_bar_get_content_area (GTK_INFO_BAR (self
->priv
->info_bar
))),
695 self
->priv
->info_bar_label
);
696 gtk_widget_show (self
->priv
->info_bar_label
);
698 /* prepare the account manager */
699 am
= tp_account_manager_dup ();
701 factory
= tp_proxy_get_factory (am
);
702 tp_simple_client_factory_add_connection_features_varargs (factory
,
703 TP_CONNECTION_FEATURE_CONTACT_BLOCKING
, NULL
);
705 tp_proxy_prepare_async (am
, NULL
, contact_blocking_dialog_am_prepared
, self
);
709 g_object_unref (gui
);
713 empathy_contact_blocking_dialog_new (GtkWindow
*parent
)
715 GtkWidget
*self
= g_object_new (EMPATHY_TYPE_CONTACT_BLOCKING_DIALOG
,
720 gtk_window_set_transient_for (GTK_WINDOW (self
), parent
);