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