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