1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2008-2010 Collabora Ltd.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 * Authors: Xavier Claessens <xclaesse@gmail.com>
20 * Travis Reitter <travis.reitter@collabora.co.uk>
24 #include "empathy-individual-menu.h"
26 #include <glib/gi18n-lib.h>
27 #include <tp-account-widgets/tpaw-camera-monitor.h>
28 #include <telepathy-glib/telepathy-glib-dbus.h>
30 #include "empathy-account-selector-dialog.h"
31 #include "empathy-call-utils.h"
32 #include "empathy-chatroom-manager.h"
33 #include "empathy-gtk-enum-types.h"
34 #include "empathy-images.h"
35 #include "empathy-individual-dialogs.h"
36 #include "empathy-individual-dialogs.h"
37 #include "empathy-individual-edit-dialog.h"
38 #include "empathy-individual-information-dialog.h"
39 #include "empathy-individual-manager.h"
40 #include "empathy-individual-store-channel.h"
41 #include "empathy-log-window.h"
42 #include "empathy-request-util.h"
43 #include "empathy-share-my-desktop.h"
44 #include "empathy-ui-utils.h"
45 #include "empathy-utils.h"
47 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
48 #include "empathy-debug.h"
50 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyIndividualMenu)
53 gchar *active_group; /* may be NULL */
54 FolksIndividual *individual; /* owned */
55 EmpathyIndividualFeatureFlags features;
56 EmpathyIndividualStore *store; /* may be NULL */
57 } EmpathyIndividualMenuPriv;
60 PROP_ACTIVE_GROUP = 1,
71 static guint signals [LAST_SIGNAL] = { 0 };
73 G_DEFINE_TYPE (EmpathyIndividualMenu, empathy_individual_menu, GTK_TYPE_MENU);
75 static GtkWidget * empathy_individual_chat_menu_item_new (
76 EmpathyIndividualMenu *self, FolksIndividual *individual);
77 static GtkWidget * empathy_individual_sms_menu_item_new (
78 EmpathyIndividualMenu *self, FolksIndividual *individual);
79 static GtkWidget * empathy_individual_log_menu_item_new (
80 FolksIndividual *individual);
81 static GtkWidget * empathy_individual_info_menu_item_new (
82 FolksIndividual *individual);
83 static GtkWidget * empathy_individual_edit_menu_item_new (
84 FolksIndividual *individual);
85 static GtkWidget * empathy_individual_invite_menu_item_new (
86 FolksIndividual *individual,
87 EmpathyContact *contact);
88 static GtkWidget * empathy_individual_file_transfer_menu_item_new (
89 EmpathyIndividualMenu *self, FolksIndividual *individual);
90 static GtkWidget * empathy_individual_share_my_desktop_menu_item_new (
91 EmpathyIndividualMenu *self, FolksIndividual *individual);
92 static GtkWidget * empathy_individual_favourite_menu_item_new (
93 FolksIndividual *individual);
94 static GtkWidget * empathy_individual_add_menu_item_new (
95 EmpathyIndividualMenu *self,
96 FolksIndividual *individual);
97 static GtkWidget * empathy_individiual_block_menu_item_new (
98 FolksIndividual *individual);
99 static GtkWidget * empathy_individiual_remove_menu_item_new (
100 EmpathyIndividualMenu *self);
103 individual_menu_add_personas (EmpathyIndividualMenu *self,
105 FolksIndividual *individual,
106 EmpathyIndividualFeatureFlags features)
111 guint persona_count = 0;
113 g_return_if_fail (GTK_IS_MENU (menu));
114 g_return_if_fail (FOLKS_IS_INDIVIDUAL (individual));
115 g_return_if_fail (empathy_folks_individual_contains_contact (individual));
117 personas = folks_individual_get_personas (individual);
118 /* we'll re-use this iterator throughout */
119 iter = gee_iterable_iterator (GEE_ITERABLE (personas));
121 /* Make sure we've got enough valid entries for these menu items to add
123 while (gee_iterator_next (iter))
125 FolksPersona *persona = gee_iterator_get (iter);
126 if (empathy_folks_persona_is_interesting (persona))
129 g_clear_object (&persona);
132 /* return early if these entries would add nothing beyond the "quick" items */
133 if (persona_count <= 1)
136 /* add a separator before the list of personas */
137 item = gtk_separator_menu_item_new ();
138 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
139 gtk_widget_show (item);
141 iter = gee_iterable_iterator (GEE_ITERABLE (personas));
142 while (gee_iterator_next (iter))
145 GtkWidget *contact_item;
146 GtkWidget *contact_submenu;
147 TpContact *tp_contact;
148 EmpathyContact *contact;
149 TpfPersona *persona = gee_iterator_get (iter);
151 FolksPersonaStore *store;
152 const gchar *account;
154 /* Individual containing only persona */
155 FolksIndividual *single_individual;
157 if (!empathy_folks_persona_is_interesting (FOLKS_PERSONA (persona)))
160 tp_contact = tpf_persona_get_contact (persona);
161 if (tp_contact == NULL)
164 contact = empathy_contact_dup_from_tp_contact (tp_contact);
165 single_individual = empathy_create_individual_from_tp_contact (
168 /* Pretty hacky. Creating single_individual had a side effect to change
169 * persona.individual from individual to single_individual which is not
170 * what we want so we set it back. See bgo#684971 for details. */
171 g_object_set (persona, "individual", individual, NULL);
173 store = folks_persona_get_store (FOLKS_PERSONA (persona));
174 account = folks_persona_store_get_display_name (store);
176 /* Translators: this is used in the context menu for a contact. The first
177 * parameter is a contact ID (e.g. foo@jabber.org) and the second is one
178 * of the user's account IDs (e.g. me@hotmail.com). */
179 label = g_strdup_printf (_("%s (%s)"),
180 folks_persona_get_display_id (FOLKS_PERSONA (persona)), account);
182 contact_item = gtk_image_menu_item_new_with_label (label);
183 gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (contact_item),
185 contact_submenu = gtk_menu_new ();
186 gtk_menu_item_set_submenu (GTK_MENU_ITEM (contact_item), contact_submenu);
187 image = gtk_image_new_from_icon_name (
188 empathy_icon_name_for_contact (contact), GTK_ICON_SIZE_MENU);
189 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (contact_item), image);
190 gtk_widget_show (image);
193 if (features & EMPATHY_INDIVIDUAL_FEATURE_CHAT)
195 action = empathy_individual_chat_menu_item_new (self,
197 gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
198 gtk_widget_show (action);
202 if (features & EMPATHY_INDIVIDUAL_FEATURE_SMS)
204 action = empathy_individual_sms_menu_item_new (self,
206 gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
207 gtk_widget_show (action);
210 if (features & EMPATHY_INDIVIDUAL_FEATURE_CALL)
213 action = empathy_individual_audio_call_menu_item_new (
214 self, single_individual);
215 gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
216 gtk_widget_show (action);
219 action = empathy_individual_video_call_menu_item_new (
220 self, single_individual);
221 gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
222 gtk_widget_show (action);
226 if (features & EMPATHY_INDIVIDUAL_FEATURE_LOG)
228 action = empathy_individual_log_menu_item_new (single_individual);
229 gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
230 gtk_widget_show (action);
234 action = empathy_individual_invite_menu_item_new (NULL, contact);
235 gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
236 gtk_widget_show (action);
239 if (features & EMPATHY_INDIVIDUAL_FEATURE_FILE_TRANSFER)
241 action = empathy_individual_file_transfer_menu_item_new (
242 self, single_individual);
243 gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
244 gtk_widget_show (action);
247 /* Share my desktop */
248 action = empathy_individual_share_my_desktop_menu_item_new (
249 self, single_individual);
250 gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
251 gtk_widget_show (action);
254 if (features & EMPATHY_INDIVIDUAL_FEATURE_BLOCK &&
255 (item = empathy_individiual_block_menu_item_new (single_individual))
259 sep = gtk_separator_menu_item_new ();
260 gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), sep);
261 gtk_widget_show (sep);
263 gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), item);
264 gtk_widget_show (item);
267 gtk_menu_shell_append (GTK_MENU_SHELL (menu), contact_item);
268 gtk_widget_show (contact_item);
271 g_object_unref (contact);
272 g_object_unref (single_individual);
275 g_clear_object (&persona);
279 g_clear_object (&iter);
283 empathy_individual_menu_init (EmpathyIndividualMenu *self)
285 EmpathyIndividualMenuPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
286 EMPATHY_TYPE_INDIVIDUAL_MENU, EmpathyIndividualMenuPriv);
292 find_phone_accounts (void)
294 TpAccountManager *am;
298 am = tp_account_manager_dup ();
299 g_return_val_if_fail (am != NULL, NULL);
301 accounts = tp_account_manager_dup_valid_accounts (am);
302 for (l = accounts; l != NULL; l = g_list_next (l))
304 TpAccount *account = l->data;
306 if (tp_account_get_connection_status (account, NULL) !=
307 TP_CONNECTION_STATUS_CONNECTED)
310 if (!tp_account_associated_with_uri_scheme (account, "tel"))
313 found = g_list_prepend (found, g_object_ref (account));
316 g_list_free_full (accounts, g_object_unref);
323 has_phone_account (void)
328 accounts = find_phone_accounts ();
329 result = (accounts != NULL);
331 g_list_free_full (accounts, (GDestroyNotify) g_object_unref);
337 call_phone_number (FolksPhoneFieldDetails *details,
340 DEBUG ("Try to call %s", folks_phone_field_details_get_normalised (details));
342 empathy_call_new_with_streams (
343 folks_phone_field_details_get_normalised (details),
344 account, TRUE, FALSE, empathy_get_current_action_time ());
348 display_call_phone_dialog (FolksPhoneFieldDetails *details,
354 dialog = empathy_account_selector_dialog_new (accounts);
356 gtk_window_set_title (GTK_WINDOW (dialog),
357 _("Select account to use to place the call"));
359 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
360 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
361 _("Call"), GTK_RESPONSE_OK,
364 response = gtk_dialog_run (GTK_DIALOG (dialog));
366 if (response == GTK_RESPONSE_OK)
370 account = empathy_account_selector_dialog_dup_selected (
371 EMPATHY_ACCOUNT_SELECTOR_DIALOG (dialog));
375 call_phone_number (details, account);
377 g_object_unref (account);
381 gtk_widget_destroy (dialog);
385 call_phone_number_cb (GtkMenuItem *item,
386 FolksPhoneFieldDetails *details)
390 accounts = find_phone_accounts ();
391 if (accounts == NULL)
393 DEBUG ("No phone aware account connected; can't call");
395 else if (g_list_length (accounts) == 1)
397 call_phone_number (details, accounts->data);
401 /* Ask which account to use */
402 display_call_phone_dialog (details, accounts);
405 g_list_free_full (accounts, (GDestroyNotify) g_object_unref);
409 find_phone_type (FolksPhoneFieldDetails *details)
411 GeeCollection *types;
414 types = folks_abstract_field_details_get_parameter_values (
415 FOLKS_ABSTRACT_FIELD_DETAILS (details), "type");
420 iter = gee_iterable_iterator (GEE_ITERABLE (types));
421 while (gee_iterator_next (iter))
423 const gchar *type = gee_iterator_get (iter);
425 if (!tp_strdiff (type, "CELL"))
427 else if (!tp_strdiff (type, "WORK"))
429 else if (!tp_strdiff (type, "HOME"))
437 add_phone_numbers (EmpathyIndividualMenu *self)
439 EmpathyIndividualMenuPriv *priv = GET_PRIV (self);
444 all_numbers = folks_phone_details_get_phone_numbers (
445 FOLKS_PHONE_DETAILS (priv->individual));
447 sensitive = has_phone_account ();
449 iter = gee_iterable_iterator (GEE_ITERABLE (all_numbers));
450 while (gee_iterator_next (iter))
452 FolksPhoneFieldDetails *details = gee_iterator_get (iter);
453 GtkWidget *item, *image;
457 type = find_phone_type (details);
461 tmp = g_strdup_printf ("Call %s (%s)",
462 folks_phone_field_details_get_normalised (details),
467 tmp = g_strdup_printf ("Call %s",
468 folks_phone_field_details_get_normalised (details));
471 item = gtk_image_menu_item_new_with_mnemonic (tmp);
474 g_signal_connect_data (item, "activate",
475 G_CALLBACK (call_phone_number_cb), g_object_ref (details),
476 (GClosureNotify) g_object_unref, 0);
478 gtk_widget_set_sensitive (item, sensitive);
480 image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_CALL,
482 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
483 gtk_widget_show (image);
485 gtk_menu_shell_append (GTK_MENU_SHELL (self), item);
486 gtk_widget_show (item);
489 g_object_unref (iter);
492 /* return a list of TpContact supporting the blocking iface */
494 get_contacts_supporting_blocking (FolksIndividual *individual)
498 GList *result = NULL;
500 personas = folks_individual_get_personas (individual);
502 iter = gee_iterable_iterator (GEE_ITERABLE (personas));
503 while (gee_iterator_next (iter))
505 TpfPersona *persona = gee_iterator_get (iter);
509 if (!TPF_IS_PERSONA (persona))
512 contact = tpf_persona_get_contact (persona);
516 conn = tp_contact_get_connection (contact);
518 if (tp_proxy_has_interface_by_id (conn,
519 TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_BLOCKING))
520 result = g_list_prepend (result, contact);
523 g_clear_object (&iter);
534 static GotAvatarCtx *
535 got_avatar_ctx_new (gboolean blocked,
538 GotAvatarCtx *ctx = g_slice_new0 (GotAvatarCtx);
540 ctx->blocked = blocked;
541 ctx->parent = parent != NULL ? g_object_ref (parent) : NULL;
546 got_avatar_ctx_free (GotAvatarCtx *ctx)
548 g_clear_object (&ctx->parent);
549 g_slice_free (GotAvatarCtx, ctx);
553 got_avatar (GObject *source_object,
554 GAsyncResult *result,
557 FolksIndividual *individual = FOLKS_INDIVIDUAL (source_object);
558 GotAvatarCtx *ctx = user_data;
560 GError *error = NULL;
561 gboolean abusive = FALSE;
562 EmpathyIndividualManager *manager;
564 avatar = empathy_pixbuf_avatar_from_individual_scaled_finish (individual,
569 DEBUG ("Could not get avatar: %s", error->message);
570 g_error_free (error);
574 /* confirm the user really wishes to block the contact */
575 if (!empathy_block_individual_dialog_show (GTK_WINDOW (ctx->parent),
576 individual, avatar, &abusive))
580 manager = empathy_individual_manager_dup_singleton ();
582 empathy_individual_manager_set_blocked (manager, individual,
583 ctx->blocked, abusive);
585 g_object_unref (manager);
588 g_clear_object (&avatar);
589 got_avatar_ctx_free (ctx);
593 empathy_individual_block_menu_item_toggled (GtkCheckMenuItem *item,
594 FolksIndividual *individual)
600 /* @item may be destroyed while the async call is running to get the things
601 * we need from it right now. */
602 blocked = gtk_check_menu_item_get_active (item);
604 parent = g_object_get_data (
605 G_OBJECT (gtk_widget_get_parent (GTK_WIDGET (item))),
608 ctx = got_avatar_ctx_new (blocked, parent);
610 empathy_pixbuf_avatar_from_individual_scaled_async (individual,
611 48, 48, NULL, got_avatar, ctx);
615 update_block_menu_item (GtkWidget *item,
616 FolksIndividual *individual)
619 gboolean is_blocked = TRUE;
621 contacts = get_contacts_supporting_blocking (individual);
623 if (contacts == NULL)
626 /* Check the menu item if all his personas are blocked */
627 for (l = contacts; l != NULL; l = g_list_next (l))
629 TpContact *contact = l->data;
631 if (!tp_contact_is_blocked (contact))
638 g_signal_handlers_block_by_func (item,
639 empathy_individual_block_menu_item_toggled, individual);
641 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), is_blocked);
643 g_signal_handlers_unblock_by_func (item,
644 empathy_individual_block_menu_item_toggled, individual);
646 g_list_free (contacts);
650 contact_blocked_changed_cb (TpContact *contact,
654 FolksIndividual *individual;
656 individual = g_object_get_data (G_OBJECT (item), "individual");
658 update_block_menu_item (item, individual);
662 empathy_individiual_block_menu_item_new (FolksIndividual *individual)
667 contacts = get_contacts_supporting_blocking (individual);
669 /* Can't block, no persona supports blocking */
670 if (contacts == NULL)
673 item = gtk_check_menu_item_new_with_mnemonic (_("_Block Contact"));
675 g_object_set_data_full (G_OBJECT (item), "individual",
676 g_object_ref (individual), g_object_unref);
678 for (l = contacts; l != NULL; l = g_list_next (l))
680 TpContact *contact = l->data;
682 tp_g_signal_connect_object (contact, "notify::is-blocked",
683 G_CALLBACK (contact_blocked_changed_cb), item, 0);
686 g_signal_connect (item, "toggled",
687 G_CALLBACK (empathy_individual_block_menu_item_toggled), individual);
689 update_block_menu_item (item, individual);
691 g_list_free (contacts);
698 REMOVE_DIALOG_RESPONSE_CANCEL = 0,
699 REMOVE_DIALOG_RESPONSE_DELETE,
700 REMOVE_DIALOG_RESPONSE_DELETE_AND_BLOCK,
701 REMOVE_DIALOG_RESPONSE_REMOVE_FROM_GROUP
705 remove_dialog_show (const gchar *message,
706 const gchar *secondary_text,
707 gboolean show_remove_from_group,
708 gboolean block_button,
710 const gchar *active_group)
715 dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
716 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s", message);
720 GtkWidget *image = gtk_image_new_from_pixbuf (avatar);
721 gtk_message_dialog_set_image (GTK_MESSAGE_DIALOG (dialog), image);
722 gtk_widget_show (image);
725 if (show_remove_from_group)
728 gchar *button_text = g_strdup_printf (_("Remove from _Group \'%s\'"),
731 /* gtk_dialog_add_button() doesn't allow us to pass a string with a
732 * mnemonic so we have to create the button manually. */
733 button = gtk_button_new_with_mnemonic (button_text);
734 g_free (button_text);
736 gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button,
737 REMOVE_DIALOG_RESPONSE_REMOVE_FROM_GROUP);
739 gtk_widget_show (button);
746 /* gtk_dialog_add_button() doesn't allow us to pass a string with a
747 * mnemonic so we have to create the button manually. */
748 button = gtk_button_new_with_mnemonic (
749 _("Delete and _Block"));
751 gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button,
752 REMOVE_DIALOG_RESPONSE_DELETE_AND_BLOCK);
754 gtk_widget_show (button);
757 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
758 GTK_STOCK_CANCEL, REMOVE_DIALOG_RESPONSE_CANCEL,
759 GTK_STOCK_DELETE, REMOVE_DIALOG_RESPONSE_DELETE, NULL);
760 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
761 "%s", secondary_text);
763 gtk_widget_show (dialog);
765 res = gtk_dialog_run (GTK_DIALOG (dialog));
766 gtk_widget_destroy (dialog);
772 individual_removed_from_group_cb (GObject *source_object,
776 GError *error = NULL;
777 FolksIndividual *individual = FOLKS_INDIVIDUAL (source_object);
779 folks_group_details_change_group_finish (
780 FOLKS_GROUP_DETAILS (individual), res, &error);
783 DEBUG ("Individual could not be removed from group: %s",
785 g_error_free (error);
790 remove_got_avatar (GObject *source_object,
791 GAsyncResult *result,
794 FolksIndividual *individual = FOLKS_INDIVIDUAL (source_object);
795 EmpathyIndividualMenu *self = EMPATHY_INDIVIDUAL_MENU (user_data);
796 EmpathyIndividualMenuPriv *priv = GET_PRIV (self);
798 EmpathyIndividualManager *manager;
801 guint persona_count = 0;
803 GError *error = NULL;
805 gboolean show_remove_from_group;
808 avatar = empathy_pixbuf_avatar_from_individual_scaled_finish (individual,
813 DEBUG ("Could not get avatar: %s", error->message);
814 g_error_free (error);
817 /* We couldn't retrieve the avatar, but that isn't a fatal error,
818 * so we still display the remove dialog. */
820 groups = folks_group_details_get_groups (FOLKS_GROUP_DETAILS (individual));
821 show_remove_from_group =
822 gee_collection_get_size (GEE_COLLECTION (groups)) > 1;
824 personas = folks_individual_get_personas (individual);
826 persona_count = gee_collection_get_size (GEE_COLLECTION (personas));
828 /* If we have more than one TpfPersona, display a different message
829 * ensuring the user knows that *all* of the meta-contacts' personas will
832 if (persona_count < 2)
834 /* Not a meta-contact */
837 _("Do you really want to remove the contact '%s'?"),
838 folks_alias_details_get_alias (
839 FOLKS_ALIAS_DETAILS (individual)));
846 _("Do you really want to remove the linked contact '%s'? "
847 "Note that this will remove all the contacts which make up "
848 "this linked contact."),
849 folks_alias_details_get_alias (
850 FOLKS_ALIAS_DETAILS (individual)));
854 manager = empathy_individual_manager_dup_singleton ();
855 can_block = empathy_individual_manager_supports_blocking (manager,
857 res = remove_dialog_show (_("Removing contact"), text,
858 show_remove_from_group, can_block, avatar, priv->active_group);
860 if (res == REMOVE_DIALOG_RESPONSE_REMOVE_FROM_GROUP)
862 folks_group_details_change_group (FOLKS_GROUP_DETAILS (individual),
863 priv->active_group, false, individual_removed_from_group_cb, NULL);
867 if (res == REMOVE_DIALOG_RESPONSE_DELETE ||
868 res == REMOVE_DIALOG_RESPONSE_DELETE_AND_BLOCK)
872 if (res == REMOVE_DIALOG_RESPONSE_DELETE_AND_BLOCK)
874 if (!empathy_block_individual_dialog_show (NULL, individual,
878 empathy_individual_manager_set_blocked (manager, individual,
882 empathy_individual_manager_remove (manager, individual, "");
887 g_object_unref (manager);
888 g_object_unref (self);
892 remove_activate_cb (GtkMenuItem *menuitem,
893 EmpathyIndividualMenu *self)
895 EmpathyIndividualMenuPriv *priv = GET_PRIV (self);
897 empathy_pixbuf_avatar_from_individual_scaled_async (priv->individual,
898 48, 48, NULL, remove_got_avatar, g_object_ref (self));
902 empathy_individiual_remove_menu_item_new (EmpathyIndividualMenu *self)
906 gboolean can_remove = FALSE;
907 GtkWidget *item, *image;
908 EmpathyIndividualMenuPriv *priv = GET_PRIV (self);
910 /* If any of the Individual's personas can be removed, add an option to
911 * remove. This will act as a best-effort option. If any Personas cannot be
912 * removed from the server, then this option will just be inactive upon
913 * subsequent menu openings */
914 personas = folks_individual_get_personas (priv->individual);
915 iter = gee_iterable_iterator (GEE_ITERABLE (personas));
916 while (!can_remove && gee_iterator_next (iter))
918 FolksPersona *persona = gee_iterator_get (iter);
919 FolksPersonaStore *store = folks_persona_get_store (persona);
920 FolksMaybeBool maybe_can_remove =
921 folks_persona_store_get_can_remove_personas (store);
923 if (maybe_can_remove == FOLKS_MAYBE_BOOL_TRUE)
926 g_clear_object (&persona);
928 g_clear_object (&iter);
933 item = gtk_image_menu_item_new_with_mnemonic (_("_Remove"));
934 image = gtk_image_new_from_icon_name (GTK_STOCK_REMOVE,
936 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
938 g_signal_connect (item, "activate",
939 G_CALLBACK (remove_activate_cb), self);
945 constructed (GObject *object)
947 EmpathyIndividualMenu *self = EMPATHY_INDIVIDUAL_MENU (object);
948 EmpathyIndividualMenuPriv *priv = GET_PRIV (object);
951 FolksIndividual *individual;
952 EmpathyIndividualFeatureFlags features;
955 shell = GTK_MENU_SHELL (object);
956 individual = priv->individual;
957 features = priv->features;
960 if (features & EMPATHY_INDIVIDUAL_FEATURE_ADD_CONTACT)
962 item = empathy_individual_add_menu_item_new (self, individual);
965 gtk_menu_shell_append (GTK_MENU_SHELL (shell), item);
966 gtk_widget_show (item);
971 if (features & EMPATHY_INDIVIDUAL_FEATURE_CHAT)
973 item = empathy_individual_chat_menu_item_new (self, individual);
976 gtk_menu_shell_append (shell, item);
977 gtk_widget_show (item);
982 if (features & EMPATHY_INDIVIDUAL_FEATURE_SMS)
984 item = empathy_individual_sms_menu_item_new (self, individual);
987 gtk_menu_shell_append (shell, item);
988 gtk_widget_show (item);
992 if (features & EMPATHY_INDIVIDUAL_FEATURE_CALL)
995 item = empathy_individual_audio_call_menu_item_new (self, individual);
996 gtk_menu_shell_append (shell, item);
997 gtk_widget_show (item);
1000 item = empathy_individual_video_call_menu_item_new (self, individual);
1001 gtk_menu_shell_append (shell, item);
1002 gtk_widget_show (item);
1005 if (features & EMPATHY_INDIVIDUAL_FEATURE_CALL_PHONE)
1006 add_phone_numbers (self);
1009 item = empathy_individual_invite_menu_item_new (individual, NULL);
1010 gtk_menu_shell_append (shell, item);
1011 gtk_widget_show (item);
1014 if (features & EMPATHY_INDIVIDUAL_FEATURE_FILE_TRANSFER)
1016 item = empathy_individual_file_transfer_menu_item_new (self, individual);
1017 gtk_menu_shell_append (shell, item);
1018 gtk_widget_show (item);
1021 /* Share my desktop */
1022 /* FIXME we should add the "Share my desktop" menu item if Vino is
1023 a registered handler in MC5 */
1024 item = empathy_individual_share_my_desktop_menu_item_new (self, individual);
1025 gtk_menu_shell_append (shell, item);
1026 gtk_widget_show (item);
1028 /* Menu items to target specific contacts */
1029 individual_menu_add_personas (self, GTK_MENU_SHELL (object),
1030 individual, features);
1033 if (features & (EMPATHY_INDIVIDUAL_FEATURE_EDIT |
1034 EMPATHY_INDIVIDUAL_FEATURE_INFO |
1035 EMPATHY_INDIVIDUAL_FEATURE_FAVOURITE))
1037 item = gtk_separator_menu_item_new ();
1038 gtk_menu_shell_append (shell, item);
1039 gtk_widget_show (item);
1043 if (features & EMPATHY_INDIVIDUAL_FEATURE_EDIT)
1045 item = empathy_individual_edit_menu_item_new (individual);
1046 gtk_menu_shell_append (shell, item);
1047 gtk_widget_show (item);
1051 if (features & EMPATHY_INDIVIDUAL_FEATURE_LOG)
1053 item = empathy_individual_log_menu_item_new (individual);
1054 gtk_menu_shell_append (shell, item);
1055 gtk_widget_show (item);
1059 if (features & EMPATHY_INDIVIDUAL_FEATURE_INFO)
1061 item = empathy_individual_info_menu_item_new (individual);
1062 gtk_menu_shell_append (shell, item);
1063 gtk_widget_show (item);
1066 /* Favorite checkbox */
1067 if (features & EMPATHY_INDIVIDUAL_FEATURE_FAVOURITE)
1069 item = empathy_individual_favourite_menu_item_new (individual);
1070 gtk_menu_shell_append (shell, item);
1071 gtk_widget_show (item);
1074 /* Separator & Block */
1075 if (features & EMPATHY_INDIVIDUAL_FEATURE_BLOCK &&
1076 (item = empathy_individiual_block_menu_item_new (individual)) != NULL) {
1079 sep = gtk_separator_menu_item_new ();
1080 gtk_menu_shell_append (shell, sep);
1081 gtk_widget_show (sep);
1083 gtk_menu_shell_append (shell, item);
1084 gtk_widget_show (item);
1087 /* Separator & Remove */
1088 if (features & EMPATHY_INDIVIDUAL_FEATURE_REMOVE &&
1089 (item = empathy_individiual_remove_menu_item_new (self)) != NULL) {
1092 sep = gtk_separator_menu_item_new ();
1093 gtk_menu_shell_append (shell, sep);
1094 gtk_widget_show (sep);
1096 gtk_menu_shell_append (shell, item);
1097 gtk_widget_show (item);
1102 get_property (GObject *object,
1107 EmpathyIndividualMenuPriv *priv;
1109 priv = GET_PRIV (object);
1113 case PROP_ACTIVE_GROUP:
1114 g_value_set_string (value, priv->active_group);
1116 case PROP_INDIVIDUAL:
1117 g_value_set_object (value, priv->individual);
1120 g_value_set_flags (value, priv->features);
1123 g_value_set_object (value, priv->store);
1126 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1132 set_property (GObject *object,
1134 const GValue *value,
1137 EmpathyIndividualMenuPriv *priv;
1139 priv = GET_PRIV (object);
1143 case PROP_ACTIVE_GROUP:
1144 g_assert (priv->active_group == NULL); /* construct only */
1145 priv->active_group = g_value_dup_string (value);
1147 case PROP_INDIVIDUAL:
1148 priv->individual = g_value_dup_object (value);
1151 priv->features = g_value_get_flags (value);
1154 priv->store = g_value_dup_object (value); /* read only */
1157 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1163 dispose (GObject *object)
1165 EmpathyIndividualMenuPriv *priv = GET_PRIV (object);
1167 tp_clear_object (&priv->individual);
1168 tp_clear_object (&priv->store);
1170 G_OBJECT_CLASS (empathy_individual_menu_parent_class)->dispose (object);
1174 finalize (GObject *object)
1176 EmpathyIndividualMenuPriv *priv = GET_PRIV (object);
1178 g_free (priv->active_group);
1180 G_OBJECT_CLASS (empathy_individual_menu_parent_class)->finalize (object);
1184 empathy_individual_menu_class_init (EmpathyIndividualMenuClass *klass)
1186 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1188 object_class->constructed = constructed;
1189 object_class->get_property = get_property;
1190 object_class->set_property = set_property;
1191 object_class->dispose = dispose;
1192 object_class->finalize = finalize;
1195 * gchar *:active-group:
1197 * The group the selected roster-contact widget belongs, or NULL.
1199 g_object_class_install_property (object_class, PROP_ACTIVE_GROUP,
1200 g_param_spec_string ("active-group",
1202 "The group the selected roster-contact widget belongs, or NULL",
1204 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
1207 * EmpathyIndividualMenu:individual:
1209 * The #FolksIndividual the menu is for.
1211 g_object_class_install_property (object_class, PROP_INDIVIDUAL,
1212 g_param_spec_object ("individual",
1214 "The #FolksIndividual the menu is for.",
1215 FOLKS_TYPE_INDIVIDUAL,
1216 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
1219 * EmpathyIndividualMenu:features:
1221 * A set of feature flags controlling which entries are shown.
1223 g_object_class_install_property (object_class, PROP_FEATURES,
1224 g_param_spec_flags ("features",
1226 "A set of feature flags controlling which entries are shown.",
1227 EMPATHY_TYPE_INDIVIDUAL_FEATURE_FLAGS,
1228 EMPATHY_INDIVIDUAL_FEATURE_NONE,
1229 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
1231 g_object_class_install_property (object_class, PROP_STORE,
1232 g_param_spec_object ("store",
1234 "The EmpathyIndividualStore to use to get contact owner",
1235 EMPATHY_TYPE_INDIVIDUAL_STORE,
1236 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
1238 signals[MENU_ITEM_ACTIVATED] =
1239 g_signal_new ("menu-item-activated",
1240 G_TYPE_FROM_CLASS (klass),
1244 g_cclosure_marshal_generic,
1248 g_type_class_add_private (object_class, sizeof (EmpathyIndividualMenuPriv));
1252 empathy_individual_menu_new (FolksIndividual *individual,
1253 const gchar *active_group,
1254 EmpathyIndividualFeatureFlags features,
1255 EmpathyIndividualStore *store)
1257 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
1258 g_return_val_if_fail (store == NULL ||
1259 EMPATHY_IS_INDIVIDUAL_STORE (store), NULL);
1260 g_return_val_if_fail (features != EMPATHY_INDIVIDUAL_FEATURE_NONE, NULL);
1262 return g_object_new (EMPATHY_TYPE_INDIVIDUAL_MENU,
1263 "active-group", active_group,
1264 "individual", individual,
1265 "features", features,
1270 /* Like menu_item_set_first_contact(), but always operates upon the given
1271 * contact. If the contact is non-NULL, it is assumed that the menu entry should
1274 menu_item_set_contact (GtkWidget *item,
1275 EmpathyContact *contact,
1276 GCallback activate_callback,
1277 EmpathyActionType action_type)
1279 gboolean can_do_action = FALSE;
1281 if (contact != NULL)
1282 can_do_action = empathy_contact_can_do_action (contact, action_type);
1283 gtk_widget_set_sensitive (item, can_do_action);
1285 if (can_do_action == TRUE)
1287 /* We want to make sure that the EmpathyContact stays alive while the
1288 * signal is connected. */
1289 g_signal_connect_data (item, "activate", G_CALLBACK (activate_callback),
1290 g_object_ref (contact), (GClosureNotify) g_object_unref, 0);
1293 return can_do_action;
1297 * Set the given menu @item to call @activate_callback using the TpContact
1298 * (associated with @individual) with the highest availability who is also valid
1299 * whenever @item is activated.
1301 * @action_type is the type of action performed by the menu entry; this is used
1302 * so that only contacts which can perform that action (e.g. are capable of
1303 * receiving video calls) are selected, as appropriate.
1306 menu_item_set_first_contact (GtkWidget *item,
1307 FolksIndividual *individual,
1308 GCallback activate_callback,
1309 EmpathyActionType action_type)
1311 EmpathyContact *best_contact;
1313 best_contact = empathy_contact_dup_best_for_action (individual, action_type);
1314 menu_item_set_contact (item, best_contact, G_CALLBACK (activate_callback),
1316 tp_clear_object (&best_contact);
1322 emit_menu_item_activated (GtkMenuItem *item)
1324 EmpathyIndividualMenu *self;
1326 self = EMPATHY_INDIVIDUAL_MENU (g_object_get_data (G_OBJECT (item),
1327 "individual-menu"));
1328 g_signal_emit (self, signals [MENU_ITEM_ACTIVATED], 0);
1332 empathy_individual_chat_menu_item_activated (GtkMenuItem *item,
1333 EmpathyContact *contact)
1335 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1337 empathy_chat_with_contact (contact, empathy_get_current_action_time ());
1339 emit_menu_item_activated (item);
1343 empathy_individual_chat_menu_item_new (EmpathyIndividualMenu *self,
1344 FolksIndividual *individual)
1349 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual) &&
1350 empathy_folks_individual_contains_contact (individual), NULL);
1352 item = gtk_image_menu_item_new_with_mnemonic (_("_Chat"));
1353 image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_MESSAGE,
1354 GTK_ICON_SIZE_MENU);
1355 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1356 gtk_widget_show (image);
1358 g_object_set_data (G_OBJECT (item), "individual-menu", self);
1359 menu_item_set_first_contact (item, individual,
1360 G_CALLBACK (empathy_individual_chat_menu_item_activated),
1361 EMPATHY_ACTION_CHAT);
1367 empathy_individual_sms_menu_item_activated (GtkMenuItem *item,
1368 EmpathyContact *contact)
1370 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1372 empathy_sms_contact_id (
1373 empathy_contact_get_account (contact),
1374 empathy_contact_get_id (contact),
1375 empathy_get_current_action_time (),
1378 emit_menu_item_activated (item);
1382 empathy_individual_sms_menu_item_new (EmpathyIndividualMenu *self,
1383 FolksIndividual *individual)
1388 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual) &&
1389 empathy_folks_individual_contains_contact (individual), NULL);
1391 item = gtk_image_menu_item_new_with_mnemonic (_("_SMS"));
1392 image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_SMS,
1393 GTK_ICON_SIZE_MENU);
1394 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1395 gtk_widget_show (image);
1397 g_object_set_data (G_OBJECT (item), "individual-menu", self);
1398 menu_item_set_first_contact (item, individual,
1399 G_CALLBACK (empathy_individual_sms_menu_item_activated),
1400 EMPATHY_ACTION_SMS);
1406 empathy_individual_audio_call_menu_item_activated (GtkMenuItem *item,
1407 EmpathyContact *contact)
1409 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1411 empathy_call_new_with_streams (empathy_contact_get_id (contact),
1412 empathy_contact_get_account (contact),
1414 empathy_get_current_action_time ());
1416 emit_menu_item_activated (item);
1420 empathy_individual_audio_call_menu_item_new (EmpathyIndividualMenu *self,
1421 FolksIndividual *individual)
1426 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
1428 item = gtk_image_menu_item_new_with_mnemonic (C_("menu item", "_Audio Call"));
1429 image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_VOIP, GTK_ICON_SIZE_MENU);
1430 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1431 gtk_widget_show (image);
1433 g_object_set_data (G_OBJECT (item), "individual-menu", self);
1434 menu_item_set_first_contact (item, individual,
1435 G_CALLBACK (empathy_individual_audio_call_menu_item_activated),
1436 EMPATHY_ACTION_AUDIO_CALL);
1442 empathy_individual_video_call_menu_item_activated (GtkMenuItem *item,
1443 EmpathyContact *contact)
1445 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1447 empathy_call_new_with_streams (empathy_contact_get_id (contact),
1448 empathy_contact_get_account (contact),
1450 empathy_get_current_action_time ());
1452 emit_menu_item_activated (item);
1456 empathy_individual_video_call_menu_item_new (EmpathyIndividualMenu *self,
1457 FolksIndividual *individual)
1461 TpawCameraMonitor *monitor;
1463 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
1465 item = gtk_image_menu_item_new_with_mnemonic (C_("menu item", "_Video Call"));
1466 image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_VIDEO_CALL,
1467 GTK_ICON_SIZE_MENU);
1468 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1469 gtk_widget_show (image);
1471 g_object_set_data (G_OBJECT (item), "individual-menu", self);
1472 menu_item_set_first_contact (item, individual,
1473 G_CALLBACK (empathy_individual_video_call_menu_item_activated),
1474 EMPATHY_ACTION_VIDEO_CALL);
1476 /* Only follow available cameras if the contact can do Video calls */
1477 if (gtk_widget_get_sensitive (item))
1479 monitor = tpaw_camera_monitor_dup_singleton ();
1480 g_object_set_data_full (G_OBJECT (item),
1481 "monitor", monitor, g_object_unref);
1482 g_object_bind_property (monitor, "available", item, "sensitive",
1483 G_BINDING_SYNC_CREATE);
1490 empathy_individual_log_menu_item_activated (GtkMenuItem *item,
1491 EmpathyContact *contact)
1493 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1495 empathy_log_window_show (empathy_contact_get_account (contact),
1496 empathy_contact_get_id (contact), FALSE, NULL);
1500 empathy_individual_log_menu_item_new (FolksIndividual *individual)
1505 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
1507 item = gtk_image_menu_item_new_with_mnemonic (_("_Previous Conversations"));
1508 image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_LOG, GTK_ICON_SIZE_MENU);
1509 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1510 gtk_widget_show (image);
1512 menu_item_set_first_contact (item, individual,
1513 G_CALLBACK (empathy_individual_log_menu_item_activated),
1514 EMPATHY_ACTION_VIEW_LOGS);
1520 empathy_individual_file_transfer_menu_item_activated (GtkMenuItem *item,
1521 EmpathyContact *contact)
1523 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1525 empathy_send_file_with_file_chooser (contact);
1527 emit_menu_item_activated (item);
1531 empathy_individual_file_transfer_menu_item_new (EmpathyIndividualMenu *self,
1532 FolksIndividual *individual)
1537 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
1539 item = gtk_image_menu_item_new_with_mnemonic (_("Send File"));
1540 image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_DOCUMENT_SEND,
1541 GTK_ICON_SIZE_MENU);
1542 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1543 gtk_widget_show (image);
1545 g_object_set_data (G_OBJECT (item), "individual-menu", self);
1546 menu_item_set_first_contact (item, individual,
1547 G_CALLBACK (empathy_individual_file_transfer_menu_item_activated),
1548 EMPATHY_ACTION_SEND_FILE);
1554 empathy_individual_share_my_desktop_menu_item_activated (GtkMenuItem *item,
1555 EmpathyContact *contact)
1557 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1559 empathy_share_my_desktop_share_with_contact (contact);
1561 emit_menu_item_activated (item);
1565 empathy_individual_share_my_desktop_menu_item_new (EmpathyIndividualMenu *self,
1566 FolksIndividual *individual)
1571 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
1573 item = gtk_image_menu_item_new_with_mnemonic (_("Share My Desktop"));
1574 image = gtk_image_new_from_icon_name (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU);
1575 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1576 gtk_widget_show (image);
1578 g_object_set_data (G_OBJECT (item), "individual-menu", self);
1579 menu_item_set_first_contact (item, individual,
1580 G_CALLBACK (empathy_individual_share_my_desktop_menu_item_activated),
1581 EMPATHY_ACTION_SHARE_MY_DESKTOP);
1587 favourite_menu_item_toggled_cb (GtkCheckMenuItem *item,
1588 FolksIndividual *individual)
1590 folks_favourite_details_set_is_favourite (
1591 FOLKS_FAVOURITE_DETAILS (individual),
1592 gtk_check_menu_item_get_active (item));
1596 empathy_individual_favourite_menu_item_new (FolksIndividual *individual)
1600 item = gtk_check_menu_item_new_with_label (_("Favorite"));
1602 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
1603 folks_favourite_details_get_is_favourite (
1604 FOLKS_FAVOURITE_DETAILS (individual)));
1606 g_signal_connect (item, "toggled",
1607 G_CALLBACK (favourite_menu_item_toggled_cb), individual);
1613 individual_info_menu_item_activate_cb (GtkMenuItem *item,
1614 FolksIndividual *individual)
1616 empathy_display_individual_info (individual);
1620 empathy_individual_info_menu_item_new (FolksIndividual *individual)
1625 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
1626 g_return_val_if_fail (empathy_folks_individual_contains_contact (individual),
1629 item = gtk_image_menu_item_new_with_mnemonic (_("Infor_mation"));
1630 image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_CONTACT_INFORMATION,
1631 GTK_ICON_SIZE_MENU);
1632 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1633 gtk_widget_show (image);
1635 g_signal_connect (item, "activate",
1636 G_CALLBACK (individual_info_menu_item_activate_cb),
1643 individual_edit_menu_item_activate_cb (FolksIndividual *individual)
1645 empathy_individual_edit_dialog_show (individual, NULL);
1649 empathy_individual_edit_menu_item_new (FolksIndividual *individual)
1651 EmpathyIndividualManager *manager;
1654 gboolean enable = FALSE;
1655 EmpathyContact *contact;
1657 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
1659 contact = empathy_contact_dup_from_folks_individual (individual);
1661 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
1663 if (empathy_individual_manager_initialized ())
1665 TpConnection *connection;
1667 manager = empathy_individual_manager_dup_singleton ();
1668 connection = empathy_contact_get_connection (contact);
1670 enable = (empathy_connection_can_alias_personas (connection,
1672 empathy_connection_can_group_personas (connection, individual));
1674 g_object_unref (manager);
1677 item = gtk_image_menu_item_new_with_mnemonic (
1678 C_("Edit individual (contextual menu)", "_Edit"));
1679 image = gtk_image_new_from_icon_name (GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU);
1680 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1681 gtk_widget_show (image);
1683 gtk_widget_set_sensitive (item, enable);
1685 g_signal_connect_swapped (item, "activate",
1686 G_CALLBACK (individual_edit_menu_item_activate_cb), individual);
1688 g_object_unref (contact);
1695 FolksIndividual *individual;
1696 EmpathyContact *contact;
1697 EmpathyChatroom *chatroom;
1700 static RoomSubMenuData *
1701 room_sub_menu_data_new (FolksIndividual *individual,
1702 EmpathyContact *contact,
1703 EmpathyChatroom *chatroom)
1705 RoomSubMenuData *data;
1707 data = g_slice_new0 (RoomSubMenuData);
1708 if (individual != NULL)
1709 data->individual = g_object_ref (individual);
1710 if (contact != NULL)
1711 data->contact = g_object_ref (contact);
1712 data->chatroom = g_object_ref (chatroom);
1718 room_sub_menu_data_free (RoomSubMenuData *data)
1720 tp_clear_object (&data->individual);
1721 tp_clear_object (&data->contact);
1722 g_object_unref (data->chatroom);
1723 g_slice_free (RoomSubMenuData, data);
1727 room_sub_menu_activate_cb (GtkWidget *item,
1728 RoomSubMenuData *data)
1730 EmpathyTpChat *chat;
1731 EmpathyChatroomManager *mgr;
1732 EmpathyContact *contact = NULL;
1734 chat = empathy_chatroom_get_tp_chat (data->chatroom);
1737 /* channel was invalidated. Ignoring */
1741 mgr = empathy_chatroom_manager_dup_singleton (NULL);
1743 if (data->contact != NULL)
1744 contact = g_object_ref (data->contact);
1750 /* find the first of this Individual's contacts who can join this room */
1751 personas = folks_individual_get_personas (data->individual);
1753 iter = gee_iterable_iterator (GEE_ITERABLE (personas));
1754 while (gee_iterator_next (iter) && (contact == NULL))
1756 TpfPersona *persona = gee_iterator_get (iter);
1757 TpContact *tp_contact;
1760 if (empathy_folks_persona_is_interesting (FOLKS_PERSONA (persona)))
1762 tp_contact = tpf_persona_get_contact (persona);
1763 if (tp_contact != NULL)
1765 contact = empathy_contact_dup_from_tp_contact (tp_contact);
1767 rooms = empathy_chatroom_manager_get_chatrooms (mgr,
1768 empathy_contact_get_account (contact));
1770 if (g_list_find (rooms, data->chatroom) == NULL)
1771 g_clear_object (&contact);
1773 /* if contact != NULL here, we've found our match */
1775 g_list_free (rooms);
1778 g_clear_object (&persona);
1780 g_clear_object (&iter);
1783 g_object_unref (mgr);
1785 if (contact == NULL)
1787 /* contact disappeared. Ignoring */
1791 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1793 /* send invitation */
1794 empathy_tp_chat_add (chat, contact, _("Inviting you to this room"));
1797 g_object_unref (contact);
1801 create_room_sub_menu (FolksIndividual *individual,
1802 EmpathyContact *contact,
1803 EmpathyChatroom *chatroom)
1806 RoomSubMenuData *data;
1808 item = gtk_menu_item_new_with_label (empathy_chatroom_get_name (chatroom));
1809 data = room_sub_menu_data_new (individual, contact, chatroom);
1810 g_signal_connect_data (item, "activate",
1811 G_CALLBACK (room_sub_menu_activate_cb), data,
1812 (GClosureNotify) room_sub_menu_data_free, 0);
1818 empathy_individual_invite_menu_item_new (FolksIndividual *individual,
1819 EmpathyContact *contact)
1823 GtkWidget *room_item;
1824 EmpathyChatroomManager *mgr;
1825 GList *rooms = NULL;
1826 GList *names = NULL;
1828 GtkWidget *submenu = NULL;
1829 /* map of chat room names to their objects; just a utility to remove
1830 * duplicates and to make construction of the alphabetized list easier */
1831 GHashTable *name_room_map;
1833 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual) ||
1834 EMPATHY_IS_CONTACT (contact),
1837 name_room_map = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
1840 item = gtk_image_menu_item_new_with_mnemonic (_("_Invite to Chat Room"));
1841 image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_GROUP_MESSAGE,
1842 GTK_ICON_SIZE_MENU);
1843 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1845 mgr = empathy_chatroom_manager_dup_singleton (NULL);
1847 if (contact != NULL)
1849 rooms = empathy_chatroom_manager_get_chatrooms (mgr,
1850 empathy_contact_get_account (contact));
1857 /* find the first of this Individual's contacts who can join this room */
1858 personas = folks_individual_get_personas (individual);
1859 iter = gee_iterable_iterator (GEE_ITERABLE (personas));
1860 while (gee_iterator_next (iter))
1862 TpfPersona *persona = gee_iterator_get (iter);
1864 TpContact *tp_contact;
1865 EmpathyContact *contact_cur;
1867 if (empathy_folks_persona_is_interesting (FOLKS_PERSONA (persona)))
1869 tp_contact = tpf_persona_get_contact (persona);
1870 if (tp_contact != NULL)
1872 contact_cur = empathy_contact_dup_from_tp_contact (
1875 rooms_cur = empathy_chatroom_manager_get_chatrooms (mgr,
1876 empathy_contact_get_account (contact_cur));
1877 rooms = g_list_concat (rooms, rooms_cur);
1879 g_object_unref (contact_cur);
1882 g_clear_object (&persona);
1884 g_clear_object (&iter);
1887 /* alphabetize the rooms */
1888 for (l = rooms; l != NULL; l = g_list_next (l))
1890 EmpathyChatroom *chatroom = l->data;
1893 if (empathy_chatroom_get_tp_chat (chatroom) != NULL)
1897 name = empathy_chatroom_get_name (chatroom);
1898 existed = (g_hash_table_lookup (name_room_map, name) != NULL);
1899 g_hash_table_insert (name_room_map, (gpointer) name,
1900 g_object_ref (chatroom));
1902 /* this will take care of duplicates in rooms */
1905 names = g_list_insert_sorted (names, (gpointer) name,
1906 (GCompareFunc) g_strcmp0);
1911 for (l = names; l != NULL; l = g_list_next (l))
1913 const gchar *name = l->data;
1914 EmpathyChatroom *chatroom;
1916 if (G_UNLIKELY (submenu == NULL))
1917 submenu = gtk_menu_new ();
1919 chatroom = g_hash_table_lookup (name_room_map, name);
1920 room_item = create_room_sub_menu (individual, contact, chatroom);
1921 gtk_menu_shell_append ((GtkMenuShell *) submenu, room_item);
1922 gtk_widget_show (room_item);
1926 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
1928 gtk_widget_set_sensitive (item, FALSE);
1930 gtk_widget_show (image);
1932 g_hash_table_unref (name_room_map);
1933 g_object_unref (mgr);
1934 g_list_free (names);
1935 g_list_free (rooms);
1941 add_menu_item_activated (GtkMenuItem *item,
1942 TpContact *tp_contact)
1944 GtkWidget *toplevel;
1945 FolksIndividual *individual;
1947 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (item));
1948 if (!gtk_widget_is_toplevel (toplevel) || !GTK_IS_WINDOW (toplevel))
1951 individual = empathy_ensure_individual_from_tp_contact (tp_contact);
1953 empathy_new_individual_dialog_show_with_individual (GTK_WINDOW (toplevel),
1956 g_object_unref (individual);
1960 empathy_individual_add_menu_item_new (EmpathyIndividualMenu *self,
1961 FolksIndividual *individual)
1963 EmpathyIndividualMenuPriv *priv = GET_PRIV (self);
1964 GtkWidget *item, *image;
1967 TpContact *to_add = NULL;
1969 /* find the first of this Individual's personas which are not in our contact
1971 personas = folks_individual_get_personas (individual);
1972 iter = gee_iterable_iterator (GEE_ITERABLE (personas));
1973 while (gee_iterator_next (iter))
1975 TpfPersona *persona = gee_iterator_get (iter);
1979 if (!TPF_IS_PERSONA (persona))
1982 contact = tpf_persona_get_contact (persona);
1983 if (contact == NULL)
1986 /* be sure to use a not channel specific contact.
1987 * TODO: Ideally tp-glib should do this for us (fdo #42702)*/
1988 if (EMPATHY_IS_INDIVIDUAL_STORE_CHANNEL (priv->store))
1991 TpChannelGroupFlags flags;
1993 channel = empathy_individual_store_channel_get_channel (
1994 EMPATHY_INDIVIDUAL_STORE_CHANNEL (priv->store));
1996 flags = tp_channel_group_get_flags (channel);
1997 if ((flags & TP_CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES) != 0)
1999 /* Channel uses channel specific handles (thanks XMPP...) */
2000 contact = tp_channel_group_get_contact_owner (channel, contact);
2002 /* If we don't know the owner, we can't add the contact */
2003 if (contact == NULL)
2008 conn = tp_contact_get_connection (contact);
2012 /* No point to try adding a contact if the CM doesn't support it */
2013 if (!tp_connection_get_can_change_contact_list (conn))
2016 /* Can't add ourself */
2017 if (tp_connection_get_self_contact (conn) == contact)
2020 if (tp_contact_get_subscribe_state (contact) == TP_SUBSCRIPTION_STATE_YES)
2023 g_object_unref (persona);
2028 g_object_unref (persona);
2031 g_object_unref (iter);
2036 item = gtk_image_menu_item_new_with_mnemonic (_("_Add Contact…"));
2037 image = gtk_image_new_from_icon_name (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU);
2038 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
2040 g_signal_connect_data (item, "activate",
2041 G_CALLBACK (add_menu_item_activated),
2042 g_object_ref (to_add), (GClosureNotify) g_object_unref, 0);