]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-new-call-dialog.c
NewCallDialog: support Calls
[empathy.git] / libempathy-gtk / empathy-new-call-dialog.c
1 /*
2  * Copyright (C) 2009 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: Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
19  */
20
21 #include <config.h>
22
23 #include <string.h>
24 #include <stdlib.h>
25
26 #include <gtk/gtk.h>
27 #include <glib/gi18n-lib.h>
28
29 #include <telepathy-glib/interfaces.h>
30
31 #if HAVE_CALL
32 #include <telepathy-yell/telepathy-yell.h>
33 #endif
34
35 #include <libempathy/empathy-tp-contact-factory.h>
36 #include <libempathy/empathy-contact-manager.h>
37 #include <libempathy/empathy-utils.h>
38 #include <libempathy/empathy-request-util.h>
39
40 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
41 #include <libempathy/empathy-debug.h>
42
43 #include <libempathy-gtk/empathy-ui-utils.h>
44 #include <libempathy-gtk/empathy-images.h>
45
46 #include "empathy-new-call-dialog.h"
47 #include "empathy-account-chooser.h"
48
49 static EmpathyNewCallDialog *dialog_singleton = NULL;
50
51 G_DEFINE_TYPE(EmpathyNewCallDialog, empathy_new_call_dialog,
52                EMPATHY_TYPE_CONTACT_SELECTOR_DIALOG)
53
54 typedef struct _EmpathyNewCallDialogPriv EmpathyNewCallDialogPriv;
55
56 typedef struct {
57   EmpathyAccountChooserFilterResultCallback callback;
58   gpointer                                  user_data;
59 } FilterCallbackData;
60
61 typedef struct {
62   gboolean video;
63   gint64 timestamp;
64 } ContactCallbackData;
65
66 struct _EmpathyNewCallDialogPriv {
67   GtkWidget *check_video;
68 };
69
70 #define GET_PRIV(o) \
71   (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_NEW_CALL_DIALOG, \
72     EmpathyNewCallDialogPriv))
73
74 /**
75  * SECTION:empathy-new-call-dialog
76  * @title: EmpathyNewCallDialog
77  * @short_description: A dialog to show a new call
78  * @include: libempathy-gtk/empathy-new-call-dialog.h
79  *
80  * #EmpathyNewCallDialog is a dialog which allows a call
81  * to be started with any contact on any enabled account.
82  */
83
84 static void
85 got_contact_cb (TpConnection *connection,
86     EmpathyContact *contact,
87     const GError *error,
88     gpointer user_data,
89     GObject *weak_object)
90 {
91   ContactCallbackData *data = user_data;
92
93   if (error != NULL)
94     g_warning ("Could not get contact: %s", error->message);
95   else
96     empathy_call_new_with_streams (contact,
97         TRUE, data->video, data->timestamp);
98
99   g_slice_free (ContactCallbackData, data);
100 }
101
102 static void
103 call_contact (TpAccount *account,
104     const gchar *contact_id,
105     gboolean video,
106     gint64 timestamp)
107 {
108   ContactCallbackData *data = g_slice_new0 (ContactCallbackData);
109
110   data->video = video;
111   data->timestamp = timestamp;
112
113   empathy_tp_contact_factory_get_from_id (tp_account_get_connection (account),
114       contact_id,
115       got_contact_cb, data,
116       NULL, NULL);
117 }
118
119 static void
120 empathy_new_call_dialog_response (GtkDialog *dialog, int response_id)
121 {
122   EmpathyNewCallDialogPriv *priv = GET_PRIV (dialog);
123   gboolean video;
124   TpAccount *account;
125   const gchar *contact_id;
126
127   if (response_id != GTK_RESPONSE_ACCEPT) goto out;
128
129   contact_id = empathy_contact_selector_dialog_get_selected (
130       EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, &account);
131
132   if (EMP_STR_EMPTY (contact_id) || account == NULL) goto out;
133
134   /* check if video is enabled now because the dialog will be destroyed once
135    * we return from this function. */
136   video = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->check_video));
137
138   call_contact (account, contact_id, video,
139       empathy_get_current_action_time ());
140
141 out:
142   gtk_widget_destroy (GTK_WIDGET (dialog));
143 }
144
145 static void
146 conn_prepared_cb (GObject *conn,
147     GAsyncResult *result,
148     gpointer user_data)
149 {
150   FilterCallbackData *data = user_data;
151   GError *myerr = NULL;
152   TpCapabilities *caps;
153   GPtrArray *classes;
154   guint i;
155
156   if (!tp_proxy_prepare_finish (conn, result, &myerr))
157       goto out;
158
159   caps = tp_connection_get_capabilities (TP_CONNECTION (conn));
160   classes = tp_capabilities_get_channel_classes (caps);
161
162   for (i = 0; i < classes->len; i++)
163     {
164       GHashTable *fixed;
165       GStrv allowed;
166       const gchar *chan_type;
167
168       tp_value_array_unpack (g_ptr_array_index (classes, i), 2,
169           &fixed, &allowed);
170
171       chan_type = tp_asv_get_string (fixed, TP_PROP_CHANNEL_CHANNEL_TYPE);
172
173       if (tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA)
174 #if HAVE_CALL
175           && tp_strdiff (chan_type, TPY_IFACE_CHANNEL_TYPE_CALL)
176 #endif
177          )
178         continue;
179
180       if (tp_asv_get_uint32 (fixed, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL) !=
181           TP_HANDLE_TYPE_CONTACT)
182         continue;
183
184       data->callback (TRUE, data->user_data);
185       g_slice_free (FilterCallbackData, data);
186       return;
187     }
188
189 out:
190   data->callback (FALSE, data->user_data);
191   g_slice_free (FilterCallbackData, data);
192 }
193
194 static void
195 empathy_new_call_dialog_account_filter (EmpathyContactSelectorDialog *dialog,
196     EmpathyAccountChooserFilterResultCallback callback,
197     gpointer callback_data,
198     TpAccount *account)
199 {
200   TpConnection *connection;
201   FilterCallbackData *cb_data;
202   GQuark features[] = { TP_CONNECTION_FEATURE_CAPABILITIES, 0 };
203
204   if (tp_account_get_connection_status (account, NULL) !=
205       TP_CONNECTION_STATUS_CONNECTED)
206     {
207       callback (FALSE, callback_data);
208       return;
209     }
210
211   /* check if CM supports calls */
212   connection = tp_account_get_connection (account);
213   if (connection == NULL)
214     {
215       callback (FALSE, callback_data);
216       return;
217     }
218
219   cb_data = g_slice_new0 (FilterCallbackData);
220   cb_data->callback = callback;
221   cb_data->user_data = callback_data;
222   tp_proxy_prepare_async (connection, features, conn_prepared_cb, cb_data);
223 }
224
225 static GObject *
226 empathy_new_call_dialog_constructor (GType type,
227     guint n_props,
228     GObjectConstructParam *props)
229 {
230   GObject *retval;
231
232   if (dialog_singleton)
233     {
234       retval = G_OBJECT (dialog_singleton);
235       g_object_ref (retval);
236     }
237   else
238     {
239       retval = G_OBJECT_CLASS (
240       empathy_new_call_dialog_parent_class)->constructor (type,
241         n_props, props);
242
243       dialog_singleton = EMPATHY_NEW_CALL_DIALOG (retval);
244       g_object_add_weak_pointer (retval, (gpointer) &dialog_singleton);
245     }
246
247   return retval;
248 }
249
250 static void
251 empathy_new_call_dialog_init (EmpathyNewCallDialog *dialog)
252 {
253   EmpathyContactSelectorDialog *parent = EMPATHY_CONTACT_SELECTOR_DIALOG (
254         dialog);
255   EmpathyNewCallDialogPriv *priv = GET_PRIV (dialog);
256   GtkWidget *image;
257
258   /* add video toggle */
259   priv->check_video = gtk_check_button_new_with_mnemonic (_("Send _Video"));
260
261   gtk_box_pack_end (GTK_BOX (parent->vbox), priv->check_video,
262       FALSE, TRUE, 0);
263
264   gtk_widget_show (priv->check_video);
265
266   /* add chat button */
267   parent->button_action = gtk_button_new_with_mnemonic (_("C_all"));
268   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_VOIP,
269       GTK_ICON_SIZE_BUTTON);
270   gtk_button_set_image (GTK_BUTTON (parent->button_action), image);
271
272   gtk_dialog_add_action_widget (GTK_DIALOG (dialog), parent->button_action,
273       GTK_RESPONSE_ACCEPT);
274   gtk_widget_show (parent->button_action);
275
276   /* Tweak the dialog */
277   gtk_window_set_title (GTK_WINDOW (dialog), _("New Call"));
278   gtk_window_set_role (GTK_WINDOW (dialog), "new_call");
279
280   gtk_widget_set_sensitive (parent->button_action, FALSE);
281 }
282
283 static void
284 empathy_new_call_dialog_class_init (
285   EmpathyNewCallDialogClass *class)
286 {
287   GObjectClass *object_class = G_OBJECT_CLASS (class);
288   GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (class);
289   EmpathyContactSelectorDialogClass *selector_dialog_class = \
290     EMPATHY_CONTACT_SELECTOR_DIALOG_CLASS (class);
291
292   g_type_class_add_private (class, sizeof (EmpathyNewCallDialogPriv));
293
294   object_class->constructor = empathy_new_call_dialog_constructor;
295
296   dialog_class->response = empathy_new_call_dialog_response;
297
298   selector_dialog_class->account_filter = empathy_new_call_dialog_account_filter;
299 }
300
301 /**
302  * empathy_new_call_dialog_new:
303  * @parent: parent #GtkWindow of the dialog
304  *
305  * Create a new #EmpathyNewCallDialog it.
306  *
307  * Return value: the new #EmpathyNewCallDialog
308  */
309 GtkWidget *
310 empathy_new_call_dialog_show (GtkWindow *parent)
311 {
312   GtkWidget *dialog;
313
314   dialog = g_object_new (EMPATHY_TYPE_NEW_CALL_DIALOG, NULL);
315
316   if (parent)
317     {
318       gtk_window_set_transient_for (GTK_WINDOW (dialog),
319                   GTK_WINDOW (parent));
320     }
321
322   gtk_widget_show (dialog);
323   return dialog;
324 }