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 "empathy-contact-blocking-dialog.h"
28 #include <glib/gi18n-lib.h>
29 #include <tp-account-widgets/tpaw-builder.h>
30 #include <telepathy-glib/telepathy-glib-dbus.h>
32 #include "empathy-account-chooser.h"
33 #include "empathy-ui-utils.h"
34 #include "empathy-utils.h"
36 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
37 #include "empathy-debug.h"
39 #define GET_PRIVATE(o) (EMPATHY_CONTACT_BLOCKING_DIALOG (o)->priv)
40 #define DECLARE_CALLBACK(func) \
41 static void func (GObject *, GAsyncResult *, gpointer);
43 G_DEFINE_TYPE (EmpathyContactBlockingDialog
, empathy_contact_blocking_dialog
,
46 struct _EmpathyContactBlockingDialogPrivate
48 guint block_account_changed
;
50 GtkListStore
*blocked_contacts
;
51 GtkListStore
*completion_contacts
;
52 GtkTreeSelection
*selection
;
54 GtkWidget
*account_chooser
;
55 GtkWidget
*add_button
;
56 GtkWidget
*add_contact_entry
;
58 GtkWidget
*info_bar_label
;
59 GtkWidget
*remove_button
;
61 TpConnection
*current_conn
;
64 enum /* blocked-contacts columns */
66 COL_BLOCKED_IDENTIFIER
,
71 enum /* completion_contacts columns */
73 COL_COMPLETION_IDENTIFIER
,
79 get_pretty_conn_name (TpConnection
*conn
)
81 return tp_proxy_get_object_path (conn
) + strlen (TP_CONN_OBJECT_PATH_BASE
);
85 contact_blocking_dialog_filter_account_chooser (TpAccount
*account
,
86 EmpathyAccountChooserFilterResultCallback callback
,
87 gpointer callback_data
,
90 TpConnection
*conn
= tp_account_get_connection (account
);
95 tp_proxy_has_interface_by_id (conn
,
96 TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_BLOCKING
);
98 callback (enable
, callback_data
);
101 static void contact_blocking_dialog_account_changed (GtkWidget
*,
102 EmpathyContactBlockingDialog
*);
105 contact_blocking_dialog_refilter_account_chooser (
106 EmpathyContactBlockingDialog
*self
)
108 EmpathyAccountChooser
*chooser
=
109 EMPATHY_ACCOUNT_CHOOSER (self
->priv
->account_chooser
);
113 DEBUG ("Refiltering account chooser");
115 /* set the filter to refilter the account chooser */
116 self
->priv
->block_account_changed
++;
117 empathy_account_chooser_set_filter (chooser
,
118 contact_blocking_dialog_filter_account_chooser
, self
);
119 self
->priv
->block_account_changed
--;
121 conn
= empathy_account_chooser_get_connection (chooser
);
122 enabled
= (empathy_account_chooser_get_account (chooser
) != NULL
&&
124 tp_proxy_has_interface_by_id (conn
,
125 TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_BLOCKING
));
128 DEBUG ("No account selected");
130 gtk_widget_set_sensitive (self
->priv
->add_button
, enabled
);
131 gtk_widget_set_sensitive (self
->priv
->add_contact_entry
, enabled
);
133 contact_blocking_dialog_account_changed (self
->priv
->account_chooser
, self
);
137 contact_blocking_dialog_add_blocked (
138 EmpathyContactBlockingDialog
*self
,
141 EmpathyContactBlockingDialogPrivate
*priv
= GET_PRIVATE (self
);
147 for (i
= 0; i
< blocked
->len
; i
++)
149 TpContact
*contact
= g_ptr_array_index (blocked
, i
);
151 gtk_list_store_insert_with_values (priv
->blocked_contacts
, NULL
, -1,
152 COL_BLOCKED_IDENTIFIER
, tp_contact_get_identifier (contact
),
153 COL_BLOCKED_CONTACT
, contact
,
159 blocked_contacts_changed_cb (TpConnection
*conn
,
162 EmpathyContactBlockingDialog
*self
)
164 GtkTreeModel
*model
= GTK_TREE_MODEL (self
->priv
->blocked_contacts
);
168 DEBUG ("blocked contacts changed on %s: %u added, %u removed",
169 get_pretty_conn_name (conn
), added
->len
, removed
->len
);
172 contact_blocking_dialog_add_blocked (self
, added
);
174 /* remove contacts */
175 valid
= gtk_tree_model_get_iter_first (model
, &iter
);
180 gtk_tree_model_get (model
, &iter
,
181 COL_BLOCKED_CONTACT
, &contact
,
184 if (tp_g_ptr_array_contains (removed
, contact
))
185 valid
= gtk_list_store_remove (self
->priv
->blocked_contacts
, &iter
);
187 valid
= gtk_tree_model_iter_next (model
, &iter
);
189 g_object_unref (contact
);
194 contact_blocking_dialog_connection_status_changed (TpAccount
*account
,
198 const char *dbus_reason
,
200 EmpathyContactBlockingDialog
*self
)
202 TpConnection
*conn
= tp_account_get_connection (account
);
206 case TP_CONNECTION_STATUS_DISCONNECTED
:
207 DEBUG ("Connection %s invalidated", get_pretty_conn_name (conn
));
209 contact_blocking_dialog_refilter_account_chooser (self
);
212 case TP_CONNECTION_STATUS_CONNECTING
:
215 case TP_CONNECTION_STATUS_CONNECTED
:
216 DEBUG ("Connection %s reconnected", get_pretty_conn_name (conn
));
218 contact_blocking_dialog_refilter_account_chooser (self
);
223 contact_blocking_dialog_am_prepared (GObject
*am
,
224 GAsyncResult
*result
,
227 EmpathyContactBlockingDialog
*self
= user_data
;
228 GList
*accounts
, *ptr
;
229 GError
*error
= NULL
;
231 if (!tp_proxy_prepare_finish (am
, result
, &error
))
233 g_critical ("Could not prepare Account Manager: %s", error
->message
);
234 g_error_free (error
);
238 accounts
= tp_account_manager_dup_valid_accounts (TP_ACCOUNT_MANAGER (am
));
240 for (ptr
= accounts
; ptr
!= NULL
; ptr
= ptr
->next
)
242 TpAccount
*account
= ptr
->data
;
244 tp_g_signal_connect_object (account
, "status-changed",
245 G_CALLBACK (contact_blocking_dialog_connection_status_changed
),
248 contact_blocking_dialog_refilter_account_chooser (self
);
251 g_list_free_full (accounts
, g_object_unref
);
255 contact_blocking_dialog_set_error (EmpathyContactBlockingDialog
*self
,
258 const char *msg
= NULL
;
260 if (error
->domain
== TP_ERROR
)
262 if (error
->code
== TP_ERROR_INVALID_HANDLE
)
263 msg
= _("Unknown or invalid identifier");
264 else if (error
->code
== TP_ERROR_NOT_AVAILABLE
)
265 msg
= _("Contact blocking temporarily unavailable");
266 else if (error
->code
== TP_ERROR_NOT_CAPABLE
)
267 msg
= _("Contact blocking unavailable");
268 else if (error
->code
== TP_ERROR_PERMISSION_DENIED
)
269 msg
= _("Permission Denied");
273 msg
= _("Could not block contact");
275 gtk_label_set_text (GTK_LABEL (self
->priv
->info_bar_label
), msg
);
276 gtk_widget_show (self
->priv
->info_bar
);
280 block_cb (GObject
*source
,
281 GAsyncResult
*result
,
284 EmpathyContactBlockingDialog
*self
= user_data
;
285 GError
*error
= NULL
;
287 if (!tp_contact_block_finish (TP_CONTACT (source
), result
,
290 DEBUG ("Error blocking contacts: %s", error
->message
);
292 contact_blocking_dialog_set_error (
293 EMPATHY_CONTACT_BLOCKING_DIALOG (self
), error
);
295 g_error_free (error
);
299 DEBUG ("Contact blocked");
303 block_contact_got_contact (GObject
*source
,
304 GAsyncResult
*result
,
307 EmpathyContactBlockingDialog
*self
;
308 TpConnection
*conn
= TP_CONNECTION (source
);
309 TpWeakRef
*wr
= user_data
;
311 GError
*error
= NULL
;
313 self
= tp_weak_ref_dup_object (wr
);
317 contact
= tp_connection_dup_contact_by_id_finish (conn
, result
, &error
);
320 DEBUG ("Error getting contact on %s: %s",
321 get_pretty_conn_name (conn
), error
->message
);
323 contact_blocking_dialog_set_error (
324 EMPATHY_CONTACT_BLOCKING_DIALOG (self
), error
);
326 g_error_free (error
);
330 tp_contact_block_async (contact
, FALSE
, block_cb
, self
);
331 g_object_unref (contact
);
334 g_clear_object (&self
);
335 tp_weak_ref_destroy (wr
);
339 contact_blocking_dialog_add_contact (GtkWidget
*widget
,
340 EmpathyContactBlockingDialog
*self
)
342 TpConnection
*conn
= empathy_account_chooser_get_connection (
343 EMPATHY_ACCOUNT_CHOOSER (self
->priv
->account_chooser
));
344 const char *identifier
;
346 identifier
= gtk_entry_get_text (
347 GTK_ENTRY (self
->priv
->add_contact_entry
));
349 DEBUG ("Looking up handle for '%s' on %s",
350 identifier
, get_pretty_conn_name (conn
));
352 tp_connection_dup_contact_by_id_async (conn
, identifier
,
353 0, NULL
, block_contact_got_contact
,
354 tp_weak_ref_new (self
, NULL
, NULL
));
356 gtk_entry_set_text (GTK_ENTRY (self
->priv
->add_contact_entry
), "");
357 gtk_widget_hide (self
->priv
->info_bar
);
361 unblock_cb (GObject
*source
,
362 GAsyncResult
*result
,
365 EmpathyContactBlockingDialog
*self
= user_data
;
366 GError
*error
= NULL
;
368 if (!tp_connection_unblock_contacts_finish (TP_CONNECTION (source
), result
,
371 DEBUG ("Error unblocking contacts: %s", error
->message
);
373 contact_blocking_dialog_set_error (
374 EMPATHY_CONTACT_BLOCKING_DIALOG (self
), error
);
376 g_error_free (error
);
380 DEBUG ("Contacts unblocked");
384 contact_blocking_dialog_remove_contacts (GtkWidget
*button
,
385 EmpathyContactBlockingDialog
*self
)
387 TpConnection
*conn
= empathy_account_chooser_get_connection (
388 EMPATHY_ACCOUNT_CHOOSER (self
->priv
->account_chooser
));
393 rows
= gtk_tree_selection_get_selected_rows (self
->priv
->selection
, &model
);
395 contacts
= g_ptr_array_new_with_free_func (g_object_unref
);
397 for (ptr
= rows
; ptr
!= NULL
; ptr
= ptr
->next
)
399 GtkTreePath
*path
= ptr
->data
;
403 if (!gtk_tree_model_get_iter (model
, &iter
, path
))
406 gtk_tree_model_get (model
, &iter
,
407 COL_BLOCKED_CONTACT
, &contact
,
410 g_ptr_array_add (contacts
, contact
);
412 gtk_tree_path_free (path
);
417 if (contacts
->len
> 0)
419 DEBUG ("Unblocking %u contacts", contacts
->len
);
421 tp_connection_unblock_contacts_async (conn
, contacts
->len
,
422 (TpContact
* const *) contacts
->pdata
, unblock_cb
, self
);
425 g_ptr_array_unref (contacts
);
429 contact_blocking_dialog_account_changed (GtkWidget
*account_chooser
,
430 EmpathyContactBlockingDialog
*self
)
432 TpConnection
*conn
= empathy_account_chooser_get_connection (
433 EMPATHY_ACCOUNT_CHOOSER (account_chooser
));
438 if (self
->priv
->block_account_changed
> 0)
441 if (conn
== self
->priv
->current_conn
)
444 /* clear the lists of contacts */
445 gtk_list_store_clear (self
->priv
->blocked_contacts
);
446 gtk_list_store_clear (self
->priv
->completion_contacts
);
448 if (self
->priv
->current_conn
!= NULL
)
450 g_signal_handlers_disconnect_by_func (self
->priv
->current_conn
,
451 blocked_contacts_changed_cb
, self
);
453 g_clear_object (&self
->priv
->current_conn
);
459 DEBUG ("Account changed: %s", get_pretty_conn_name (conn
));
461 self
->priv
->current_conn
= g_object_ref (conn
);
463 tp_g_signal_connect_object (conn
, "blocked-contacts-changed",
464 G_CALLBACK (blocked_contacts_changed_cb
), self
, 0);
466 blocked
= tp_connection_get_blocked_contacts (conn
);
468 DEBUG ("%u contacts blocked on %s",
469 blocked
!= NULL
? blocked
->len
: 0, get_pretty_conn_name (conn
));
471 contact_blocking_dialog_add_blocked (self
, blocked
);
473 DEBUG ("Loading contacts");
475 members
= tp_connection_dup_contact_list (conn
);
477 for (i
= 0; i
< members
->len
; i
++)
479 TpContact
*contact
= g_ptr_array_index (members
, i
);
482 tmpstr
= g_strdup_printf ("%s (%s)",
483 tp_contact_get_alias (contact
),
484 tp_contact_get_identifier (contact
));
486 gtk_list_store_insert_with_values (self
->priv
->completion_contacts
,
488 COL_COMPLETION_IDENTIFIER
, tp_contact_get_identifier (contact
),
489 COL_COMPLETION_TEXT
, tmpstr
,
495 g_ptr_array_unref (members
);
499 contact_blocking_dialog_view_selection_changed (GtkTreeSelection
*selection
,
500 EmpathyContactBlockingDialog
*self
)
502 GList
*rows
= gtk_tree_selection_get_selected_rows (selection
, NULL
);
504 /* update the sensitivity of the remove button */
505 gtk_widget_set_sensitive (self
->priv
->remove_button
, rows
!= NULL
);
507 g_list_foreach (rows
, (GFunc
) gtk_tree_path_free
, NULL
);
512 contact_selector_dialog_match_func (GtkEntryCompletion
*completion
,
521 model
= gtk_entry_completion_get_model (completion
);
522 if (model
== NULL
|| iter
== NULL
)
525 gtk_tree_model_get (model
, iter
, COL_COMPLETION_TEXT
, &str
, -1);
526 lower
= g_utf8_strdown (str
, -1);
527 if (strstr (lower
, key
))
529 DEBUG ("Key %s is matching name **%s**", key
, str
);
536 gtk_tree_model_get (model
, iter
, COL_COMPLETION_IDENTIFIER
, &str
, -1);
537 lower
= g_utf8_strdown (str
, -1);
538 if (strstr (lower
, key
))
540 DEBUG ("Key %s is matching ID **%s**", key
, str
);
553 contact_selector_dialog_match_selected_cb (GtkEntryCompletion
*widget
,
556 EmpathyContactBlockingDialog
*self
)
560 if (iter
== NULL
|| model
== NULL
)
563 gtk_tree_model_get (model
, iter
, COL_COMPLETION_IDENTIFIER
, &id
, -1);
564 gtk_entry_set_text (GTK_ENTRY (self
->priv
->add_contact_entry
), id
);
566 DEBUG ("Got selected match **%s**", id
);
574 contact_blocking_dialog_dispose (GObject
*self
)
576 EmpathyContactBlockingDialogPrivate
*priv
= GET_PRIVATE (self
);
578 g_clear_object (&priv
->current_conn
);
580 G_OBJECT_CLASS (empathy_contact_blocking_dialog_parent_class
)->dispose (self
);
584 empathy_contact_blocking_dialog_class_init (
585 EmpathyContactBlockingDialogClass
*klass
)
587 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
589 gobject_class
->dispose
= contact_blocking_dialog_dispose
;
591 g_type_class_add_private (gobject_class
,
592 sizeof (EmpathyContactBlockingDialogPrivate
));
596 empathy_contact_blocking_dialog_init (EmpathyContactBlockingDialog
*self
)
601 GtkWidget
*account_hbox
, *blocked_contacts_view
, *blocked_contacts_sw
,
603 GtkEntryCompletion
*completion
;
604 TpAccountManager
*am
;
605 GtkStyleContext
*context
;
606 TpSimpleClientFactory
*factory
;
608 self
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
609 EMPATHY_TYPE_CONTACT_BLOCKING_DIALOG
,
610 EmpathyContactBlockingDialogPrivate
);
612 gtk_window_set_title (GTK_WINDOW (self
), _("Edit Blocked Contacts"));
613 gtk_dialog_add_button (GTK_DIALOG (self
),
614 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
);
616 filename
= empathy_file_lookup ("empathy-contact-blocking-dialog.ui",
619 gui
= tpaw_builder_get_file (filename
,
620 "contents", &contents
,
621 "account-hbox", &account_hbox
,
622 "add-button", &self
->priv
->add_button
,
623 "add-contact-entry", &self
->priv
->add_contact_entry
,
624 "blocked-contacts", &self
->priv
->blocked_contacts
,
625 "blocked-contacts-sw", &blocked_contacts_sw
,
626 "blocked-contacts-view", &blocked_contacts_view
,
627 "remove-button", &self
->priv
->remove_button
,
628 "remove-toolbar", &remove_toolbar
,
631 tpaw_builder_connect (gui
, self
,
632 "add-button", "clicked", contact_blocking_dialog_add_contact
,
633 "add-contact-entry", "activate", contact_blocking_dialog_add_contact
,
634 "remove-button", "clicked", contact_blocking_dialog_remove_contacts
,
637 /* join the remove toolbar to the treeview */
638 context
= gtk_widget_get_style_context (blocked_contacts_sw
);
639 gtk_style_context_set_junction_sides (context
, GTK_JUNCTION_BOTTOM
);
640 context
= gtk_widget_get_style_context (remove_toolbar
);
641 gtk_style_context_set_junction_sides (context
, GTK_JUNCTION_TOP
);
643 /* add the contents to the dialog */
645 GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (self
))),
647 gtk_widget_show (contents
);
649 /* set up the tree selection */
650 self
->priv
->selection
= gtk_tree_view_get_selection (
651 GTK_TREE_VIEW (blocked_contacts_view
));
652 gtk_tree_selection_set_mode (self
->priv
->selection
, GTK_SELECTION_MULTIPLE
);
653 g_signal_connect (self
->priv
->selection
, "changed",
654 G_CALLBACK (contact_blocking_dialog_view_selection_changed
), self
);
656 /* build the contact entry */
657 self
->priv
->completion_contacts
= gtk_list_store_new (N_COMPLETION_COLUMNS
,
658 G_TYPE_STRING
, /* id */
659 G_TYPE_STRING
, /* text */
660 TP_TYPE_CONTACT
); /* contact */
662 completion
= gtk_entry_completion_new ();
663 gtk_entry_completion_set_model (completion
,
664 GTK_TREE_MODEL (self
->priv
->completion_contacts
));
665 gtk_entry_completion_set_text_column (completion
, COL_COMPLETION_TEXT
);
666 gtk_entry_completion_set_match_func (completion
,
667 contact_selector_dialog_match_func
,
669 g_signal_connect (completion
, "match-selected",
670 G_CALLBACK (contact_selector_dialog_match_selected_cb
),
672 gtk_entry_set_completion (GTK_ENTRY (self
->priv
->add_contact_entry
),
674 g_object_unref (completion
);
675 g_object_unref (self
->priv
->completion_contacts
);
677 /* add the account chooser */
678 self
->priv
->account_chooser
= empathy_account_chooser_new ();
679 contact_blocking_dialog_refilter_account_chooser (self
);
680 g_signal_connect (self
->priv
->account_chooser
, "changed",
681 G_CALLBACK (contact_blocking_dialog_account_changed
), self
);
683 gtk_box_pack_start (GTK_BOX (account_hbox
), self
->priv
->account_chooser
,
685 gtk_widget_show (self
->priv
->account_chooser
);
687 /* add an error warning info bar */
688 self
->priv
->info_bar
= gtk_info_bar_new ();
689 gtk_box_pack_start (GTK_BOX (contents
), self
->priv
->info_bar
, FALSE
, TRUE
, 0);
690 gtk_info_bar_set_message_type (GTK_INFO_BAR (self
->priv
->info_bar
),
693 self
->priv
->info_bar_label
= gtk_label_new ("");
694 gtk_container_add (GTK_CONTAINER (
695 gtk_info_bar_get_content_area (GTK_INFO_BAR (self
->priv
->info_bar
))),
696 self
->priv
->info_bar_label
);
697 gtk_widget_show (self
->priv
->info_bar_label
);
699 /* prepare the account manager */
700 am
= tp_account_manager_dup ();
702 factory
= tp_proxy_get_factory (am
);
703 tp_simple_client_factory_add_connection_features_varargs (factory
,
704 TP_CONNECTION_FEATURE_CONTACT_BLOCKING
, NULL
);
706 tp_proxy_prepare_async (am
, NULL
, contact_blocking_dialog_am_prepared
, self
);
710 g_object_unref (gui
);
714 empathy_contact_blocking_dialog_new (GtkWindow
*parent
)
716 GtkWidget
*self
= g_object_new (EMPATHY_TYPE_CONTACT_BLOCKING_DIALOG
,
721 gtk_window_set_transient_for (GTK_WINDOW (self
), parent
);