]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-individual-menu.c
Merge remote branch 'kaserf/fix-account-name-update'
[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-gtk-enum-types.h"
46 #include "empathy-individual-dialogs.h"
47 #include "empathy-individual-edit-dialog.h"
48 #include "empathy-individual-information-dialog.h"
49 #include "empathy-ui-utils.h"
50 #include "empathy-share-my-desktop.h"
51 #include "empathy-linking-dialog.h"
52
53 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyIndividualMenu)
54
55 typedef struct {
56   FolksIndividual *individual; /* owned */
57   EmpathyIndividualFeatureFlags features;
58 } EmpathyIndividualMenuPriv;
59
60 enum {
61   PROP_INDIVIDUAL = 1,
62   PROP_FEATURES,
63 };
64
65 enum {
66   SIGNAL_LINK_CONTACTS_ACTIVATED,
67   LAST_SIGNAL
68 };
69
70 static guint signals[LAST_SIGNAL];
71
72 G_DEFINE_TYPE (EmpathyIndividualMenu, empathy_individual_menu, GTK_TYPE_MENU);
73
74 static void
75 individual_menu_add_personas (GtkMenuShell *menu,
76     FolksIndividual *individual,
77     EmpathyIndividualFeatureFlags features)
78 {
79   GtkWidget *item;
80   GList *personas, *l;
81   guint persona_count = 0;
82
83   g_return_if_fail (GTK_IS_MENU (menu));
84   g_return_if_fail (FOLKS_IS_INDIVIDUAL (individual));
85   g_return_if_fail (empathy_folks_individual_contains_contact (individual));
86
87   personas = folks_individual_get_personas (individual);
88
89   /* Make sure we've got enough valid entries for these menu items to add
90    * functionality */
91   for (l = personas; l != NULL; l = l->next)
92     {
93       if (!TPF_IS_PERSONA (l->data))
94         continue;
95
96       persona_count++;
97     }
98
99   /* return early if these entries would add nothing beyond the "quick" items */
100   if (persona_count <= 1)
101     return;
102
103   /* add a separator before the list of personas */
104   item = gtk_separator_menu_item_new ();
105   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
106   gtk_widget_show (item);
107
108   personas = folks_individual_get_personas (individual);
109   for (l = personas; l != NULL; l = l->next)
110     {
111       GtkWidget *image;
112       GtkWidget *contact_item;
113       GtkWidget *contact_submenu;
114       TpContact *tp_contact;
115       EmpathyContact *contact;
116       TpfPersona *persona = l->data;
117       gchar *label;
118       FolksPersonaStore *store;
119       const gchar *account;
120       GtkWidget *action;
121
122       if (!TPF_IS_PERSONA (persona))
123         continue;
124
125       tp_contact = tpf_persona_get_contact (persona);
126       contact = empathy_contact_dup_from_tp_contact (tp_contact);
127
128       store = folks_persona_get_store (FOLKS_PERSONA (persona));
129       account = folks_persona_store_get_display_name (store);
130
131       /* Translators: this is used in the context menu for a contact. The first
132        * parameter is a contact ID (e.g. foo@jabber.org) and the second is one
133        * of the user's account IDs (e.g. me@hotmail.com). */
134       label = g_strdup_printf (_("%s (%s)"),
135           folks_persona_get_display_id (FOLKS_PERSONA (persona)), account);
136
137       contact_item = gtk_image_menu_item_new_with_label (label);
138       contact_submenu = gtk_menu_new ();
139       gtk_menu_item_set_submenu (GTK_MENU_ITEM (contact_item), contact_submenu);
140       image = gtk_image_new_from_icon_name (
141           empathy_icon_name_for_contact (contact), GTK_ICON_SIZE_MENU);
142       gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (contact_item), image);
143       gtk_widget_show (image);
144
145       /* Chat */
146       if (features & EMPATHY_INDIVIDUAL_FEATURE_CHAT)
147         {
148           action = empathy_individual_chat_menu_item_new (NULL, contact);
149           gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
150           gtk_widget_show (action);
151         }
152
153       if (features & EMPATHY_INDIVIDUAL_FEATURE_CALL)
154         {
155           /* Audio Call */
156           action = empathy_individual_audio_call_menu_item_new (NULL, contact);
157           gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
158           gtk_widget_show (action);
159
160           /* Video Call */
161           action = empathy_individual_video_call_menu_item_new (NULL, contact);
162           gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
163           gtk_widget_show (action);
164         }
165
166       /* Log */
167       if (features & EMPATHY_INDIVIDUAL_FEATURE_LOG)
168         {
169           action = empathy_individual_log_menu_item_new (NULL, contact);
170           gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
171           gtk_widget_show (action);
172         }
173
174       /* Invite */
175       action = empathy_individual_invite_menu_item_new (NULL, contact);
176       gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
177       gtk_widget_show (action);
178
179       /* File transfer */
180       action = empathy_individual_file_transfer_menu_item_new (NULL, contact);
181       gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
182       gtk_widget_show (action);
183
184       /* Share my desktop */
185       action = empathy_individual_share_my_desktop_menu_item_new (NULL,
186           contact);
187       gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
188       gtk_widget_show (action);
189
190       gtk_menu_shell_append (GTK_MENU_SHELL (menu), contact_item);
191       gtk_widget_show (contact_item);
192
193       g_free (label);
194       g_object_unref (contact);
195     }
196 }
197
198 static void
199 individual_link_menu_item_activate_cb (EmpathyIndividualMenu *self)
200 {
201   EmpathyIndividualMenuPriv *priv = GET_PRIV (self);
202   GtkWidget *dialog;
203
204   dialog = empathy_linking_dialog_show (priv->individual, NULL);
205   g_signal_emit (self, signals[SIGNAL_LINK_CONTACTS_ACTIVATED], 0, dialog);
206 }
207
208 static void
209 empathy_individual_menu_init (EmpathyIndividualMenu *self)
210 {
211   EmpathyIndividualMenuPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
212       EMPATHY_TYPE_INDIVIDUAL_MENU, EmpathyIndividualMenuPriv);
213
214   self->priv = priv;
215 }
216
217 static void
218 constructed (GObject *object)
219 {
220   EmpathyIndividualMenuPriv *priv = GET_PRIV (object);
221   GtkMenuShell *shell;
222   GtkWidget *item;
223   FolksIndividual *individual;
224   EmpathyIndividualFeatureFlags features;
225
226   /* Build the menu */
227   shell = GTK_MENU_SHELL (object);
228   individual = priv->individual;
229   features = priv->features;
230
231   /* Add Contact */
232   item = empathy_individual_add_menu_item_new (individual);
233   if (item)
234     {
235       gtk_menu_shell_append (shell, item);
236       gtk_widget_show (item);
237     }
238
239   /* Chat */
240   if (features & EMPATHY_INDIVIDUAL_FEATURE_CHAT)
241     {
242       item = empathy_individual_chat_menu_item_new (individual, NULL);
243       if (item != NULL)
244         {
245           gtk_menu_shell_append (shell, item);
246           gtk_widget_show (item);
247         }
248     }
249
250   if (features & EMPATHY_INDIVIDUAL_FEATURE_CALL)
251     {
252       /* Audio Call */
253       item = empathy_individual_audio_call_menu_item_new (individual, NULL);
254       gtk_menu_shell_append (shell, item);
255       gtk_widget_show (item);
256
257       /* Video Call */
258       item = empathy_individual_video_call_menu_item_new (individual, NULL);
259       gtk_menu_shell_append (shell, item);
260       gtk_widget_show (item);
261     }
262
263   /* Log */
264   if (features & EMPATHY_INDIVIDUAL_FEATURE_LOG)
265     {
266       item = empathy_individual_log_menu_item_new (individual, NULL);
267       gtk_menu_shell_append (shell, item);
268       gtk_widget_show (item);
269     }
270
271   /* Invite */
272   item = empathy_individual_invite_menu_item_new (individual, NULL);
273   gtk_menu_shell_append (shell, item);
274   gtk_widget_show (item);
275
276   /* File transfer */
277   item = empathy_individual_file_transfer_menu_item_new (individual, NULL);
278   gtk_menu_shell_append (shell, item);
279   gtk_widget_show (item);
280
281   /* Share my desktop */
282   /* FIXME we should add the "Share my desktop" menu item if Vino is
283   a registered handler in MC5 */
284   item = empathy_individual_share_my_desktop_menu_item_new (individual, NULL);
285   gtk_menu_shell_append (shell, item);
286   gtk_widget_show (item);
287
288   /* Menu items to target specific contacts */
289   individual_menu_add_personas (GTK_MENU_SHELL (object), individual, features);
290
291   /* Separator */
292   if (features & (EMPATHY_INDIVIDUAL_FEATURE_EDIT |
293       EMPATHY_INDIVIDUAL_FEATURE_INFO |
294       EMPATHY_INDIVIDUAL_FEATURE_FAVOURITE |
295       EMPATHY_INDIVIDUAL_FEATURE_LINK))
296     {
297       item = gtk_separator_menu_item_new ();
298       gtk_menu_shell_append (shell, item);
299       gtk_widget_show (item);
300     }
301
302   /* Edit */
303   if (features & EMPATHY_INDIVIDUAL_FEATURE_EDIT)
304     {
305       item = empathy_individual_edit_menu_item_new (individual);
306       gtk_menu_shell_append (shell, item);
307       gtk_widget_show (item);
308     }
309
310   /* Link */
311   if (features & EMPATHY_INDIVIDUAL_FEATURE_LINK)
312     {
313       item = empathy_individual_link_menu_item_new (individual);
314       gtk_menu_shell_append (shell, item);
315
316       g_signal_connect_swapped (item, "activate",
317           (GCallback) individual_link_menu_item_activate_cb, object);
318
319       gtk_widget_show (item);
320     }
321
322   /* Info */
323   if (features & EMPATHY_INDIVIDUAL_FEATURE_INFO)
324     {
325       item = empathy_individual_info_menu_item_new (individual);
326       gtk_menu_shell_append (shell, item);
327       gtk_widget_show (item);
328     }
329
330   /* Favorite checkbox */
331   if (features & EMPATHY_INDIVIDUAL_FEATURE_FAVOURITE)
332     {
333       item = empathy_individual_favourite_menu_item_new (individual);
334       gtk_menu_shell_append (shell, item);
335       gtk_widget_show (item);
336     }
337 }
338
339 static void
340 get_property (GObject *object,
341     guint param_id,
342     GValue *value,
343     GParamSpec *pspec)
344 {
345   EmpathyIndividualMenuPriv *priv;
346
347   priv = GET_PRIV (object);
348
349   switch (param_id)
350     {
351       case PROP_INDIVIDUAL:
352         g_value_set_object (value, priv->individual);
353         break;
354       case PROP_FEATURES:
355         g_value_set_flags (value, priv->features);
356         break;
357       default:
358         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
359         break;
360     }
361 }
362
363 static void
364 set_property (GObject *object,
365     guint param_id,
366     const GValue *value,
367     GParamSpec *pspec)
368 {
369   EmpathyIndividualMenuPriv *priv;
370
371   priv = GET_PRIV (object);
372
373   switch (param_id)
374     {
375       case PROP_INDIVIDUAL:
376         priv->individual = g_value_dup_object (value);
377         break;
378       case PROP_FEATURES:
379         priv->features = g_value_get_flags (value);
380         break;
381       default:
382         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
383         break;
384     }
385 }
386
387 static void
388 dispose (GObject *object)
389 {
390   EmpathyIndividualMenuPriv *priv = GET_PRIV (object);
391
392   tp_clear_object (&priv->individual);
393
394   G_OBJECT_CLASS (empathy_individual_menu_parent_class)->dispose (object);
395 }
396
397 static void
398 empathy_individual_menu_class_init (EmpathyIndividualMenuClass *klass)
399 {
400   GObjectClass *object_class = G_OBJECT_CLASS (klass);
401
402   object_class->constructed = constructed;
403   object_class->get_property = get_property;
404   object_class->set_property = set_property;
405   object_class->dispose = dispose;
406
407   /**
408    * EmpathyIndividualMenu:individual:
409    *
410    * The #FolksIndividual the menu is for.
411    */
412   g_object_class_install_property (object_class, PROP_INDIVIDUAL,
413       g_param_spec_object ("individual",
414           "Individual",
415           "The #FolksIndividual the menu is for.",
416           FOLKS_TYPE_INDIVIDUAL,
417           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
418
419   /**
420    * EmpathyIndividualMenu:features:
421    *
422    * A set of feature flags controlling which entries are shown.
423    */
424   g_object_class_install_property (object_class, PROP_FEATURES,
425       g_param_spec_flags ("features",
426           "Features",
427           "A set of feature flags controlling which entries are shown.",
428           EMPATHY_TYPE_INDIVIDUAL_FEATURE_FLAGS,
429           EMPATHY_INDIVIDUAL_FEATURE_NONE,
430           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
431
432   signals[SIGNAL_LINK_CONTACTS_ACTIVATED] =
433       g_signal_new ("link-contacts-activated", G_OBJECT_CLASS_TYPE (klass),
434           G_SIGNAL_RUN_LAST, 0, NULL, NULL,
435           g_cclosure_marshal_VOID__OBJECT,
436           G_TYPE_NONE, 1, EMPATHY_TYPE_LINKING_DIALOG);
437
438   g_type_class_add_private (object_class, sizeof (EmpathyIndividualMenuPriv));
439 }
440
441 GtkWidget *
442 empathy_individual_menu_new (FolksIndividual *individual,
443     EmpathyIndividualFeatureFlags features)
444 {
445   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
446   g_return_val_if_fail (features != EMPATHY_INDIVIDUAL_FEATURE_NONE, NULL);
447
448   return g_object_new (EMPATHY_TYPE_INDIVIDUAL_MENU,
449       "individual", individual,
450       "features", features,
451       NULL);
452 }
453
454 static void
455 empathy_individual_add_menu_item_activated (GtkMenuItem *item,
456   FolksIndividual *individual)
457 {
458   GtkWidget *toplevel;
459
460   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (item));
461   if (!gtk_widget_is_toplevel (toplevel) || !GTK_IS_WINDOW (toplevel))
462     toplevel = NULL;
463
464   empathy_new_individual_dialog_show_with_individual (GTK_WINDOW (toplevel),
465       individual);
466 }
467
468 GtkWidget *
469 empathy_individual_add_menu_item_new (FolksIndividual *individual)
470 {
471   GtkWidget *item;
472   GtkWidget *image;
473   EmpathyIndividualManager *manager = NULL;
474   EmpathyContact *contact = NULL;
475   TpConnection *connection;
476   GList *l, *members;
477   gboolean found = FALSE;
478   EmpathyIndividualManagerFlags flags;
479
480   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
481
482   if (!empathy_individual_manager_initialized ())
483     {
484       item = NULL;
485       goto out;
486     }
487
488   manager = empathy_individual_manager_dup_singleton ();
489   contact = empathy_contact_dup_from_folks_individual (individual);
490   connection = empathy_contact_get_connection (contact);
491
492   flags = empathy_individual_manager_get_flags_for_connection (manager,
493       connection);
494
495   if (!(flags & EMPATHY_INDIVIDUAL_MANAGER_CAN_ADD))
496     {
497       item = NULL;
498       goto out;
499     }
500
501   members = empathy_individual_manager_get_members (
502       EMPATHY_INDIVIDUAL_MANAGER (manager));
503
504   for (l = members; l && !found; l = l->next)
505     {
506       if (!tp_strdiff (folks_individual_get_id (l->data),
507               folks_individual_get_id (individual)))
508         {
509           found = TRUE;
510         }
511     }
512   g_list_free (members);
513
514   if (found)
515     {
516       item = NULL;
517       goto out;
518     }
519
520   item = gtk_image_menu_item_new_with_mnemonic (_("_Add Contactā€¦"));
521   image = gtk_image_new_from_icon_name (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU);
522   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
523
524   g_signal_connect (item, "activate",
525       G_CALLBACK (empathy_individual_add_menu_item_activated),
526       individual);
527
528 out:
529   tp_clear_object (&contact);
530   tp_clear_object (&manager);
531
532   return item;
533 }
534
535 typedef gboolean (*SensitivityPredicate) (EmpathyContact *contact);
536
537 /* Like menu_item_set_first_contact(), but always operates upon the given
538  * contact */
539 static gboolean
540 menu_item_set_contact (GtkWidget *item,
541     EmpathyContact *contact,
542     GCallback activate_callback,
543     SensitivityPredicate sensitivity_predicate)
544 {
545   gboolean contact_valid = TRUE;
546
547   if (sensitivity_predicate != NULL)
548     {
549       contact_valid = sensitivity_predicate (contact);
550       gtk_widget_set_sensitive (item, sensitivity_predicate (contact));
551     }
552
553   g_signal_connect (item, "activate", G_CALLBACK (activate_callback),
554       contact);
555
556   return contact_valid;
557 }
558
559 /**
560  * Set the given menu @item to call @activate_callback using the TpContact
561  * (associated with @individual) with the highest availability who is also valid
562  * whenever @item is activated.
563  *
564  * @sensitivity_predicate is an optional function to determine whether the menu
565  * item should be insensitive (if the function returns @FALSE). Otherwise, the
566  * menu item's sensitivity will not change.
567  */
568 static GtkWidget *
569 menu_item_set_first_contact (GtkWidget *item,
570     FolksIndividual *individual,
571     GCallback activate_callback,
572     SensitivityPredicate sensitivity_predicate)
573 {
574   GList *personas, *l;
575   FolksPresenceType best_presence = FOLKS_PRESENCE_TYPE_UNSET;
576   EmpathyContact *best_contact = NULL;
577
578   personas = folks_individual_get_personas (individual);
579   for (l = personas; l != NULL; l = l->next)
580     {
581       TpfPersona *persona = l->data;
582       FolksPresenceType presence;
583
584       if (!TPF_IS_PERSONA (persona))
585         continue;
586
587       /* Only choose the contact if it has a higher presence than our current
588        * best choice of contact. */
589       presence = folks_presence_get_presence_type (FOLKS_PRESENCE (l->data));
590       if (folks_presence_typecmp (presence, best_presence) > 0)
591         {
592           TpContact *tp_contact;
593           EmpathyContact *contact;
594
595           tp_contact = tpf_persona_get_contact (TPF_PERSONA (l->data));
596           contact = empathy_contact_dup_from_tp_contact (tp_contact);
597           empathy_contact_set_persona (contact, FOLKS_PERSONA (l->data));
598
599           if (best_contact == NULL || sensitivity_predicate == NULL ||
600               sensitivity_predicate (contact) == TRUE)
601             {
602               tp_clear_object (&best_contact);
603
604               best_presence = presence;
605               best_contact = g_object_ref (contact);
606             }
607
608           g_object_unref (contact);
609         }
610     }
611
612   /* Use the best contact we found */
613   if (best_contact != NULL)
614     {
615       menu_item_set_contact (item, best_contact, G_CALLBACK (activate_callback),
616           sensitivity_predicate);
617
618       g_object_unref (best_contact);
619     }
620
621   return item;
622 }
623
624 static void
625 empathy_individual_chat_menu_item_activated (GtkMenuItem *item,
626   EmpathyContact *contact)
627 {
628   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
629
630   empathy_dispatcher_chat_with_contact (contact, gtk_get_current_event_time ());
631 }
632
633 GtkWidget *
634 empathy_individual_chat_menu_item_new (FolksIndividual *individual,
635     EmpathyContact *contact)
636 {
637   GtkWidget *item;
638   GtkWidget *image;
639
640   g_return_val_if_fail ((FOLKS_IS_INDIVIDUAL (individual) &&
641       empathy_folks_individual_contains_contact (individual)) ||
642       EMPATHY_IS_CONTACT (contact),
643       NULL);
644
645   item = gtk_image_menu_item_new_with_mnemonic (_("_Chat"));
646   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_MESSAGE,
647       GTK_ICON_SIZE_MENU);
648   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
649   gtk_widget_show (image);
650
651   if (contact != NULL)
652     {
653       menu_item_set_contact (item, contact,
654           G_CALLBACK (empathy_individual_chat_menu_item_activated), NULL);
655     }
656   else
657     {
658       menu_item_set_first_contact (item, individual,
659           G_CALLBACK (empathy_individual_chat_menu_item_activated), NULL);
660     }
661
662   return item;
663 }
664
665 static void
666 empathy_individual_audio_call_menu_item_activated (GtkMenuItem *item,
667   EmpathyContact *contact)
668 {
669   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
670
671   empathy_call_factory_new_call_with_streams (contact, TRUE, FALSE,
672       gtk_get_current_event_time (), NULL);
673 }
674
675 GtkWidget *
676 empathy_individual_audio_call_menu_item_new (FolksIndividual *individual,
677     EmpathyContact *contact)
678 {
679   GtkWidget *item;
680   GtkWidget *image;
681
682   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual) ||
683       EMPATHY_IS_CONTACT (contact),
684       NULL);
685
686   item = gtk_image_menu_item_new_with_mnemonic (C_("menu item", "_Audio Call"));
687   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_VOIP, GTK_ICON_SIZE_MENU);
688   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
689   gtk_widget_show (image);
690
691   if (contact != NULL)
692     {
693       menu_item_set_contact (item, contact,
694           G_CALLBACK (empathy_individual_audio_call_menu_item_activated),
695           empathy_contact_can_voip_audio);
696     }
697   else
698     {
699       menu_item_set_first_contact (item, individual,
700           G_CALLBACK (empathy_individual_audio_call_menu_item_activated),
701           empathy_contact_can_voip_audio);
702     }
703
704   return item;
705 }
706
707 static void
708 empathy_individual_video_call_menu_item_activated (GtkMenuItem *item,
709   EmpathyContact *contact)
710 {
711   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
712
713   empathy_call_factory_new_call_with_streams (contact, TRUE, TRUE,
714       gtk_get_current_event_time (), NULL);
715 }
716
717 GtkWidget *
718 empathy_individual_video_call_menu_item_new (FolksIndividual *individual,
719     EmpathyContact *contact)
720 {
721   GtkWidget *item;
722   GtkWidget *image;
723
724   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual) ||
725       EMPATHY_IS_CONTACT (contact),
726       NULL);
727
728   item = gtk_image_menu_item_new_with_mnemonic (C_("menu item", "_Video Call"));
729   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_VIDEO_CALL,
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   if (contact != NULL)
735     {
736       menu_item_set_contact (item, contact,
737           G_CALLBACK (empathy_individual_video_call_menu_item_activated),
738           empathy_contact_can_voip_video);
739     }
740   else
741     {
742       menu_item_set_first_contact (item, individual,
743           G_CALLBACK (empathy_individual_video_call_menu_item_activated),
744           empathy_contact_can_voip_video);
745     }
746
747   return item;
748 }
749
750 static void
751 empathy_individual_log_menu_item_activated (GtkMenuItem *item,
752   EmpathyContact *contact)
753 {
754   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
755
756   empathy_log_window_show (empathy_contact_get_account (contact),
757       empathy_contact_get_id (contact), FALSE, NULL);
758 }
759
760 static gboolean
761 contact_has_log (EmpathyContact *contact)
762 {
763   TplLogManager *manager;
764   gboolean have_log;
765
766   manager = tpl_log_manager_dup_singleton ();
767   have_log = tpl_log_manager_exists (manager,
768       empathy_contact_get_account (contact), empathy_contact_get_id (contact),
769       FALSE);
770   g_object_unref (manager);
771
772   return have_log;
773 }
774
775 GtkWidget *
776 empathy_individual_log_menu_item_new (FolksIndividual *individual,
777     EmpathyContact *contact)
778 {
779   GtkWidget *item;
780   GtkWidget *image;
781
782   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual) ||
783       EMPATHY_IS_CONTACT (contact),
784       NULL);
785
786   item = gtk_image_menu_item_new_with_mnemonic (_("_Previous Conversations"));
787   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_LOG, GTK_ICON_SIZE_MENU);
788   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
789   gtk_widget_show (image);
790
791   if (contact != NULL)
792     {
793       menu_item_set_contact (item, contact,
794           G_CALLBACK (empathy_individual_log_menu_item_activated),
795           contact_has_log);
796     }
797   else
798     {
799       menu_item_set_first_contact (item, individual,
800           G_CALLBACK (empathy_individual_log_menu_item_activated),
801           contact_has_log);
802     }
803
804   return item;
805 }
806
807 static void
808 empathy_individual_file_transfer_menu_item_activated (GtkMenuItem *item,
809     EmpathyContact *contact)
810 {
811   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
812
813   empathy_send_file_with_file_chooser (contact);
814 }
815
816 GtkWidget *
817 empathy_individual_file_transfer_menu_item_new (FolksIndividual *individual,
818     EmpathyContact *contact)
819 {
820   GtkWidget *item;
821   GtkWidget *image;
822
823   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual) ||
824       EMPATHY_IS_CONTACT (contact),
825       NULL);
826
827   item = gtk_image_menu_item_new_with_mnemonic (_("Send File"));
828   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_DOCUMENT_SEND,
829       GTK_ICON_SIZE_MENU);
830   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
831   gtk_widget_show (image);
832
833   if (contact != NULL)
834     {
835       menu_item_set_contact (item, contact,
836           G_CALLBACK (empathy_individual_file_transfer_menu_item_activated),
837           empathy_contact_can_send_files);
838     }
839   else
840     {
841       menu_item_set_first_contact (item, individual,
842           G_CALLBACK (empathy_individual_file_transfer_menu_item_activated),
843           empathy_contact_can_send_files);
844     }
845
846   return item;
847 }
848
849 static void
850 empathy_individual_share_my_desktop_menu_item_activated (GtkMenuItem *item,
851     EmpathyContact *contact)
852 {
853   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
854
855   empathy_share_my_desktop_share_with_contact (contact);
856 }
857
858 GtkWidget *
859 empathy_individual_share_my_desktop_menu_item_new (FolksIndividual *individual,
860     EmpathyContact *contact)
861 {
862   GtkWidget *item;
863   GtkWidget *image;
864
865   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual) ||
866       EMPATHY_IS_CONTACT (contact),
867       NULL);
868
869   item = gtk_image_menu_item_new_with_mnemonic (_("Share My Desktop"));
870   image = gtk_image_new_from_icon_name (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU);
871   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
872   gtk_widget_show (image);
873
874   if (contact != NULL)
875     {
876       menu_item_set_contact (item, contact,
877           G_CALLBACK (empathy_individual_share_my_desktop_menu_item_activated),
878           empathy_contact_can_use_rfb_stream_tube);
879     }
880   else
881     {
882       menu_item_set_first_contact (item, individual,
883           G_CALLBACK (empathy_individual_share_my_desktop_menu_item_activated),
884           empathy_contact_can_use_rfb_stream_tube);
885     }
886
887   return item;
888 }
889
890 static void
891 favourite_menu_item_toggled_cb (GtkCheckMenuItem *item,
892   FolksIndividual *individual)
893 {
894   folks_favourite_set_is_favourite (FOLKS_FAVOURITE (individual),
895       gtk_check_menu_item_get_active (item));
896 }
897
898 GtkWidget *
899 empathy_individual_favourite_menu_item_new (FolksIndividual *individual)
900 {
901   GtkWidget *item;
902
903   item = gtk_check_menu_item_new_with_label (_("Favorite"));
904
905   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
906       folks_favourite_get_is_favourite (FOLKS_FAVOURITE (individual)));
907
908   g_signal_connect (item, "toggled",
909       G_CALLBACK (favourite_menu_item_toggled_cb), individual);
910
911   return item;
912 }
913
914 static void
915 individual_info_menu_item_activate_cb (FolksIndividual *individual)
916 {
917   empathy_individual_information_dialog_show (individual, NULL);
918 }
919
920 GtkWidget *
921 empathy_individual_info_menu_item_new (FolksIndividual *individual)
922 {
923   GtkWidget *item;
924   GtkWidget *image;
925
926   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
927   g_return_val_if_fail (empathy_folks_individual_contains_contact (individual),
928       NULL);
929
930   item = gtk_image_menu_item_new_with_mnemonic (_("Infor_mation"));
931   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_CONTACT_INFORMATION,
932                 GTK_ICON_SIZE_MENU);
933   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
934   gtk_widget_show (image);
935
936   g_signal_connect_swapped (item, "activate",
937           G_CALLBACK (individual_info_menu_item_activate_cb),
938           individual);
939
940   return item;
941 }
942
943 static void
944 individual_edit_menu_item_activate_cb (FolksIndividual *individual)
945 {
946   empathy_individual_edit_dialog_show (individual, NULL);
947 }
948
949 GtkWidget *
950 empathy_individual_edit_menu_item_new (FolksIndividual *individual)
951 {
952   EmpathyIndividualManager *manager;
953   GtkWidget *item;
954   GtkWidget *image;
955   gboolean enable = FALSE;
956   EmpathyContact *contact;
957
958   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
959
960   contact = empathy_contact_dup_from_folks_individual (individual);
961
962   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
963
964   if (empathy_individual_manager_initialized ())
965     {
966       TpConnection *connection;
967       EmpathyIndividualManagerFlags flags;
968
969       manager = empathy_individual_manager_dup_singleton ();
970       connection = empathy_contact_get_connection (contact);
971       flags = empathy_individual_manager_get_flags_for_connection (
972           manager, connection);
973
974       enable = (flags & EMPATHY_INDIVIDUAL_MANAGER_CAN_ALIAS ||
975                 flags & EMPATHY_INDIVIDUAL_MANAGER_CAN_GROUP);
976
977       g_object_unref (manager);
978     }
979
980   item = gtk_image_menu_item_new_with_mnemonic (
981       C_("Edit individual (contextual menu)", "_Edit"));
982   image = gtk_image_new_from_icon_name (GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU);
983   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
984   gtk_widget_show (image);
985
986   gtk_widget_set_sensitive (item, enable);
987
988   g_signal_connect_swapped (item, "activate",
989       G_CALLBACK (individual_edit_menu_item_activate_cb), individual);
990
991   g_object_unref (contact);
992
993   return item;
994 }
995
996 GtkWidget *
997 empathy_individual_link_menu_item_new (FolksIndividual *individual)
998 {
999   GtkWidget *item;
1000   /*GtkWidget *image;*/
1001
1002   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
1003
1004   item = gtk_image_menu_item_new_with_mnemonic (
1005       /* Translators: this is a verb meaning "to connect two contacts together
1006        * to form a meta-contact". */
1007       C_("Link individual (contextual menu)", "_Linkā€¦"));
1008   /* TODO */
1009   /*image = gtk_image_new_from_icon_name (GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU);
1010   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1011   gtk_widget_show (image);*/
1012
1013   /* Only allow trusted Individuals to be linked */
1014   gtk_widget_set_sensitive (item,
1015       folks_individual_get_trust_level (individual) ==
1016           FOLKS_TRUST_LEVEL_PERSONAS);
1017
1018   return item;
1019 }
1020
1021 typedef struct
1022 {
1023   FolksIndividual *individual;
1024   EmpathyContact *contact;
1025   EmpathyChatroom *chatroom;
1026 } RoomSubMenuData;
1027
1028 static RoomSubMenuData *
1029 room_sub_menu_data_new (FolksIndividual *individual,
1030     EmpathyContact *contact,
1031     EmpathyChatroom *chatroom)
1032 {
1033   RoomSubMenuData *data;
1034
1035   data = g_slice_new0 (RoomSubMenuData);
1036   if (individual != NULL)
1037     data->individual = g_object_ref (individual);
1038   if (contact != NULL)
1039     data->contact = g_object_ref (contact);
1040   data->chatroom = g_object_ref (chatroom);
1041
1042   return data;
1043 }
1044
1045 static void
1046 room_sub_menu_data_free (RoomSubMenuData *data)
1047 {
1048   tp_clear_object (&data->individual);
1049   tp_clear_object (&data->contact);
1050   g_object_unref (data->chatroom);
1051   g_slice_free (RoomSubMenuData, data);
1052 }
1053
1054 static void
1055 room_sub_menu_activate_cb (GtkWidget *item,
1056          RoomSubMenuData *data)
1057 {
1058   EmpathyTpChat *chat;
1059   EmpathyChatroomManager *mgr;
1060   EmpathyContact *contact = NULL;
1061   GList *personas, *l;
1062
1063   chat = empathy_chatroom_get_tp_chat (data->chatroom);
1064   if (chat == NULL)
1065     {
1066       /* channel was invalidated. Ignoring */
1067       return;
1068     }
1069
1070   mgr = empathy_chatroom_manager_dup_singleton (NULL);
1071
1072   if (data->contact != NULL)
1073     contact = g_object_ref (data->contact);
1074   else
1075     {
1076       /* find the first of this Individual's contacts who can join this room */
1077       personas = folks_individual_get_personas (data->individual);
1078       for (l = personas; l != NULL && contact == NULL; l = g_list_next (l))
1079         {
1080           TpfPersona *persona = l->data;
1081           TpContact *tp_contact;
1082           GList *rooms;
1083
1084           if (!TPF_IS_PERSONA (persona))
1085             continue;
1086
1087           tp_contact = tpf_persona_get_contact (persona);
1088           contact = empathy_contact_dup_from_tp_contact (tp_contact);
1089
1090           rooms = empathy_chatroom_manager_get_chatrooms (mgr,
1091               empathy_contact_get_account (contact));
1092
1093           if (g_list_find (rooms, data->chatroom) == NULL)
1094             tp_clear_object (&contact);
1095
1096           /* if contact != NULL here, we've found our match */
1097
1098           g_list_free (rooms);
1099         }
1100     }
1101
1102   g_object_unref (mgr);
1103
1104   if (contact == NULL)
1105     {
1106       /* contact disappeared. Ignoring */
1107       goto out;
1108     }
1109
1110   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1111
1112   /* send invitation */
1113   empathy_contact_list_add (EMPATHY_CONTACT_LIST (chat),
1114       contact, _("Inviting you to this room"));
1115
1116 out:
1117   g_object_unref (contact);
1118 }
1119
1120 static GtkWidget *
1121 create_room_sub_menu (FolksIndividual *individual,
1122                       EmpathyContact *contact,
1123                       EmpathyChatroom *chatroom)
1124 {
1125   GtkWidget *item;
1126   RoomSubMenuData *data;
1127
1128   item = gtk_menu_item_new_with_label (empathy_chatroom_get_name (chatroom));
1129   data = room_sub_menu_data_new (individual, contact, chatroom);
1130   g_signal_connect_data (item, "activate",
1131       G_CALLBACK (room_sub_menu_activate_cb), data,
1132       (GClosureNotify) room_sub_menu_data_free, 0);
1133
1134   return item;
1135 }
1136
1137 GtkWidget *
1138 empathy_individual_invite_menu_item_new (FolksIndividual *individual,
1139     EmpathyContact *contact)
1140 {
1141   GtkWidget *item;
1142   GtkWidget *image;
1143   GtkWidget *room_item;
1144   EmpathyChatroomManager *mgr;
1145   GList *personas;
1146   GList *rooms = NULL;
1147   GList *names = NULL;
1148   GList *l;
1149   GtkWidget *submenu = NULL;
1150   /* map of chat room names to their objects; just a utility to remove
1151    * duplicates and to make construction of the alphabetized list easier */
1152   GHashTable *name_room_map;
1153
1154   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual) ||
1155       EMPATHY_IS_CONTACT (contact),
1156       NULL);
1157
1158   name_room_map = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
1159       g_object_unref);
1160
1161   item = gtk_image_menu_item_new_with_mnemonic (_("_Invite to Chat Room"));
1162   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_GROUP_MESSAGE,
1163       GTK_ICON_SIZE_MENU);
1164   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1165
1166   mgr = empathy_chatroom_manager_dup_singleton (NULL);
1167
1168   if (contact != NULL)
1169     {
1170       rooms = empathy_chatroom_manager_get_chatrooms (mgr,
1171           empathy_contact_get_account (contact));
1172     }
1173   else
1174     {
1175       /* collect the rooms from amongst all accounts for this Individual */
1176       personas = folks_individual_get_personas (individual);
1177       for (l = personas; l != NULL; l = g_list_next (l))
1178         {
1179           TpfPersona *persona = l->data;
1180           GList *rooms_cur;
1181           TpContact *tp_contact;
1182           EmpathyContact *contact_cur;
1183
1184           if (!TPF_IS_PERSONA (persona))
1185             continue;
1186
1187           tp_contact = tpf_persona_get_contact (persona);
1188           contact_cur = empathy_contact_dup_from_tp_contact (tp_contact);
1189
1190           rooms_cur = empathy_chatroom_manager_get_chatrooms (mgr,
1191               empathy_contact_get_account (contact_cur));
1192           rooms = g_list_concat (rooms, rooms_cur);
1193
1194           g_object_unref (contact_cur);
1195         }
1196     }
1197
1198   /* alphabetize the rooms */
1199   for (l = rooms; l != NULL; l = g_list_next (l))
1200     {
1201       EmpathyChatroom *chatroom = l->data;
1202       gboolean existed;
1203
1204       if (empathy_chatroom_get_tp_chat (chatroom) != NULL)
1205         {
1206           const gchar *name;
1207
1208           name = empathy_chatroom_get_name (chatroom);
1209           existed = (g_hash_table_lookup (name_room_map, name) != NULL);
1210           g_hash_table_insert (name_room_map, (gpointer) name,
1211               g_object_ref (chatroom));
1212
1213           /* this will take care of duplicates in rooms */
1214           if (!existed)
1215             {
1216               names = g_list_insert_sorted (names, (gpointer) name,
1217                   (GCompareFunc) g_strcmp0);
1218             }
1219         }
1220     }
1221
1222   for (l = names; l != NULL; l = g_list_next (l))
1223     {
1224       const gchar *name = l->data;
1225       EmpathyChatroom *chatroom;
1226
1227       if (G_UNLIKELY (submenu == NULL))
1228         submenu = gtk_menu_new ();
1229
1230       chatroom = g_hash_table_lookup (name_room_map, name);
1231       room_item = create_room_sub_menu (individual, contact, chatroom);
1232       gtk_menu_shell_append ((GtkMenuShell *) submenu, room_item);
1233       gtk_widget_show (room_item);
1234     }
1235
1236   if (submenu)
1237     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
1238   else
1239     gtk_widget_set_sensitive (item, FALSE);
1240
1241   gtk_widget_show (image);
1242
1243   g_hash_table_destroy (name_room_map);
1244   g_object_unref (mgr);
1245   g_list_free (names);
1246   g_list_free (rooms);
1247
1248   return item;
1249 }