]> git.0d.be Git - empathy.git/blob - src/empathy-event-manager.c
Let the event-manager manage the sounds
[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
36 #include <extensions/extensions.h>
37
38 #include <libempathy-gtk/empathy-images.h>
39 #include <libempathy-gtk/empathy-contact-dialogs.h>
40 #include <libempathy-gtk/empathy-ui-utils.h>
41
42 #include "empathy-event-manager.h"
43 #include "empathy-main-window.h"
44 #include "empathy-tube-dispatch.h"
45
46 #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
47 #include <libempathy/empathy-debug.h>
48
49 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyEventManager)
50
51 typedef struct {
52   EmpathyEventManager *manager;
53   EmpathyDispatchOperation *operation;
54   gulong approved_handler;
55   gulong claimed_handler;
56   gulong invalidated_handler;
57   /* Remove contact if applicable */
58   EmpathyContact *contact;
59   /* Tube dispatcher if applicable */
60   EmpathyTubeDispatch *tube_dispatch;
61   /* option signal handler */
62   gulong handler;
63 } EventManagerApproval;
64
65 typedef struct {
66   EmpathyDispatcher *dispatcher;
67   EmpathyContactManager *contact_manager;
68   GSList *events;
69   /* Ongoing approvals */
70   GSList *approvals;
71
72   /* voip ringing sound */
73   guint voip_timeout;
74   gint ringing;
75 } EmpathyEventManagerPriv;
76
77 typedef struct _EventPriv EventPriv;
78 typedef void (*EventFunc) (EventPriv *event);
79
80 struct _EventPriv {
81   EmpathyEvent public;
82   EmpathyEventManager *manager;
83   EventManagerApproval *approval;
84   EventFunc func;
85   gboolean inhibit;
86   gpointer user_data;
87 };
88
89 enum {
90   EVENT_ADDED,
91   EVENT_REMOVED,
92   EVENT_UPDATED,
93   LAST_SIGNAL
94 };
95
96 static guint signals[LAST_SIGNAL];
97
98 G_DEFINE_TYPE (EmpathyEventManager, empathy_event_manager, G_TYPE_OBJECT);
99
100 static EmpathyEventManager * manager_singleton = NULL;
101
102 static EventManagerApproval *
103 event_manager_approval_new (EmpathyEventManager *manager,
104   EmpathyDispatchOperation *operation)
105 {
106   EventManagerApproval *result = g_slice_new0 (EventManagerApproval);
107   result->operation = g_object_ref (operation);
108   result->manager = manager;
109
110   return result;
111 }
112
113 static void
114 event_manager_approval_free (EventManagerApproval *approval)
115 {
116   g_signal_handler_disconnect (approval->operation,
117     approval->approved_handler);
118   g_signal_handler_disconnect (approval->operation,
119     approval->claimed_handler);
120   g_signal_handler_disconnect (approval->operation,
121     approval->invalidated_handler);
122   g_object_unref (approval->operation);
123
124   if (approval->contact != NULL)
125     g_object_unref (approval->contact);
126
127   if (approval->tube_dispatch != NULL)
128     g_object_unref (approval->tube_dispatch);
129
130   g_slice_free (EventManagerApproval, approval);
131 }
132
133 static void event_remove (EventPriv *event);
134
135 static void
136 event_free (EventPriv *event)
137 {
138   g_free (event->public.icon_name);
139   g_free (event->public.header);
140   g_free (event->public.message);
141
142   if (event->public.contact)
143     {
144       g_object_unref (event->public.contact);
145     }
146
147   g_slice_free (EventPriv, event);
148 }
149
150 static void event_manager_ringing_finished_cb (ca_context *c, guint id,
151   int error_code, gpointer user_data);
152
153 static gboolean
154 event_manager_ringing_timeout_cb (gpointer data)
155 {
156   EmpathyEventManager *manager = EMPATHY_EVENT_MANAGER (data);
157   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
158
159   priv->voip_timeout = 0;
160
161   empathy_sound_play_full (empathy_main_window_get (),
162       EMPATHY_SOUND_PHONE_INCOMING, event_manager_ringing_finished_cb,
163       manager);
164
165   return FALSE;
166 }
167
168 static gboolean
169 event_manager_ringing_idle_cb (gpointer data)
170 {
171   EmpathyEventManager *manager = EMPATHY_EVENT_MANAGER (data);
172   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
173
174   if (priv->ringing > 0)
175     priv->voip_timeout = g_timeout_add (500, event_manager_ringing_timeout_cb,
176       data);
177
178   return FALSE;
179 }
180
181 static void
182 event_manager_ringing_finished_cb (ca_context *c, guint id, int error_code,
183   gpointer user_data)
184 {
185   if (error_code == CA_ERROR_CANCELED)
186     return;
187
188   g_idle_add (event_manager_ringing_idle_cb, user_data);
189 }
190
191 static void
192 event_manager_start_ringing (EmpathyEventManager *manager)
193 {
194   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
195
196   priv->ringing++;
197
198   if (priv->ringing == 1)
199     {
200       empathy_sound_play_full (empathy_main_window_get (),
201         EMPATHY_SOUND_PHONE_INCOMING, event_manager_ringing_finished_cb,
202         manager);
203     }
204 }
205
206 static void
207 event_manager_stop_ringing (EmpathyEventManager *manager)
208 {
209   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
210
211   priv->ringing--;
212
213   if (priv->ringing > 0)
214     return;
215
216   empathy_sound_stop (EMPATHY_SOUND_PHONE_INCOMING);
217
218   if (priv->voip_timeout != 0)
219     {
220       g_source_remove (priv->voip_timeout);
221       priv->voip_timeout = 0;
222     }
223 }
224
225 static void
226 event_remove (EventPriv *event)
227 {
228   EmpathyEventManagerPriv *priv = GET_PRIV (event->manager);
229
230   DEBUG ("Removing event %p", event);
231   priv->events = g_slist_remove (priv->events, event);
232   g_signal_emit (event->manager, signals[EVENT_REMOVED], 0, event);
233   event_free (event);
234 }
235
236 static void
237 event_manager_add (EmpathyEventManager *manager, EmpathyContact *contact,
238   const gchar *icon_name, const gchar *header, const gchar *message,
239   EventManagerApproval *approval, EventFunc func, gpointer user_data)
240 {
241   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
242   EventPriv               *event;
243
244   event = g_slice_new0 (EventPriv);
245   event->public.contact = contact ? g_object_ref (contact) : NULL;
246   event->public.icon_name = g_strdup (icon_name);
247   event->public.header = g_strdup (header);
248   event->public.message = g_strdup (message);
249   event->inhibit = FALSE;
250   event->func = func;
251   event->user_data = user_data;
252   event->manager = manager;
253   event->approval = approval;
254
255   DEBUG ("Adding event %p", event);
256   priv->events = g_slist_prepend (priv->events, event);
257   g_signal_emit (event->manager, signals[EVENT_ADDED], 0, event);
258 }
259
260 static void
261 event_channel_process_func (EventPriv *event)
262 {
263   empathy_dispatch_operation_approve (event->approval->operation);
264 }
265
266 static void
267 event_text_channel_process_func (EventPriv *event)
268 {
269   EmpathyTpChat *tp_chat;
270
271   if (event->approval->handler != 0)
272     {
273       tp_chat = EMPATHY_TP_CHAT
274         (empathy_dispatch_operation_get_channel_wrapper (event->approval->operation));
275   
276       g_signal_handler_disconnect (tp_chat, event->approval->handler);
277       event->approval->handler = 0;
278     }
279
280   empathy_dispatch_operation_approve (event->approval->operation);
281 }
282
283 static EventPriv *
284 event_lookup_by_approval (EmpathyEventManager *manager,
285   EventManagerApproval *approval)
286 {
287   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
288   GSList *l;
289   EventPriv *retval = NULL;
290
291   for (l = priv->events; l; l = l->next)
292     {
293       EventPriv *event = l->data;
294
295       if (event->approval == approval)
296         {
297           retval = event;
298           break;
299         }
300     }
301
302   return retval;
303 }
304
305 static void
306 event_update (EmpathyEventManager *manager, EventPriv *event,
307   const char *icon_name, const char *header, const char *msg)
308 {
309   g_free (event->public.icon_name);
310   g_free (event->public.header);
311   g_free (event->public.message);
312
313   event->public.icon_name = g_strdup (icon_name);
314   event->public.header = g_strdup (header);
315   event->public.message = g_strdup (msg);
316
317   g_signal_emit (manager, signals[EVENT_UPDATED], 0, event);
318 }
319
320 static void
321 event_manager_chat_message_received_cb (EmpathyTpChat *tp_chat,
322   EmpathyMessage *message, EventManagerApproval *approval)
323 {
324   EmpathyContact  *sender;
325   gchar           *header;
326   const gchar     *msg;
327   TpChannel       *channel;
328   EventPriv       *event;
329
330   /* try to update the event if it's referring to a chat which is already in the
331    * queue. */
332   event = event_lookup_by_approval (approval->manager, approval);
333
334   if (event != NULL && event->inhibit && approval->handler != 0)
335     {
336       g_signal_handler_disconnect (tp_chat, approval->handler);
337       approval->handler = 0;
338       return;
339     }
340
341   sender = empathy_message_get_sender (message);
342   header = g_strdup_printf (_("New message from %s"),
343                             empathy_contact_get_name (sender));
344   msg = empathy_message_get_body (message);
345
346   channel = empathy_tp_chat_get_channel (tp_chat);
347
348   if (event != NULL)
349     event_update (approval->manager, event, EMPATHY_IMAGE_NEW_MESSAGE, header, msg);
350   else
351     event_manager_add (approval->manager, sender, EMPATHY_IMAGE_NEW_MESSAGE, header,
352       msg, approval, event_text_channel_process_func, NULL);
353
354   g_free (header);
355   empathy_sound_play (empathy_main_window_get (),
356     EMPATHY_SOUND_CONVERSATION_NEW);
357
358   g_free (msg);
359 }
360
361 static void
362 event_manager_approval_done (EventManagerApproval *approval)
363 {
364   EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager);
365   GSList                  *l;
366
367   if (approval->operation != NULL)
368     {
369       GQuark channel_type;
370
371       channel_type = empathy_dispatch_operation_get_channel_type_id (
372           approval->operation);
373       if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA)
374         {
375           event_manager_stop_ringing (approval->manager);
376         }
377     }
378
379   priv->approvals = g_slist_remove (priv->approvals, approval);
380
381   for (l = priv->events; l; l = l->next)
382     {
383       EventPriv *event = l->data;
384
385       if (event->approval == approval)
386         {
387           event_remove (event);
388           break;
389         }
390     }
391
392   event_manager_approval_free (approval);
393 }
394
395 static void
396 event_manager_operation_approved_cb (EmpathyDispatchOperation *operation,
397   EventManagerApproval *approval)
398 {
399   event_manager_approval_done (approval);
400 }
401
402 static void
403 event_manager_operation_claimed_cb (EmpathyDispatchOperation *operation,
404   EventManagerApproval *approval)
405 {
406   event_manager_approval_done (approval);
407 }
408
409 static void
410 event_manager_operation_invalidated_cb (EmpathyDispatchOperation *operation,
411   guint domain, gint code, gchar *message,
412   EventManagerApproval *approval)
413 {
414   event_manager_approval_done (approval);
415 }
416
417 static void
418 event_manager_media_channel_got_name_cb (EmpathyContact *contact,
419   const GError *error, gpointer user_data, GObject *object)
420 {
421   EventManagerApproval *approval = user_data;
422   gchar *header;
423
424   if (error != NULL)
425     {
426       /* FIXME just returning assuming the operation will be invalidated as
427        * well */
428       return;
429     }
430
431   header = g_strdup_printf (_("Incoming call from %s"),
432     empathy_contact_get_name (contact));
433
434   event_manager_add (approval->manager,
435     approval->contact, EMPATHY_IMAGE_VOIP, header, NULL,
436     approval, event_channel_process_func, NULL);
437
438   g_free (header);
439   event_manager_start_ringing (approval->manager);
440
441   g_free (msg);
442 }
443
444 static void
445 event_manager_media_channel_got_contact (EventManagerApproval *approval)
446 {
447   empathy_contact_call_when_ready (approval->contact,
448      EMPATHY_CONTACT_READY_NAME, event_manager_media_channel_got_name_cb,
449         approval, NULL, G_OBJECT (approval->manager));
450 }
451
452 static void
453 event_manager_media_channel_contact_changed_cb (EmpathyTpCall *call,
454   GParamSpec *param, EventManagerApproval *approval)
455 {
456   EmpathyContact *contact;
457
458   g_object_get (G_OBJECT (call), "contact", &contact, NULL);
459
460   if (contact == NULL)
461     return;
462
463   approval->contact = contact;
464   event_manager_media_channel_got_contact (approval);
465 }
466
467 static void
468 event_manager_tube_approved_cb (EventPriv *event)
469 {
470   empathy_tube_dispatch_handle (event->approval->tube_dispatch);
471 }
472
473 static void
474 event_manager_add_tube_approval (EventManagerApproval *approval,
475   EmpathyTubeDispatchAbility ability)
476 {
477   const gchar *icon_name;
478   gchar       *header;
479   const gchar *msg;
480
481   header = g_strdup_printf (_("%s is offering you an invitation"),
482     empathy_contact_get_name (approval->contact));
483
484   if (ability == EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE)
485     {
486       icon_name = GTK_STOCK_EXECUTE;
487       msg = _("An external application will be started to handle it.");
488     }
489   else
490     {
491       icon_name = GTK_STOCK_DIALOG_ERROR;
492       msg = _("You don't have the needed external "
493               "application to handle it.");
494     }
495
496   event_manager_add (approval->manager, approval->contact, icon_name, header,
497     msg, approval, event_manager_tube_approved_cb, approval);
498
499   g_free (header);
500   /* FIXME better sound for incoming tubes ? */
501   empathy_sound_play (empathy_main_window_get (),
502     EMPATHY_SOUND_CONVERSATION_NEW);
503
504   g_free (msg);
505 }
506
507 static void
508 event_manager_tube_dispatch_ability_cb (GObject *object,
509    GParamSpec *spec, gpointer user_data)
510 {
511   EventManagerApproval *approval = (EventManagerApproval *)user_data;
512   EmpathyTubeDispatchAbility dispatchability;
513
514   dispatchability =
515     empathy_tube_dispatch_is_dispatchable (approval->tube_dispatch);
516
517   if (dispatchability != EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN)
518     {
519       event_manager_add_tube_approval (approval, dispatchability);
520       g_signal_handler_disconnect (object, approval->handler);
521       approval->handler = 0;
522     }
523 }
524
525 static void
526 event_manager_tube_got_contact_name_cb (EmpathyContact *contact,
527   const GError *error, gpointer user_data, GObject *object)
528 {
529   EventManagerApproval *approval = (EventManagerApproval *)user_data;
530   EmpathyTubeDispatchAbility dispatchability;
531
532   if (error != NULL)
533     {
534       /* FIXME?, we assume that the operation gets invalidated as well (if it
535        * didn't already */
536        return;
537     }
538
539   dispatchability = empathy_tube_dispatch_is_dispatchable
540     (approval->tube_dispatch);
541
542
543   switch (dispatchability)
544     {
545       case EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN:
546         approval->handler = g_signal_connect (approval->tube_dispatch,
547           "notify::dispatchability",
548           G_CALLBACK (event_manager_tube_dispatch_ability_cb), approval);
549         break;
550       case EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE:
551         /* fallthrough */
552       case EMPATHY_TUBE_DISPATCHABILITY_IMPOSSIBLE:
553         event_manager_add_tube_approval (approval, dispatchability);
554         break;
555     }
556 }
557
558 static void
559 event_manager_approve_channel_cb (EmpathyDispatcher *dispatcher,
560   EmpathyDispatchOperation  *operation, EmpathyEventManager *manager)
561 {
562   const gchar *channel_type;
563   EventManagerApproval *approval;
564   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
565
566   channel_type = empathy_dispatch_operation_get_channel_type (operation);
567
568   approval = event_manager_approval_new (manager, operation);
569   priv->approvals = g_slist_prepend (priv->approvals, approval);
570
571   approval->approved_handler = g_signal_connect (operation, "approved",
572     G_CALLBACK (event_manager_operation_approved_cb), approval);
573
574   approval->claimed_handler = g_signal_connect (operation, "claimed",
575      G_CALLBACK (event_manager_operation_claimed_cb), approval);
576
577   approval->invalidated_handler = g_signal_connect (operation, "invalidated",
578      G_CALLBACK (event_manager_operation_invalidated_cb), approval);
579
580   if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT))
581     {
582       EmpathyTpChat *tp_chat =
583         EMPATHY_TP_CHAT (
584           empathy_dispatch_operation_get_channel_wrapper (operation));
585
586       approval->handler = g_signal_connect (tp_chat, "message-received",
587         G_CALLBACK (event_manager_chat_message_received_cb), approval);
588
589     }
590   else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA))
591     {
592       EmpathyContact *contact;
593       EmpathyTpCall *call = EMPATHY_TP_CALL (
594           empathy_dispatch_operation_get_channel_wrapper (operation));
595
596       g_object_get (G_OBJECT (call), "contact", &contact, NULL);
597
598       if (contact == NULL)
599         {
600           g_signal_connect (call, "notify::contact",
601             G_CALLBACK (event_manager_media_channel_contact_changed_cb),
602             approval);
603         }
604       else
605         {
606           approval->contact = contact;
607           event_manager_media_channel_got_contact (approval);
608         }
609
610     }
611   else if (!tp_strdiff (channel_type, EMP_IFACE_CHANNEL_TYPE_FILE_TRANSFER))
612     {
613       EmpathyContact        *contact;
614       gchar                 *header;
615       TpHandle               handle;
616       McAccount             *account;
617       EmpathyContactFactory *factory;
618       TpChannel *channel = empathy_dispatch_operation_get_channel (operation);
619
620       factory = empathy_contact_factory_dup_singleton ();
621       handle = tp_channel_get_handle (channel, NULL);
622       account = empathy_channel_get_account (channel);
623
624       contact = empathy_contact_factory_get_from_handle (factory, account,
625         handle);
626
627       empathy_contact_run_until_ready (contact,
628         EMPATHY_CONTACT_READY_NAME, NULL);
629
630       header = g_strdup_printf (_("Incoming file transfer from %s"),
631         empathy_contact_get_name (contact));
632
633       event_manager_add (manager, contact, EMPATHY_IMAGE_DOCUMENT_SEND,
634         header, NULL, approval, event_channel_process_func, NULL);
635
636       /* FIXME better sound for incoming file transfers ?*/
637       empathy_sound_play (empathy_main_window_get (),
638         EMPATHY_SOUND_CONVERSATION_NEW);
639
640       g_object_unref (factory);
641       g_object_unref (account);
642       g_free (header);
643     }
644   else if (!tp_strdiff (channel_type, EMP_IFACE_CHANNEL_TYPE_STREAM_TUBE) ||
645       !tp_strdiff (channel_type, EMP_IFACE_CHANNEL_TYPE_DBUS_TUBE))
646     {
647       EmpathyContact        *contact;
648       TpHandle               handle;
649       TpHandleType           handle_type;
650       McAccount             *account;
651       EmpathyContactFactory *factory;
652       EmpathyTubeDispatch *tube_dispatch;
653       TpChannel *channel;
654
655       channel = empathy_dispatch_operation_get_channel (operation);
656
657       handle = tp_channel_get_handle (channel, &handle_type);
658
659       /* Only understand p2p tubes */
660       if (handle_type != TP_HANDLE_TYPE_CONTACT)
661         return;
662
663       factory = empathy_contact_factory_dup_singleton ();
664       account = empathy_channel_get_account (channel);
665
666       contact = empathy_contact_factory_get_from_handle (factory, account,
667         handle);
668
669       tube_dispatch = empathy_tube_dispatch_new (operation);
670
671       approval->contact = contact;
672       approval->tube_dispatch = tube_dispatch;
673
674       empathy_contact_call_when_ready (contact,
675         EMPATHY_CONTACT_READY_NAME, event_manager_tube_got_contact_name_cb,
676         approval, NULL, G_OBJECT (manager));
677
678       g_object_unref (factory);
679       g_object_unref (account);
680     }
681   else
682     {
683       DEBUG ("Unknown channel type, ignoring..");
684     }
685 }
686
687 static void
688 event_pending_subscribe_func (EventPriv *event)
689 {
690   empathy_subscription_dialog_show (event->public.contact, NULL);
691   event_remove (event);
692 }
693
694 static void
695 event_manager_pendings_changed_cb (EmpathyContactList  *list,
696   EmpathyContact *contact, EmpathyContact *actor,
697   guint reason, gchar *message, gboolean is_pending,
698   EmpathyEventManager *manager)
699 {
700   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
701   gchar                   *header, *event_msg;
702
703   if (!is_pending)
704     {
705       GSList *l;
706
707       for (l = priv->events; l; l = l->next)
708         {
709           EventPriv *event = l->data;
710
711       if (event->public.contact == contact &&
712           event->func == event_pending_subscribe_func)
713         {
714           event_remove (event);
715           break;
716         }
717       }
718
719       return;
720     }
721
722   empathy_contact_run_until_ready (contact, EMPATHY_CONTACT_READY_NAME, NULL);
723
724   header = g_strdup_printf (_("Subscription requested by %s"),
725     empathy_contact_get_name (contact));
726
727   if (!EMP_STR_EMPTY (message))
728     event_msg = g_strdup_printf (_("\nMessage: %s"), message);
729   else
730     event_msg = NULL;
731
732   event_manager_add (manager, contact, GTK_STOCK_DIALOG_QUESTION, header,
733     event_msg, NULL, event_pending_subscribe_func, NULL);
734
735   g_free (event_msg);
736   g_free (header);
737 }
738
739 static GObject *
740 event_manager_constructor (GType type,
741                            guint n_props,
742                            GObjectConstructParam *props)
743 {
744         GObject *retval;
745
746         if (manager_singleton) {
747                 retval = g_object_ref (manager_singleton);
748         } else {
749                 retval = G_OBJECT_CLASS (empathy_event_manager_parent_class)->constructor
750                         (type, n_props, props);
751
752                 manager_singleton = EMPATHY_EVENT_MANAGER (retval);
753                 g_object_add_weak_pointer (retval, (gpointer *) &manager_singleton);
754         }
755
756         return retval;
757 }
758
759 static void
760 event_manager_finalize (GObject *object)
761 {
762   EmpathyEventManagerPriv *priv = GET_PRIV (object);
763
764   g_slist_foreach (priv->events, (GFunc) event_free, NULL);
765   g_slist_free (priv->events);
766   g_slist_foreach (priv->approvals, (GFunc) event_manager_approval_free, NULL);
767   g_slist_free (priv->approvals);
768   g_object_unref (priv->contact_manager);
769   g_object_unref (priv->dispatcher);
770 }
771
772 static void
773 empathy_event_manager_class_init (EmpathyEventManagerClass *klass)
774 {
775   GObjectClass *object_class = G_OBJECT_CLASS (klass);
776
777   object_class->finalize = event_manager_finalize;
778   object_class->constructor = event_manager_constructor;
779
780   signals[EVENT_ADDED] =
781     g_signal_new ("event-added",
782       G_TYPE_FROM_CLASS (klass),
783       G_SIGNAL_RUN_LAST,
784       0,
785       NULL, NULL,
786       g_cclosure_marshal_VOID__POINTER,
787       G_TYPE_NONE,
788       1, G_TYPE_POINTER);
789
790   signals[EVENT_REMOVED] =
791   g_signal_new ("event-removed",
792       G_TYPE_FROM_CLASS (klass),
793       G_SIGNAL_RUN_LAST,
794       0,
795       NULL, NULL,
796       g_cclosure_marshal_VOID__POINTER,
797       G_TYPE_NONE, 1, G_TYPE_POINTER);
798
799   signals[EVENT_UPDATED] =
800   g_signal_new ("event-updated",
801       G_TYPE_FROM_CLASS (klass),
802       G_SIGNAL_RUN_LAST,
803       0,
804       NULL, NULL,
805       g_cclosure_marshal_VOID__POINTER,
806       G_TYPE_NONE, 1, G_TYPE_POINTER);
807
808
809   g_type_class_add_private (object_class, sizeof (EmpathyEventManagerPriv));
810 }
811
812 static void
813 empathy_event_manager_init (EmpathyEventManager *manager)
814 {
815   EmpathyEventManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
816     EMPATHY_TYPE_EVENT_MANAGER, EmpathyEventManagerPriv);
817
818   manager->priv = priv;
819
820   priv->dispatcher = empathy_dispatcher_dup_singleton ();
821   priv->contact_manager = empathy_contact_manager_dup_singleton ();
822   g_signal_connect (priv->dispatcher, "approve",
823     G_CALLBACK (event_manager_approve_channel_cb), manager);
824   g_signal_connect (priv->contact_manager, "pendings-changed",
825     G_CALLBACK (event_manager_pendings_changed_cb), manager);
826 }
827
828 EmpathyEventManager *
829 empathy_event_manager_dup_singleton (void)
830 {
831   return g_object_new (EMPATHY_TYPE_EVENT_MANAGER, NULL);
832 }
833
834 GSList *
835 empathy_event_manager_get_events (EmpathyEventManager *manager)
836 {
837   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
838
839   g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
840
841   return priv->events;
842 }
843
844 EmpathyEvent *
845 empathy_event_manager_get_top_event (EmpathyEventManager *manager)
846 {
847   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
848
849   g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
850
851   return priv->events ? priv->events->data : NULL;
852 }
853
854 void
855 empathy_event_activate (EmpathyEvent *event_public)
856 {
857   EventPriv *event = (EventPriv*) event_public;
858
859   g_return_if_fail (event_public != NULL);
860
861   if (event->func)
862     event->func (event);
863   else
864     event_remove (event);
865 }
866
867 void
868 empathy_event_inhibit_updates (EmpathyEvent *event_public)
869 {
870   EventPriv *event = (EventPriv *) event_public;
871
872   g_return_if_fail (event_public != NULL);
873
874   event->inhibit = TRUE;
875 }
876