]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-individual-menu.c
Desensitise the "Link…" menu entry for untrusted Individuals
[empathy.git] / libempathy-gtk / empathy-individual-menu.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2008-2010 Collabora Ltd.
4  *
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.
9  *
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.
14  *
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
18  *
19  * Authors: Xavier Claessens <xclaesse@gmail.com>
20  *          Travis Reitter <travis.reitter@collabora.co.uk>
21  */
22
23 #include "config.h"
24
25 #include <string.h>
26
27 #include <glib/gi18n-lib.h>
28 #include <gtk/gtk.h>
29 #include <telepathy-glib/util.h>
30 #include <telepathy-logger/log-manager.h>
31 #include <folks/folks.h>
32 #include <folks/folks-telepathy.h>
33
34 #include <libempathy/empathy-call-factory.h>
35 #include <libempathy/empathy-dispatcher.h>
36 #include <libempathy/empathy-contact-manager.h>
37 #include <libempathy/empathy-individual-manager.h>
38 #include <libempathy/empathy-chatroom-manager.h>
39 #include <libempathy/empathy-utils.h>
40
41 #include "empathy-individual-menu.h"
42 #include "empathy-images.h"
43 #include "empathy-log-window.h"
44 #include "empathy-contact-dialogs.h"
45 #include "empathy-individual-dialogs.h"
46 #include "empathy-individual-edit-dialog.h"
47 #include "empathy-individual-information-dialog.h"
48 #include "empathy-ui-utils.h"
49 #include "empathy-share-my-desktop.h"
50 #include "empathy-linking-dialog.h"
51
52 static void
53 individual_menu_add_personas (GtkMenuShell *menu,
54     FolksIndividual *individual,
55     EmpathyIndividualFeatureFlags features)
56 {
57   GtkWidget *item;
58   GList *personas, *l;
59   guint persona_count = 0;
60
61   g_return_if_fail (GTK_IS_MENU (menu));
62   g_return_if_fail (FOLKS_IS_INDIVIDUAL (individual));
63   g_return_if_fail (empathy_folks_individual_contains_contact (individual));
64
65   personas = folks_individual_get_personas (individual);
66
67   /* Make sure we've got enough valid entries for these menu items to add
68    * functionality */
69   for (l = personas; l != NULL; l = l->next)
70     {
71       if (!TPF_IS_PERSONA (l->data))
72         continue;
73
74       persona_count++;
75     }
76
77   /* return early if these entries would add nothing beyond the "quick" items */
78   if (persona_count <= 1)
79     return;
80
81   /* add a separator before the list of personas */
82   item = gtk_separator_menu_item_new ();
83   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
84   gtk_widget_show (item);
85
86   personas = folks_individual_get_personas (individual);
87   for (l = personas; l != NULL; l = l->next)
88     {
89       GtkWidget *image;
90       GtkWidget *contact_item;
91       GtkWidget *contact_submenu;
92       TpContact *tp_contact;
93       EmpathyContact *contact;
94       TpfPersona *persona = l->data;
95       gchar *label;
96       FolksPersonaStore *store;
97       const gchar *account;
98       GtkWidget *action;
99
100       if (!TPF_IS_PERSONA (persona))
101         continue;
102
103       tp_contact = tpf_persona_get_contact (persona);
104       contact = empathy_contact_dup_from_tp_contact (tp_contact);
105
106       store = folks_persona_get_store (FOLKS_PERSONA (persona));
107       account = folks_persona_store_get_display_name (store);
108       label = g_strdup_printf (_("%s (%s)"),
109           folks_persona_get_display_id (FOLKS_PERSONA (persona)), account);
110
111       contact_item = gtk_image_menu_item_new_with_label (label);
112       contact_submenu = gtk_menu_new ();
113       gtk_menu_item_set_submenu (GTK_MENU_ITEM (contact_item), contact_submenu);
114       image = gtk_image_new_from_icon_name (
115           empathy_icon_name_for_contact (contact), GTK_ICON_SIZE_MENU);
116       gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (contact_item), image);
117       gtk_widget_show (image);
118
119       /* Chat */
120       if (features & EMPATHY_INDIVIDUAL_FEATURE_CHAT)
121         {
122           action = empathy_individual_chat_menu_item_new (NULL, contact);
123           gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
124           gtk_widget_show (action);
125         }
126
127       if (features & EMPATHY_INDIVIDUAL_FEATURE_CALL)
128         {
129           /* Audio Call */
130           action = empathy_individual_audio_call_menu_item_new (NULL, contact);
131           gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
132           gtk_widget_show (action);
133
134           /* Video Call */
135           action = empathy_individual_video_call_menu_item_new (NULL, contact);
136           gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
137           gtk_widget_show (action);
138         }
139
140       /* Log */
141       if (features & EMPATHY_INDIVIDUAL_FEATURE_LOG)
142         {
143           action = empathy_individual_log_menu_item_new (NULL, contact);
144           gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
145           gtk_widget_show (action);
146         }
147
148       /* Invite */
149       action = empathy_individual_invite_menu_item_new (NULL, contact);
150       gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
151       gtk_widget_show (action);
152
153       /* File transfer */
154       action = empathy_individual_file_transfer_menu_item_new (NULL, contact);
155       gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
156       gtk_widget_show (action);
157
158       /* Share my desktop */
159       action = empathy_individual_share_my_desktop_menu_item_new (NULL,
160           contact);
161       gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
162       gtk_widget_show (action);
163
164       gtk_menu_shell_append (GTK_MENU_SHELL (menu), contact_item);
165       gtk_widget_show (contact_item);
166
167       g_free (label);
168       g_object_unref (contact);
169     }
170 }
171
172 GtkWidget *
173 empathy_individual_menu_new (FolksIndividual *individual,
174     EmpathyIndividualFeatureFlags features)
175 {
176   GtkWidget *menu;
177   GtkMenuShell *shell;
178   GtkWidget *item;
179
180   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
181
182   if (features == EMPATHY_INDIVIDUAL_FEATURE_NONE)
183     return NULL;
184
185   menu = gtk_menu_new ();
186   shell = GTK_MENU_SHELL (menu);
187
188   /* Add Contact */
189   item = empathy_individual_add_menu_item_new (individual);
190   if (item)
191     {
192       gtk_menu_shell_append (shell, item);
193       gtk_widget_show (item);
194     }
195
196   /* Chat */
197   if (features & EMPATHY_INDIVIDUAL_FEATURE_CHAT)
198     {
199       item = empathy_individual_chat_menu_item_new (individual, NULL);
200       if (item != NULL)
201         {
202           gtk_menu_shell_append (shell, item);
203           gtk_widget_show (item);
204         }
205     }
206
207   if (features & EMPATHY_INDIVIDUAL_FEATURE_CALL)
208     {
209       /* Audio Call */
210       item = empathy_individual_audio_call_menu_item_new (individual, NULL);
211       gtk_menu_shell_append (shell, item);
212       gtk_widget_show (item);
213
214       /* Video Call */
215       item = empathy_individual_video_call_menu_item_new (individual, NULL);
216       gtk_menu_shell_append (shell, item);
217       gtk_widget_show (item);
218     }
219
220   /* Log */
221   if (features & EMPATHY_INDIVIDUAL_FEATURE_LOG)
222     {
223       item = empathy_individual_log_menu_item_new (individual, NULL);
224       gtk_menu_shell_append (shell, item);
225       gtk_widget_show (item);
226     }
227
228   /* Invite */
229   item = empathy_individual_invite_menu_item_new (individual, NULL);
230   gtk_menu_shell_append (shell, item);
231   gtk_widget_show (item);
232
233   /* File transfer */
234   item = empathy_individual_file_transfer_menu_item_new (individual, NULL);
235   gtk_menu_shell_append (shell, item);
236   gtk_widget_show (item);
237
238   /* Share my desktop */
239   /* FIXME we should add the "Share my desktop" menu item if Vino is
240   a registered handler in MC5 */
241   item = empathy_individual_share_my_desktop_menu_item_new (individual, NULL);
242   gtk_menu_shell_append (shell, item);
243   gtk_widget_show (item);
244
245   /* Menu items to target specific contacts */
246   individual_menu_add_personas (GTK_MENU_SHELL (menu), individual, features);
247
248   /* Separator */
249   if (features & (EMPATHY_INDIVIDUAL_FEATURE_EDIT |
250       EMPATHY_INDIVIDUAL_FEATURE_INFO |
251       EMPATHY_INDIVIDUAL_FEATURE_FAVOURITE |
252       EMPATHY_INDIVIDUAL_FEATURE_LINK))
253     {
254       item = gtk_separator_menu_item_new ();
255       gtk_menu_shell_append (shell, item);
256       gtk_widget_show (item);
257     }
258
259   /* Edit */
260   if (features & EMPATHY_INDIVIDUAL_FEATURE_EDIT)
261     {
262       item = empathy_individual_edit_menu_item_new (individual);
263       gtk_menu_shell_append (shell, item);
264       gtk_widget_show (item);
265     }
266
267   /* Link */
268   if (features & EMPATHY_INDIVIDUAL_FEATURE_LINK)
269     {
270       item = empathy_individual_link_menu_item_new (individual);
271       gtk_menu_shell_append (shell, item);
272       gtk_widget_show (item);
273     }
274
275   /* Info */
276   if (features & EMPATHY_INDIVIDUAL_FEATURE_INFO)
277     {
278       item = empathy_individual_info_menu_item_new (individual);
279       gtk_menu_shell_append (shell, item);
280       gtk_widget_show (item);
281     }
282
283   /* Favorite checkbox */
284   if (features & EMPATHY_INDIVIDUAL_FEATURE_FAVOURITE)
285     {
286       item = empathy_individual_favourite_menu_item_new (individual);
287       gtk_menu_shell_append (shell, item);
288       gtk_widget_show (item);
289     }
290
291   return menu;
292 }
293
294 static void
295 empathy_individual_add_menu_item_activated (GtkMenuItem *item,
296   FolksIndividual *individual)
297 {
298   GtkWidget *toplevel;
299
300   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (item));
301   if (!gtk_widget_is_toplevel (toplevel) || !GTK_IS_WINDOW (toplevel))
302     toplevel = NULL;
303
304   empathy_new_individual_dialog_show_with_individual (GTK_WINDOW (toplevel),
305       individual);
306 }
307
308 GtkWidget *
309 empathy_individual_add_menu_item_new (FolksIndividual *individual)
310 {
311   GtkWidget *item;
312   GtkWidget *image;
313   EmpathyIndividualManager *manager = NULL;
314   EmpathyContact *contact = NULL;
315   TpConnection *connection;
316   GList *l, *members;
317   gboolean found = FALSE;
318   EmpathyIndividualManagerFlags flags;
319
320   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
321
322   if (!empathy_individual_manager_initialized ())
323     {
324       item = NULL;
325       goto out;
326     }
327
328   manager = empathy_individual_manager_dup_singleton ();
329   contact = empathy_contact_dup_from_folks_individual (individual);
330   connection = empathy_contact_get_connection (contact);
331
332   flags = empathy_individual_manager_get_flags_for_connection (manager,
333       connection);
334
335   if (!(flags & EMPATHY_INDIVIDUAL_MANAGER_CAN_ADD))
336     {
337       item = NULL;
338       goto out;
339     }
340
341   members = empathy_individual_manager_get_members (
342       EMPATHY_INDIVIDUAL_MANAGER (manager));
343
344   for (l = members; l && !found; l = l->next)
345     {
346       if (!tp_strdiff (folks_individual_get_id (l->data),
347               folks_individual_get_id (individual)))
348         {
349           found = TRUE;
350         }
351     }
352   g_list_free (members);
353
354   if (found)
355     {
356       item = NULL;
357       goto out;
358     }
359
360   item = gtk_image_menu_item_new_with_mnemonic (_("_Add Contact…"));
361   image = gtk_image_new_from_icon_name (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU);
362   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
363
364   g_signal_connect (item, "activate",
365       G_CALLBACK (empathy_individual_add_menu_item_activated),
366       individual);
367
368 out:
369   tp_clear_object (&contact);
370   tp_clear_object (&manager);
371
372   return item;
373 }
374
375 typedef gboolean (*SensitivityPredicate) (EmpathyContact *contact);
376
377 /* Like menu_item_set_first_contact(), but always operates upon the given
378  * contact */
379 static gboolean
380 menu_item_set_contact (GtkWidget *item,
381     EmpathyContact *contact,
382     GCallback activate_callback,
383     SensitivityPredicate sensitivity_predicate)
384 {
385   gboolean contact_valid = TRUE;
386
387   if (sensitivity_predicate != NULL)
388     {
389       contact_valid = sensitivity_predicate (contact);
390       gtk_widget_set_sensitive (item, sensitivity_predicate (contact));
391     }
392
393   g_signal_connect (item, "activate", G_CALLBACK (activate_callback),
394       contact);
395
396   return contact_valid;
397 }
398
399 /**
400  * Set the given menu @item to call @activate_callback upon the first valid
401  * TpContact associated with @individual whenever @item is activated.
402  *
403  * @sensitivity_predicate is an optional function to determine whether the menu
404  * item should be insensitive (if the function returns @FALSE). Otherwise, the
405  * menu item's sensitivity will not change.
406  */
407 static GtkWidget *
408 menu_item_set_first_contact (GtkWidget *item,
409     FolksIndividual *individual,
410     GCallback activate_callback,
411     SensitivityPredicate sensitivity_predicate)
412 {
413   GList *personas, *l;
414
415   personas = folks_individual_get_personas (individual);
416   for (l = personas; l != NULL; l = l->next)
417     {
418       TpContact *tp_contact;
419       EmpathyContact *contact;
420       TpfPersona *persona = l->data;
421       gboolean contact_valid = TRUE;
422
423       if (!TPF_IS_PERSONA (persona))
424         continue;
425
426       tp_contact = tpf_persona_get_contact (persona);
427       contact = empathy_contact_dup_from_tp_contact (tp_contact);
428
429       contact_valid = menu_item_set_contact (item, contact,
430           G_CALLBACK (activate_callback), sensitivity_predicate);
431
432       g_object_unref (contact);
433
434       /* stop after the first valid match */
435       if (contact_valid)
436         break;
437     }
438
439   return item;
440 }
441
442 static void
443 empathy_individual_chat_menu_item_activated (GtkMenuItem *item,
444   EmpathyContact *contact)
445 {
446   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
447
448   empathy_dispatcher_chat_with_contact (contact, gtk_get_current_event_time ());
449 }
450
451 GtkWidget *
452 empathy_individual_chat_menu_item_new (FolksIndividual *individual,
453     EmpathyContact *contact)
454 {
455   GtkWidget *item;
456   GtkWidget *image;
457
458   g_return_val_if_fail ((FOLKS_IS_INDIVIDUAL (individual) &&
459       empathy_folks_individual_contains_contact (individual)) ||
460       EMPATHY_IS_CONTACT (contact),
461       NULL);
462
463   item = gtk_image_menu_item_new_with_mnemonic (_("_Chat"));
464   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_MESSAGE,
465       GTK_ICON_SIZE_MENU);
466   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
467   gtk_widget_show (image);
468
469   if (contact != NULL)
470     {
471       menu_item_set_contact (item, contact,
472           G_CALLBACK (empathy_individual_chat_menu_item_activated), NULL);
473     }
474   else
475     {
476       menu_item_set_first_contact (item, individual,
477           G_CALLBACK (empathy_individual_chat_menu_item_activated), NULL);
478     }
479
480   return item;
481 }
482
483 static void
484 empathy_individual_audio_call_menu_item_activated (GtkMenuItem *item,
485   EmpathyContact *contact)
486 {
487   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
488
489   empathy_call_factory_new_call_with_streams (contact, TRUE, FALSE,
490       gtk_get_current_event_time (), NULL);
491 }
492
493 GtkWidget *
494 empathy_individual_audio_call_menu_item_new (FolksIndividual *individual,
495     EmpathyContact *contact)
496 {
497   GtkWidget *item;
498   GtkWidget *image;
499
500   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual) ||
501       EMPATHY_IS_CONTACT (contact),
502       NULL);
503
504   item = gtk_image_menu_item_new_with_mnemonic (C_("menu item", "_Audio Call"));
505   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_VOIP, GTK_ICON_SIZE_MENU);
506   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
507   gtk_widget_show (image);
508
509   if (contact != NULL)
510     {
511       menu_item_set_contact (item, contact,
512           G_CALLBACK (empathy_individual_audio_call_menu_item_activated),
513           empathy_contact_can_voip_audio);
514     }
515   else
516     {
517       menu_item_set_first_contact (item, individual,
518           G_CALLBACK (empathy_individual_audio_call_menu_item_activated),
519           empathy_contact_can_voip_audio);
520     }
521
522   return item;
523 }
524
525 static void
526 empathy_individual_video_call_menu_item_activated (GtkMenuItem *item,
527   EmpathyContact *contact)
528 {
529   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
530
531   empathy_call_factory_new_call_with_streams (contact, TRUE, TRUE,
532       gtk_get_current_event_time (), NULL);
533 }
534
535 GtkWidget *
536 empathy_individual_video_call_menu_item_new (FolksIndividual *individual,
537     EmpathyContact *contact)
538 {
539   GtkWidget *item;
540   GtkWidget *image;
541
542   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual) ||
543       EMPATHY_IS_CONTACT (contact),
544       NULL);
545
546   item = gtk_image_menu_item_new_with_mnemonic (C_("menu item", "_Video Call"));
547   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_VIDEO_CALL,
548       GTK_ICON_SIZE_MENU);
549   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
550   gtk_widget_show (image);
551
552   if (contact != NULL)
553     {
554       menu_item_set_contact (item, contact,
555           G_CALLBACK (empathy_individual_video_call_menu_item_activated),
556           empathy_contact_can_voip_video);
557     }
558   else
559     {
560       menu_item_set_first_contact (item, individual,
561           G_CALLBACK (empathy_individual_video_call_menu_item_activated),
562           empathy_contact_can_voip_video);
563     }
564
565   return item;
566 }
567
568 static void
569 empathy_individual_log_menu_item_activated (GtkMenuItem *item,
570   EmpathyContact *contact)
571 {
572   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
573
574   empathy_log_window_show (empathy_contact_get_account (contact),
575       empathy_contact_get_id (contact), FALSE, NULL);
576 }
577
578 static gboolean
579 contact_has_log (EmpathyContact *contact)
580 {
581   TplLogManager *manager;
582   gboolean have_log;
583
584   manager = tpl_log_manager_dup_singleton ();
585   have_log = tpl_log_manager_exists (manager,
586       empathy_contact_get_account (contact), empathy_contact_get_id (contact),
587       FALSE);
588   g_object_unref (manager);
589
590   return have_log;
591 }
592
593 GtkWidget *
594 empathy_individual_log_menu_item_new (FolksIndividual *individual,
595     EmpathyContact *contact)
596 {
597   GtkWidget *item;
598   GtkWidget *image;
599
600   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual) ||
601       EMPATHY_IS_CONTACT (contact),
602       NULL);
603
604   item = gtk_image_menu_item_new_with_mnemonic (_("_Previous Conversations"));
605   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_LOG, GTK_ICON_SIZE_MENU);
606   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
607   gtk_widget_show (image);
608
609   if (contact != NULL)
610     {
611       menu_item_set_contact (item, contact,
612           G_CALLBACK (empathy_individual_log_menu_item_activated),
613           contact_has_log);
614     }
615   else
616     {
617       menu_item_set_first_contact (item, individual,
618           G_CALLBACK (empathy_individual_log_menu_item_activated),
619           contact_has_log);
620     }
621
622   return item;
623 }
624
625 static void
626 empathy_individual_file_transfer_menu_item_activated (GtkMenuItem *item,
627     EmpathyContact *contact)
628 {
629   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
630
631   empathy_send_file_with_file_chooser (contact);
632 }
633
634 GtkWidget *
635 empathy_individual_file_transfer_menu_item_new (FolksIndividual *individual,
636     EmpathyContact *contact)
637 {
638   GtkWidget *item;
639   GtkWidget *image;
640
641   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual) ||
642       EMPATHY_IS_CONTACT (contact),
643       NULL);
644
645   item = gtk_image_menu_item_new_with_mnemonic (_("Send File"));
646   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_DOCUMENT_SEND,
647       GTK_ICON_SIZE_MENU);
648   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
649   gtk_widget_show (image);
650
651   if (contact != NULL)
652     {
653       menu_item_set_contact (item, contact,
654           G_CALLBACK (empathy_individual_file_transfer_menu_item_activated),
655           empathy_contact_can_send_files);
656     }
657   else
658     {
659       menu_item_set_first_contact (item, individual,
660           G_CALLBACK (empathy_individual_file_transfer_menu_item_activated),
661           empathy_contact_can_send_files);
662     }
663
664   return item;
665 }
666
667 static void
668 empathy_individual_share_my_desktop_menu_item_activated (GtkMenuItem *item,
669     EmpathyContact *contact)
670 {
671   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
672
673   empathy_share_my_desktop_share_with_contact (contact);
674 }
675
676 GtkWidget *
677 empathy_individual_share_my_desktop_menu_item_new (FolksIndividual *individual,
678     EmpathyContact *contact)
679 {
680   GtkWidget *item;
681   GtkWidget *image;
682
683   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual) ||
684       EMPATHY_IS_CONTACT (contact),
685       NULL);
686
687   item = gtk_image_menu_item_new_with_mnemonic (_("Share My Desktop"));
688   image = gtk_image_new_from_icon_name (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU);
689   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
690   gtk_widget_show (image);
691
692   if (contact != NULL)
693     {
694       menu_item_set_contact (item, contact,
695           G_CALLBACK (empathy_individual_share_my_desktop_menu_item_activated),
696           empathy_contact_can_use_rfb_stream_tube);
697     }
698   else
699     {
700       menu_item_set_first_contact (item, individual,
701           G_CALLBACK (empathy_individual_share_my_desktop_menu_item_activated),
702           empathy_contact_can_use_rfb_stream_tube);
703     }
704
705   return item;
706 }
707
708 static void
709 favourite_menu_item_toggled_cb (GtkCheckMenuItem *item,
710   FolksIndividual *individual)
711 {
712   folks_favourite_set_is_favourite (FOLKS_FAVOURITE (individual),
713       gtk_check_menu_item_get_active (item));
714 }
715
716 GtkWidget *
717 empathy_individual_favourite_menu_item_new (FolksIndividual *individual)
718 {
719   GtkWidget *item;
720
721   item = gtk_check_menu_item_new_with_label (_("Favorite"));
722
723   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
724       folks_favourite_get_is_favourite (FOLKS_FAVOURITE (individual)));
725
726   g_signal_connect (item, "toggled",
727       G_CALLBACK (favourite_menu_item_toggled_cb), individual);
728
729   return item;
730 }
731
732 static void
733 individual_info_menu_item_activate_cb (FolksIndividual *individual)
734 {
735   empathy_individual_information_dialog_show (individual, NULL);
736 }
737
738 GtkWidget *
739 empathy_individual_info_menu_item_new (FolksIndividual *individual)
740 {
741   GtkWidget *item;
742   GtkWidget *image;
743
744   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
745   g_return_val_if_fail (empathy_folks_individual_contains_contact (individual),
746       NULL);
747
748   item = gtk_image_menu_item_new_with_mnemonic (_("Infor_mation"));
749   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_CONTACT_INFORMATION,
750                 GTK_ICON_SIZE_MENU);
751   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
752   gtk_widget_show (image);
753
754   g_signal_connect_swapped (item, "activate",
755           G_CALLBACK (individual_info_menu_item_activate_cb),
756           individual);
757
758   return item;
759 }
760
761 static void
762 individual_edit_menu_item_activate_cb (FolksIndividual *individual)
763 {
764   empathy_individual_edit_dialog_show (individual, NULL);
765 }
766
767 GtkWidget *
768 empathy_individual_edit_menu_item_new (FolksIndividual *individual)
769 {
770   EmpathyIndividualManager *manager;
771   GtkWidget *item;
772   GtkWidget *image;
773   gboolean enable = FALSE;
774   EmpathyContact *contact;
775
776   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
777
778   contact = empathy_contact_dup_from_folks_individual (individual);
779
780   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
781
782   if (empathy_individual_manager_initialized ())
783     {
784       TpConnection *connection;
785       EmpathyIndividualManagerFlags flags;
786
787       manager = empathy_individual_manager_dup_singleton ();
788       connection = empathy_contact_get_connection (contact);
789       flags = empathy_individual_manager_get_flags_for_connection (
790           manager, connection);
791
792       enable = (flags & EMPATHY_INDIVIDUAL_MANAGER_CAN_ALIAS ||
793                 flags & EMPATHY_INDIVIDUAL_MANAGER_CAN_GROUP);
794
795       g_object_unref (manager);
796     }
797
798   item = gtk_image_menu_item_new_with_mnemonic (
799       C_("Edit individual (contextual menu)", "_Edit"));
800   image = gtk_image_new_from_icon_name (GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU);
801   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
802   gtk_widget_show (image);
803
804   gtk_widget_set_sensitive (item, enable);
805
806   g_signal_connect_swapped (item, "activate",
807       G_CALLBACK (individual_edit_menu_item_activate_cb), individual);
808
809   g_object_unref (contact);
810
811   return item;
812 }
813
814 static void
815 individual_link_menu_item_activate_cb (FolksIndividual *individual)
816 {
817   empathy_linking_dialog_show (individual, NULL);
818 }
819
820 GtkWidget *
821 empathy_individual_link_menu_item_new (FolksIndividual *individual)
822 {
823   GtkWidget *item;
824   /*GtkWidget *image;*/
825
826   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
827
828   item = gtk_image_menu_item_new_with_mnemonic (
829       C_("Link individual (contextual menu)", "_Link…"));
830   /* TODO */
831   /*image = gtk_image_new_from_icon_name (GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU);
832   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
833   gtk_widget_show (image);*/
834
835   /* Only allow trusted Individuals to be linked */
836   gtk_widget_set_sensitive (item,
837       folks_individual_get_trust_level (individual) ==
838           FOLKS_TRUST_LEVEL_PERSONAS);
839
840   g_signal_connect_swapped (item, "activate",
841       G_CALLBACK (individual_link_menu_item_activate_cb), individual);
842
843   return item;
844 }
845
846 typedef struct
847 {
848   FolksIndividual *individual;
849   EmpathyContact *contact;
850   EmpathyChatroom *chatroom;
851 } RoomSubMenuData;
852
853 static RoomSubMenuData *
854 room_sub_menu_data_new (FolksIndividual *individual,
855     EmpathyContact *contact,
856     EmpathyChatroom *chatroom)
857 {
858   RoomSubMenuData *data;
859
860   data = g_slice_new0 (RoomSubMenuData);
861   if (individual != NULL)
862     data->individual = g_object_ref (individual);
863   if (contact != NULL)
864     data->contact = g_object_ref (contact);
865   data->chatroom = g_object_ref (chatroom);
866
867   return data;
868 }
869
870 static void
871 room_sub_menu_data_free (RoomSubMenuData *data)
872 {
873   tp_clear_object (&data->individual);
874   tp_clear_object (&data->contact);
875   g_object_unref (data->chatroom);
876   g_slice_free (RoomSubMenuData, data);
877 }
878
879 static void
880 room_sub_menu_activate_cb (GtkWidget *item,
881          RoomSubMenuData *data)
882 {
883   EmpathyTpChat *chat;
884   EmpathyChatroomManager *mgr;
885   EmpathyContact *contact = NULL;
886   GList *personas, *l;
887
888   chat = empathy_chatroom_get_tp_chat (data->chatroom);
889   if (chat == NULL)
890     {
891       /* channel was invalidated. Ignoring */
892       return;
893     }
894
895   mgr = empathy_chatroom_manager_dup_singleton (NULL);
896
897   if (data->contact != NULL)
898     contact = g_object_ref (data->contact);
899   else
900     {
901       /* find the first of this Individual's contacts who can join this room */
902       personas = folks_individual_get_personas (data->individual);
903       for (l = personas; l != NULL && contact == NULL; l = g_list_next (l))
904         {
905           TpfPersona *persona = l->data;
906           TpContact *tp_contact;
907           GList *rooms;
908
909           if (!TPF_IS_PERSONA (persona))
910             continue;
911
912           tp_contact = tpf_persona_get_contact (persona);
913           contact = empathy_contact_dup_from_tp_contact (tp_contact);
914
915           rooms = empathy_chatroom_manager_get_chatrooms (mgr,
916               empathy_contact_get_account (contact));
917
918           if (g_list_find (rooms, data->chatroom) == NULL)
919             tp_clear_object (&contact);
920
921           /* if contact != NULL here, we've found our match */
922
923           g_list_free (rooms);
924         }
925     }
926
927   g_object_unref (mgr);
928
929   if (contact == NULL)
930     {
931       /* contact disappeared. Ignoring */
932       goto out;
933     }
934
935   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
936
937   /* send invitation */
938   empathy_contact_list_add (EMPATHY_CONTACT_LIST (chat),
939       contact, _("Inviting you to this room"));
940
941 out:
942   g_object_unref (contact);
943 }
944
945 static GtkWidget *
946 create_room_sub_menu (FolksIndividual *individual,
947                       EmpathyContact *contact,
948                       EmpathyChatroom *chatroom)
949 {
950   GtkWidget *item;
951   RoomSubMenuData *data;
952
953   item = gtk_menu_item_new_with_label (empathy_chatroom_get_name (chatroom));
954   data = room_sub_menu_data_new (individual, contact, chatroom);
955   g_signal_connect_data (item, "activate",
956       G_CALLBACK (room_sub_menu_activate_cb), data,
957       (GClosureNotify) room_sub_menu_data_free, 0);
958
959   return item;
960 }
961
962 GtkWidget *
963 empathy_individual_invite_menu_item_new (FolksIndividual *individual,
964     EmpathyContact *contact)
965 {
966   GtkWidget *item;
967   GtkWidget *image;
968   GtkWidget *room_item;
969   EmpathyChatroomManager *mgr;
970   GList *personas;
971   GList *rooms = NULL;
972   GList *names = NULL;
973   GList *l;
974   GtkWidget *submenu = NULL;
975   /* map of chat room names to their objects; just a utility to remove
976    * duplicates and to make construction of the alphabetized list easier */
977   GHashTable *name_room_map;
978
979   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual) ||
980       EMPATHY_IS_CONTACT (contact),
981       NULL);
982
983   name_room_map = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
984       g_object_unref);
985
986   item = gtk_image_menu_item_new_with_mnemonic (_("_Invite to Chat Room"));
987   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_GROUP_MESSAGE,
988       GTK_ICON_SIZE_MENU);
989   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
990
991   mgr = empathy_chatroom_manager_dup_singleton (NULL);
992
993   if (contact != NULL)
994     {
995       rooms = empathy_chatroom_manager_get_chatrooms (mgr,
996           empathy_contact_get_account (contact));
997     }
998   else
999     {
1000       /* collect the rooms from amongst all accounts for this Individual */
1001       personas = folks_individual_get_personas (individual);
1002       for (l = personas; l != NULL; l = g_list_next (l))
1003         {
1004           TpfPersona *persona = l->data;
1005           GList *rooms_cur;
1006           TpContact *tp_contact;
1007           EmpathyContact *contact_cur;
1008
1009           if (!TPF_IS_PERSONA (persona))
1010             continue;
1011
1012           tp_contact = tpf_persona_get_contact (persona);
1013           contact_cur = empathy_contact_dup_from_tp_contact (tp_contact);
1014
1015           rooms_cur = empathy_chatroom_manager_get_chatrooms (mgr,
1016               empathy_contact_get_account (contact_cur));
1017           rooms = g_list_concat (rooms, rooms_cur);
1018
1019           g_object_unref (contact_cur);
1020         }
1021     }
1022
1023   /* alphabetize the rooms */
1024   for (l = rooms; l != NULL; l = g_list_next (l))
1025     {
1026       EmpathyChatroom *chatroom = l->data;
1027       gboolean existed;
1028
1029       if (empathy_chatroom_get_tp_chat (chatroom) != NULL)
1030         {
1031           const gchar *name;
1032
1033           name = empathy_chatroom_get_name (chatroom);
1034           existed = (g_hash_table_lookup (name_room_map, name) != NULL);
1035           g_hash_table_insert (name_room_map, (gpointer) name,
1036               g_object_ref (chatroom));
1037
1038           /* this will take care of duplicates in rooms */
1039           if (!existed)
1040             {
1041               names = g_list_insert_sorted (names, (gpointer) name,
1042                   (GCompareFunc) g_strcmp0);
1043             }
1044         }
1045     }
1046
1047   for (l = names; l != NULL; l = g_list_next (l))
1048     {
1049       const gchar *name = l->data;
1050       EmpathyChatroom *chatroom;
1051
1052       if (G_UNLIKELY (submenu == NULL))
1053         submenu = gtk_menu_new ();
1054
1055       chatroom = g_hash_table_lookup (name_room_map, name);
1056       room_item = create_room_sub_menu (individual, contact, chatroom);
1057       gtk_menu_shell_append ((GtkMenuShell *) submenu, room_item);
1058       gtk_widget_show (room_item);
1059     }
1060
1061   if (submenu)
1062     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
1063   else
1064     gtk_widget_set_sensitive (item, FALSE);
1065
1066   gtk_widget_show (image);
1067
1068   g_hash_table_destroy (name_room_map);
1069   g_object_unref (mgr);
1070   g_list_free (names);
1071   g_list_free (rooms);
1072
1073   return item;
1074 }