aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libempathy-gtk/Makefile.am3
-rw-r--r--libempathy-gtk/empathy-contact-blocking-dialog.c514
-rw-r--r--libempathy-gtk/empathy-contact-blocking-dialog.h60
-rw-r--r--libempathy-gtk/empathy-contact-blocking-dialog.ui145
4 files changed, 722 insertions, 0 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-contact-blocking-dialog.c b/libempathy-gtk/empathy-contact-blocking-dialog.c
new file mode 100644
index 000000000..33dfb2e28
--- /dev/null
+++ b/libempathy-gtk/empathy-contact-blocking-dialog.c
@@ -0,0 +1,514 @@
+/*
+ * 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-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
+{
+ GHashTable *channels; /* TpConnection* -> TpChannel* */
+ GtkListStore *blocked_contacts;
+
+ GtkWidget *account_chooser;
+};
+
+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_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);
+
+ /* set the filter again to refilter the account list */
+ empathy_account_chooser_set_filter (
+ EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser),
+ contact_blocking_dialog_filter_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_account_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;
+
+ tp_proxy_prepare_async (account, NULL,
+ contact_blocking_dialog_account_prepared, self);
+ }
+
+ g_list_free (accounts);
+}
+
+static void
+contact_blocking_dialog_account_prepared (GObject *account,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ EmpathyContactBlockingDialog *self = user_data;
+ TpConnection *conn;
+ GError *error = NULL;
+
+ if (!tp_proxy_prepare_finish (account, result, &error))
+ {
+ DEBUG ("Could not prepare Account: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ g_signal_connect (account, "status-changed",
+ G_CALLBACK (contact_blocking_dialog_connection_status_changed), self);
+
+ conn = tp_account_get_connection (TP_ACCOUNT (account));
+
+ if (conn != NULL)
+ {
+ tp_proxy_prepare_async (conn, NULL,
+ contact_blocking_dialog_connection_prepared, self);
+ }
+}
+
+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);
+
+ /* set the filter again to refilter the account list */
+ empathy_account_chooser_set_filter (
+ EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser),
+ contact_blocking_dialog_filter_account_chooser, self);
+
+ g_signal_connect (channel, "group-members-changed",
+ G_CALLBACK (contact_blocking_dialog_deny_channel_members_changed), self);
+}
+
+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;
+
+ if (conn == NULL)
+ return;
+
+ DEBUG ("Account changed: %s", get_pretty_conn_name (conn));
+
+ /* FIXME: clear the completion, get the new blocked list */
+
+ /* clear the list of blocked contacts */
+ gtk_list_store_clear (self->priv->blocked_contacts);
+
+ /* load the deny list */
+ channel = g_hash_table_lookup (self->priv->channels, conn);
+
+ 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);
+}
+
+static void
+contact_blocking_dialog_dispose (GObject *self)
+{
+ EmpathyContactBlockingDialogPrivate *priv = GET_PRIVATE (self);
+
+ tp_clear_pointer (&priv->channels, g_hash_table_destroy);
+}
+
+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, *add_contact_entry;
+ 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-contact-entry", &add_contact_entry,
+ "blocked-contacts", &self->priv->blocked_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);
+
+ /* add the account chooser */
+ self->priv->account_chooser = empathy_account_chooser_new ();
+ empathy_account_chooser_set_filter (
+ EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser),
+ contact_blocking_dialog_filter_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);
+
+ /* build the contact entry */
+ // FIXME
+
+ /* prepare the account manager */
+ am = tp_account_manager_dup ();
+ tp_proxy_prepare_async (am, NULL, contact_blocking_dialog_am_prepared, self);
+
+ 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..648850e13
--- /dev/null
+++ b/libempathy-gtk/empathy-contact-blocking-dialog.ui
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <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="treeview1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">blocked-contacts</property>
+ <property name="headers_clickable">False</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+ <property name="title">Blocked Contacts</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="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">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="GtkListStore" id="blocked-contacts">
+ <columns>
+ <!-- column-name identifier -->
+ <column type="gchararray"/>
+ <!-- column-name handle -->
+ <column type="guint"/>
+ </columns>
+ </object>
+ <object class="GtkSizeGroup" id="sizegroup1">
+ <widgets>
+ <widget name="remove-button"/>
+ <widget name="add-button"/>
+ </widgets>
+ </object>
+</interface>