]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-individual-menu.c
Support calls 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);
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);
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 {
629   GtkWidget *item;
630   GtkWidget *image;
631
632   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
633
634   item = gtk_image_menu_item_new_with_mnemonic (_("Send File"));
635   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_DOCUMENT_SEND,
636       GTK_ICON_SIZE_MENU);
637   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
638   gtk_widget_show (image);
639
640   menu_item_set_first_contact (item, individual,
641       G_CALLBACK (empathy_individual_file_transfer_menu_item_activated),
642       empathy_contact_can_send_files);
643
644   return item;
645 }
646
647 static void
648 empathy_individual_share_my_desktop_menu_item_activated (GtkMenuItem *item,
649     EmpathyContact *contact)
650 {
651   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
652
653   empathy_share_my_desktop_share_with_contact (contact);
654 }
655
656 GtkWidget *
657 empathy_individual_share_my_desktop_menu_item_new (FolksIndividual *individual)
658 {
659   GtkWidget *item;
660   GtkWidget *image;
661
662   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
663
664   item = gtk_image_menu_item_new_with_mnemonic (_("Share My Desktop"));
665   image = gtk_image_new_from_icon_name (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU);
666   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
667   gtk_widget_show (image);
668
669   menu_item_set_first_contact (item, individual,
670       G_CALLBACK (empathy_individual_share_my_desktop_menu_item_activated),
671       empathy_contact_can_use_rfb_stream_tube);
672
673   return item;
674 }
675
676 static void
677 favourite_menu_item_toggled_cb (GtkCheckMenuItem *item,
678   FolksIndividual *individual)
679 {
680   folks_favourite_set_is_favourite (FOLKS_FAVOURITE (individual),
681       gtk_check_menu_item_get_active (item));
682 }
683
684 GtkWidget *
685 empathy_individual_favourite_menu_item_new (FolksIndividual *individual)
686 {
687   GtkWidget *item;
688
689   item = gtk_check_menu_item_new_with_label (_("Favorite"));
690
691   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
692       folks_favourite_get_is_favourite (FOLKS_FAVOURITE (individual)));
693
694   g_signal_connect (item, "toggled",
695       G_CALLBACK (favourite_menu_item_toggled_cb), individual);
696
697   return item;
698 }
699
700 static void
701 individual_info_menu_item_activate_cb (FolksIndividual *individual)
702 {
703   empathy_individual_information_dialog_show (individual, NULL);
704 }
705
706 GtkWidget *
707 empathy_individual_info_menu_item_new (FolksIndividual *individual)
708 {
709   GtkWidget *item;
710   GtkWidget *image;
711
712   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
713   g_return_val_if_fail (empathy_folks_individual_contains_contact (individual),
714       NULL);
715
716   item = gtk_image_menu_item_new_with_mnemonic (_("Infor_mation"));
717   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_CONTACT_INFORMATION,
718                 GTK_ICON_SIZE_MENU);
719   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
720   gtk_widget_show (image);
721
722   g_signal_connect_swapped (item, "activate",
723           G_CALLBACK (individual_info_menu_item_activate_cb),
724           individual);
725
726   return item;
727 }
728
729 static void
730 individual_edit_menu_item_activate_cb (FolksIndividual *individual)
731 {
732   EmpathyContact *contact;
733
734   contact = empathy_contact_dup_from_folks_individual (individual);
735   empathy_contact_edit_dialog_show (contact, NULL);
736
737   tp_clear_object (&contact);
738 }
739
740 GtkWidget *
741 empathy_individual_edit_menu_item_new (FolksIndividual *individual)
742 {
743   EmpathyIndividualManager *manager;
744   GtkWidget *item;
745   GtkWidget *image;
746   gboolean enable = FALSE;
747   EmpathyContact *contact;
748
749   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
750
751   contact = empathy_contact_dup_from_folks_individual (individual);
752
753   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
754
755   if (empathy_individual_manager_initialized ())
756     {
757       TpConnection *connection;
758       EmpathyIndividualManagerFlags flags;
759
760       manager = empathy_individual_manager_dup_singleton ();
761       connection = empathy_contact_get_connection (contact);
762       flags = empathy_individual_manager_get_flags_for_connection (
763           manager, connection);
764
765       enable = (flags & EMPATHY_INDIVIDUAL_MANAGER_CAN_ALIAS ||
766                 flags & EMPATHY_INDIVIDUAL_MANAGER_CAN_GROUP);
767
768       g_object_unref (manager);
769     }
770
771   item = gtk_image_menu_item_new_with_mnemonic (
772       C_("Edit individual (contextual menu)", "_Edit"));
773   image = gtk_image_new_from_icon_name (GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU);
774   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
775   gtk_widget_show (image);
776
777   gtk_widget_set_sensitive (item, enable);
778
779   g_signal_connect_swapped (item, "activate",
780       G_CALLBACK (individual_edit_menu_item_activate_cb), individual);
781
782   g_object_unref (contact);
783
784   return item;
785 }
786
787 static void
788 individual_link_menu_item_activate_cb (FolksIndividual *individual)
789 {
790   empathy_linking_dialog_show (individual, NULL);
791 }
792
793 GtkWidget *
794 empathy_individual_link_menu_item_new (FolksIndividual *individual)
795 {
796   GtkWidget *item;
797   /*GtkWidget *image;*/
798
799   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
800
801   item = gtk_image_menu_item_new_with_mnemonic (
802       C_("Link individual (contextual menu)", "_Linkā€¦"));
803   /* TODO */
804   /*image = gtk_image_new_from_icon_name (GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU);
805   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
806   gtk_widget_show (image);*/
807
808   g_signal_connect_swapped (item, "activate",
809       G_CALLBACK (individual_link_menu_item_activate_cb), individual);
810
811   return item;
812 }
813
814 typedef struct
815 {
816   FolksIndividual *individual;
817   EmpathyChatroom *chatroom;
818 } RoomSubMenuData;
819
820 static RoomSubMenuData *
821 room_sub_menu_data_new (FolksIndividual *individual,
822       EmpathyChatroom *chatroom)
823 {
824   RoomSubMenuData *data;
825
826   data = g_slice_new (RoomSubMenuData);
827   data->individual = g_object_ref (individual);
828   data->chatroom = g_object_ref (chatroom);
829   return data;
830 }
831
832 static void
833 room_sub_menu_data_free (RoomSubMenuData *data)
834 {
835   g_object_unref (data->individual);
836   g_object_unref (data->chatroom);
837   g_slice_free (RoomSubMenuData, data);
838 }
839
840 static void
841 room_sub_menu_activate_cb (GtkWidget *item,
842          RoomSubMenuData *data)
843 {
844   EmpathyTpChat *chat;
845   EmpathyChatroomManager *mgr;
846   EmpathyContact *contact = NULL;
847   GList *personas, *l;
848
849   chat = empathy_chatroom_get_tp_chat (data->chatroom);
850   if (chat == NULL)
851     {
852       /* channel was invalidated. Ignoring */
853       return;
854     }
855
856   mgr = empathy_chatroom_manager_dup_singleton (NULL);
857
858   /* find the first of this Individual's contacts who can join this room */
859   personas = folks_individual_get_personas (data->individual);
860   for (l = personas; l != NULL && contact == NULL; l = g_list_next (l))
861     {
862       TpfPersona *persona = l->data;
863       TpContact *tp_contact;
864       GList *rooms;
865
866       if (!TPF_IS_PERSONA (persona))
867         continue;
868
869       tp_contact = tpf_persona_get_contact (persona);
870       contact = empathy_contact_dup_from_tp_contact (tp_contact);
871
872       rooms = empathy_chatroom_manager_get_chatrooms (mgr,
873           empathy_contact_get_account (contact));
874
875       if (g_list_find (rooms, data->chatroom) == NULL)
876         tp_clear_object (&contact);
877
878       /* if contact != NULL here, we've found our match */
879
880       g_list_free (rooms);
881     }
882
883   g_object_unref (mgr);
884
885   if (contact == NULL)
886     {
887       /* contact disappeared. Ignoring */
888       return;
889     }
890
891   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
892
893   /* send invitation */
894   empathy_contact_list_add (EMPATHY_CONTACT_LIST (chat),
895       contact, _("Inviting you to this room"));
896
897   g_object_unref (contact);
898 }
899
900 static GtkWidget *
901 create_room_sub_menu (FolksIndividual *individual,
902                       EmpathyChatroom *chatroom)
903 {
904   GtkWidget *item;
905   RoomSubMenuData *data;
906
907   item = gtk_menu_item_new_with_label (empathy_chatroom_get_name (chatroom));
908   data = room_sub_menu_data_new (individual, chatroom);
909   g_signal_connect_data (item, "activate",
910       G_CALLBACK (room_sub_menu_activate_cb), data,
911       (GClosureNotify) room_sub_menu_data_free, 0);
912
913   return item;
914 }
915
916 GtkWidget *
917 empathy_individual_invite_menu_item_new (FolksIndividual *individual)
918 {
919   GtkWidget *item;
920   GtkWidget *image;
921   GtkWidget *room_item;
922   EmpathyChatroomManager *mgr;
923   GList *personas;
924   GList *rooms = NULL;
925   GList *names = NULL;
926   GList *l;
927   GtkWidget *submenu = NULL;
928   /* map of chat room names to their objects; just a utility to remove
929    * duplicates and to make construction of the alphabetized list easier */
930   GHashTable *name_room_map;
931
932   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
933
934   name_room_map = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
935       g_object_unref);
936
937   item = gtk_image_menu_item_new_with_mnemonic (_("_Invite to Chat Room"));
938   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_GROUP_MESSAGE,
939       GTK_ICON_SIZE_MENU);
940   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
941
942   mgr = empathy_chatroom_manager_dup_singleton (NULL);
943
944   /* collect the rooms from amongst all accounts for this Individual */
945   personas = folks_individual_get_personas (individual);
946   for (l = personas; l != NULL; l = g_list_next (l))
947     {
948       TpfPersona *persona = l->data;
949       GList *rooms_cur;
950       TpContact *tp_contact;
951       EmpathyContact *contact;
952
953       if (!TPF_IS_PERSONA (persona))
954         continue;
955
956       tp_contact = tpf_persona_get_contact (persona);
957       contact = empathy_contact_dup_from_tp_contact (tp_contact);
958
959       rooms_cur = empathy_chatroom_manager_get_chatrooms (mgr,
960           empathy_contact_get_account (contact));
961       rooms = g_list_concat (rooms, rooms_cur);
962
963       g_object_unref (contact);
964     }
965
966   /* alphabetize the rooms */
967   for (l = rooms; l != NULL; l = g_list_next (l))
968     {
969       EmpathyChatroom *chatroom = l->data;
970       gboolean existed;
971
972       if (empathy_chatroom_get_tp_chat (chatroom) != NULL)
973         {
974           const gchar *name;
975
976           name = empathy_chatroom_get_name (chatroom);
977           existed = (g_hash_table_lookup (name_room_map, name) != NULL);
978           g_hash_table_insert (name_room_map, (gpointer) name,
979               g_object_ref (chatroom));
980
981           /* this will take care of duplicates in rooms */
982           if (!existed)
983             {
984               names = g_list_insert_sorted (names, (gpointer) name,
985                   (GCompareFunc) g_strcmp0);
986             }
987         }
988     }
989
990   for (l = names; l != NULL; l = g_list_next (l))
991     {
992       const gchar *name = l->data;
993       EmpathyChatroom *chatroom;
994
995       if (G_UNLIKELY (submenu == NULL))
996         submenu = gtk_menu_new ();
997
998       chatroom = g_hash_table_lookup (name_room_map, name);
999       room_item = create_room_sub_menu (individual, chatroom);
1000       gtk_menu_shell_append ((GtkMenuShell *) submenu, room_item);
1001       gtk_widget_show (room_item);
1002     }
1003
1004   if (submenu)
1005     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
1006   else
1007     gtk_widget_set_sensitive (item, FALSE);
1008
1009   gtk_widget_show (image);
1010
1011   g_hash_table_destroy (name_room_map);
1012   g_object_unref (mgr);
1013   g_list_free (names);
1014   g_list_free (rooms);
1015
1016   return item;
1017 }