]> git.0d.be Git - empathy.git/blob - src/empathy-event-manager.c
Port to the new empathy_call_when_ready api
[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   const GError *error, gpointer user_data, GObject *object)
242 {
243   EventManagerApproval *approval = user_data;
244   gchar *msg;
245
246   if (error != NULL)
247     {
248       /* FIXME just returning assuming the operation will be invalidated as
249        * well */
250       return;
251     }
252
253   msg = g_strdup_printf (_("Incoming call from %s"),
254     empathy_contact_get_name (contact));
255
256   event_manager_add (approval->manager,
257     approval->contact, EMPATHY_IMAGE_VOIP, msg,
258     approval, event_channel_process_func, NULL);
259
260   g_free (msg);
261 }
262
263 static void
264 event_manager_media_channel_got_contact (EventManagerApproval *approval)
265 {
266   empathy_contact_call_when_ready (approval->contact,
267      EMPATHY_CONTACT_READY_NAME, event_manager_media_channel_got_name_cb,
268         approval, NULL, G_OBJECT (approval->manager));
269 }
270
271 static void
272 event_manager_media_channel_contact_changed_cb (EmpathyTpCall *call,
273   GParamSpec *param, EventManagerApproval *approval)
274 {
275   EmpathyContact *contact;
276
277   g_object_get (G_OBJECT (call), "contact", &contact, NULL);
278
279   if (contact == NULL)
280     return;
281
282   approval->contact = contact;
283   event_manager_media_channel_got_contact (approval);
284 }
285
286 static void
287 event_manager_tube_approved_cb (EventPriv *event)
288 {
289   empathy_tube_dispatch_handle (event->approval->tube_dispatch);
290 }
291
292 static void
293 event_manager_add_tube_approval (EventManagerApproval *approval,
294   EmpathyTubeDispatchAbility ability)
295 {
296   const gchar *icon_name;
297   gchar       *msg;
298
299   if (ability == EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE)
300     {
301       icon_name = GTK_STOCK_EXECUTE;
302       msg = g_strdup_printf (_("%s is offering you an invitation."
303         " An external application will be started to handle it."),
304       empathy_contact_get_name (approval->contact));
305     }
306   else
307     {
308       icon_name = GTK_STOCK_DIALOG_ERROR;
309       msg = g_strdup_printf (_("%s is offering you an invitation, but "
310         "you don't have the needed external "
311         "application to handle it."),
312         empathy_contact_get_name (approval->contact));
313     }
314
315   event_manager_add (approval->manager, approval->contact, icon_name, msg,
316     approval, event_manager_tube_approved_cb, approval);
317
318   g_free (msg);
319 }
320
321 static void
322 event_manager_tube_dispatch_ability_cb (GObject *object,
323    GParamSpec *spec, gpointer user_data)
324 {
325   EventManagerApproval *approval = (EventManagerApproval *)user_data;
326   EmpathyTubeDispatchAbility dispatchability;
327
328   dispatchability =
329     empathy_tube_dispatch_is_dispatchable (approval->tube_dispatch);
330
331   if (dispatchability != EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN)
332     {
333       event_manager_add_tube_approval (approval, dispatchability);
334       g_signal_handler_disconnect (object, approval->handler);
335       approval->handler = 0;
336     }
337 }
338
339 static void
340 event_manager_tube_got_contact_name_cb (EmpathyContact *contact,
341   const GError *error, gpointer user_data, GObject *object)
342 {
343   EventManagerApproval *approval = (EventManagerApproval *)user_data;
344   EmpathyTubeDispatchAbility dispatchability;
345
346   if (error != NULL)
347     {
348       /* FIXME?, we assume that the operation gets invalidated as well (if it
349        * didn't already */
350        return;
351     }
352
353   dispatchability = empathy_tube_dispatch_is_dispatchable
354     (approval->tube_dispatch);
355
356
357   switch (dispatchability)
358     {
359       case EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN:
360         approval->handler = g_signal_connect (approval->tube_dispatch,
361           "notify::dispatchability",
362           G_CALLBACK (event_manager_tube_dispatch_ability_cb), approval);
363         break;
364       case EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE:
365         /* fallthrough */
366       case EMPATHY_TUBE_DISPATCHABILITY_IMPOSSIBLE:
367         event_manager_add_tube_approval (approval, dispatchability);
368         break;
369     }
370 }
371
372 static void
373 event_manager_approve_channel_cb (EmpathyDispatcher *dispatcher,
374   EmpathyDispatchOperation  *operation, EmpathyEventManager *manager)
375 {
376   const gchar *channel_type;
377   EventManagerApproval *approval;
378   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
379
380   channel_type = empathy_dispatch_operation_get_channel_type (operation);
381
382   approval = event_manager_approval_new (manager, operation);
383   priv->approvals = g_slist_prepend (priv->approvals, approval);
384
385   approval->approved_handler = g_signal_connect (operation, "approved",
386     G_CALLBACK (event_manager_operation_approved_cb), approval);
387
388   approval->claimed_handler = g_signal_connect (operation, "claimed",
389      G_CALLBACK (event_manager_operation_claimed_cb), approval);
390
391   if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT))
392     {
393       EmpathyTpChat *tp_chat =
394         EMPATHY_TP_CHAT (
395           empathy_dispatch_operation_get_channel_wrapper (operation));
396
397       g_signal_connect (tp_chat, "message-received",
398         G_CALLBACK (event_manager_chat_message_received_cb), approval);
399
400     }
401   else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA))
402     {
403       EmpathyContact *contact;
404       EmpathyTpCall *call = EMPATHY_TP_CALL (
405           empathy_dispatch_operation_get_channel_wrapper (operation));
406
407       g_object_get (G_OBJECT (call), "contact", &contact, NULL);
408
409       if (contact == NULL)
410         {
411           g_signal_connect (call, "notify::contact",
412             G_CALLBACK (event_manager_media_channel_contact_changed_cb),
413             approval);
414         }
415       else
416         {
417           approval->contact = contact;
418           event_manager_media_channel_got_contact (approval);
419         }
420
421     }
422   else if (!tp_strdiff (channel_type, EMP_IFACE_CHANNEL_TYPE_FILE_TRANSFER))
423     {
424       EmpathyContact        *contact;
425       gchar                 *msg;
426       TpHandle               handle;
427       McAccount             *account;
428       EmpathyContactFactory *factory;
429       TpChannel *channel = empathy_dispatch_operation_get_channel (operation);
430
431       factory = empathy_contact_factory_dup_singleton ();
432       handle = tp_channel_get_handle (channel, NULL);
433       account = empathy_channel_get_account (channel);
434
435       contact = empathy_contact_factory_get_from_handle (factory, account,
436         handle);
437
438       empathy_contact_run_until_ready (contact,
439         EMPATHY_CONTACT_READY_NAME, NULL);
440
441       msg = g_strdup_printf (_("Incoming file transfer from %s"),
442         empathy_contact_get_name (contact));
443
444       event_manager_add (manager, contact, EMPATHY_IMAGE_DOCUMENT_SEND,
445         msg, approval, event_channel_process_func, NULL);
446
447       g_object_unref (factory);
448       g_object_unref (account);
449     }
450   else if (!tp_strdiff (channel_type, EMP_IFACE_CHANNEL_TYPE_STREAM_TUBE))
451     {
452       EmpathyContact        *contact;
453       TpHandle               handle;
454       McAccount             *account;
455       EmpathyContactFactory *factory;
456       EmpathyTubeDispatch *tube_dispatch;
457       TpChannel *channel;
458
459       channel = empathy_dispatch_operation_get_channel (operation);
460
461       factory = empathy_contact_factory_new ();
462       handle = tp_channel_get_handle (channel, NULL);
463       account = empathy_channel_get_account (channel);
464
465       contact = empathy_contact_factory_get_from_handle (factory, account,
466         handle);
467
468       tube_dispatch = empathy_tube_dispatch_new (operation);
469
470       approval->contact = contact;
471       approval->tube_dispatch = tube_dispatch;
472
473       empathy_contact_call_when_ready (contact,
474         EMPATHY_CONTACT_READY_NAME, event_manager_tube_got_contact_name_cb,
475         approval, NULL, G_OBJECT (manager));
476
477       g_object_unref (factory);
478       g_object_unref (account);
479     }
480   else
481     {
482       DEBUG ("Unknown channel type, ignoring..");
483     }
484 }
485
486 static void
487 event_pending_subscribe_func (EventPriv *event)
488 {
489   empathy_subscription_dialog_show (event->public.contact, NULL);
490   event_remove (event);
491 }
492
493 static void
494 event_manager_pendings_changed_cb (EmpathyContactList  *list,
495   EmpathyContact *contact, EmpathyContact *actor,
496   guint reason, gchar *message, gboolean is_pending,
497   EmpathyEventManager *manager)
498 {
499   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
500   GString                 *str;
501
502   if (!is_pending)
503     {
504       GSList *l;
505
506       for (l = priv->events; l; l = l->next)
507         {
508           EventPriv *event = l->data;
509
510       if (event->public.contact == contact &&
511           event->func == event_pending_subscribe_func)
512         {
513           event_remove (event);
514           break;
515         }
516       }
517
518       return;
519     }
520
521   empathy_contact_run_until_ready (contact, EMPATHY_CONTACT_READY_NAME, NULL);
522
523   str = g_string_new (NULL);
524   g_string_printf (str, _("Subscription requested by %s"),
525     empathy_contact_get_name (contact));
526
527   if (!G_STR_EMPTY (message))
528     g_string_append_printf (str, _("\nMessage: %s"), message);
529
530   event_manager_add (manager, contact, GTK_STOCK_DIALOG_QUESTION, str->str,
531     NULL, event_pending_subscribe_func, NULL);
532
533   g_string_free (str, TRUE);
534 }
535
536 static GObject *
537 event_manager_constructor (GType type,
538                            guint n_props,
539                            GObjectConstructParam *props)
540 {
541         GObject *retval;
542
543         if (manager_singleton) {
544                 retval = g_object_ref (manager_singleton);
545         } else {
546                 retval = G_OBJECT_CLASS (empathy_event_manager_parent_class)->constructor
547                         (type, n_props, props);
548
549                 manager_singleton = EMPATHY_EVENT_MANAGER (retval);
550                 g_object_add_weak_pointer (retval, (gpointer *) &manager_singleton);
551         }
552
553         return retval;
554 }
555
556 static void
557 event_manager_finalize (GObject *object)
558 {
559   EmpathyEventManagerPriv *priv = GET_PRIV (object);
560
561   g_slist_foreach (priv->events, (GFunc) event_free, NULL);
562   g_slist_free (priv->events);
563   g_slist_foreach (priv->approvals, (GFunc) event_manager_approval_free, NULL);
564   g_slist_free (priv->approvals);
565   g_object_unref (priv->contact_manager);
566   g_object_unref (priv->dispatcher);
567 }
568
569 static void
570 empathy_event_manager_class_init (EmpathyEventManagerClass *klass)
571 {
572   GObjectClass *object_class = G_OBJECT_CLASS (klass);
573
574   object_class->finalize = event_manager_finalize;
575   object_class->constructor = event_manager_constructor;
576
577   signals[EVENT_ADDED] =
578     g_signal_new ("event-added",
579       G_TYPE_FROM_CLASS (klass),
580       G_SIGNAL_RUN_LAST,
581       0,
582       NULL, NULL,
583       g_cclosure_marshal_VOID__POINTER,
584       G_TYPE_NONE,
585       1, G_TYPE_POINTER);
586
587   signals[EVENT_REMOVED] =
588   g_signal_new ("event-removed",
589       G_TYPE_FROM_CLASS (klass),
590       G_SIGNAL_RUN_LAST,
591       0,
592       NULL, NULL,
593       g_cclosure_marshal_VOID__POINTER,
594       G_TYPE_NONE, 1, G_TYPE_POINTER);
595
596   g_type_class_add_private (object_class, sizeof (EmpathyEventManagerPriv));
597 }
598
599 static void
600 empathy_event_manager_init (EmpathyEventManager *manager)
601 {
602   EmpathyEventManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
603     EMPATHY_TYPE_EVENT_MANAGER, EmpathyEventManagerPriv);
604
605   manager->priv = priv;
606
607   priv->dispatcher = empathy_dispatcher_dup_singleton ();
608   priv->contact_manager = empathy_contact_manager_dup_singleton ();
609   g_signal_connect (priv->dispatcher, "approve",
610     G_CALLBACK (event_manager_approve_channel_cb), manager);
611   g_signal_connect (priv->contact_manager, "pendings-changed",
612     G_CALLBACK (event_manager_pendings_changed_cb), manager);
613 }
614
615 EmpathyEventManager *
616 empathy_event_manager_dup_singleton (void)
617 {
618   return g_object_new (EMPATHY_TYPE_EVENT_MANAGER, NULL);
619 }
620
621 GSList *
622 empathy_event_manager_get_events (EmpathyEventManager *manager)
623 {
624   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
625
626   g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
627
628   return priv->events;
629 }
630
631 EmpathyEvent *
632 empathy_event_manager_get_top_event (EmpathyEventManager *manager)
633 {
634   EmpathyEventManagerPriv *priv = GET_PRIV (manager);
635
636   g_return_val_if_fail (EMPATHY_IS_EVENT_MANAGER (manager), NULL);
637
638   return priv->events ? priv->events->data : NULL;
639 }
640
641 void
642 empathy_event_activate (EmpathyEvent *event_public)
643 {
644   EventPriv *event = (EventPriv*) event_public;
645
646   g_return_if_fail (event_public != NULL);
647
648   if (event->func)
649     event->func (event);
650   else
651     event_remove (event);
652 }