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