]> git.0d.be Git - empathy.git/blob - src/empathy-event-manager.c
Move the dispatcher to the new singleton policy
[empathy.git] / src / empathy-event-manager.c
1 /*
2  * Copyright (C) 2007-2008 Collabora Ltd.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  * 
18  * Authors: Xavier Claessens <xclaesse@gmail.com>
19  *          Sjoerd Simons <sjoerd.simons@collabora.co.uk>
20  */
21
22 #include <config.h>
23
24 #include <string.h>
25 #include <glib/gi18n.h>
26
27 #include <telepathy-glib/util.h>
28
29 #include <libempathy/empathy-dispatcher.h>
30 #include <libempathy/empathy-contact-factory.h>
31 #include <libempathy/empathy-contact-manager.h>
32 #include <libempathy/empathy-tp-chat.h>
33 #include <libempathy/empathy-tp-call.h>
34 #include <libempathy/empathy-tp-group.h>
35 #include <libempathy/empathy-utils.h>
36
37 #include <extensions/extensions.h>
38
39 #include <libempathy-gtk/empathy-images.h>
40 #include <libempathy-gtk/empathy-contact-dialogs.h>
41
42 #include "empathy-event-manager.h"
43 #include "empathy-tube-dispatch.h"
44
45 #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
46 #include <libempathy/empathy-debug.h>
47
48 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyEventManager)
49
50 typedef struct {
51   EmpathyEventManager *manager;
52   EmpathyDispatchOperation *operation;
53   gulong approved_handler;
54   gulong claimed_handler;
55   /* Remove contact if applicable */
56   EmpathyContact *contact;
57   /* Tube dispatcher if applicable */
58   EmpathyTubeDispatch *tube_dispatch;
59   /* option signal handler */
60   gulong handler;
61 } EventManagerApproval;
62
63 typedef struct {
64   EmpathyDispatcher *dispatcher;
65   EmpathyContactManager *contact_manager;
66   GSList *events;
67   /* Ongoing approvals */
68   GSList *approvals;
69 } EmpathyEventManagerPriv;
70
71 typedef struct _EventPriv EventPriv;
72 typedef void (*EventFunc) (EventPriv *event);
73
74 struct _EventPriv {
75   EmpathyEvent public;
76   EmpathyEventManager *manager;
77   EventManagerApproval *approval;
78   EventFunc func;
79   gpointer user_data;
80 };
81
82 enum {
83   EVENT_ADDED,
84   EVENT_REMOVED,
85   LAST_SIGNAL
86 };
87
88 static guint signals[LAST_SIGNAL];
89
90 G_DEFINE_TYPE (EmpathyEventManager, empathy_event_manager, G_TYPE_OBJECT);
91
92 static EmpathyEventManager * manager_singleton = NULL;
93
94 static EventManagerApproval *
95 event_manager_approval_new (EmpathyEventManager *manager,
96   EmpathyDispatchOperation *operation)
97 {
98   EventManagerApproval *result = g_slice_new0 (EventManagerApproval);
99   result->operation = g_object_ref (operation);
100   result->manager = manager;
101
102   return result;
103 }
104
105 static void
106 event_manager_approval_free (EventManagerApproval *approval)
107 {
108   g_signal_handler_disconnect (approval->operation,
109     approval->approved_handler);
110   g_signal_handler_disconnect (approval->operation,
111     approval->claimed_handler);
112   g_object_unref (approval->operation);
113
114   if (approval->contact != NULL)
115     g_object_unref (approval->contact);
116
117   if (approval->tube_dispatch != NULL)
118     g_object_unref (approval->tube_dispatch);
119
120   g_slice_free (EventManagerApproval, approval);
121 }
122
123 static void event_remove (EventPriv *event);
124
125 static void
126 event_free (EventPriv *event)
127 {
128   g_free (event->public.icon_name);
129   g_free (event->public.message);
130
131   if (event->public.contact)
132     {
133       g_object_unref (event->public.contact);
134     }
135
136   g_slice_free (EventPriv, event);
137 }
138
139 static void
140 event_remove (EventPriv *event)
141 {
142   EmpathyEventManagerPriv *priv = GET_PRIV (event->manager);
143
144   DEBUG ("Removing event %p", event);
145   priv->events = g_slist_remove (priv->events, event);
146   g_signal_emit (event->manager, signals[EVENT_REMOVED], 0, event);
147   event_free (event);
148 }
149
150 static void
151 event_manager_add (EmpathyEventManager *manager, EmpathyContact      *contact,
152   const gchar         *icon_name, const gchar         *message,
153   EventManagerApproval *approval, EventFunc            func,
154   gpointer             user_data)
155 {
156   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
157   EventPriv               *event;
158
159   event = g_slice_new0 (EventPriv);
160   event->public.contact = contact ? g_object_ref (contact) : NULL;
161   event->public.icon_name = g_strdup (icon_name);
162   event->public.message = g_strdup (message);
163   event->func = func;
164   event->user_data = user_data;
165   event->manager = manager;
166
167   if (approval)
168     event->approval = approval;
169
170   DEBUG ("Adding event %p", event);
171   priv->events = g_slist_prepend (priv->events, event);
172   g_signal_emit (event->manager, signals[EVENT_ADDED], 0, event);
173 }
174
175 static void
176 event_channel_process_func (EventPriv *event)
177 {
178   empathy_dispatch_operation_approve (event->approval->operation);
179 }
180
181 static void
182 event_manager_chat_message_received_cb (EmpathyTpChat       *tp_chat,
183   EmpathyMessage      *message, EventManagerApproval *approval)
184 {
185   EmpathyContact  *sender;
186   gchar           *msg;
187   TpChannel       *channel;
188
189   g_signal_handlers_disconnect_by_func (tp_chat,
190     event_manager_chat_message_received_cb, approval);
191
192   sender = empathy_message_get_sender (message);
193   msg = g_strdup_printf (_("New message from %s:\n%s"),
194     empathy_contact_get_name (sender), empathy_message_get_body (message));
195
196   channel = empathy_tp_chat_get_channel (tp_chat);
197   event_manager_add (approval->manager, sender, EMPATHY_IMAGE_NEW_MESSAGE, msg,
198     approval, event_channel_process_func, NULL);
199
200   g_free (msg);
201 }
202
203 static void
204 event_manager_approval_done (EventManagerApproval *approval)
205 {
206   EmpathyEventManagerPriv *priv = GET_PRIV (approval->manager);
207   GSList                  *l;
208
209   priv->approvals = g_slist_remove (priv->approvals, approval);
210
211   for (l = priv->events; l; l = l->next)
212     {
213       EventPriv *event = l->data;
214
215       if (event->approval == approval)
216         {
217           event_remove (event);
218           break;
219         }
220     }
221
222   event_manager_approval_free (approval);
223 }
224
225 static void
226 event_manager_operation_approved_cb (EmpathyDispatchOperation *operation,
227   EventManagerApproval *approval)
228 {
229   event_manager_approval_done (approval);
230 }
231
232 static void
233 event_manager_operation_claimed_cb (EmpathyDispatchOperation *operation,
234   EventManagerApproval *approval)
235 {
236   event_manager_approval_done (approval);
237 }
238
239 static void
240 event_manager_media_channel_got_name_cb (EmpathyContact *contact,
241   gpointer user_data)
242 {
243   EventManagerApproval *approval = user_data;
244   gchar *msg;
245
246   msg = g_strdup_printf (_("Incoming call from %s"),
247     empathy_contact_get_name (contact));
248
249   event_manager_add (approval->manager,
250     approval->contact, EMPATHY_IMAGE_VOIP, msg,
251     approval, event_channel_process_func, NULL);
252
253   g_free (msg);
254 }
255
256 static void
257 event_manager_media_channel_got_contact (EventManagerApproval *approval)
258 {
259   empathy_contact_call_when_ready (approval->contact,
260      EMPATHY_CONTACT_READY_NAME, event_manager_media_channel_got_name_cb,
261         approval);
262 }
263
264 static void
265 event_manager_media_channel_contact_changed_cb (EmpathyTpCall *call,
266   GParamSpec *param, EventManagerApproval *approval)
267 {
268   EmpathyContact *contact;
269
270   g_object_get (G_OBJECT (call), "contact", &contact, NULL);
271
272   if (contact == NULL)
273     return;
274
275   approval->contact = contact;
276   event_manager_media_channel_got_contact (approval);
277 }
278
279 static void
280 event_manager_tube_approved_cb (EventPriv *event)
281 {
282   empathy_tube_dispatch_handle (event->approval->tube_dispatch);
283 }
284
285 static void
286 event_manager_add_tube_approval (EventManagerApproval *approval,
287   EmpathyTubeDispatchAbility ability)
288 {
289   const gchar *icon_name;
290   gchar       *msg;
291
292   if (ability == EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE)
293     {
294       icon_name = GTK_STOCK_EXECUTE;
295       msg = g_strdup_printf (_("%s is offering you an invitation."
296         " An external application will be started to handle it."),
297       empathy_contact_get_name (approval->contact));
298     }
299   else
300     {
301       icon_name = GTK_STOCK_DIALOG_ERROR;
302       msg = g_strdup_printf (_("%s is offering you an invitation, but "
303         "you don't have the needed external "
304         "application to handle it."),
305         empathy_contact_get_name (approval->contact));
306     }
307
308   event_manager_add (approval->manager, approval->contact, icon_name, msg,
309     approval, event_manager_tube_approved_cb, approval);
310
311   g_free (msg);
312 }
313
314 static void
315 event_manager_tube_dispatch_ability_cb (GObject *object,
316    GParamSpec *spec, gpointer user_data)
317 {
318   EventManagerApproval *approval = (EventManagerApproval *)user_data;
319   EmpathyTubeDispatchAbility dispatchability;
320
321   dispatchability =
322     empathy_tube_dispatch_is_dispatchable (approval->tube_dispatch);
323
324   if (dispatchability != EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN)
325     {
326       event_manager_add_tube_approval (approval, dispatchability);
327       g_signal_handler_disconnect (object, approval->handler);
328       approval->handler = 0;
329     }
330 }
331
332 static void
333 event_manager_tube_got_contact_name_cb (EmpathyContact *contact,
334   gpointer user_data)
335 {
336   EventManagerApproval *approval = (EventManagerApproval *)user_data;
337   EmpathyTubeDispatchAbility dispatchability;
338
339   dispatchability = empathy_tube_dispatch_is_dispatchable
340     (approval->tube_dispatch);
341
342   switch (dispatchability)
343     {
344       case EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN:
345         approval->handler = g_signal_connect (approval->tube_dispatch,
346           "notify::dispatchability",
347           G_CALLBACK (event_manager_tube_dispatch_ability_cb), approval);
348         break;
349       case EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE:
350         /* fallthrough */
351       case EMPATHY_TUBE_DISPATCHABILITY_IMPOSSIBLE:
352         event_manager_add_tube_approval (approval, dispatchability);
353         break;
354     }
355 }
356
357 static void
358 event_manager_approve_channel_cb (EmpathyDispatcher *dispatcher,
359   EmpathyDispatchOperation  *operation, EmpathyEventManager *manager)
360 {
361   const gchar *channel_type;
362   EventManagerApproval *approval;
363   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
364
365   channel_type = empathy_dispatch_operation_get_channel_type (operation);
366
367   approval = event_manager_approval_new (manager, operation);
368   priv->approvals = g_slist_prepend (priv->approvals, approval);
369
370   approval->approved_handler = g_signal_connect (operation, "approved",
371     G_CALLBACK (event_manager_operation_approved_cb), approval);
372
373   approval->claimed_handler = g_signal_connect (operation, "claimed",
374      G_CALLBACK (event_manager_operation_claimed_cb), approval);
375
376   if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT))
377     {
378       EmpathyTpChat *tp_chat =
379         EMPATHY_TP_CHAT (
380           empathy_dispatch_operation_get_channel_wrapper (operation));
381
382       g_signal_connect (tp_chat, "message-received",
383         G_CALLBACK (event_manager_chat_message_received_cb), approval);
384       g_object_unref (G_OBJECT (tp_chat));
385
386     }
387   else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA))
388     {
389       EmpathyContact *contact;
390       EmpathyTpCall *call = EMPATHY_TP_CALL (
391           empathy_dispatch_operation_get_channel_wrapper (operation));
392
393       g_object_get (G_OBJECT (call), "contact", &contact, NULL);
394
395       if (contact == NULL)
396         {
397           g_signal_connect (call, "notify::contact",
398             G_CALLBACK (event_manager_media_channel_contact_changed_cb),
399             approval);
400         }
401       else
402         {
403           approval->contact = contact;
404           event_manager_media_channel_got_contact (approval);
405         }
406
407     }
408   else if (!tp_strdiff (channel_type, EMP_IFACE_CHANNEL_TYPE_FILE_TRANSFER))
409     {
410       EmpathyContact        *contact;
411       gchar                 *msg;
412       TpHandle               handle;
413       McAccount             *account;
414       EmpathyContactFactory *factory;
415       TpChannel *channel = empathy_dispatch_operation_get_channel (operation);
416
417       factory = empathy_contact_factory_dup_singleton ();
418       handle = tp_channel_get_handle (channel, NULL);
419       account = empathy_channel_get_account (channel);
420
421       contact = empathy_contact_factory_get_from_handle (factory, account,
422         handle);
423
424       empathy_contact_run_until_ready (contact,
425         EMPATHY_CONTACT_READY_NAME, NULL);
426
427       msg = g_strdup_printf (_("Incoming file transfer from %s"),
428         empathy_contact_get_name (contact));
429
430       event_manager_add (manager, contact, EMPATHY_IMAGE_DOCUMENT_SEND,
431         msg, approval, event_channel_process_func, NULL);
432
433       g_object_unref (channel);
434       g_object_unref (factory);
435       g_object_unref (account);
436     }
437   else if (!tp_strdiff (channel_type, EMP_IFACE_CHANNEL_TYPE_STREAM_TUBE))
438     {
439       EmpathyContact        *contact;
440       TpHandle               handle;
441       McAccount             *account;
442       EmpathyContactFactory *factory;
443       EmpathyTubeDispatch *tube_dispatch;
444       TpChannel *channel;
445
446       channel = empathy_dispatch_operation_get_channel (operation);
447
448       factory = empathy_contact_factory_new ();
449       handle = tp_channel_get_handle (channel, NULL);
450       account = empathy_channel_get_account (channel);
451
452       contact = empathy_contact_factory_get_from_handle (factory, account,
453         handle);
454
455       tube_dispatch = empathy_tube_dispatch_new (operation);
456
457       approval->contact = contact;
458       approval->tube_dispatch = tube_dispatch;
459
460       empathy_contact_call_when_ready (contact,
461         EMPATHY_CONTACT_READY_NAME, event_manager_tube_got_contact_name_cb,
462         approval);
463
464       g_object_unref (channel);
465       g_object_unref (factory);
466       g_object_unref (account);
467     }
468   else
469     {
470       DEBUG ("Unknown channel type, ignoring..");
471     }
472 }
473
474 static void
475 event_pending_subscribe_func (EventPriv *event)
476 {
477   empathy_subscription_dialog_show (event->public.contact, NULL);
478   event_remove (event);
479 }
480
481 static void
482 event_manager_pendings_changed_cb (EmpathyContactList  *list,
483   EmpathyContact *contact, EmpathyContact *actor,
484   guint reason, gchar *message, gboolean is_pending,
485   EmpathyEventManager *manager)
486 {
487   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
488   GString                 *str;
489
490   if (!is_pending)
491     {
492       GSList *l;
493
494       for (l = priv->events; l; l = l->next)
495         {
496           EventPriv *event = l->data;
497
498       if (event->public.contact == contact &&
499           event->func == event_pending_subscribe_func)
500         {
501           event_remove (event);
502           break;
503         }
504       }
505
506       return;
507     }
508
509   empathy_contact_run_until_ready (contact, EMPATHY_CONTACT_READY_NAME, NULL);
510
511   str = g_string_new (NULL);
512   g_string_printf (str, _("Subscription requested by %s"),
513     empathy_contact_get_name (contact));
514
515   if (!G_STR_EMPTY (message))
516     g_string_append_printf (str, _("\nMessage: %s"), message);
517
518   event_manager_add (manager, contact, GTK_STOCK_DIALOG_QUESTION, str->str,
519     NULL, event_pending_subscribe_func, NULL);
520
521   g_string_free (str, TRUE);
522 }
523
524 static GObject *
525 event_manager_constructor (GType type,
526                            guint n_props,
527                            GObjectConstructParam *props)
528 {
529         GObject *retval;
530
531         if (manager_singleton) {
532                 retval = g_object_ref (manager_singleton);
533         } else {
534                 retval = G_OBJECT_CLASS (empathy_event_manager_parent_class)->constructor
535                         (type, n_props, props);
536
537                 manager_singleton = EMPATHY_EVENT_MANAGER (retval);
538                 g_object_add_weak_pointer (retval, (gpointer *) &manager_singleton);
539         }
540
541         return retval;
542 }
543
544 static void
545 event_manager_finalize (GObject *object)
546 {
547   EmpathyEventManagerPriv *priv = GET_PRIV (object);
548
549   g_slist_foreach (priv->events, (GFunc) event_free, NULL);
550   g_slist_free (priv->events);
551   g_slist_foreach (priv->approvals, (GFunc) event_manager_approval_free, NULL);
552   g_slist_free (priv->approvals);
553   g_object_unref (priv->contact_manager);
554   g_object_unref (priv->dispatcher);
555 }
556
557 static void
558 empathy_event_manager_class_init (EmpathyEventManagerClass *klass)
559 {
560   GObjectClass *object_class = G_OBJECT_CLASS (klass);
561
562   object_class->finalize = event_manager_finalize;
563   object_class->constructor = event_manager_constructor;
564
565   signals[EVENT_ADDED] =
566     g_signal_new ("event-added",
567       G_TYPE_FROM_CLASS (klass),
568       G_SIGNAL_RUN_LAST,
569       0,
570       NULL, NULL,
571       g_cclosure_marshal_VOID__POINTER,
572       G_TYPE_NONE,
573       1, G_TYPE_POINTER);
574
575   signals[EVENT_REMOVED] =
576   g_signal_new ("event-removed",
577       G_TYPE_FROM_CLASS (klass),
578       G_SIGNAL_RUN_LAST,
579       0,
580       NULL, NULL,
581       g_cclosure_marshal_VOID__POINTER,
582       G_TYPE_NONE, 1, G_TYPE_POINTER);
583
584   g_type_class_add_private (object_class, sizeof (EmpathyEventManagerPriv));
585 }
586
587 static void
588 empathy_event_manager_init (EmpathyEventManager *manager)
589 {
590   EmpathyEventManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
591     EMPATHY_TYPE_EVENT_MANAGER, EmpathyEventManagerPriv);
592
593   manager->priv = priv;
594
595   priv->dispatcher = empathy_dispatcher_dup_singleton ();
596   priv->contact_manager = empathy_contact_manager_dup_singleton ();
597   g_signal_connect (priv->dispatcher, "approve",
598     G_CALLBACK (event_manager_approve_channel_cb), manager);
599   g_signal_connect (priv->contact_manager, "pendings-changed",
600     G_CALLBACK (event_manager_pendings_changed_cb), manager);
601 }
602
603 EmpathyEventManager *
604 empathy_event_manager_dup_singleton (void)
605 {
606   return g_object_new (EMPATHY_TYPE_EVENT_MANAGER, NULL);
607 }
608
609 GSList *
610 empathy_event_manager_get_events (EmpathyEventManager *manager)
611 {
612   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
613
614   g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
615
616   return priv->events;
617 }
618
619 EmpathyEvent *
620 empathy_event_manager_get_top_event (EmpathyEventManager *manager)
621 {
622   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
623
624   g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
625
626   return priv->events ? priv->events->data : NULL;
627 }
628
629 void
630 empathy_event_activate (EmpathyEvent *event_public)
631 {
632   EventPriv *event = (EventPriv*) event_public;
633
634   g_return_if_fail (event_public != NULL);
635
636   if (event->func)
637     event->func (event);
638   else
639     event_remove (event);
640 }