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