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