1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2005-2007 Imendio AB
4 * Copyright (C) 2007-2008 Collabora Ltd.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301 USA
21 * Authors: Mikael Hallendal <micke@imendio.com>
22 * Martyn Russell <martyn@imendio.com>
23 * Xavier Claessens <xclaesse@gmail.com>
30 #include <glib/gi18n-lib.h>
31 #include <gdk/gdkkeysyms.h>
34 #include <telepathy-glib/account-manager.h>
35 #include <telepathy-glib/util.h>
37 #include <libempathy/empathy-call-factory.h>
38 #include <libempathy/empathy-tp-contact-factory.h>
39 #include <libempathy/empathy-contact-list.h>
40 #include <libempathy/empathy-contact-groups.h>
41 #include <libempathy/empathy-dispatcher.h>
42 #include <libempathy/empathy-utils.h>
44 #include "empathy-contact-list-view.h"
45 #include "empathy-contact-list-store.h"
46 #include "empathy-images.h"
47 #include "empathy-cell-renderer-expander.h"
48 #include "empathy-cell-renderer-text.h"
49 #include "empathy-cell-renderer-activatable.h"
50 #include "empathy-ui-utils.h"
51 #include "empathy-gtk-enum-types.h"
52 #include "empathy-gtk-marshal.h"
54 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
55 #include <libempathy/empathy-debug.h>
57 /* Active users are those which have recently changed state
58 * (e.g. online, offline or from normal to a busy state).
61 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyContactListView)
63 EmpathyContactListStore *store;
64 GtkTreeRowReference *drag_row;
65 EmpathyContactListFeatureFlags list_features;
66 EmpathyContactFeatureFlags contact_features;
67 GtkWidget *tooltip_widget;
68 GtkTargetList *file_targets;
69 } EmpathyContactListViewPriv;
72 EmpathyContactListView *view;
78 EmpathyContactListView *view;
79 EmpathyContact *contact;
87 PROP_CONTACT_FEATURES,
91 DND_DRAG_TYPE_CONTACT_ID,
92 DND_DRAG_TYPE_URI_LIST,
96 static const GtkTargetEntry drag_types_dest[] = {
97 { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
98 { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
99 { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
100 { "text/plain", 0, DND_DRAG_TYPE_STRING },
101 { "STRING", 0, DND_DRAG_TYPE_STRING },
104 static const GtkTargetEntry drag_types_dest_file[] = {
105 { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
106 { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
109 static const GtkTargetEntry drag_types_source[] = {
110 { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
113 static GdkAtom drag_atoms_dest[G_N_ELEMENTS (drag_types_dest)];
114 static GdkAtom drag_atoms_source[G_N_ELEMENTS (drag_types_source)];
117 DRAG_CONTACT_RECEIVED,
121 static guint signals[LAST_SIGNAL];
123 G_DEFINE_TYPE (EmpathyContactListView, empathy_contact_list_view, GTK_TYPE_TREE_VIEW);
126 contact_list_view_tooltip_destroy_cb (GtkWidget *widget,
127 EmpathyContactListView *view)
129 EmpathyContactListViewPriv *priv = GET_PRIV (view);
131 if (priv->tooltip_widget) {
132 DEBUG ("Tooltip destroyed");
133 g_object_unref (priv->tooltip_widget);
134 priv->tooltip_widget = NULL;
139 contact_list_view_query_tooltip_cb (EmpathyContactListView *view,
142 gboolean keyboard_mode,
146 EmpathyContactListViewPriv *priv = GET_PRIV (view);
147 EmpathyContact *contact;
151 static gint running = 0;
152 gboolean ret = FALSE;
154 /* Avoid an infinite loop. See GNOME bug #574377 */
160 /* Don't show the tooltip if there's already a popup menu */
161 if (gtk_menu_get_for_attach_widget (GTK_WIDGET (view)) != NULL) {
165 if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (view), &x, &y,
167 &model, &path, &iter)) {
171 gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (view), tooltip, path);
172 gtk_tree_path_free (path);
174 gtk_tree_model_get (model, &iter,
175 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
181 if (!priv->tooltip_widget) {
182 priv->tooltip_widget = empathy_contact_widget_new (contact,
183 EMPATHY_CONTACT_WIDGET_FOR_TOOLTIP |
184 EMPATHY_CONTACT_WIDGET_SHOW_LOCATION);
185 gtk_container_set_border_width (
186 GTK_CONTAINER (priv->tooltip_widget), 8);
187 g_object_ref (priv->tooltip_widget);
188 g_signal_connect (priv->tooltip_widget, "destroy",
189 G_CALLBACK (contact_list_view_tooltip_destroy_cb),
191 gtk_widget_show (priv->tooltip_widget);
193 empathy_contact_widget_set_contact (priv->tooltip_widget,
197 gtk_tooltip_set_custom (tooltip, priv->tooltip_widget);
200 g_object_unref (contact);
210 GdkDragAction action;
214 contact_list_view_dnd_get_contact_free (DndGetContactData *data)
216 g_free (data->new_group);
217 g_free (data->old_group);
218 g_slice_free (DndGetContactData, data);
222 contact_list_view_drag_got_contact (EmpathyTpContactFactory *factory,
223 EmpathyContact *contact,
228 EmpathyContactListViewPriv *priv = GET_PRIV (view);
229 DndGetContactData *data = user_data;
230 EmpathyContactList *list;
233 DEBUG ("Error: %s", error->message);
237 DEBUG ("contact %s (%d) dragged from '%s' to '%s'",
238 empathy_contact_get_id (contact),
239 empathy_contact_get_handle (contact),
240 data->old_group, data->new_group);
242 list = empathy_contact_list_store_get_list_iface (priv->store);
244 if (!tp_strdiff (data->new_group, EMPATHY_CONTACT_LIST_STORE_FAVORITE)) {
245 /* Mark contact as favourite */
246 empathy_contact_list_add_to_favourites (list, contact);
250 if (!tp_strdiff (data->old_group, EMPATHY_CONTACT_LIST_STORE_FAVORITE)) {
251 /* Remove contact as favourite */
252 empathy_contact_list_remove_from_favourites (list, contact);
253 /* Don't try to remove it */
254 g_free (data->old_group);
255 data->old_group = NULL;
258 if (data->new_group) {
259 empathy_contact_list_add_to_group (list, contact, data->new_group);
261 if (data->old_group && data->action == GDK_ACTION_MOVE) {
262 empathy_contact_list_remove_from_group (list, contact, data->old_group);
267 group_can_be_modified (const gchar *name,
268 gboolean is_fake_group,
271 /* Real groups can always be modified */
275 /* The favorite fake group can be modified so users can
276 * add/remove favorites using DnD */
277 if (!tp_strdiff (name, EMPATHY_CONTACT_LIST_STORE_FAVORITE))
280 /* We can remove contacts from the 'ungrouped' fake group */
281 if (!adding && !tp_strdiff (name, EMPATHY_CONTACT_LIST_STORE_UNGROUPED))
288 contact_list_view_contact_drag_received (GtkWidget *view,
289 GdkDragContext *context,
292 GtkSelectionData *selection)
294 EmpathyContactListViewPriv *priv;
295 TpAccountManager *account_manager;
296 EmpathyTpContactFactory *factory = NULL;
298 DndGetContactData *data;
299 GtkTreePath *source_path;
300 const gchar *sel_data;
302 const gchar *account_id = NULL;
303 const gchar *contact_id = NULL;
304 gchar *new_group = NULL;
305 gchar *old_group = NULL;
306 gboolean success = TRUE;
307 gboolean new_group_is_fake, old_group_is_fake = TRUE;
309 priv = GET_PRIV (view);
311 sel_data = (const gchar *) gtk_selection_data_get_data (selection);
312 new_group = empathy_contact_list_store_get_parent_group (model,
313 path, NULL, &new_group_is_fake);
315 if (!group_can_be_modified (new_group, new_group_is_fake, TRUE))
318 /* Get source group information. */
319 if (priv->drag_row) {
320 source_path = gtk_tree_row_reference_get_path (priv->drag_row);
322 old_group = empathy_contact_list_store_get_parent_group (
323 model, source_path, NULL, &old_group_is_fake);
324 gtk_tree_path_free (source_path);
328 if (!group_can_be_modified (old_group, old_group_is_fake, FALSE))
331 if (!tp_strdiff (old_group, new_group)) {
337 account_manager = tp_account_manager_dup ();
338 strv = g_strsplit (sel_data, ":", 2);
339 if (g_strv_length (strv) == 2) {
340 account_id = strv[0];
341 contact_id = strv[1];
342 account = tp_account_manager_ensure_account (account_manager, account_id);
345 TpConnection *connection;
347 connection = tp_account_get_connection (account);
349 factory = empathy_tp_contact_factory_dup_singleton (connection);
352 g_object_unref (account_manager);
355 DEBUG ("Failed to get factory for account '%s'", account_id);
362 data = g_slice_new0 (DndGetContactData);
363 data->new_group = new_group;
364 data->old_group = old_group;
365 data->action = context->action;
367 /* FIXME: We should probably wait for the cb before calling
369 empathy_tp_contact_factory_get_from_id (factory, contact_id,
370 contact_list_view_drag_got_contact,
371 data, (GDestroyNotify) contact_list_view_dnd_get_contact_free,
374 g_object_unref (factory);
380 contact_list_view_file_drag_received (GtkWidget *view,
381 GdkDragContext *context,
384 GtkSelectionData *selection)
387 const gchar *sel_data;
388 EmpathyContact *contact;
390 sel_data = (const gchar *) gtk_selection_data_get_data (selection);
392 gtk_tree_model_get_iter (model, &iter, path);
393 gtk_tree_model_get (model, &iter,
394 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
400 empathy_send_file_from_uri_list (contact, sel_data);
402 g_object_unref (contact);
408 contact_list_view_drag_data_received (GtkWidget *view,
409 GdkDragContext *context,
412 GtkSelectionData *selection,
418 GtkTreeViewDropPosition position;
420 gboolean success = TRUE;
422 model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
424 /* Get destination group information. */
425 is_row = gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (view),
433 else if (info == DND_DRAG_TYPE_CONTACT_ID || info == DND_DRAG_TYPE_STRING) {
434 success = contact_list_view_contact_drag_received (view,
440 else if (info == DND_DRAG_TYPE_URI_LIST) {
441 success = contact_list_view_file_drag_received (view,
448 gtk_tree_path_free (path);
449 gtk_drag_finish (context, success, FALSE, GDK_CURRENT_TIME);
453 contact_list_view_drag_motion_cb (DragMotionData *data)
455 gtk_tree_view_expand_row (GTK_TREE_VIEW (data->view),
459 data->timeout_id = 0;
465 contact_list_view_drag_motion (GtkWidget *widget,
466 GdkDragContext *context,
471 EmpathyContactListViewPriv *priv;
475 static DragMotionData *dm = NULL;
478 gboolean is_different = FALSE;
479 gboolean cleanup = TRUE;
480 gboolean retval = TRUE;
482 priv = GET_PRIV (EMPATHY_CONTACT_LIST_VIEW (widget));
483 model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
485 is_row = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
496 cleanup &= (dm && gtk_tree_path_compare (dm->path, path) != 0);
497 is_different = (!dm || (dm && gtk_tree_path_compare (dm->path, path) != 0));
503 /* Coordinates don't point to an actual row, so make sure the pointer
504 and highlighting don't indicate that a drag is possible.
506 gdk_drag_status (context, GDK_ACTION_DEFAULT, time_);
507 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), NULL, 0);
510 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
511 gtk_tree_model_get_iter (model, &iter, path);
513 if (target == GDK_NONE) {
514 /* If target == GDK_NONE, then we don't have a target that can be
515 dropped on a contact. This means a contact drag. If we're
516 pointing to a group, highlight it. Otherwise, if the contact
517 we're pointing to is in a group, highlight that. Otherwise,
518 set the drag position to before the first row for a drag into
519 the "non-group" at the top.
521 GtkTreeIter group_iter;
523 GtkTreePath *group_path;
524 gtk_tree_model_get (model, &iter,
525 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
531 if (gtk_tree_model_iter_parent (model, &group_iter, &iter))
532 gtk_tree_model_get (model, &group_iter,
533 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
537 gdk_drag_status (context, GDK_ACTION_MOVE, time_);
538 group_path = gtk_tree_model_get_path (model, &group_iter);
539 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
541 GTK_TREE_VIEW_DROP_INTO_OR_BEFORE);
542 gtk_tree_path_free (group_path);
545 group_path = gtk_tree_path_new_first ();
546 gdk_drag_status (context, GDK_ACTION_MOVE, time_);
547 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
549 GTK_TREE_VIEW_DROP_BEFORE);
553 /* This is a file drag, and it can only be dropped on contacts,
556 EmpathyContact *contact;
557 gtk_tree_model_get (model, &iter,
558 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
560 if (contact != NULL &&
561 empathy_contact_is_online (contact) &&
562 (empathy_contact_get_capabilities (contact) & EMPATHY_CAPABILITIES_FT)) {
563 gdk_drag_status (context, GDK_ACTION_COPY, time_);
564 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
566 GTK_TREE_VIEW_DROP_INTO_OR_BEFORE);
567 g_object_unref (contact);
570 gdk_drag_status (context, 0, time_);
571 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), NULL, 0);
576 if (!is_different && !cleanup) {
581 gtk_tree_path_free (dm->path);
582 if (dm->timeout_id) {
583 g_source_remove (dm->timeout_id);
591 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), path)) {
592 dm = g_new0 (DragMotionData, 1);
594 dm->view = EMPATHY_CONTACT_LIST_VIEW (widget);
595 dm->path = gtk_tree_path_copy (path);
597 dm->timeout_id = g_timeout_add_seconds (1,
598 (GSourceFunc) contact_list_view_drag_motion_cb,
606 contact_list_view_drag_begin (GtkWidget *widget,
607 GdkDragContext *context)
609 EmpathyContactListViewPriv *priv;
610 GtkTreeSelection *selection;
615 priv = GET_PRIV (widget);
617 GTK_WIDGET_CLASS (empathy_contact_list_view_parent_class)->drag_begin (widget,
620 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
621 if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
625 path = gtk_tree_model_get_path (model, &iter);
626 priv->drag_row = gtk_tree_row_reference_new (model, path);
627 gtk_tree_path_free (path);
631 contact_list_view_drag_data_get (GtkWidget *widget,
632 GdkDragContext *context,
633 GtkSelectionData *selection,
637 EmpathyContactListViewPriv *priv;
638 GtkTreePath *src_path;
641 EmpathyContact *contact;
643 const gchar *contact_id;
644 const gchar *account_id;
647 priv = GET_PRIV (widget);
649 model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
650 if (!priv->drag_row) {
654 src_path = gtk_tree_row_reference_get_path (priv->drag_row);
659 if (!gtk_tree_model_get_iter (model, &iter, src_path)) {
660 gtk_tree_path_free (src_path);
664 gtk_tree_path_free (src_path);
666 contact = empathy_contact_list_view_dup_selected (EMPATHY_CONTACT_LIST_VIEW (widget));
671 account = empathy_contact_get_account (contact);
672 account_id = tp_proxy_get_object_path (account);
673 contact_id = empathy_contact_get_id (contact);
674 g_object_unref (contact);
675 str = g_strconcat (account_id, ":", contact_id, NULL);
678 case DND_DRAG_TYPE_CONTACT_ID:
679 gtk_selection_data_set (selection, drag_atoms_source[info], 8,
680 (guchar *) str, strlen (str) + 1);
688 contact_list_view_drag_end (GtkWidget *widget,
689 GdkDragContext *context)
691 EmpathyContactListViewPriv *priv;
693 priv = GET_PRIV (widget);
695 GTK_WIDGET_CLASS (empathy_contact_list_view_parent_class)->drag_end (widget,
698 if (priv->drag_row) {
699 gtk_tree_row_reference_free (priv->drag_row);
700 priv->drag_row = NULL;
705 contact_list_view_drag_drop (GtkWidget *widget,
706 GdkDragContext *drag_context,
715 EmpathyContactListView *view;
721 contact_list_view_popup_menu_idle_cb (gpointer user_data)
723 MenuPopupData *data = user_data;
726 menu = empathy_contact_list_view_get_contact_menu (data->view);
728 menu = empathy_contact_list_view_get_group_menu (data->view);
732 g_signal_connect (menu, "deactivate",
733 G_CALLBACK (gtk_menu_detach), NULL);
734 gtk_menu_attach_to_widget (GTK_MENU (menu),
735 GTK_WIDGET (data->view), NULL);
736 gtk_widget_show (menu);
737 gtk_menu_popup (GTK_MENU (menu),
738 NULL, NULL, NULL, NULL,
739 data->button, data->time);
740 g_object_ref_sink (menu);
741 g_object_unref (menu);
744 g_slice_free (MenuPopupData, data);
750 contact_list_view_button_press_event_cb (EmpathyContactListView *view,
751 GdkEventButton *event,
754 if (event->button == 3) {
757 data = g_slice_new (MenuPopupData);
759 data->button = event->button;
760 data->time = event->time;
761 g_idle_add (contact_list_view_popup_menu_idle_cb, data);
768 contact_list_view_key_press_event_cb (EmpathyContactListView *view,
772 if (event->keyval == GDK_Menu) {
775 data = g_slice_new (MenuPopupData);
778 data->time = event->time;
779 g_idle_add (contact_list_view_popup_menu_idle_cb, data);
786 contact_list_view_row_activated (GtkTreeView *view,
788 GtkTreeViewColumn *column)
790 EmpathyContactListViewPriv *priv = GET_PRIV (view);
791 EmpathyContact *contact;
795 if (!(priv->contact_features & EMPATHY_CONTACT_FEATURE_CHAT)) {
799 model = GTK_TREE_MODEL (priv->store);
800 gtk_tree_model_get_iter (model, &iter, path);
801 gtk_tree_model_get (model, &iter,
802 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
806 DEBUG ("Starting a chat");
807 empathy_dispatcher_chat_with_contact (contact,
808 gtk_get_current_event_time (), NULL, NULL);
809 g_object_unref (contact);
814 contact_list_view_call_activated_cb (
815 EmpathyCellRendererActivatable *cell,
816 const gchar *path_string,
817 EmpathyContactListView *view)
822 EmpathyContact *contact;
823 GdkEventButton *event;
827 model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
828 if (!gtk_tree_model_get_iter_from_string (model, &iter, path_string))
831 gtk_tree_model_get (model, &iter,
832 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
837 event = (GdkEventButton *) gtk_get_current_event ();
839 menu = gtk_menu_new ();
840 shell = GTK_MENU_SHELL (menu);
843 item = empathy_contact_audio_call_menu_item_new (contact);
844 gtk_menu_shell_append (shell, item);
845 gtk_widget_show (item);
848 item = empathy_contact_video_call_menu_item_new (contact);
849 gtk_menu_shell_append (shell, item);
850 gtk_widget_show (item);
852 g_signal_connect (menu, "deactivate",
853 G_CALLBACK (gtk_menu_detach), NULL);
854 gtk_menu_attach_to_widget (GTK_MENU (menu),
855 GTK_WIDGET (view), NULL);
856 gtk_widget_show (menu);
857 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
858 event->button, event->time);
859 g_object_ref_sink (menu);
860 g_object_unref (menu);
862 g_object_unref (contact);
866 contact_list_view_cell_set_background (EmpathyContactListView *view,
867 GtkCellRenderer *cell,
874 style = gtk_widget_get_style (GTK_WIDGET (view));
876 if (!is_group && is_active) {
877 color = style->bg[GTK_STATE_SELECTED];
879 /* Here we take the current theme colour and add it to
880 * the colour for white and average the two. This
881 * gives a colour which is inline with the theme but
884 color.red = (color.red + (style->white).red) / 2;
885 color.green = (color.green + (style->white).green) / 2;
886 color.blue = (color.blue + (style->white).blue) / 2;
889 "cell-background-gdk", &color,
893 "cell-background-gdk", NULL,
899 contact_list_view_pixbuf_cell_data_func (GtkTreeViewColumn *tree_column,
900 GtkCellRenderer *cell,
903 EmpathyContactListView *view)
909 gtk_tree_model_get (model, iter,
910 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
911 EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
912 EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, &pixbuf,
916 "visible", !is_group,
920 if (pixbuf != NULL) {
921 g_object_unref (pixbuf);
924 contact_list_view_cell_set_background (view, cell, is_group, is_active);
928 contact_list_view_group_icon_cell_data_func (GtkTreeViewColumn *tree_column,
929 GtkCellRenderer *cell,
932 EmpathyContactListView *view)
934 GdkPixbuf *pixbuf = NULL;
938 gtk_tree_model_get (model, iter,
939 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
940 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
946 if (!tp_strdiff (name, EMPATHY_CONTACT_LIST_STORE_FAVORITE)) {
947 pixbuf = empathy_pixbuf_from_icon_name ("emblem-favorite",
950 else if (!tp_strdiff (name, EMPATHY_CONTACT_LIST_STORE_PEOPLE_NEARBY)) {
951 pixbuf = empathy_pixbuf_from_icon_name ("im-local-xmpp",
957 "visible", pixbuf != NULL,
962 g_object_unref (pixbuf);
968 contact_list_view_audio_call_cell_data_func (
969 GtkTreeViewColumn *tree_column,
970 GtkCellRenderer *cell,
973 EmpathyContactListView *view)
977 gboolean can_audio, can_video;
979 gtk_tree_model_get (model, iter,
980 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
981 EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
982 EMPATHY_CONTACT_LIST_STORE_COL_CAN_AUDIO_CALL, &can_audio,
983 EMPATHY_CONTACT_LIST_STORE_COL_CAN_VIDEO_CALL, &can_video,
987 "visible", !is_group && (can_audio || can_video),
988 "icon-name", can_video? EMPATHY_IMAGE_VIDEO_CALL : EMPATHY_IMAGE_VOIP,
991 contact_list_view_cell_set_background (view, cell, is_group, is_active);
995 contact_list_view_avatar_cell_data_func (GtkTreeViewColumn *tree_column,
996 GtkCellRenderer *cell,
999 EmpathyContactListView *view)
1002 gboolean show_avatar;
1006 gtk_tree_model_get (model, iter,
1007 EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR, &pixbuf,
1008 EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR_VISIBLE, &show_avatar,
1009 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
1010 EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
1014 "visible", !is_group && show_avatar,
1019 g_object_unref (pixbuf);
1022 contact_list_view_cell_set_background (view, cell, is_group, is_active);
1026 contact_list_view_text_cell_data_func (GtkTreeViewColumn *tree_column,
1027 GtkCellRenderer *cell,
1028 GtkTreeModel *model,
1030 EmpathyContactListView *view)
1035 gtk_tree_model_get (model, iter,
1036 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
1037 EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
1040 contact_list_view_cell_set_background (view, cell, is_group, is_active);
1044 contact_list_view_expander_cell_data_func (GtkTreeViewColumn *column,
1045 GtkCellRenderer *cell,
1046 GtkTreeModel *model,
1048 EmpathyContactListView *view)
1053 gtk_tree_model_get (model, iter,
1054 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
1055 EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
1058 if (gtk_tree_model_iter_has_child (model, iter)) {
1060 gboolean row_expanded;
1062 path = gtk_tree_model_get_path (model, iter);
1063 row_expanded = gtk_tree_view_row_expanded (GTK_TREE_VIEW (gtk_tree_view_column_get_tree_view (column)), path);
1064 gtk_tree_path_free (path);
1068 "expander-style", row_expanded ? GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED,
1071 g_object_set (cell, "visible", FALSE, NULL);
1074 contact_list_view_cell_set_background (view, cell, is_group, is_active);
1078 contact_list_view_row_expand_or_collapse_cb (EmpathyContactListView *view,
1083 EmpathyContactListViewPriv *priv = GET_PRIV (view);
1084 GtkTreeModel *model;
1088 if (!(priv->list_features & EMPATHY_CONTACT_LIST_FEATURE_GROUPS_SAVE)) {
1092 model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
1094 gtk_tree_model_get (model, iter,
1095 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
1098 expanded = GPOINTER_TO_INT (user_data);
1099 empathy_contact_group_set_expanded (name, expanded);
1105 contact_list_view_row_has_child_toggled_cb (GtkTreeModel *model,
1108 EmpathyContactListView *view)
1110 EmpathyContactListViewPriv *priv = GET_PRIV (view);
1111 gboolean is_group = FALSE;
1114 gtk_tree_model_get (model, iter,
1115 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
1116 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
1119 if (!is_group || EMP_STR_EMPTY (name)) {
1124 if (!(priv->list_features & EMPATHY_CONTACT_LIST_FEATURE_GROUPS_SAVE) ||
1125 empathy_contact_group_get_expanded (name)) {
1126 g_signal_handlers_block_by_func (view,
1127 contact_list_view_row_expand_or_collapse_cb,
1128 GINT_TO_POINTER (TRUE));
1129 gtk_tree_view_expand_row (GTK_TREE_VIEW (view), path, TRUE);
1130 g_signal_handlers_unblock_by_func (view,
1131 contact_list_view_row_expand_or_collapse_cb,
1132 GINT_TO_POINTER (TRUE));
1134 g_signal_handlers_block_by_func (view,
1135 contact_list_view_row_expand_or_collapse_cb,
1136 GINT_TO_POINTER (FALSE));
1137 gtk_tree_view_collapse_row (GTK_TREE_VIEW (view), path);
1138 g_signal_handlers_unblock_by_func (view,
1139 contact_list_view_row_expand_or_collapse_cb,
1140 GINT_TO_POINTER (FALSE));
1147 contact_list_view_setup (EmpathyContactListView *view)
1149 EmpathyContactListViewPriv *priv;
1150 GtkCellRenderer *cell;
1151 GtkTreeViewColumn *col;
1154 priv = GET_PRIV (view);
1156 gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (view),
1157 empathy_contact_list_store_search_equal_func,
1160 g_signal_connect (priv->store, "row-has-child-toggled",
1161 G_CALLBACK (contact_list_view_row_has_child_toggled_cb),
1163 gtk_tree_view_set_model (GTK_TREE_VIEW (view),
1164 GTK_TREE_MODEL (priv->store));
1167 /* Setting reorderable is a hack that gets us row previews as drag icons
1168 for free. We override all the drag handlers. It's tricky to get the
1169 position of the drag icon right in drag_begin. GtkTreeView has special
1170 voodoo for it, so we let it do the voodoo that he do.
1173 "headers-visible", FALSE,
1174 "reorderable", TRUE,
1175 "show-expanders", FALSE,
1178 col = gtk_tree_view_column_new ();
1181 cell = gtk_cell_renderer_pixbuf_new ();
1182 gtk_tree_view_column_pack_start (col, cell, FALSE);
1183 gtk_tree_view_column_set_cell_data_func (
1185 (GtkTreeCellDataFunc) contact_list_view_pixbuf_cell_data_func,
1195 cell = gtk_cell_renderer_pixbuf_new ();
1196 gtk_tree_view_column_pack_start (col, cell, FALSE);
1197 gtk_tree_view_column_set_cell_data_func (
1199 (GtkTreeCellDataFunc) contact_list_view_group_icon_cell_data_func,
1211 cell = empathy_cell_renderer_text_new ();
1212 gtk_tree_view_column_pack_start (col, cell, TRUE);
1213 gtk_tree_view_column_set_cell_data_func (
1215 (GtkTreeCellDataFunc) contact_list_view_text_cell_data_func,
1218 gtk_tree_view_column_add_attribute (col, cell,
1219 "name", EMPATHY_CONTACT_LIST_STORE_COL_NAME);
1220 gtk_tree_view_column_add_attribute (col, cell,
1221 "text", EMPATHY_CONTACT_LIST_STORE_COL_NAME);
1222 gtk_tree_view_column_add_attribute (col, cell,
1223 "presence-type", EMPATHY_CONTACT_LIST_STORE_COL_PRESENCE_TYPE);
1224 gtk_tree_view_column_add_attribute (col, cell,
1225 "status", EMPATHY_CONTACT_LIST_STORE_COL_STATUS);
1226 gtk_tree_view_column_add_attribute (col, cell,
1227 "is_group", EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP);
1228 gtk_tree_view_column_add_attribute (col, cell,
1229 "compact", EMPATHY_CONTACT_LIST_STORE_COL_COMPACT);
1231 /* Audio Call Icon */
1232 cell = empathy_cell_renderer_activatable_new ();
1233 gtk_tree_view_column_pack_start (col, cell, FALSE);
1234 gtk_tree_view_column_set_cell_data_func (
1236 (GtkTreeCellDataFunc) contact_list_view_audio_call_cell_data_func,
1243 g_signal_connect (cell, "path-activated",
1244 G_CALLBACK (contact_list_view_call_activated_cb),
1248 cell = gtk_cell_renderer_pixbuf_new ();
1249 gtk_tree_view_column_pack_start (col, cell, FALSE);
1250 gtk_tree_view_column_set_cell_data_func (
1252 (GtkTreeCellDataFunc) contact_list_view_avatar_cell_data_func,
1264 cell = empathy_cell_renderer_expander_new ();
1265 gtk_tree_view_column_pack_end (col, cell, FALSE);
1266 gtk_tree_view_column_set_cell_data_func (
1268 (GtkTreeCellDataFunc) contact_list_view_expander_cell_data_func,
1271 /* Actually add the column now we have added all cell renderers */
1272 gtk_tree_view_append_column (GTK_TREE_VIEW (view), col);
1275 for (i = 0; i < G_N_ELEMENTS (drag_types_dest); ++i) {
1276 drag_atoms_dest[i] = gdk_atom_intern (drag_types_dest[i].target,
1280 for (i = 0; i < G_N_ELEMENTS (drag_types_source); ++i) {
1281 drag_atoms_source[i] = gdk_atom_intern (drag_types_source[i].target,
1287 contact_list_view_set_list_features (EmpathyContactListView *view,
1288 EmpathyContactListFeatureFlags features)
1290 EmpathyContactListViewPriv *priv = GET_PRIV (view);
1291 gboolean has_tooltip;
1293 g_return_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view));
1295 priv->list_features = features;
1297 /* Update DnD source/dest */
1298 if (features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_DRAG) {
1299 gtk_drag_source_set (GTK_WIDGET (view),
1302 G_N_ELEMENTS (drag_types_source),
1303 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1305 gtk_drag_source_unset (GTK_WIDGET (view));
1309 if (features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_DROP) {
1310 gtk_drag_dest_set (GTK_WIDGET (view),
1311 GTK_DEST_DEFAULT_ALL,
1313 G_N_ELEMENTS (drag_types_dest),
1314 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1316 /* FIXME: URI could still be droped depending on FT feature */
1317 gtk_drag_dest_unset (GTK_WIDGET (view));
1320 /* Update has-tooltip */
1321 has_tooltip = (features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_TOOLTIP) != 0;
1322 gtk_widget_set_has_tooltip (GTK_WIDGET (view), has_tooltip);
1326 contact_list_view_finalize (GObject *object)
1328 EmpathyContactListViewPriv *priv;
1330 priv = GET_PRIV (object);
1333 g_object_unref (priv->store);
1335 if (priv->tooltip_widget) {
1336 gtk_widget_destroy (priv->tooltip_widget);
1338 if (priv->file_targets) {
1339 gtk_target_list_unref (priv->file_targets);
1342 G_OBJECT_CLASS (empathy_contact_list_view_parent_class)->finalize (object);
1346 contact_list_view_get_property (GObject *object,
1351 EmpathyContactListViewPriv *priv;
1353 priv = GET_PRIV (object);
1357 g_value_set_object (value, priv->store);
1359 case PROP_LIST_FEATURES:
1360 g_value_set_flags (value, priv->list_features);
1362 case PROP_CONTACT_FEATURES:
1363 g_value_set_flags (value, priv->contact_features);
1366 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1372 contact_list_view_set_property (GObject *object,
1374 const GValue *value,
1377 EmpathyContactListView *view = EMPATHY_CONTACT_LIST_VIEW (object);
1378 EmpathyContactListViewPriv *priv = GET_PRIV (object);
1382 priv->store = g_value_dup_object (value);
1383 contact_list_view_setup (view);
1385 case PROP_LIST_FEATURES:
1386 contact_list_view_set_list_features (view, g_value_get_flags (value));
1388 case PROP_CONTACT_FEATURES:
1389 priv->contact_features = g_value_get_flags (value);
1392 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1398 empathy_contact_list_view_class_init (EmpathyContactListViewClass *klass)
1400 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1401 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1402 GtkTreeViewClass *tree_view_class = GTK_TREE_VIEW_CLASS (klass);
1404 object_class->finalize = contact_list_view_finalize;
1405 object_class->get_property = contact_list_view_get_property;
1406 object_class->set_property = contact_list_view_set_property;
1408 widget_class->drag_data_received = contact_list_view_drag_data_received;
1409 widget_class->drag_drop = contact_list_view_drag_drop;
1410 widget_class->drag_begin = contact_list_view_drag_begin;
1411 widget_class->drag_data_get = contact_list_view_drag_data_get;
1412 widget_class->drag_end = contact_list_view_drag_end;
1413 widget_class->drag_motion = contact_list_view_drag_motion;
1415 /* We use the class method to let user of this widget to connect to
1416 * the signal and stop emission of the signal so the default handler
1417 * won't be called. */
1418 tree_view_class->row_activated = contact_list_view_row_activated;
1420 signals[DRAG_CONTACT_RECEIVED] =
1421 g_signal_new ("drag-contact-received",
1422 G_OBJECT_CLASS_TYPE (klass),
1426 _empathy_gtk_marshal_VOID__OBJECT_STRING_STRING,
1428 3, EMPATHY_TYPE_CONTACT, G_TYPE_STRING, G_TYPE_STRING);
1430 g_object_class_install_property (object_class,
1432 g_param_spec_object ("store",
1433 "The store of the view",
1434 "The store of the view",
1435 EMPATHY_TYPE_CONTACT_LIST_STORE,
1436 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
1437 g_object_class_install_property (object_class,
1439 g_param_spec_flags ("list-features",
1440 "Features of the view",
1441 "Flags for all enabled features",
1442 EMPATHY_TYPE_CONTACT_LIST_FEATURE_FLAGS,
1443 EMPATHY_CONTACT_LIST_FEATURE_NONE,
1444 G_PARAM_READWRITE));
1445 g_object_class_install_property (object_class,
1446 PROP_CONTACT_FEATURES,
1447 g_param_spec_flags ("contact-features",
1448 "Features of the contact menu",
1449 "Flags for all enabled features for the menu",
1450 EMPATHY_TYPE_CONTACT_FEATURE_FLAGS,
1451 EMPATHY_CONTACT_FEATURE_NONE,
1452 G_PARAM_READWRITE));
1454 g_type_class_add_private (object_class, sizeof (EmpathyContactListViewPriv));
1458 empathy_contact_list_view_init (EmpathyContactListView *view)
1460 EmpathyContactListViewPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (view,
1461 EMPATHY_TYPE_CONTACT_LIST_VIEW, EmpathyContactListViewPriv);
1464 /* Get saved group states. */
1465 empathy_contact_groups_get_all ();
1467 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (view),
1468 empathy_contact_list_store_row_separator_func,
1471 /* Set up drag target lists. */
1472 priv->file_targets = gtk_target_list_new (drag_types_dest_file,
1473 G_N_ELEMENTS (drag_types_dest_file));
1475 /* Connect to tree view signals rather than override. */
1476 g_signal_connect (view, "button-press-event",
1477 G_CALLBACK (contact_list_view_button_press_event_cb),
1479 g_signal_connect (view, "key-press-event",
1480 G_CALLBACK (contact_list_view_key_press_event_cb),
1482 g_signal_connect (view, "row-expanded",
1483 G_CALLBACK (contact_list_view_row_expand_or_collapse_cb),
1484 GINT_TO_POINTER (TRUE));
1485 g_signal_connect (view, "row-collapsed",
1486 G_CALLBACK (contact_list_view_row_expand_or_collapse_cb),
1487 GINT_TO_POINTER (FALSE));
1488 g_signal_connect (view, "query-tooltip",
1489 G_CALLBACK (contact_list_view_query_tooltip_cb),
1493 EmpathyContactListView *
1494 empathy_contact_list_view_new (EmpathyContactListStore *store,
1495 EmpathyContactListFeatureFlags list_features,
1496 EmpathyContactFeatureFlags contact_features)
1498 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), NULL);
1500 return g_object_new (EMPATHY_TYPE_CONTACT_LIST_VIEW,
1502 "contact-features", contact_features,
1503 "list-features", list_features,
1508 empathy_contact_list_view_dup_selected (EmpathyContactListView *view)
1510 EmpathyContactListViewPriv *priv;
1511 GtkTreeSelection *selection;
1513 GtkTreeModel *model;
1514 EmpathyContact *contact;
1516 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL);
1518 priv = GET_PRIV (view);
1520 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1521 if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
1525 gtk_tree_model_get (model, &iter,
1526 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
1532 EmpathyContactListFlags
1533 empathy_contact_list_view_get_flags (EmpathyContactListView *view)
1535 EmpathyContactListViewPriv *priv;
1536 GtkTreeSelection *selection;
1538 GtkTreeModel *model;
1539 EmpathyContactListFlags flags;
1541 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), 0);
1543 priv = GET_PRIV (view);
1545 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1546 if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
1550 gtk_tree_model_get (model, &iter,
1551 EMPATHY_CONTACT_LIST_STORE_COL_FLAGS, &flags,
1558 empathy_contact_list_view_get_selected_group (EmpathyContactListView *view,
1559 gboolean *is_fake_group)
1561 EmpathyContactListViewPriv *priv;
1562 GtkTreeSelection *selection;
1564 GtkTreeModel *model;
1569 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL);
1571 priv = GET_PRIV (view);
1573 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1574 if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
1578 gtk_tree_model_get (model, &iter,
1579 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
1580 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
1581 EMPATHY_CONTACT_LIST_STORE_COL_IS_FAKE_GROUP, &fake,
1589 if (is_fake_group != NULL)
1590 *is_fake_group = fake;
1596 contact_list_view_remove_dialog_show (GtkWindow *parent,
1597 const gchar *message,
1598 const gchar *secondary_text)
1603 dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL,
1604 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
1606 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
1607 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1608 GTK_STOCK_DELETE, GTK_RESPONSE_YES,
1610 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
1611 "%s", secondary_text);
1613 gtk_widget_show (dialog);
1615 res = gtk_dialog_run (GTK_DIALOG (dialog));
1616 gtk_widget_destroy (dialog);
1618 return (res == GTK_RESPONSE_YES);
1622 contact_list_view_group_remove_activate_cb (GtkMenuItem *menuitem,
1623 EmpathyContactListView *view)
1625 EmpathyContactListViewPriv *priv = GET_PRIV (view);
1628 group = empathy_contact_list_view_get_selected_group (view, NULL);
1633 text = g_strdup_printf (_("Do you really want to remove the group '%s'?"), group);
1634 parent = empathy_get_toplevel_window (GTK_WIDGET (view));
1635 if (contact_list_view_remove_dialog_show (parent, _("Removing group"), text)) {
1636 EmpathyContactList *list;
1638 list = empathy_contact_list_store_get_list_iface (priv->store);
1639 empathy_contact_list_remove_group (list, group);
1649 empathy_contact_list_view_get_group_menu (EmpathyContactListView *view)
1651 EmpathyContactListViewPriv *priv = GET_PRIV (view);
1656 gboolean is_fake_group;
1658 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL);
1660 if (!(priv->list_features & (EMPATHY_CONTACT_LIST_FEATURE_GROUPS_RENAME |
1661 EMPATHY_CONTACT_LIST_FEATURE_GROUPS_REMOVE))) {
1665 group = empathy_contact_list_view_get_selected_group (view, &is_fake_group);
1666 if (!group || is_fake_group) {
1667 /* We can't alter fake groups */
1671 menu = gtk_menu_new ();
1673 /* FIXME: Not implemented yet
1674 if (priv->features & EMPATHY_CONTACT_LIST_FEATURE_GROUPS_RENAME) {
1675 item = gtk_menu_item_new_with_mnemonic (_("Re_name"));
1676 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1677 gtk_widget_show (item);
1678 g_signal_connect (item, "activate",
1679 G_CALLBACK (contact_list_view_group_rename_activate_cb),
1683 if (priv->list_features & EMPATHY_CONTACT_LIST_FEATURE_GROUPS_REMOVE) {
1684 item = gtk_image_menu_item_new_with_mnemonic (_("_Remove"));
1685 image = gtk_image_new_from_icon_name (GTK_STOCK_REMOVE,
1686 GTK_ICON_SIZE_MENU);
1687 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1688 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1689 gtk_widget_show (item);
1690 g_signal_connect (item, "activate",
1691 G_CALLBACK (contact_list_view_group_remove_activate_cb),
1701 contact_list_view_remove_activate_cb (GtkMenuItem *menuitem,
1702 EmpathyContactListView *view)
1704 EmpathyContactListViewPriv *priv = GET_PRIV (view);
1705 EmpathyContact *contact;
1707 contact = empathy_contact_list_view_dup_selected (view);
1713 parent = empathy_get_toplevel_window (GTK_WIDGET (view));
1714 text = g_strdup_printf (_("Do you really want to remove the contact '%s'?"),
1715 empathy_contact_get_name (contact));
1716 if (contact_list_view_remove_dialog_show (parent, _("Removing contact"), text)) {
1717 EmpathyContactList *list;
1719 list = empathy_contact_list_store_get_list_iface (priv->store);
1720 empathy_contact_list_remove (list, contact, "");
1724 g_object_unref (contact);
1729 empathy_contact_list_view_get_contact_menu (EmpathyContactListView *view)
1731 EmpathyContactListViewPriv *priv = GET_PRIV (view);
1732 EmpathyContact *contact;
1736 EmpathyContactListFlags flags;
1738 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL);
1740 contact = empathy_contact_list_view_dup_selected (view);
1744 flags = empathy_contact_list_view_get_flags (view);
1746 menu = empathy_contact_menu_new (contact, priv->contact_features);
1748 /* Remove contact */
1749 if (priv->list_features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_REMOVE &&
1750 flags & EMPATHY_CONTACT_LIST_CAN_REMOVE) {
1751 /* create the menu if required, or just add a separator */
1753 menu = gtk_menu_new ();
1755 item = gtk_separator_menu_item_new ();
1756 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1757 gtk_widget_show (item);
1761 item = gtk_image_menu_item_new_with_mnemonic (_("_Remove"));
1762 image = gtk_image_new_from_icon_name (GTK_STOCK_REMOVE,
1763 GTK_ICON_SIZE_MENU);
1764 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1765 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1766 gtk_widget_show (item);
1767 g_signal_connect (item, "activate",
1768 G_CALLBACK (contact_list_view_remove_activate_cb),
1772 g_object_unref (contact);