]> git.0d.be Git - empathy.git/blob - src/empathy-event-manager.c
Updated Galician translations
[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 #include <telepathy-glib/simple-approver.h>
31
32 #include <libempathy/empathy-dispatcher.h>
33 #include <libempathy/empathy-idle.h>
34 #include <libempathy/empathy-tp-contact-factory.h>
35 #include <libempathy/empathy-contact-manager.h>
36 #include <libempathy/empathy-tp-chat.h>
37 #include <libempathy/empathy-tp-call.h>
38 #include <libempathy/empathy-tp-file.h>
39 #include <libempathy/empathy-utils.h>
40 #include <libempathy/empathy-call-factory.h>
41 #include <libempathy/empathy-gsettings.h>
42
43 #include <extensions/extensions.h>
44
45 #include <libempathy-gtk/empathy-images.h>
46 #include <libempathy-gtk/empathy-contact-dialogs.h>
47 #include <libempathy-gtk/empathy-sound.h>
48
49 #include "empathy-event-manager.h"
50 #include "empathy-main-window.h"
51
52 #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
53 #include <libempathy/empathy-debug.h>
54
55 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyEventManager)
56
57 #define NOTIFICATION_TIMEOUT 2 /* seconds */
58
59 /* The time interval in milliseconds between 2 incoming rings */
60 #define MS_BETWEEN_RING 500
61
62 typedef struct {
63   EmpathyEventManager *manager;
64   TpChannelDispatchOperation *operation;
65   gulong invalidated_handler;
66   /* Remove contact if applicable */
67   EmpathyContact *contact;
68   /* option signal handler and it's instance */
69   gulong handler;
70   GObject *handler_instance;
71   /* optional accept widget */
72   GtkWidget *dialog;
73   /* Channel of the CDO that will be used during the approval */
74   TpChannel *main_channel;
75   gboolean auto_approved;
76 } EventManagerApproval;
77
78 typedef struct {
79   EmpathyDispatcher *dispatcher;
80   TpBaseClient *approver;
81   EmpathyContactManager *contact_manager;
82   GSList *events;
83   /* Ongoing approvals */
84   GSList *approvals;
85
86   gint ringing;
87 } EmpathyEventManagerPriv;
88
89 typedef struct _EventPriv EventPriv;
90 typedef void (*EventFunc) (EventPriv *event);
91
92 struct _EventPriv {
93   EmpathyEvent public;
94   EmpathyEventManager *manager;
95   EventManagerApproval *approval;
96   EventFunc func;
97   gboolean inhibit;
98   gpointer user_data;
99   guint autoremove_timeout_id;
100 };
101
102 enum {
103   EVENT_ADDED,
104   EVENT_REMOVED,
105   EVENT_UPDATED,
106   LAST_SIGNAL
107 };
108
109 static guint signals[LAST_SIGNAL];
110
111 G_DEFINE_TYPE (EmpathyEventManager, empathy_event_manager, G_TYPE_OBJECT);
112
113 static EmpathyEventManager * manager_singleton = NULL;
114
115 static EventManagerApproval *
116 event_manager_approval_new (EmpathyEventManager *manager,
117   TpChannelDispatchOperation *operation,
118   TpChannel *main_channel)
119 {
120   EventManagerApproval *result = g_slice_new0 (EventManagerApproval);
121   result->operation = g_object_ref (operation);
122   result->manager = manager;
123   result->main_channel = g_object_ref (main_channel);
124
125   return result;
126 }
127
128 static void
129 event_manager_approval_free (EventManagerApproval *approval)
130 {
131   g_signal_handler_disconnect (approval->operation,
132     approval->invalidated_handler);
133   g_object_unref (approval->operation);
134
135   g_object_unref (approval->main_channel);
136
137   if (approval->handler != 0)
138     g_signal_handler_disconnect (approval->handler_instance,
139       approval->handler);
140
141   if (approval->handler_instance != NULL)
142     g_object_unref (approval->handler_instance);
143
144   if (approval->contact != NULL)
145     g_object_unref (approval->contact);
146
147   if (approval->dialog != NULL)
148     {
149       gtk_widget_destroy (approval->dialog);
150     }
151
152   g_slice_free (EventManagerApproval, approval);
153 }
154
155 static void event_remove (EventPriv *event);
156
157 static void
158 event_free (EventPriv *event)
159 {
160   g_free (event->public.icon_name);
161   g_free (event->public.header);
162   g_free (event->public.message);
163
164   if (event->autoremove_timeout_id != 0)
165     g_source_remove (event->autoremove_timeout_id);
166
167   if (event->public.contact)
168     {
169       g_object_unref (event->public.contact);
170     }
171
172   g_slice_free (EventPriv, event);
173 }
174
175 static void
176 event_remove (EventPriv *event)
177 {
178   EmpathyEventManagerPriv *priv = GET_PRIV (event->manager);
179
180   DEBUG ("Removing event %p", event);
181
182   priv->events = g_slist_remove (priv->events, event);
183   g_signal_emit (event->manager, signals[EVENT_REMOVED], 0, event);
184   event_free (event);
185 }
186
187 static gboolean
188 autoremove_event_timeout_cb (EventPriv *event)
189 {
190   event->autoremove_timeout_id = 0;
191   event_remove (event);
192   return FALSE;
193 }
194
195 static gboolean
196 display_notify_area (void)
197
198 {
199   GSettings *gsettings;
200   gboolean result;
201
202   gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
203
204   result = g_settings_get_boolean (gsettings,
205       EMPATHY_PREFS_UI_EVENTS_NOTIFY_AREA);
206   g_object_unref (gsettings);
207
208   return result;
209 }
210
211 static void
212 event_manager_add (EmpathyEventManager *manager,
213     EmpathyContact *contact,
214     EmpathyEventType type,
215     const gchar *icon_name,
216     const gchar *header,
217     const gchar *message,
218     EventManagerApproval *approval,
219     EventFunc func,
220     gpointer user_data)
221 {
222   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
223   EventPriv               *event;
224
225   event = g_slice_new0 (EventPriv);
226   event->public.contact = contact ? g_object_ref (contact) : NULL;
227   event->public.type = type;
228   event->public.icon_name = g_strdup (icon_name);
229   event->public.header = g_strdup (header);
230   event->public.message = g_strdup (message);
231   event->public.must_ack = (func != NULL);
232   event->inhibit = FALSE;
233   event->func = func;
234   event->user_data = user_data;
235   event->manager = manager;
236   event->approval = approval;
237
238   DEBUG ("Adding event %p", event);
239   priv->events = g_slist_prepend (priv->events, event);
240
241   if (!display_notify_area ())
242     {
243       /* Don't fire the 'event-added' signal as we activate the event now */
244       if (approval != NULL)
245         approval->auto_approved = TRUE;
246
247       empathy_event_activate (&event->public);
248       return;
249     }
250
251   g_signal_emit (event->manager, signals[EVENT_ADDED], 0, event);
252
253   if (!event->public.must_ack)
254     {
255       event->autoremove_timeout_id = g_timeout_add_seconds (
256           NOTIFICATION_TIMEOUT, (GSourceFunc) autoremove_event_timeout_cb,
257           event);
258     }
259 }
260
261 static void
262 handle_with_cb (GObject *source,
263     GAsyncResult *result,
264     gpointer user_data)
265 {
266   TpChannelDispatchOperation *cdo = TP_CHANNEL_DISPATCH_OPERATION (source);
267   GError *error = NULL;
268
269   if (!tp_channel_dispatch_operation_handle_with_finish (cdo, result, &error))
270     {
271       DEBUG ("HandleWith failed: %s\n", error->message);
272       g_error_free (error);
273     }
274 }
275
276 static void
277 handle_with_time_cb (GObject *source,
278     GAsyncResult *result,
279     gpointer user_data)
280 {
281   TpChannelDispatchOperation *cdo = TP_CHANNEL_DISPATCH_OPERATION (source);
282   GError *error = NULL;
283
284   if (!tp_channel_dispatch_operation_handle_with_time_finish (cdo, result,
285         &error))
286     {
287       if (g_error_matches (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED))
288         {
289           EventManagerApproval *approval = user_data;
290
291           DEBUG ("HandleWithTime() is not implemented, falling back to "
292               "HandleWith(). Please upgrade to telepathy-mission-control "
293               "5.5.0 or later");
294
295           tp_channel_dispatch_operation_handle_with_async (approval->operation,
296               NULL, handle_with_cb, approval);
297         }
298       else
299         {
300           DEBUG ("HandleWithTime failed: %s\n", error->message);
301         }
302       g_error_free (error);
303     }
304 }
305
306 static void
307 event_manager_approval_approve (EventManagerApproval *approval)
308 {
309   gint64 timestamp;
310
311   if (approval->auto_approved)
312     {
313       timestamp = EMPATHY_DISPATCHER_NON_USER_ACTION;
314     }
315   else
316     {
317       timestamp = gtk_get_current_event_time ();
318
319       if (timestamp == GDK_CURRENT_TIME)
320         timestamp = EMPATHY_DISPATCHER_CURRENT_TIME;
321     }
322
323   g_assert (approval->operation != NULL);
324
325   tp_channel_dispatch_operation_handle_with_time_async (approval->operation,
326       NULL, timestamp, handle_with_time_cb, approval);
327 }
328
329 static void
330 event_channel_process_func (EventPriv *event)
331 {
332   event_manager_approval_approve (event->approval);
333 }
334
335 static void
336 event_text_channel_process_func (EventPriv *event)
337 {
338   EmpathyTpChat *tp_chat;
339   gint64 timestamp = gtk_get_current_event_time ();
340   if (timestamp == GDK_CURRENT_TIME)
341     timestamp = EMPATHY_DISPATCHER_CURRENT_TIME;
342
343   if (event->approval->handler != 0)
344     {
345       tp_chat = EMPATHY_TP_CHAT (event->approval->handler_instance);
346
347       g_signal_handler_disconnect (tp_chat, event->approval->handler);
348       event->approval->handler = 0;
349     }
350
351   event_manager_approval_approve (event->approval);
352 }
353
354 static EventPriv *
355 event_lookup_by_approval (EmpathyEventManager *manager,
356   EventManagerApproval *approval)
357 {
358   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
359   GSList *l;
360   EventPriv *retval = NULL;
361
362   for (l = priv->events; l; l = l->next)
363     {
364       EventPriv *event = l->data;
365
366       if (event->approval == approval)
367         {
368           retval = event;
369           break;
370         }
371     }
372
373   return retval;
374 }
375
376 static void
377 event_update (EmpathyEventManager *manager, EventPriv *event,
378   const char *icon_name, const char *header, const char *msg)
379 {
380   g_free (event->public.icon_name);
381   g_free (event->public.header);
382   g_free (event->public.message);
383
384   event->public.icon_name = g_strdup (icon_name);
385   event->public.header = g_strdup (header);
386   event->public.message = g_strdup (msg);
387
388   g_signal_emit (manager, signals[EVENT_UPDATED], 0, event);
389 }
390
391 static void
392 reject_channel_claim_cb (GObject *source,
393     GAsyncResult *result,
394     gpointer user_data)
395 {
396   TpChannelDispatchOperation *cdo = TP_CHANNEL_DISPATCH_OPERATION (source);
397   GError *error = NULL;
398
399   if (!tp_channel_dispatch_operation_claim_finish (cdo, result, &error))
400     {
401       DEBUG ("Failed to claim channel: %s", error->message);
402
403       g_error_free (error);
404       goto out;
405     }
406
407   if (EMPATHY_IS_TP_CALL (user_data))
408     {
409       empathy_tp_call_close (user_data);
410     }
411   else if (EMPATHY_IS_TP_CHAT (user_data))
412     {
413       empathy_tp_chat_leave (user_data);
414     }
415   else if (EMPATHY_IS_TP_FILE (user_data))
416     {
417       empathy_tp_file_close (user_data);
418     }
419
420 out:
421   g_object_unref (user_data);
422 }
423
424 static void
425 reject_approval (EventManagerApproval *approval)
426 {
427   /* We have to claim the channel before closing it */
428   tp_channel_dispatch_operation_claim_async (approval->operation,
429       reject_channel_claim_cb, g_object_ref (approval->handler_instance));
430 }
431
432 static void
433 event_manager_call_window_confirmation_dialog_response_cb (GtkDialog *dialog,
434   gint response, gpointer user_data)
435 {
436   EventManagerApproval *approval = user_data;
437
438   gtk_widget_destroy (approval->dialog);
439   approval->dialog = NULL;
440
441   if (response != GTK_RESPONSE_ACCEPT)
442     {
443       reject_approval (approval);
444     }
445   else
446     {
447       event_manager_approval_approve (approval);
448     }
449 }
450
451 static void
452 event_channel_process_voip_func (EventPriv *event)
453 {
454   GtkWidget *dialog;
455   GtkWidget *button;
456   GtkWidget *image;
457   EmpathyTpCall *call;
458   gboolean video;
459   gchar *title;
460
461   if (event->approval->dialog != NULL)
462     {
463       gtk_window_present (GTK_WINDOW (event->approval->dialog));
464       return;
465     }
466
467   call = EMPATHY_TP_CALL (event->approval->handler_instance);
468
469   video = empathy_tp_call_has_initial_video (call);
470
471   dialog = gtk_message_dialog_new (NULL, 0,
472       GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
473       video ? _("Incoming video call"): _("Incoming call"));
474
475   gtk_message_dialog_format_secondary_text (
476     GTK_MESSAGE_DIALOG (dialog), video ?
477       _("%s is video calling you. Do you want to answer?"):
478       _("%s is calling you. Do you want to answer?"),
479       empathy_contact_get_alias (event->approval->contact));
480
481   title = g_strdup_printf (_("Incoming call from %s"),
482       empathy_contact_get_alias (event->approval->contact));
483
484   gtk_window_set_title (GTK_WINDOW (dialog), title);
485   g_free (title);
486
487   /* Set image of the dialog */
488   if (video)
489     {
490       image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_VIDEO_CALL,
491           GTK_ICON_SIZE_DIALOG);
492     }
493   else
494     {
495       image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_VOIP,
496           GTK_ICON_SIZE_DIALOG);
497     }
498
499   gtk_message_dialog_set_image (GTK_MESSAGE_DIALOG (dialog), image);
500   gtk_widget_show (image);
501
502   gtk_dialog_set_default_response (GTK_DIALOG (dialog),
503       GTK_RESPONSE_OK);
504
505   button = gtk_dialog_add_button (GTK_DIALOG (dialog),
506       _("_Reject"), GTK_RESPONSE_REJECT);
507   image = gtk_image_new_from_icon_name ("call-stop",
508     GTK_ICON_SIZE_BUTTON);
509   gtk_button_set_image (GTK_BUTTON (button), image);
510
511   button = gtk_dialog_add_button (GTK_DIALOG (dialog),
512       _("_Answer"), GTK_RESPONSE_ACCEPT);
513
514   image = gtk_image_new_from_icon_name ("call-start", GTK_ICON_SIZE_BUTTON);
515   gtk_button_set_image (GTK_BUTTON (button), image);
516
517   g_signal_connect (dialog, "response",
518       G_CALLBACK (event_manager_call_window_confirmation_dialog_response_cb),
519       event->approval);
520
521   gtk_widget_show (dialog);
522
523   event->approval->dialog = dialog;
524 }
525
526 static void
527 event_manager_chat_message_received_cb (EmpathyTpChat *tp_chat,
528   EmpathyMessage *message,
529   EventManagerApproval *approval)
530 {
531   GtkWidget       *window = empathy_main_window_dup ();
532   EmpathyContact  *sender;
533   const gchar     *header;
534   const gchar     *msg;
535   TpChannel       *channel;
536   EventPriv       *event;
537
538   /* try to update the event if it's referring to a chat which is already in the
539    * queue. */
540   event = event_lookup_by_approval (approval->manager, approval);
541
542   sender = empathy_message_get_sender (message);
543   header = empathy_contact_get_alias (sender);
544   msg = empathy_message_get_body (message);
545
546   channel = empathy_tp_chat_get_channel (tp_chat);
547
548   if (event != NULL)
549     event_update (approval->manager, event, EMPATHY_IMAGE_NEW_MESSAGE, header,
550         msg);
551   else
552     event_manager_add (approval->manager, sender, EMPATHY_EVENT_TYPE_CHAT,
553         EMPATHY_IMAGE_NEW_MESSAGE, header, msg, approval,
554         event_text_channel_process_func, NULL);
555
556   empathy_sound_play (window, EMPATHY_SOUND_CONVERSATION_NEW);
557
558   g_object_unref (window);
559 }
560
561 static void
562 event_manager_approval_done (EventManagerApproval *approval)
563 {
564   EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager);
565   GSList                  *l;
566
567   if (approval->operation != NULL)
568     {
569       GQuark channel_type;
570
571       channel_type = tp_channel_get_channel_type_id (approval->main_channel);
572
573       if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA)
574         {
575           priv->ringing--;
576           if (priv->ringing == 0)
577             empathy_sound_stop (EMPATHY_SOUND_PHONE_INCOMING);
578         }
579     }
580
581   priv->approvals = g_slist_remove (priv->approvals, approval);
582
583   for (l = priv->events; l; l = l->next)
584     {
585       EventPriv *event = l->data;
586
587       if (event->approval == approval)
588         {
589           event_remove (event);
590           break;
591         }
592     }
593
594   event_manager_approval_free (approval);
595 }
596
597 static void
598 cdo_invalidated_cb (TpProxy *cdo,
599     guint domain,
600     gint code,
601     gchar *message,
602     EventManagerApproval *approval)
603 {
604   DEBUG ("ChannelDispatchOperation has been invalidated: %s", message);
605
606   event_manager_approval_done (approval);
607 }
608
609 static void
610 event_manager_media_channel_got_contact (EventManagerApproval *approval)
611 {
612   EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager);
613   GtkWidget *window = empathy_main_window_dup ();
614   gchar *header;
615   EmpathyTpCall *call;
616   gboolean video;
617
618   call = EMPATHY_TP_CALL (approval->handler_instance);
619
620   video = empathy_tp_call_has_initial_video (call);
621
622   header = g_strdup_printf (
623     video ? _("Incoming video call from %s") :_("Incoming call from %s"),
624     empathy_contact_get_alias (approval->contact));
625
626   event_manager_add (approval->manager, approval->contact,
627       EMPATHY_EVENT_TYPE_VOIP,
628       video ? EMPATHY_IMAGE_VIDEO_CALL : EMPATHY_IMAGE_VOIP,
629       header, NULL, approval,
630       event_channel_process_voip_func, NULL);
631
632   g_free (header);
633
634   priv->ringing++;
635   if (priv->ringing == 1)
636     empathy_sound_start_playing (window,
637         EMPATHY_SOUND_PHONE_INCOMING, MS_BETWEEN_RING);
638
639   g_object_unref (window);
640 }
641
642 static void
643 event_manager_media_channel_contact_changed_cb (EmpathyTpCall *call,
644   GParamSpec *param, EventManagerApproval *approval)
645 {
646   EmpathyContact *contact;
647
648   g_object_get (G_OBJECT (call), "contact", &contact, NULL);
649
650   if (contact == NULL)
651     return;
652
653   approval->contact = contact;
654   event_manager_media_channel_got_contact (approval);
655 }
656
657 static void
658 invite_dialog_response_cb (GtkDialog *dialog,
659                            gint response,
660                            EventManagerApproval *approval)
661 {
662   EmpathyTpChat *tp_chat;
663
664   gtk_widget_destroy (GTK_WIDGET (approval->dialog));
665   approval->dialog = NULL;
666
667   tp_chat = EMPATHY_TP_CHAT (approval->handler_instance);
668
669   if (response != GTK_RESPONSE_OK)
670     {
671       /* close channel */
672       DEBUG ("Muc invitation rejected");
673
674       reject_approval (approval);
675
676       return;
677     }
678
679   DEBUG ("Muc invitation accepted");
680
681   /* We'll join the room when handling the channel */
682   event_manager_approval_approve (approval);
683 }
684
685 static void
686 event_room_channel_process_func (EventPriv *event)
687 {
688   GtkWidget *dialog, *button, *image;
689   TpChannel *channel = event->approval->main_channel;
690   gchar *title;
691
692   if (event->approval->dialog != NULL)
693     {
694       gtk_window_present (GTK_WINDOW (event->approval->dialog));
695       return;
696     }
697
698   /* create dialog */
699   dialog = gtk_message_dialog_new (NULL, 0,
700       GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("Room invitation"));
701
702   title = g_strdup_printf (_("Invitation to join %s"),
703       tp_channel_get_identifier (channel));
704
705   gtk_window_set_title (GTK_WINDOW (dialog), title);
706   g_free (title);
707
708   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
709       _("%s is inviting you to join %s"),
710       empathy_contact_get_alias (event->approval->contact),
711       tp_channel_get_identifier (channel));
712
713   gtk_dialog_set_default_response (GTK_DIALOG (dialog),
714       GTK_RESPONSE_OK);
715
716   button = gtk_dialog_add_button (GTK_DIALOG (dialog),
717       _("_Decline"), GTK_RESPONSE_CANCEL);
718   image = gtk_image_new_from_icon_name (GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
719   gtk_button_set_image (GTK_BUTTON (button), image);
720
721   button = gtk_dialog_add_button (GTK_DIALOG (dialog),
722       _("_Join"), GTK_RESPONSE_OK);
723   image = gtk_image_new_from_icon_name (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
724   gtk_button_set_image (GTK_BUTTON (button), image);
725
726   g_signal_connect (dialog, "response",
727       G_CALLBACK (invite_dialog_response_cb), event->approval);
728
729   gtk_widget_show (dialog);
730
731   event->approval->dialog = dialog;
732 }
733
734 static void
735 event_manager_muc_invite_got_contact_cb (TpConnection *connection,
736                                          EmpathyContact *contact,
737                                          const GError *error,
738                                          gpointer user_data,
739                                          GObject *object)
740 {
741   EventManagerApproval *approval = (EventManagerApproval *) user_data;
742   GtkWidget *window = empathy_main_window_dup ();
743   const gchar *invite_msg;
744   gchar *msg;
745   TpHandle self_handle;
746
747   if (error != NULL)
748     {
749       /* FIXME: We should probably still display the event */
750       DEBUG ("Error: %s", error->message);
751       return;
752     }
753
754   approval->contact = g_object_ref (contact);
755
756   self_handle = tp_channel_group_get_self_handle (approval->main_channel);
757   tp_channel_group_get_local_pending_info (approval->main_channel, self_handle,
758       NULL, NULL, &invite_msg);
759
760   msg = g_strdup_printf (_("%s invited you to join %s"),
761       empathy_contact_get_alias (approval->contact),
762       tp_channel_get_identifier (approval->main_channel));
763
764   event_manager_add (approval->manager, approval->contact,
765       EMPATHY_EVENT_TYPE_INVITATION, EMPATHY_IMAGE_GROUP_MESSAGE, msg,
766       invite_msg, approval, event_room_channel_process_func, NULL);
767
768   empathy_sound_play (window, EMPATHY_SOUND_CONVERSATION_NEW);
769
770   g_free (msg);
771   g_object_unref (window);
772 }
773
774 static void
775 event_manager_ft_got_contact_cb (TpConnection *connection,
776                                  EmpathyContact *contact,
777                                  const GError *error,
778                                  gpointer user_data,
779                                  GObject *object)
780 {
781   EventManagerApproval *approval = (EventManagerApproval *) user_data;
782   GtkWidget *window = empathy_main_window_dup ();
783   char *header;
784
785   approval->contact = g_object_ref (contact);
786
787   header = g_strdup_printf (_("Incoming file transfer from %s"),
788                             empathy_contact_get_alias (approval->contact));
789
790   event_manager_add (approval->manager, approval->contact,
791       EMPATHY_EVENT_TYPE_TRANSFER, EMPATHY_IMAGE_DOCUMENT_SEND, header, NULL,
792       approval, event_channel_process_func, NULL);
793
794   /* FIXME better sound for incoming file transfers ?*/
795   empathy_sound_play (window, EMPATHY_SOUND_CONVERSATION_NEW);
796
797   g_free (header);
798   g_object_unref (window);
799 }
800
801 /* If there is a file-transfer or media channel consider it as the
802  * main one. */
803 static TpChannel *
804 find_main_channel (GList *channels)
805 {
806   GList *l;
807   TpChannel *text = NULL;
808
809   for (l = channels; l != NULL; l = g_list_next (l))
810     {
811       TpChannel *channel = l->data;
812       GQuark channel_type;
813
814       if (tp_proxy_get_invalidated (channel) != NULL)
815         continue;
816
817       channel_type = tp_channel_get_channel_type_id (channel);
818
819       if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA ||
820           channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER)
821         return channel;
822
823       else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_TEXT)
824         text = channel;
825     }
826
827   return text;
828 }
829
830 static void
831 approve_channels (TpSimpleApprover *approver,
832     TpAccount *account,
833     TpConnection *connection,
834     GList *channels,
835     TpChannelDispatchOperation *dispatch_operation,
836     TpAddDispatchOperationContext *context,
837     gpointer user_data)
838 {
839   EmpathyEventManager *self = user_data;
840   EmpathyEventManagerPriv *priv = GET_PRIV (self);
841   TpChannel *channel;
842   EventManagerApproval *approval;
843   GQuark channel_type;
844
845   channel = find_main_channel (channels);
846   if (channel == NULL)
847     {
848       GError error = { TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
849           "Unknown channel type" };
850
851       DEBUG ("Failed to find the main channel; ignoring");
852
853       tp_add_dispatch_operation_context_fail (context, &error);
854       return;
855     }
856
857   approval = event_manager_approval_new (self, dispatch_operation, channel);
858   priv->approvals = g_slist_prepend (priv->approvals, approval);
859
860   approval->invalidated_handler = g_signal_connect (dispatch_operation,
861       "invalidated", G_CALLBACK (cdo_invalidated_cb), approval);
862
863   channel_type = tp_channel_get_channel_type_id (channel);
864
865   if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_TEXT)
866     {
867       EmpathyTpChat *tp_chat;
868
869       tp_chat = empathy_tp_chat_new (channel);
870       approval->handler_instance = G_OBJECT (tp_chat);
871
872       if (tp_proxy_has_interface (channel, TP_IFACE_CHANNEL_INTERFACE_GROUP))
873         {
874           /* Are we in local-pending ? */
875           TpHandle inviter;
876
877           if (empathy_tp_chat_is_invited (tp_chat, &inviter))
878             {
879               /* We are invited to a room */
880               DEBUG ("Have been invited to %s. Ask user if he wants to accept",
881                   tp_channel_get_identifier (channel));
882
883               empathy_tp_contact_factory_get_from_handle (connection,
884                   inviter, event_manager_muc_invite_got_contact_cb,
885                   approval, NULL, G_OBJECT (self));
886
887               goto out;
888             }
889
890           /* We are not invited, approve the channel right now */
891           tp_add_dispatch_operation_context_accept (context);
892
893           approval->auto_approved = TRUE;
894           event_manager_approval_approve (approval);
895           return;
896         }
897
898       /* 1-1 text channel, wait for the first message */
899       approval->handler = g_signal_connect (tp_chat, "message-received",
900         G_CALLBACK (event_manager_chat_message_received_cb), approval);
901     }
902   else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA)
903     {
904       EmpathyContact *contact;
905       EmpathyTpCall *call = empathy_tp_call_new (channel);
906
907       approval->handler_instance = G_OBJECT (call);
908
909       g_object_get (G_OBJECT (call), "contact", &contact, NULL);
910
911       if (contact == NULL)
912         {
913           g_signal_connect (call, "notify::contact",
914             G_CALLBACK (event_manager_media_channel_contact_changed_cb),
915             approval);
916         }
917       else
918         {
919           approval->contact = contact;
920           event_manager_media_channel_got_contact (approval);
921         }
922
923     }
924   else if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_FILE_TRANSFER)
925     {
926       TpHandle handle;
927       EmpathyTpFile *tp_file = empathy_tp_file_new (channel, TRUE);
928
929       approval->handler_instance = G_OBJECT (tp_file);
930
931       handle = tp_channel_get_handle (channel, NULL);
932
933       connection = tp_channel_borrow_connection (channel);
934       empathy_tp_contact_factory_get_from_handle (connection, handle,
935         event_manager_ft_got_contact_cb, approval, NULL, G_OBJECT (self));
936     }
937   else
938     {
939       GError error = { TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
940           "Invalid channel type" };
941
942       DEBUG ("Unknown channel type (%s), ignoring..",
943           g_quark_to_string (channel_type));
944
945       tp_add_dispatch_operation_context_fail (context, &error);
946       return;
947     }
948
949 out:
950   tp_add_dispatch_operation_context_accept (context);
951 }
952
953 static void
954 event_pending_subscribe_func (EventPriv *event)
955 {
956   empathy_subscription_dialog_show (event->public.contact, NULL);
957   event_remove (event);
958 }
959
960 static void
961 event_manager_pendings_changed_cb (EmpathyContactList  *list,
962   EmpathyContact *contact, EmpathyContact *actor,
963   guint reason, gchar *message, gboolean is_pending,
964   EmpathyEventManager *manager)
965 {
966   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
967   gchar                   *header, *event_msg;
968
969   if (!is_pending)
970     {
971       GSList *l;
972
973       for (l = priv->events; l; l = l->next)
974         {
975           EventPriv *event = l->data;
976
977           if (event->public.contact == contact &&
978               event->func == event_pending_subscribe_func)
979             {
980               event_remove (event);
981               break;
982             }
983         }
984
985       return;
986     }
987
988   header = g_strdup_printf (_("Subscription requested by %s"),
989     empathy_contact_get_alias (contact));
990
991   if (!EMP_STR_EMPTY (message))
992     event_msg = g_strdup_printf (_("\nMessage: %s"), message);
993   else
994     event_msg = NULL;
995
996   event_manager_add (manager, contact, EMPATHY_EVENT_TYPE_SUBSCRIPTION,
997       GTK_STOCK_DIALOG_QUESTION, header, event_msg, NULL,
998       event_pending_subscribe_func, NULL);
999
1000   g_free (event_msg);
1001   g_free (header);
1002 }
1003
1004 static void
1005 event_manager_presence_changed_cb (EmpathyContact *contact,
1006     TpConnectionPresenceType current,
1007     TpConnectionPresenceType previous,
1008     EmpathyEventManager *manager)
1009 {
1010   TpAccount *account;
1011   gchar *header = NULL;
1012   EmpathyIdle *idle;
1013   GSettings *gsettings = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
1014   GtkWidget *window = empathy_main_window_dup ();
1015
1016   account = empathy_contact_get_account (contact);
1017   idle = empathy_idle_dup_singleton ();
1018
1019   if (empathy_idle_account_is_just_connected (idle, account))
1020     goto out;
1021
1022   if (tp_connection_presence_type_cmp_availability (previous,
1023         TP_CONNECTION_PRESENCE_TYPE_OFFLINE) > 0)
1024     {
1025       /* contact was online */
1026       if (tp_connection_presence_type_cmp_availability (current,
1027           TP_CONNECTION_PRESENCE_TYPE_OFFLINE) <= 0)
1028         {
1029           /* someone is logging off */
1030           empathy_sound_play (window, EMPATHY_SOUND_CONTACT_DISCONNECTED);
1031
1032           if (g_settings_get_boolean (gsettings,
1033                 EMPATHY_PREFS_NOTIFICATIONS_CONTACT_SIGNOUT))
1034             {
1035               header = g_strdup_printf (_("%s is now offline."),
1036                   empathy_contact_get_alias (contact));
1037
1038               event_manager_add (manager, contact, EMPATHY_EVENT_TYPE_PRESENCE,
1039                   EMPATHY_IMAGE_AVATAR_DEFAULT, header, NULL, NULL, NULL, NULL);
1040             }
1041         }
1042     }
1043   else
1044     {
1045       /* contact was offline */
1046       if (tp_connection_presence_type_cmp_availability (current,
1047             TP_CONNECTION_PRESENCE_TYPE_OFFLINE) > 0)
1048         {
1049           /* someone is logging in */
1050           empathy_sound_play (window, EMPATHY_SOUND_CONTACT_CONNECTED);
1051
1052           if (g_settings_get_boolean (gsettings,
1053                 EMPATHY_PREFS_NOTIFICATIONS_CONTACT_SIGNIN))
1054             {
1055               header = g_strdup_printf (_("%s is now online."),
1056                   empathy_contact_get_alias (contact));
1057
1058               event_manager_add (manager, contact, EMPATHY_EVENT_TYPE_PRESENCE,
1059                   EMPATHY_IMAGE_AVATAR_DEFAULT, header, NULL, NULL, NULL, NULL);
1060             }
1061         }
1062     }
1063   g_free (header);
1064
1065 out:
1066   g_object_unref (idle);
1067   g_object_unref (gsettings);
1068   g_object_unref (window);
1069 }
1070
1071 static void
1072 event_manager_members_changed_cb (EmpathyContactList  *list,
1073     EmpathyContact *contact,
1074     EmpathyContact *actor,
1075     guint reason,
1076     gchar *message,
1077     gboolean is_member,
1078     EmpathyEventManager *manager)
1079 {
1080   if (is_member)
1081     g_signal_connect (contact, "presence-changed",
1082         G_CALLBACK (event_manager_presence_changed_cb), manager);
1083   else
1084     g_signal_handlers_disconnect_by_func (contact,
1085         event_manager_presence_changed_cb, manager);
1086 }
1087
1088 static GObject *
1089 event_manager_constructor (GType type,
1090                            guint n_props,
1091                            GObjectConstructParam *props)
1092 {
1093         GObject *retval;
1094
1095         if (manager_singleton) {
1096                 retval = g_object_ref (manager_singleton);
1097         } else {
1098                 retval = G_OBJECT_CLASS (empathy_event_manager_parent_class)->constructor
1099                         (type, n_props, props);
1100
1101                 manager_singleton = EMPATHY_EVENT_MANAGER (retval);
1102                 g_object_add_weak_pointer (retval, (gpointer) &manager_singleton);
1103         }
1104
1105         return retval;
1106 }
1107
1108 static void
1109 event_manager_finalize (GObject *object)
1110 {
1111   EmpathyEventManagerPriv *priv = GET_PRIV (object);
1112
1113   if (priv->ringing > 0)
1114     empathy_sound_stop (EMPATHY_SOUND_PHONE_INCOMING);
1115
1116   g_slist_foreach (priv->events, (GFunc) event_free, NULL);
1117   g_slist_free (priv->events);
1118   g_slist_foreach (priv->approvals, (GFunc) event_manager_approval_free, NULL);
1119   g_slist_free (priv->approvals);
1120   g_object_unref (priv->contact_manager);
1121   g_object_unref (priv->dispatcher);
1122   g_object_unref (priv->approver);
1123 }
1124
1125 static void
1126 empathy_event_manager_class_init (EmpathyEventManagerClass *klass)
1127 {
1128   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1129
1130   object_class->finalize = event_manager_finalize;
1131   object_class->constructor = event_manager_constructor;
1132
1133   signals[EVENT_ADDED] =
1134     g_signal_new ("event-added",
1135       G_TYPE_FROM_CLASS (klass),
1136       G_SIGNAL_RUN_LAST,
1137       0,
1138       NULL, NULL,
1139       g_cclosure_marshal_VOID__POINTER,
1140       G_TYPE_NONE,
1141       1, G_TYPE_POINTER);
1142
1143   signals[EVENT_REMOVED] =
1144   g_signal_new ("event-removed",
1145       G_TYPE_FROM_CLASS (klass),
1146       G_SIGNAL_RUN_LAST,
1147       0,
1148       NULL, NULL,
1149       g_cclosure_marshal_VOID__POINTER,
1150       G_TYPE_NONE, 1, G_TYPE_POINTER);
1151
1152   signals[EVENT_UPDATED] =
1153   g_signal_new ("event-updated",
1154       G_TYPE_FROM_CLASS (klass),
1155       G_SIGNAL_RUN_LAST,
1156       0,
1157       NULL, NULL,
1158       g_cclosure_marshal_VOID__POINTER,
1159       G_TYPE_NONE, 1, G_TYPE_POINTER);
1160
1161
1162   g_type_class_add_private (object_class, sizeof (EmpathyEventManagerPriv));
1163 }
1164
1165 static void
1166 empathy_event_manager_init (EmpathyEventManager *manager)
1167 {
1168   EmpathyEventManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
1169     EMPATHY_TYPE_EVENT_MANAGER, EmpathyEventManagerPriv);
1170   TpDBusDaemon *dbus;
1171   GError *error = NULL;
1172
1173   manager->priv = priv;
1174
1175   priv->dispatcher = empathy_dispatcher_dup_singleton ();
1176   priv->contact_manager = empathy_contact_manager_dup_singleton ();
1177   g_signal_connect (priv->contact_manager, "pendings-changed",
1178     G_CALLBACK (event_manager_pendings_changed_cb), manager);
1179
1180   g_signal_connect (priv->contact_manager, "members-changed",
1181     G_CALLBACK (event_manager_members_changed_cb), manager);
1182
1183   dbus = tp_dbus_daemon_dup (&error);
1184   if (dbus == NULL)
1185     {
1186       DEBUG ("Failed to get TpDBusDaemon: %s", error->message);
1187       g_error_free (error);
1188       return;
1189     }
1190
1191   priv->approver = tp_simple_approver_new (dbus, "Empathy.EventManager", FALSE,
1192       approve_channels, manager, NULL);
1193
1194   /* Private text channels */
1195   tp_base_client_take_approver_filter (priv->approver,
1196       tp_asv_new (
1197         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_TEXT,
1198         TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
1199         NULL));
1200
1201   /* Muc text channels */
1202   tp_base_client_take_approver_filter (priv->approver,
1203       tp_asv_new (
1204         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_TEXT,
1205         TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_ROOM,
1206         NULL));
1207
1208   /* File transfer */
1209   tp_base_client_take_approver_filter (priv->approver,
1210       tp_asv_new (
1211         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
1212           TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER,
1213         TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
1214         NULL));
1215
1216   /* Calls */
1217   tp_base_client_take_approver_filter (priv->approver,
1218       tp_asv_new (
1219         TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
1220           TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
1221         TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
1222         NULL));
1223
1224   if (!tp_base_client_register (priv->approver, &error))
1225     {
1226       DEBUG ("Failed to register Approver: %s", error->message);
1227       g_error_free (error);
1228     }
1229
1230   g_object_unref (dbus);
1231 }
1232
1233 EmpathyEventManager *
1234 empathy_event_manager_dup_singleton (void)
1235 {
1236   return g_object_new (EMPATHY_TYPE_EVENT_MANAGER, NULL);
1237 }
1238
1239 GSList *
1240 empathy_event_manager_get_events (EmpathyEventManager *manager)
1241 {
1242   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
1243
1244   g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
1245
1246   return priv->events;
1247 }
1248
1249 EmpathyEvent *
1250 empathy_event_manager_get_top_event (EmpathyEventManager *manager)
1251 {
1252   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
1253
1254   g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
1255
1256   return priv->events ? priv->events->data : NULL;
1257 }
1258
1259 void
1260 empathy_event_activate (EmpathyEvent *event_public)
1261 {
1262   EventPriv *event = (EventPriv *) event_public;
1263
1264   g_return_if_fail (event_public != NULL);
1265
1266   if (event->func)
1267     event->func (event);
1268   else
1269     event_remove (event);
1270 }
1271
1272 void
1273 empathy_event_inhibit_updates (EmpathyEvent *event_public)
1274 {
1275   EventPriv *event = (EventPriv *) event_public;
1276
1277   g_return_if_fail (event_public != NULL);
1278
1279   event->inhibit = TRUE;
1280 }
1281
1282 void
1283 empathy_event_approve (EmpathyEvent *event_public)
1284 {
1285   EventPriv *event = (EventPriv *) event_public;
1286
1287   g_return_if_fail (event_public != NULL);
1288
1289   event_manager_approval_approve (event->approval);
1290 }
1291
1292 void
1293 empathy_event_decline (EmpathyEvent *event_public)
1294 {
1295   EventPriv *event = (EventPriv *) event_public;
1296
1297   g_return_if_fail (event_public != NULL);
1298
1299   reject_approval (event->approval);
1300 }