]> git.0d.be Git - empathy.git/blob - src/empathy-event-manager.c
Bring up notifications until the conversation is accepted (#597125)
[empathy.git] / src / empathy-event-manager.c
1 /*
2  * Copyright (C) 2007-2008 Collabora Ltd.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  * Authors: Xavier Claessens <xclaesse@gmail.com>
19  *          Sjoerd Simons <sjoerd.simons@collabora.co.uk>
20  */
21
22 #include <config.h>
23
24 #include <string.h>
25 #include <glib/gi18n.h>
26
27 #include <telepathy-glib/util.h>
28 #include <telepathy-glib/interfaces.h>
29
30 #include <libempathy/empathy-account-manager.h>
31 #include <libempathy/empathy-dispatcher.h>
32 #include <libempathy/empathy-tp-contact-factory.h>
33 #include <libempathy/empathy-contact-manager.h>
34 #include <libempathy/empathy-tp-chat.h>
35 #include <libempathy/empathy-tp-call.h>
36 #include <libempathy/empathy-tp-file.h>
37 #include <libempathy/empathy-utils.h>
38 #include <libempathy/empathy-call-factory.h>
39
40 #include <extensions/extensions.h>
41
42 #include <libempathy-gtk/empathy-conf.h>
43 #include <libempathy-gtk/empathy-images.h>
44 #include <libempathy-gtk/empathy-contact-dialogs.h>
45 #include <libempathy-gtk/empathy-sound.h>
46
47 #include "empathy-event-manager.h"
48 #include "empathy-main-window.h"
49 #include "empathy-tube-dispatch.h"
50
51 #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
52 #include <libempathy/empathy-debug.h>
53
54 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyEventManager)
55
56 #define NOTIFICATION_TIMEOUT 2 /* seconds */
57
58 /* The time interval in milliseconds between 2 incoming rings */
59 #define MS_BETWEEN_RING 500
60
61 typedef struct {
62   EmpathyEventManager *manager;
63   EmpathyDispatchOperation *operation;
64   gulong approved_handler;
65   gulong claimed_handler;
66   gulong invalidated_handler;
67   /* Remove contact if applicable */
68   EmpathyContact *contact;
69   /* Tube dispatcher if applicable */
70   EmpathyTubeDispatch *tube_dispatch;
71   /* option signal handler and it's instance */
72   gulong handler;
73   GObject *handler_instance;
74   /* optional accept widget */
75   GtkWidget *dialog;
76 } EventManagerApproval;
77
78 typedef struct {
79   EmpathyDispatcher *dispatcher;
80   EmpathyContactManager *contact_manager;
81   GSList *events;
82   /* Ongoing approvals */
83   GSList *approvals;
84
85   gint ringing;
86 } EmpathyEventManagerPriv;
87
88 typedef struct _EventPriv EventPriv;
89 typedef void (*EventFunc) (EventPriv *event);
90
91 struct _EventPriv {
92   EmpathyEvent public;
93   EmpathyEventManager *manager;
94   EventManagerApproval *approval;
95   EventFunc func;
96   gboolean inhibit;
97   gpointer user_data;
98 };
99
100 enum {
101   EVENT_ADDED,
102   EVENT_REMOVED,
103   EVENT_UPDATED,
104   LAST_SIGNAL
105 };
106
107 static guint signals[LAST_SIGNAL];
108
109 G_DEFINE_TYPE (EmpathyEventManager, empathy_event_manager, G_TYPE_OBJECT);
110
111 static EmpathyEventManager * manager_singleton = NULL;
112
113 static EventManagerApproval *
114 event_manager_approval_new (EmpathyEventManager *manager,
115   EmpathyDispatchOperation *operation)
116 {
117   EventManagerApproval *result = g_slice_new0 (EventManagerApproval);
118   result->operation = g_object_ref (operation);
119   result->manager = manager;
120
121   return result;
122 }
123
124 static void
125 event_manager_approval_free (EventManagerApproval *approval)
126 {
127   g_signal_handler_disconnect (approval->operation,
128     approval->approved_handler);
129   g_signal_handler_disconnect (approval->operation,
130     approval->claimed_handler);
131   g_signal_handler_disconnect (approval->operation,
132     approval->invalidated_handler);
133   g_object_unref (approval->operation);
134
135   if (approval->handler != 0)
136     g_signal_handler_disconnect (approval->handler_instance,
137       approval->handler);
138
139   if (approval->contact != NULL)
140     g_object_unref (approval->contact);
141
142   if (approval->tube_dispatch != NULL)
143     g_object_unref (approval->tube_dispatch);
144
145   if (approval->dialog != NULL)
146     {
147       gtk_widget_destroy (approval->dialog);
148     }
149
150   g_slice_free (EventManagerApproval, approval);
151 }
152
153 static void event_remove (EventPriv *event);
154
155 static void
156 event_free (EventPriv *event)
157 {
158   g_free (event->public.icon_name);
159   g_free (event->public.header);
160   g_free (event->public.message);
161
162   if (event->public.contact)
163     {
164       g_object_unref (event->public.contact);
165     }
166
167   g_slice_free (EventPriv, event);
168 }
169
170 static void
171 event_remove (EventPriv *event)
172 {
173   EmpathyEventManagerPriv *priv = GET_PRIV (event->manager);
174
175   DEBUG ("Removing event %p", event);
176   priv->events = g_slist_remove (priv->events, event);
177   g_signal_emit (event->manager, signals[EVENT_REMOVED], 0, event);
178   event_free (event);
179 }
180
181 static gboolean
182 autoremove_event_timeout_cb (EventPriv *event)
183 {
184   event_remove (event);
185   return FALSE;
186 }
187
188 static void
189 event_manager_add (EmpathyEventManager *manager,
190     EmpathyContact *contact,
191     EmpathyEventType type,
192     const gchar *icon_name,
193     const gchar *header,
194     const gchar *message,
195     EventManagerApproval *approval,
196     EventFunc func,
197     gpointer user_data)
198 {
199   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
200   EventPriv               *event;
201
202   event = g_slice_new0 (EventPriv);
203   event->public.contact = contact ? g_object_ref (contact) : NULL;
204   event->public.type = type;
205   event->public.icon_name = g_strdup (icon_name);
206   event->public.header = g_strdup (header);
207   event->public.message = g_strdup (message);
208   event->public.must_ack = (func != NULL);
209   event->inhibit = FALSE;
210   event->func = func;
211   event->user_data = user_data;
212   event->manager = manager;
213   event->approval = approval;
214
215   DEBUG ("Adding event %p", event);
216   priv->events = g_slist_prepend (priv->events, event);
217   g_signal_emit (event->manager, signals[EVENT_ADDED], 0, event);
218
219   if (!event->public.must_ack)
220     {
221       g_timeout_add_seconds (NOTIFICATION_TIMEOUT,
222         (GSourceFunc) autoremove_event_timeout_cb, event);
223     }
224 }
225
226 static void
227 event_channel_process_func (EventPriv *event)
228 {
229   empathy_dispatch_operation_approve (event->approval->operation);
230 }
231
232 static void
233 event_text_channel_process_func (EventPriv *event)
234 {
235   EmpathyTpChat *tp_chat;
236
237   if (event->approval->handler != 0)
238     {
239       tp_chat = EMPATHY_TP_CHAT
240         (empathy_dispatch_operation_get_channel_wrapper (event->approval->operation));
241
242       g_signal_handler_disconnect (tp_chat, event->approval->handler);
243       event->approval->handler = 0;
244     }
245
246   empathy_dispatch_operation_approve (event->approval->operation);
247 }
248
249 static EventPriv *
250 event_lookup_by_approval (EmpathyEventManager *manager,
251   EventManagerApproval *approval)
252 {
253   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
254   GSList *l;
255   EventPriv *retval = NULL;
256
257   for (l = priv->events; l; l = l->next)
258     {
259       EventPriv *event = l->data;
260
261       if (event->approval == approval)
262         {
263           retval = event;
264           break;
265         }
266     }
267
268   return retval;
269 }
270
271 static void
272 event_update (EmpathyEventManager *manager, EventPriv *event,
273   const char *icon_name, const char *header, const char *msg)
274 {
275   g_free (event->public.icon_name);
276   g_free (event->public.header);
277   g_free (event->public.message);
278
279   event->public.icon_name = g_strdup (icon_name);
280   event->public.header = g_strdup (header);
281   event->public.message = g_strdup (msg);
282
283   g_signal_emit (manager, signals[EVENT_UPDATED], 0, event);
284 }
285
286 static void
287 event_manager_call_window_confirmation_dialog_response_cb (GtkDialog *dialog,
288   gint response, gpointer user_data)
289 {
290   EventManagerApproval *approval = user_data;
291
292   gtk_widget_destroy (approval->dialog);
293   approval->dialog = NULL;
294
295   if (response != GTK_RESPONSE_ACCEPT)
296     {
297       EmpathyTpCall *call =
298         EMPATHY_TP_CALL (
299           empathy_dispatch_operation_get_channel_wrapper (
300             approval->operation));
301
302       g_object_ref (call);
303       if (empathy_dispatch_operation_claim (approval->operation))
304         empathy_tp_call_close (call);
305       g_object_unref (call);
306
307     }
308   else
309     {
310       EmpathyCallFactory *factory = empathy_call_factory_get ();
311       empathy_call_factory_claim_channel (factory, approval->operation);
312     }
313 }
314
315 static void
316 event_channel_process_voip_func (EventPriv *event)
317 {
318   GtkWidget *dialog;
319   GtkWidget *button;
320   GtkWidget *image;
321
322   if (event->approval->dialog != NULL)
323     {
324       gtk_window_present (GTK_WINDOW (event->approval->dialog));
325       return;
326     }
327
328   dialog = gtk_message_dialog_new (NULL, 0,
329       GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("Incoming call"));
330   gtk_message_dialog_format_secondary_text (
331     GTK_MESSAGE_DIALOG (dialog),
332       _("%s is calling you, do you want to answer?"),
333       empathy_contact_get_name (event->approval->contact));
334
335   gtk_dialog_set_default_response (GTK_DIALOG (dialog),
336       GTK_RESPONSE_OK);
337
338   button = gtk_dialog_add_button (GTK_DIALOG (dialog),
339       _("_Reject"), GTK_RESPONSE_REJECT);
340   image = gtk_image_new_from_icon_name (GTK_STOCK_CANCEL,
341     GTK_ICON_SIZE_BUTTON);
342   gtk_button_set_image (GTK_BUTTON (button), image);
343
344   button = gtk_dialog_add_button (GTK_DIALOG (dialog),
345       _("_Answer"), GTK_RESPONSE_ACCEPT);
346
347   image = gtk_image_new_from_icon_name (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
348   gtk_button_set_image (GTK_BUTTON (button), image);
349
350   g_signal_connect (dialog, "response",
351       G_CALLBACK (event_manager_call_window_confirmation_dialog_response_cb),
352       event->approval);
353
354   gtk_widget_show (dialog);
355
356   event->approval->dialog = dialog;
357 }
358
359 static void
360 event_manager_chat_message_received_cb (EmpathyTpChat *tp_chat,
361   EmpathyMessage *message, EventManagerApproval *approval)
362 {
363   EmpathyContact  *sender;
364   const gchar     *header;
365   const gchar     *msg;
366   TpChannel       *channel;
367   EventPriv       *event;
368
369   /* try to update the event if it's referring to a chat which is already in the
370    * queue. */
371   event = event_lookup_by_approval (approval->manager, approval);
372
373   sender = empathy_message_get_sender (message);
374   header = empathy_contact_get_name (sender);
375   msg = empathy_message_get_body (message);
376
377   channel = empathy_tp_chat_get_channel (tp_chat);
378
379   if (event != NULL)
380     event_update (approval->manager, event, EMPATHY_IMAGE_NEW_MESSAGE, header, msg);
381   else
382     event_manager_add (approval->manager, sender, EMPATHY_EVENT_TYPE_CHAT,
383         EMPATHY_IMAGE_NEW_MESSAGE, header, msg, approval,
384         event_text_channel_process_func, NULL);
385
386   empathy_sound_play (empathy_main_window_get (),
387     EMPATHY_SOUND_CONVERSATION_NEW);
388 }
389
390 static void
391 event_manager_approval_done (EventManagerApproval *approval)
392 {
393   EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager);
394   GSList                  *l;
395
396   if (approval->operation != NULL)
397     {
398       GQuark channel_type;
399
400       channel_type = empathy_dispatch_operation_get_channel_type_id (
401           approval->operation);
402       if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA)
403         {
404           priv->ringing--;
405           if (priv->ringing == 0)
406             empathy_sound_stop (EMPATHY_SOUND_PHONE_INCOMING);
407         }
408     }
409
410   priv->approvals = g_slist_remove (priv->approvals, approval);
411
412   for (l = priv->events; l; l = l->next)
413     {
414       EventPriv *event = l->data;
415
416       if (event->approval == approval)
417         {
418           event_remove (event);
419           break;
420         }
421     }
422
423   event_manager_approval_free (approval);
424 }
425
426 static void
427 event_manager_operation_approved_cb (EmpathyDispatchOperation *operation,
428   EventManagerApproval *approval)
429 {
430   event_manager_approval_done (approval);
431 }
432
433 static void
434 event_manager_operation_claimed_cb (EmpathyDispatchOperation *operation,
435   EventManagerApproval *approval)
436 {
437   event_manager_approval_done (approval);
438 }
439
440 static void
441 event_manager_operation_invalidated_cb (EmpathyDispatchOperation *operation,
442   guint domain, gint code, gchar *message,
443   EventManagerApproval *approval)
444 {
445   event_manager_approval_done (approval);
446 }
447
448 static void
449 event_manager_media_channel_got_contact (EventManagerApproval *approval)
450 {
451   EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager);
452   gchar *header;
453
454   header = g_strdup_printf (_("Incoming call from %s"),
455     empathy_contact_get_name (approval->contact));
456
457   event_manager_add (approval->manager, approval->contact,
458       EMPATHY_EVENT_TYPE_VOIP, EMPATHY_IMAGE_VOIP, header, NULL, approval,
459       event_channel_process_voip_func, NULL);
460
461   g_free (header);
462
463   priv->ringing++;
464   if (priv->ringing == 1)
465     empathy_sound_start_playing (empathy_main_window_get (),
466         EMPATHY_SOUND_PHONE_INCOMING, MS_BETWEEN_RING);
467 }
468
469 static void
470 event_manager_media_channel_contact_changed_cb (EmpathyTpCall *call,
471   GParamSpec *param, EventManagerApproval *approval)
472 {
473   EmpathyContact *contact;
474
475   g_object_get (G_OBJECT (call), "contact", &contact, NULL);
476
477   if (contact == NULL)
478     return;
479
480   approval->contact = contact;
481   event_manager_media_channel_got_contact (approval);
482 }
483
484 static void
485 event_manager_tube_approved_cb (EventPriv *event)
486 {
487   empathy_tube_dispatch_handle (event->approval->tube_dispatch);
488 }
489
490 static void
491 event_manager_add_tube_approval (EventManagerApproval *approval,
492   EmpathyTubeDispatchAbility ability)
493 {
494   const gchar *icon_name;
495   gchar       *header;
496   const gchar *msg;
497
498   header = g_strdup_printf (_("%s is offering you an invitation"),
499     empathy_contact_get_name (approval->contact));
500
501   if (ability == EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE)
502     {
503       icon_name = GTK_STOCK_EXECUTE;
504       msg = _("An external application will be started to handle it.");
505     }
506   else
507     {
508       icon_name = GTK_STOCK_DIALOG_ERROR;
509       msg = _("You don't have the needed external "
510               "application to handle it.");
511     }
512
513   event_manager_add (approval->manager, approval->contact,
514       EMPATHY_EVENT_TYPE_TUBE, icon_name, header, msg, approval,
515       event_manager_tube_approved_cb, approval);
516
517   g_free (header);
518   /* FIXME better sound for incoming tubes ? */
519   empathy_sound_play (empathy_main_window_get (),
520     EMPATHY_SOUND_CONVERSATION_NEW);
521 }
522
523 static void
524 event_manager_tube_dispatch_ability_cb (GObject *object,
525    GParamSpec *spec, gpointer user_data)
526 {
527   EventManagerApproval *approval = (EventManagerApproval *) user_data;
528   EmpathyTubeDispatchAbility dispatchability;
529
530   dispatchability =
531     empathy_tube_dispatch_is_dispatchable (approval->tube_dispatch);
532
533   if (dispatchability != EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN)
534     {
535       event_manager_add_tube_approval (approval, dispatchability);
536       g_signal_handler_disconnect (object, approval->handler);
537       approval->handler = 0;
538     }
539 }
540
541 static void
542 event_manager_tube_got_contact_cb (EmpathyTpContactFactory *factory,
543                                    EmpathyContact *contact,
544                                    const GError *error,
545                                    gpointer user_data,
546                                    GObject *object)
547 {
548   EventManagerApproval *approval = (EventManagerApproval *) user_data;
549   EmpathyTubeDispatchAbility dispatchability;
550
551   if (error != NULL)
552     {
553       /* FIXME: We should probably still display the event */
554       DEBUG ("Error: %s", error->message);
555       return;
556     }
557
558   approval->contact = g_object_ref (contact);
559
560   dispatchability = empathy_tube_dispatch_is_dispatchable
561     (approval->tube_dispatch);
562
563   switch (dispatchability)
564     {
565       case EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN:
566         approval->handler = g_signal_connect (approval->tube_dispatch,
567           "notify::dispatchability",
568           G_CALLBACK (event_manager_tube_dispatch_ability_cb), approval);
569         approval->handler_instance = G_OBJECT (approval->tube_dispatch);
570         break;
571       case EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE:
572         /* fallthrough */
573       case EMPATHY_TUBE_DISPATCHABILITY_IMPOSSIBLE:
574         event_manager_add_tube_approval (approval, dispatchability);
575         break;
576     }
577 }
578
579 static void
580 invite_dialog_response_cb (GtkDialog *dialog,
581                            gint response,
582                            EventManagerApproval *approval)
583 {
584   EmpathyTpChat *tp_chat;
585   TpChannel *channel;
586   TpHandle self_handle;
587   GArray *members;
588
589   gtk_widget_destroy (GTK_WIDGET (approval->dialog));
590   approval->dialog = NULL;
591
592   tp_chat = EMPATHY_TP_CHAT (empathy_dispatch_operation_get_channel_wrapper (
593         approval->operation));
594
595   if (response != GTK_RESPONSE_OK)
596     {
597       /* close channel */
598       DEBUG ("Muc invitation rejected");
599
600       if (empathy_dispatch_operation_claim (approval->operation))
601         empathy_tp_chat_close (tp_chat);
602       return;
603     }
604
605   DEBUG ("Muc invitation accepted");
606
607   /* join the room */
608   channel = empathy_tp_chat_get_channel (tp_chat);
609
610   self_handle = tp_channel_group_get_self_handle (channel);
611   members = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), 1);
612   g_array_append_val (members, self_handle);
613
614   tp_cli_channel_interface_group_call_add_members (channel, -1, members,
615       "", NULL, NULL, NULL, NULL);
616
617   empathy_dispatch_operation_approve (approval->operation);
618
619   g_array_free (members, TRUE);
620 }
621
622 static void
623 event_room_channel_process_func (EventPriv *event)
624 {
625   GtkWidget *dialog, *button, *image;
626   TpChannel *channel = empathy_dispatch_operation_get_channel (
627       event->approval->operation);
628
629   if (event->approval->dialog != NULL)
630     {
631       gtk_window_present (GTK_WINDOW (event->approval->dialog));
632       return;
633     }
634
635   /* create dialog */
636   dialog = gtk_message_dialog_new (NULL, 0,
637       GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("Room invitation"));
638
639   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
640       _("%s is inviting you to join %s"),
641       empathy_contact_get_name (event->approval->contact),
642       tp_channel_get_identifier (channel));
643
644   gtk_dialog_set_default_response (GTK_DIALOG (dialog),
645       GTK_RESPONSE_OK);
646
647   button = gtk_dialog_add_button (GTK_DIALOG (dialog),
648       _("_Decline"), GTK_RESPONSE_CANCEL);
649   image = gtk_image_new_from_icon_name (GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
650   gtk_button_set_image (GTK_BUTTON (button), image);
651
652   button = gtk_dialog_add_button (GTK_DIALOG (dialog),
653       _("_Join"), GTK_RESPONSE_OK);
654   image = gtk_image_new_from_icon_name (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
655   gtk_button_set_image (GTK_BUTTON (button), image);
656
657   g_signal_connect (dialog, "response",
658       G_CALLBACK (invite_dialog_response_cb), event->approval);
659
660   gtk_widget_show (dialog);
661
662   event->approval->dialog = dialog;
663 }
664
665 static void
666 event_manager_muc_invite_got_contact_cb (EmpathyTpContactFactory *factory,
667                                          EmpathyContact *contact,
668                                          const GError *error,
669                                          gpointer user_data,
670                                          GObject *object)
671 {
672   EventManagerApproval *approval = (EventManagerApproval *) user_data;
673   TpChannel *channel;
674   const gchar *invite_msg;
675   gchar *msg;
676   TpHandle self_handle;
677
678   if (error != NULL)
679     {
680       /* FIXME: We should probably still display the event */
681       DEBUG ("Error: %s", error->message);
682       return;
683     }
684
685   approval->contact = g_object_ref (contact);
686   channel = empathy_dispatch_operation_get_channel (approval->operation);
687
688   self_handle = tp_channel_group_get_self_handle (channel);
689   tp_channel_group_get_local_pending_info (channel, self_handle, NULL, NULL,
690       &invite_msg);
691
692   msg = g_strdup_printf (_("%s invited you to join %s"),
693       empathy_contact_get_name (approval->contact),
694       tp_channel_get_identifier (channel));
695
696   event_manager_add (approval->manager, approval->contact,
697       EMPATHY_EVENT_TYPE_CHAT, EMPATHY_IMAGE_GROUP_MESSAGE, msg, invite_msg,
698       approval, event_room_channel_process_func, NULL);
699
700   empathy_sound_play (empathy_main_window_get (),
701     EMPATHY_SOUND_CONVERSATION_NEW);
702
703   g_free (msg);
704 }
705
706 static void
707 event_manager_ft_got_contact_cb (EmpathyTpContactFactory *factory,
708                                  EmpathyContact *contact,
709                                  const GError *error,
710                                  gpointer user_data,
711                                  GObject *object)
712 {
713   EventManagerApproval *approval = (EventManagerApproval *) user_data;
714   char *header;
715
716   approval->contact = contact;
717
718   header = g_strdup_printf (_("Incoming file transfer from %s"),
719                             empathy_contact_get_name (approval->contact));
720
721   event_manager_add (approval->manager, approval->contact,
722       EMPATHY_EVENT_TYPE_TRANSFER, EMPATHY_IMAGE_DOCUMENT_SEND, header, NULL,
723       approval, event_channel_process_func, NULL);
724
725   /* FIXME better sound for incoming file transfers ?*/
726   empathy_sound_play (empathy_main_window_get (),
727                       EMPATHY_SOUND_CONVERSATION_NEW);
728
729   g_free (header);
730 }
731
732 static void
733 event_manager_approve_channel_cb (EmpathyDispatcher *dispatcher,
734   EmpathyDispatchOperation  *operation, EmpathyEventManager *manager)
735 {
736   const gchar *channel_type;
737   EventManagerApproval *approval;
738   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
739
740   channel_type = empathy_dispatch_operation_get_channel_type (operation);
741
742   approval = event_manager_approval_new (manager, operation);
743   priv->approvals = g_slist_prepend (priv->approvals, approval);
744
745   approval->approved_handler = g_signal_connect (operation, "approved",
746     G_CALLBACK (event_manager_operation_approved_cb), approval);
747
748   approval->claimed_handler = g_signal_connect (operation, "claimed",
749      G_CALLBACK (event_manager_operation_claimed_cb), approval);
750
751   approval->invalidated_handler = g_signal_connect (operation, "invalidated",
752      G_CALLBACK (event_manager_operation_invalidated_cb), approval);
753
754   if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT))
755     {
756       EmpathyTpChat *tp_chat =
757         EMPATHY_TP_CHAT (
758           empathy_dispatch_operation_get_channel_wrapper (operation));
759       TpChannel *channel = empathy_tp_chat_get_channel (tp_chat);
760
761       if (tp_proxy_has_interface (channel, TP_IFACE_CHANNEL_INTERFACE_GROUP))
762         {
763           /* Are we in local-pending ? */
764           TpHandle self_handle, inviter;
765
766           self_handle = tp_channel_group_get_self_handle (channel);
767
768           if (self_handle != 0 && tp_channel_group_get_local_pending_info (
769                 channel, self_handle, &inviter, NULL, NULL))
770             {
771               /* We are invited to a room */
772               EmpathyTpContactFactory *factory;
773               TpConnection *connection;
774
775               DEBUG ("Have been invited to %s. Ask user if he wants to accept",
776                   tp_channel_get_identifier (channel));
777
778               connection = empathy_tp_chat_get_connection (tp_chat);
779               factory = empathy_tp_contact_factory_dup_singleton (connection);
780
781               empathy_tp_contact_factory_get_from_handle (factory,
782                   inviter, event_manager_muc_invite_got_contact_cb,
783                   approval, NULL, G_OBJECT (manager));
784
785               g_object_unref (factory);
786               return;
787             }
788
789           /* if we are not invited, let's wait for the first message */
790         }
791
792       /* 1-1 text channel, wait for the first message */
793       approval->handler = g_signal_connect (tp_chat, "message-received",
794         G_CALLBACK (event_manager_chat_message_received_cb), approval);
795       approval->handler_instance = G_OBJECT (tp_chat);
796     }
797   else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA))
798     {
799       EmpathyContact *contact;
800       EmpathyTpCall *call = EMPATHY_TP_CALL (
801           empathy_dispatch_operation_get_channel_wrapper (operation));
802
803       g_object_get (G_OBJECT (call), "contact", &contact, NULL);
804
805       if (contact == NULL)
806         {
807           g_signal_connect (call, "notify::contact",
808             G_CALLBACK (event_manager_media_channel_contact_changed_cb),
809             approval);
810         }
811       else
812         {
813           approval->contact = contact;
814           event_manager_media_channel_got_contact (approval);
815         }
816
817     }
818   else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER))
819     {
820       TpChannel *channel;
821       TpConnection *connection;
822       TpHandle handle;
823       EmpathyTpContactFactory *factory;
824
825       channel = empathy_dispatch_operation_get_channel (operation);
826       handle = tp_channel_get_handle (channel, NULL);
827
828       connection = tp_channel_borrow_connection (channel);
829       factory = empathy_tp_contact_factory_dup_singleton (connection);
830       empathy_tp_contact_factory_get_from_handle (factory, handle,
831         event_manager_ft_got_contact_cb, approval, NULL, G_OBJECT (manager));
832
833       g_object_unref (factory);
834     }
835   else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE) ||
836       !tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE))
837     {
838       TpChannel *channel;
839       TpHandle handle;
840       TpHandleType handle_type;
841       TpConnection *connection;
842       EmpathyTpContactFactory *factory;
843
844       channel = empathy_dispatch_operation_get_channel (operation);
845       handle = tp_channel_get_handle (channel, &handle_type);
846
847       /* Only understand p2p tubes */
848       if (handle_type != TP_HANDLE_TYPE_CONTACT)
849         return;
850
851       approval->tube_dispatch = empathy_tube_dispatch_new (operation);
852       connection = tp_channel_borrow_connection (channel);
853       factory = empathy_tp_contact_factory_dup_singleton (connection);
854       empathy_tp_contact_factory_get_from_handle (factory, handle,
855         event_manager_tube_got_contact_cb, approval, NULL, G_OBJECT (manager));
856     }
857   else
858     {
859       DEBUG ("Unknown channel type (%s), ignoring..", channel_type);
860     }
861 }
862
863 static void
864 event_pending_subscribe_func (EventPriv *event)
865 {
866   empathy_subscription_dialog_show (event->public.contact, NULL);
867   event_remove (event);
868 }
869
870 static void
871 event_manager_pendings_changed_cb (EmpathyContactList  *list,
872   EmpathyContact *contact, EmpathyContact *actor,
873   guint reason, gchar *message, gboolean is_pending,
874   EmpathyEventManager *manager)
875 {
876   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
877   gchar                   *header, *event_msg;
878
879   if (!is_pending)
880     {
881       GSList *l;
882
883       for (l = priv->events; l; l = l->next)
884         {
885           EventPriv *event = l->data;
886
887           if (event->public.contact == contact &&
888               event->func == event_pending_subscribe_func)
889             {
890               event_remove (event);
891               break;
892             }
893         }
894
895       return;
896     }
897
898   header = g_strdup_printf (_("Subscription requested by %s"),
899     empathy_contact_get_name (contact));
900
901   if (!EMP_STR_EMPTY (message))
902     event_msg = g_strdup_printf (_("\nMessage: %s"), message);
903   else
904     event_msg = NULL;
905
906   event_manager_add (manager, contact, EMPATHY_EVENT_TYPE_SUBSCRIPTION,
907       GTK_STOCK_DIALOG_QUESTION, header, event_msg, NULL,
908       event_pending_subscribe_func, NULL);
909
910   g_free (event_msg);
911   g_free (header);
912 }
913
914 static void
915 event_manager_presence_changed_cb (EmpathyContactMonitor *monitor,
916     EmpathyContact *contact,
917     TpConnectionPresenceType current,
918     TpConnectionPresenceType previous,
919     EmpathyEventManager *manager)
920 {
921   EmpathyAccount *account;
922   gchar *header = NULL;
923   gboolean preference = FALSE;
924
925   account = empathy_contact_get_account (contact);
926   if (empathy_account_is_just_connected (account))
927     return;
928
929   if (tp_connection_presence_type_cmp_availability (previous,
930      TP_CONNECTION_PRESENCE_TYPE_OFFLINE) > 0)
931     {
932       /* contact was online */
933       empathy_conf_get_bool (empathy_conf_get (),
934                       EMPATHY_PREFS_NOTIFICATIONS_CONTACT_SIGNOUT, &preference);
935       if (preference && tp_connection_presence_type_cmp_availability (current,
936           TP_CONNECTION_PRESENCE_TYPE_OFFLINE) <= 0)
937         {
938           /* someone is logging off */
939           header = g_strdup_printf (_("%s is now offline."),
940             empathy_contact_get_name (contact));
941
942           event_manager_add (manager, contact, EMPATHY_EVENT_TYPE_PRESENCE,
943               GTK_STOCK_DIALOG_INFO, header, NULL, NULL, NULL, NULL);
944         }
945     }
946   else
947     {
948       /* contact was offline */
949       empathy_conf_get_bool (empathy_conf_get (),
950                       EMPATHY_PREFS_NOTIFICATIONS_CONTACT_SIGNIN, &preference);
951       if (preference && tp_connection_presence_type_cmp_availability (current,
952           TP_CONNECTION_PRESENCE_TYPE_OFFLINE) > 0)
953         {
954           /* someone is logging in */
955           header = g_strdup_printf (_("%s is now online."),
956             empathy_contact_get_name (contact));
957
958           event_manager_add (manager, contact, EMPATHY_EVENT_TYPE_PRESENCE,
959               GTK_STOCK_DIALOG_INFO, header, NULL, NULL, NULL, NULL);
960         }
961     }
962   g_free (header);
963 }
964
965
966 static GObject *
967 event_manager_constructor (GType type,
968                            guint n_props,
969                            GObjectConstructParam *props)
970 {
971         GObject *retval;
972
973         if (manager_singleton) {
974                 retval = g_object_ref (manager_singleton);
975         } else {
976                 retval = G_OBJECT_CLASS (empathy_event_manager_parent_class)->constructor
977                         (type, n_props, props);
978
979                 manager_singleton = EMPATHY_EVENT_MANAGER (retval);
980                 g_object_add_weak_pointer (retval, (gpointer) &manager_singleton);
981         }
982
983         return retval;
984 }
985
986 static void
987 event_manager_finalize (GObject *object)
988 {
989   EmpathyEventManagerPriv *priv = GET_PRIV (object);
990
991   if (priv->ringing > 0)
992     empathy_sound_stop (EMPATHY_SOUND_PHONE_INCOMING);
993
994   g_slist_foreach (priv->events, (GFunc) event_free, NULL);
995   g_slist_free (priv->events);
996   g_slist_foreach (priv->approvals, (GFunc) event_manager_approval_free, NULL);
997   g_slist_free (priv->approvals);
998   g_object_unref (priv->contact_manager);
999   g_object_unref (priv->dispatcher);
1000 }
1001
1002 static void
1003 empathy_event_manager_class_init (EmpathyEventManagerClass *klass)
1004 {
1005   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1006
1007   object_class->finalize = event_manager_finalize;
1008   object_class->constructor = event_manager_constructor;
1009
1010   signals[EVENT_ADDED] =
1011     g_signal_new ("event-added",
1012       G_TYPE_FROM_CLASS (klass),
1013       G_SIGNAL_RUN_LAST,
1014       0,
1015       NULL, NULL,
1016       g_cclosure_marshal_VOID__POINTER,
1017       G_TYPE_NONE,
1018       1, G_TYPE_POINTER);
1019
1020   signals[EVENT_REMOVED] =
1021   g_signal_new ("event-removed",
1022       G_TYPE_FROM_CLASS (klass),
1023       G_SIGNAL_RUN_LAST,
1024       0,
1025       NULL, NULL,
1026       g_cclosure_marshal_VOID__POINTER,
1027       G_TYPE_NONE, 1, G_TYPE_POINTER);
1028
1029   signals[EVENT_UPDATED] =
1030   g_signal_new ("event-updated",
1031       G_TYPE_FROM_CLASS (klass),
1032       G_SIGNAL_RUN_LAST,
1033       0,
1034       NULL, NULL,
1035       g_cclosure_marshal_VOID__POINTER,
1036       G_TYPE_NONE, 1, G_TYPE_POINTER);
1037
1038
1039   g_type_class_add_private (object_class, sizeof (EmpathyEventManagerPriv));
1040 }
1041
1042 static void
1043 empathy_event_manager_init (EmpathyEventManager *manager)
1044 {
1045   EmpathyEventManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
1046     EMPATHY_TYPE_EVENT_MANAGER, EmpathyEventManagerPriv);
1047   EmpathyContactMonitor *monitor;
1048   EmpathyContactList *list_iface;
1049
1050   list_iface = EMPATHY_CONTACT_LIST (empathy_contact_manager_dup_singleton ());
1051   monitor = empathy_contact_list_get_monitor (list_iface);
1052   g_object_unref (list_iface);
1053
1054   manager->priv = priv;
1055
1056   priv->dispatcher = empathy_dispatcher_dup_singleton ();
1057   priv->contact_manager = empathy_contact_manager_dup_singleton ();
1058   g_signal_connect (priv->dispatcher, "approve",
1059     G_CALLBACK (event_manager_approve_channel_cb), manager);
1060   g_signal_connect (priv->contact_manager, "pendings-changed",
1061     G_CALLBACK (event_manager_pendings_changed_cb), manager);
1062   g_signal_connect (monitor, "contact-presence-changed",
1063     G_CALLBACK (event_manager_presence_changed_cb), manager);
1064 }
1065
1066 EmpathyEventManager *
1067 empathy_event_manager_dup_singleton (void)
1068 {
1069   return g_object_new (EMPATHY_TYPE_EVENT_MANAGER, NULL);
1070 }
1071
1072 GSList *
1073 empathy_event_manager_get_events (EmpathyEventManager *manager)
1074 {
1075   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
1076
1077   g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
1078
1079   return priv->events;
1080 }
1081
1082 EmpathyEvent *
1083 empathy_event_manager_get_top_event (EmpathyEventManager *manager)
1084 {
1085   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
1086
1087   g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
1088
1089   return priv->events ? priv->events->data : NULL;
1090 }
1091
1092 void
1093 empathy_event_activate (EmpathyEvent *event_public)
1094 {
1095   EventPriv *event = (EventPriv *) event_public;
1096
1097   g_return_if_fail (event_public != NULL);
1098
1099   if (event->func)
1100     event->func (event);
1101   else
1102     event_remove (event);
1103 }
1104
1105 void
1106 empathy_event_inhibit_updates (EmpathyEvent *event_public)
1107 {
1108   EventPriv *event = (EventPriv *) event_public;
1109
1110   g_return_if_fail (event_public != NULL);
1111
1112   event->inhibit = TRUE;
1113 }