2 * empathy-invite-participant-dialog.c
4 * EmpathyInviteParticipantDialog
6 * (c) 2009, Collabora Ltd.
9 * Danielle Madeley <danielle.madeley@collabora.co.uk>
12 #include <glib/gi18n.h>
13 #include <folks/folks-telepathy.h>
15 #include "empathy-invite-participant-dialog.h"
17 #include <libempathy-gtk/empathy-individual-view.h>
18 #include <libempathy-gtk/empathy-ui-utils.h>
20 G_DEFINE_TYPE (EmpathyInviteParticipantDialog,
21 empathy_invite_participant_dialog, GTK_TYPE_DIALOG);
28 typedef struct _AddTemporaryIndividualCtx AddTemporaryIndividualCtx;
30 struct _EmpathyInviteParticipantDialogPrivate
32 EmpathyTpChat *tp_chat;
33 TpAccountManager *account_mgr;
35 EmpathyIndividualStore *store;
36 EmpathyIndividualView *view;
38 GtkWidget *invite_button;
40 GPtrArray *search_words;
43 /* Context representing the FolksIndividual which are added because of the
44 * current search from the user. */
45 AddTemporaryIndividualCtx *add_temp_ctx;
49 invite_participant_dialog_get_property (GObject *object,
54 EmpathyInviteParticipantDialog *self = (EmpathyInviteParticipantDialog *)
60 g_value_set_object (value, self->priv->tp_chat);
63 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
69 invite_participant_dialog_set_property (GObject *object,
74 EmpathyInviteParticipantDialog *self = (EmpathyInviteParticipantDialog *)
80 g_assert (self->priv->tp_chat == NULL); /* construct-only */
81 self->priv->tp_chat = g_value_dup_object (value);
84 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
89 struct _AddTemporaryIndividualCtx
91 EmpathyInviteParticipantDialog *self;
92 /* List of owned FolksIndividual */
96 static AddTemporaryIndividualCtx *
97 add_temporary_individual_ctx_new (EmpathyInviteParticipantDialog *self)
99 AddTemporaryIndividualCtx *ctx = g_slice_new0 (AddTemporaryIndividualCtx);
106 add_temporary_individual_ctx_free (AddTemporaryIndividualCtx *ctx)
110 /* Remove all the individuals from the model */
111 for (l = ctx->individuals; l != NULL; l = g_list_next (l))
113 FolksIndividual *individual = l->data;
115 individual_store_remove_individual_and_disconnect (ctx->self->priv->store,
118 g_object_unref (individual);
121 g_list_free (ctx->individuals);
122 g_slice_free (AddTemporaryIndividualCtx, ctx);
126 invite_participant_dialog_dispose (GObject *object)
128 EmpathyInviteParticipantDialog *self = (EmpathyInviteParticipantDialog *)
131 tp_clear_pointer (&self->priv->add_temp_ctx,
132 add_temporary_individual_ctx_free);
134 tp_clear_object (&self->priv->tp_chat);
135 tp_clear_object (&self->priv->store);
136 tp_clear_pointer (&self->priv->search_words, g_ptr_array_unref);
137 tp_clear_pointer (&self->priv->search_str, g_free);
139 tp_clear_object (&self->priv->account_mgr);
141 G_OBJECT_CLASS (empathy_invite_participant_dialog_parent_class)->dispose (
146 empathy_invite_participant_dialog_class_init (
147 EmpathyInviteParticipantDialogClass *klass)
149 GObjectClass *object_class = G_OBJECT_CLASS (klass);
151 object_class->get_property = invite_participant_dialog_get_property;
152 object_class->set_property = invite_participant_dialog_set_property;
153 object_class->dispose = invite_participant_dialog_dispose;
155 g_type_class_add_private (object_class,
156 sizeof (EmpathyInviteParticipantDialogPrivate));
158 g_object_class_install_property (object_class,
160 g_param_spec_object ("tp-chat", "EmpathyTpChat", "EmpathyTpChat",
161 EMPATHY_TYPE_TP_CHAT,
162 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
166 view_selection_changed_cb (GtkWidget *treeview,
167 EmpathyInviteParticipantDialog *self)
169 FolksIndividual *individual;
171 individual = empathy_individual_view_dup_selected (self->priv->view);
173 gtk_widget_set_sensitive (self->priv->invite_button, individual != NULL);
175 tp_clear_object (&individual);
178 /* Return the TpContact of @individual which is on the same connection as the
181 get_tp_contact_for_chat (EmpathyInviteParticipantDialog *self,
182 FolksIndividual *individual)
184 TpContact *contact = NULL;
185 TpConnection *chat_conn;
189 chat_conn = tp_channel_borrow_connection ((TpChannel *) self->priv->tp_chat);
191 personas = folks_individual_get_personas (individual);
192 iter = gee_iterable_iterator (GEE_ITERABLE (personas));
193 while (contact == FALSE && gee_iterator_next (iter))
195 TpfPersona *persona = gee_iterator_get (iter);
196 TpConnection *contact_conn;
197 TpContact *contact_cur = NULL;
199 if (TPF_IS_PERSONA (persona))
201 contact_cur = tpf_persona_get_contact (persona);
202 if (contact_cur != NULL)
204 contact_conn = tp_contact_get_connection (contact_cur);
206 if (!tp_strdiff (tp_proxy_get_object_path (contact_conn),
207 tp_proxy_get_object_path (chat_conn)))
208 contact = contact_cur;
212 g_clear_object (&persona);
214 g_clear_object (&iter);
220 filter_func (GtkTreeModel *model,
224 EmpathyInviteParticipantDialog *self = user_data;
225 FolksIndividual *individual;
229 gboolean display = FALSE;
231 gtk_tree_model_get (model, iter,
232 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual,
233 EMPATHY_INDIVIDUAL_STORE_COL_IS_ONLINE, &is_online,
236 if (individual == NULL)
239 if (self->priv->search_words == NULL)
241 /* Not searching, display online contacts */
247 if (!empathy_individual_match_string (individual,
248 self->priv->search_str, self->priv->search_words))
252 /* Filter out individuals not having a persona on the same connection as the
254 contact = get_tp_contact_for_chat (self, individual);
259 /* Filter out contacts which are already in the chat */
260 members = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (
261 self->priv->tp_chat));
265 for (l = members; l != NULL; l = g_list_next (l))
267 EmpathyContact *member = l->data;
270 /* Try to get the non-channel specific handle. */
271 handle = tp_channel_group_get_handle_owner (
272 TP_CHANNEL (self->priv->tp_chat),
273 empathy_contact_get_handle (member));
275 handle = empathy_contact_get_handle (member);
277 if (handle == tp_contact_get_handle (contact))
284 g_list_free_full (members, g_object_unref);
287 tp_clear_object (&individual);
292 get_contacts_cb (TpConnection *connection,
294 TpContact * const *contacts,
295 const gchar * const *requested_ids,
296 GHashTable *failed_id_errors,
299 GObject *weak_object)
301 EmpathyInviteParticipantDialog *self =
302 (EmpathyInviteParticipantDialog *) weak_object;
303 AddTemporaryIndividualCtx *ctx = user_data;
305 TpfPersonaStore *store;
306 FolksIndividual *individual;
307 TpfPersona *persona_new;
310 if (self->priv->add_temp_ctx != ctx)
311 /* another request has been started */
317 account = g_object_get_data (G_OBJECT (connection), "account");
319 store = tpf_persona_store_new (account);
321 gee_hash_set_new (FOLKS_TYPE_PERSONA, g_object_ref, g_object_unref,
322 g_direct_hash, g_direct_equal));
323 persona_new = tpf_persona_new (contacts[0], store);
324 gee_collection_add (GEE_COLLECTION (personas),
325 tpf_persona_new (contacts[0], store));
327 individual = folks_individual_new (personas);
329 /* Pass ownership to the list */
330 ctx->individuals = g_list_prepend (ctx->individuals, individual);
332 individual_store_add_individual_and_connect (self->priv->store, individual);
334 g_clear_object (&persona_new);
335 g_clear_object (&personas);
336 g_object_unref (store);
340 add_temporary_individuals (EmpathyInviteParticipantDialog *self,
345 tp_clear_pointer (&self->priv->add_temp_ctx,
346 add_temporary_individual_ctx_free);
348 if (tp_str_empty (id))
351 self->priv->add_temp_ctx = add_temporary_individual_ctx_new (self);
353 /* Try to add an individual for each connected account */
354 accounts = tp_account_manager_get_valid_accounts (self->priv->account_mgr);
355 for (l = accounts; l != NULL; l = g_list_next (l))
357 TpAccount *account = l->data;
359 TpContactFeature features[] = { TP_CONTACT_FEATURE_ALIAS,
360 TP_CONTACT_FEATURE_AVATAR_DATA,
361 TP_CONTACT_FEATURE_PRESENCE,
362 TP_CONTACT_FEATURE_CAPABILITIES };
364 conn = tp_account_get_connection (account);
368 /* One day we'll have tp_connection_get_account()... */
369 g_object_set_data_full (G_OBJECT (conn), "account",
370 g_object_ref (account), g_object_unref);
372 tp_connection_get_contacts_by_id (conn, 1, &id, G_N_ELEMENTS (features),
373 features, get_contacts_cb, self->priv->add_temp_ctx, NULL,
377 g_list_free (accounts);
381 search_text_changed (GtkEntry *entry,
382 EmpathyInviteParticipantDialog *self)
386 tp_clear_pointer (&self->priv->search_words, g_ptr_array_unref);
387 tp_clear_pointer (&self->priv->search_str, g_free);
389 id = gtk_entry_get_text (entry);
391 self->priv->search_words = empathy_live_search_strip_utf8_string (id);
392 self->priv->search_str = g_strdup (id);
394 add_temporary_individuals (self, id);
396 empathy_individual_view_refilter (self->priv->view);
400 empathy_invite_participant_dialog_init (EmpathyInviteParticipantDialog *self)
402 GtkDialog *dialog = GTK_DIALOG (self);
406 EmpathyIndividualManager *mgr;
407 GtkTreeSelection *selection;
409 GtkWidget *search_entry;
410 GQuark features[] = { TP_ACCOUNT_MANAGER_FEATURE_CORE, 0 };
412 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (
413 self, EMPATHY_TYPE_INVITE_PARTICIPANT_DIALOG,
414 EmpathyInviteParticipantDialogPrivate);
416 self->priv->account_mgr = tp_account_manager_dup ();
418 /* We don't wait for the CORE feature to be prepared, which is fine as we
419 * won't use the account manager until user starts searching. Furthermore,
420 * the AM has probably already been prepared by another Empathy
422 tp_proxy_prepare_async (self->priv->account_mgr, features, NULL, NULL);
424 content = gtk_dialog_get_content_area (dialog);
426 label = gtk_label_new (NULL);
427 str = g_strdup_printf (
428 "<span size=\"x-large\" weight=\"bold\">%s</span>\n\n%s",
429 _("Invite Participant"),
430 _("Choose a contact to invite into the conversation:"));
431 gtk_label_set_markup (GTK_LABEL (label), str);
434 gtk_box_pack_start (GTK_BOX (content), label, FALSE, TRUE, 6);
435 gtk_widget_show (label);
437 gtk_dialog_add_button (dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
440 search_entry = gtk_entry_new ();
441 gtk_box_pack_start (GTK_BOX (content), search_entry, FALSE, TRUE, 6);
442 gtk_widget_show (search_entry);
444 g_signal_connect (search_entry, "changed",
445 G_CALLBACK (search_text_changed), self);
447 /* Add the treeview */
448 mgr = empathy_individual_manager_dup_singleton ();
449 self->priv->store = empathy_individual_store_new (mgr);
450 g_object_unref (mgr);
452 empathy_individual_store_set_show_groups (self->priv->store, FALSE);
454 self->priv->view = empathy_individual_view_new (self->priv->store,
455 EMPATHY_INDIVIDUAL_VIEW_FEATURE_NONE, EMPATHY_INDIVIDUAL_FEATURE_NONE);
457 empathy_individual_view_set_custom_filter (self->priv->view,
460 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->view));
462 g_signal_connect (selection, "changed",
463 G_CALLBACK (view_selection_changed_cb), self);
465 scroll = gtk_scrolled_window_new (NULL, NULL);
467 gtk_container_add (GTK_CONTAINER (scroll), GTK_WIDGET (self->priv->view));
469 gtk_box_pack_start (GTK_BOX (content), scroll, TRUE, TRUE, 6);
470 gtk_widget_show (GTK_WIDGET (self->priv->view));
471 gtk_widget_show (scroll);
473 self->priv->invite_button = gtk_dialog_add_button (dialog, _("Invite"),
474 GTK_RESPONSE_ACCEPT);
475 gtk_widget_set_sensitive (self->priv->invite_button, FALSE);
477 gtk_window_set_title (GTK_WINDOW (self), _("Invite Participant"));
478 gtk_window_set_role (GTK_WINDOW (self), "invite_participant");
480 /* Set a default height so a few contacts are displayed */
481 gtk_window_set_default_size (GTK_WINDOW (self), -1, 400);
485 empathy_invite_participant_dialog_new (GtkWindow *parent,
486 EmpathyTpChat *tp_chat)
488 GtkWidget *self = g_object_new (EMPATHY_TYPE_INVITE_PARTICIPANT_DIALOG,
494 gtk_window_set_transient_for (GTK_WINDOW (self), parent);
501 empathy_invite_participant_dialog_get_selected (
502 EmpathyInviteParticipantDialog *self)
504 FolksIndividual *individual;
507 individual = empathy_individual_view_dup_selected (self->priv->view);
508 if (individual == NULL)
511 contact = get_tp_contact_for_chat (self, individual);
513 g_object_unref (individual);