]> git.0d.be Git - empathy.git/blob - src/empathy-call-window.c
Make use of tp-glib debug system.
[empathy.git] / src / empathy-call-window.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007 Elliot Fairweather
4  * Copyright (C) 2007-2008 Collabora Ltd.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * Authors: Elliot Fairweather <elliot.fairweather@collabora.co.uk>
21  *          Xavier Claessens <xclaesse@gmail.com>
22  */
23
24 #include <string.h>
25
26 #include <glade/glade.h>
27 #include <glib/gi18n.h>
28
29 #include <telepathy-glib/enums.h>
30
31 #include <libempathy/empathy-contact.h>
32 #include <libempathy/empathy-tp-call.h>
33 #include <libempathy/empathy-utils.h>
34 #include <libempathy-gtk/empathy-ui-utils.h>
35
36 #include "empathy-call-window.h"
37
38 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
39 #include <libempathy/empathy-debug.h>
40
41 typedef struct 
42 {
43   EmpathyTpCall *call;
44   GTimeVal start_time;
45   guint timeout_event_id;
46   gboolean is_drawing;
47   guint status; 
48
49   GtkWidget *window;
50   GtkWidget *main_hbox;
51   GtkWidget *controls_vbox;
52   GtkWidget *volume_hbox;
53   GtkWidget *status_label;
54   GtkWidget *input_volume_button;
55   GtkWidget *output_volume_button;
56   GtkWidget *preview_video_socket;
57   GtkWidget *output_video_socket;
58   GtkWidget *video_button;
59   GtkWidget *hang_up_button;
60   GtkWidget *confirmation_dialog;
61 } EmpathyCallWindow;
62
63 static gboolean
64 call_window_update_timer (gpointer data)
65 {
66   EmpathyCallWindow *window = data;
67   GTimeVal current;
68   gchar *str;
69   glong now, then;
70   glong time, seconds, minutes, hours;
71
72   g_get_current_time (&current);
73
74   now = current.tv_sec;
75   then = (window->start_time).tv_sec;
76
77   time = now - then;
78
79   seconds = time % 60;
80   time /= 60;
81   minutes = time % 60;
82   time /= 60;
83   hours = time % 60;
84
85   if (hours > 0)
86       str = g_strdup_printf ("Connected  -  %02ld : %02ld : %02ld", hours,
87           minutes, seconds);
88   else
89       str = g_strdup_printf ("Connected  -  %02ld : %02ld", minutes, seconds);
90
91   gtk_label_set_text (GTK_LABEL (window->status_label), str);
92
93   g_free (str);
94
95   return TRUE;
96 }
97
98 static void
99 call_window_stop_timeout (EmpathyCallWindow *window)
100 {
101   DEBUG ("Timer stopped");
102
103   if (window->timeout_event_id)
104     {
105       g_source_remove (window->timeout_event_id);
106       window->timeout_event_id = 0;
107     }
108 }
109
110 static void
111 call_window_set_output_video_is_drawing (EmpathyCallWindow *window,
112                                          gboolean is_drawing)
113 {
114   DEBUG ("Setting output video is drawing - %d", is_drawing);
115
116   if (is_drawing && !window->is_drawing)
117     {
118       gtk_window_set_resizable (GTK_WINDOW (window->window), TRUE);
119       gtk_box_pack_end (GTK_BOX (window->main_hbox),
120           window->output_video_socket, TRUE, TRUE, 0);
121       empathy_tp_call_add_output_video (window->call,
122           gtk_socket_get_id (GTK_SOCKET (window->output_video_socket)));
123     }
124   if (!is_drawing && window->is_drawing)
125     {
126       gtk_window_set_resizable (GTK_WINDOW (window->window), FALSE);
127       empathy_tp_call_add_output_video (window->call, 0);
128       gtk_container_remove (GTK_CONTAINER (window->main_hbox),
129           window->output_video_socket);
130     }
131
132   window->is_drawing = is_drawing;
133 }
134
135 static void
136 call_window_finalize (EmpathyCallWindow *window)
137 {
138   gtk_label_set_text (GTK_LABEL (window->status_label), _("Closed"));
139   gtk_widget_set_sensitive (window->hang_up_button, FALSE);
140   gtk_widget_set_sensitive (window->video_button, FALSE);
141   gtk_widget_set_sensitive (window->output_volume_button, FALSE);
142   gtk_widget_set_sensitive (window->input_volume_button, FALSE);
143
144   if (window->call)
145     { 
146       call_window_stop_timeout (window);
147       call_window_set_output_video_is_drawing (window, FALSE);
148       empathy_tp_call_remove_preview_video (window->call,
149           gtk_socket_get_id (GTK_SOCKET (window->preview_video_socket)));
150       g_object_unref (window->call);
151       window->call = NULL;
152     }
153
154   if (window->confirmation_dialog)
155       gtk_widget_destroy (window->confirmation_dialog);
156 }
157
158 static void
159 call_window_socket_realized_cb (GtkWidget *widget,
160                                 EmpathyCallWindow *window)
161 {
162   if (widget == window->preview_video_socket)
163     {
164       DEBUG ("Preview socket realized");
165       empathy_tp_call_add_preview_video (window->call,
166           gtk_socket_get_id (GTK_SOCKET (window->preview_video_socket)));
167     }
168   else
169       DEBUG ("Output socket realized");
170 }
171
172 static void
173 call_window_video_button_toggled_cb (GtkWidget *button,
174                                      EmpathyCallWindow *window)
175 {
176   gboolean is_sending;
177   guint status;
178
179   is_sending = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
180
181   DEBUG ("Send video toggled - %d", is_sending);
182
183   g_object_get (window->call, "status", &status, NULL);
184   if (status == EMPATHY_TP_CALL_STATUS_ACCEPTED)
185       empathy_tp_call_request_video_stream_direction (window->call, is_sending);
186 }
187
188 static void
189 call_window_hang_up_button_clicked_cb (GtkWidget *widget,
190                                        EmpathyCallWindow *window)
191 {
192   DEBUG ("Call clicked, end call");
193   call_window_finalize (window);
194 }
195
196 static void
197 call_window_output_volume_changed_cb (GtkScaleButton *button,
198                                       gdouble value,
199                                       EmpathyCallWindow *window)
200 {
201   if (!window->call)
202       return;
203
204   if (value <= 0)
205       empathy_tp_call_mute_output (window->call, TRUE);
206   else
207     {
208       empathy_tp_call_mute_output (window->call, FALSE);
209       empathy_tp_call_set_output_volume (window->call, value * 100);
210     }
211 }
212
213 static void
214 call_window_input_volume_changed_cb (GtkScaleButton    *button,
215                                      gdouble            value,
216                                      EmpathyCallWindow *window)
217 {
218   if (!window->call)
219       return;
220
221   if (value <= 0)
222       empathy_tp_call_mute_input (window->call, TRUE);
223   else
224     {
225       empathy_tp_call_mute_input (window->call, FALSE);
226       /* FIXME: Not implemented?
227       empathy_tp_call_set_input_volume (window->call, value * 100);*/
228     }
229 }
230
231 static gboolean
232 call_window_delete_event_cb (GtkWidget *widget,
233                              GdkEvent *event,
234                              EmpathyCallWindow *window)
235 {
236   GtkWidget *dialog;
237   gint result;
238   guint status = EMPATHY_TP_CALL_STATUS_CLOSED;
239
240   DEBUG ("Delete event occurred");
241
242   if (window->call)
243       g_object_get (window->call, "status", &status, NULL);
244
245   if (status == EMPATHY_TP_CALL_STATUS_ACCEPTED)
246     {
247       dialog = gtk_message_dialog_new (GTK_WINDOW (window->window),
248           GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
249           GTK_MESSAGE_WARNING, GTK_BUTTONS_CANCEL, _("End this call?"));
250       gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
251           _("Closing this window will end the call in progress."));
252       gtk_dialog_add_button (GTK_DIALOG (dialog), _("_End Call"), GTK_RESPONSE_OK);
253       gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
254
255       result = gtk_dialog_run (GTK_DIALOG (dialog));
256       gtk_widget_destroy (dialog);
257
258       if (result != GTK_RESPONSE_OK)
259           return TRUE;
260     }
261
262   return FALSE;
263 }
264
265 static void
266 call_window_destroy_cb (GtkWidget *widget,
267                         EmpathyCallWindow *window)
268 {
269   call_window_finalize (window);
270   g_object_unref (window->output_video_socket);
271   g_object_unref (window->preview_video_socket);
272   g_slice_free (EmpathyCallWindow, window);
273 }
274
275 static void
276 call_window_confirmation_dialog_response_cb (GtkDialog *dialog,
277                                              gint response,
278                                              EmpathyCallWindow *window)
279 {
280   if (response == GTK_RESPONSE_OK && window->call)
281       empathy_tp_call_accept_incoming_call (window->call);
282   else
283       call_window_finalize (window);
284
285   gtk_widget_destroy (window->confirmation_dialog);
286   window->confirmation_dialog = NULL;
287 }
288
289 static void
290 call_window_show_confirmation_dialog (EmpathyCallWindow *window)
291 {
292   EmpathyContact *contact;
293   GtkWidget *button;
294   GtkWidget *image;
295
296   if (window->confirmation_dialog)
297       return;
298
299   g_object_get (window->call, "contact", &contact, NULL);
300
301   window->confirmation_dialog = gtk_message_dialog_new (GTK_WINDOW (window->window),
302       GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
303       GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("Incoming call"));
304   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (window->confirmation_dialog),
305       _("%s is calling you, do you want to answer?"),
306       empathy_contact_get_name (contact));
307   gtk_dialog_set_default_response (GTK_DIALOG (window->confirmation_dialog),
308       GTK_RESPONSE_OK);
309
310   button = gtk_dialog_add_button (GTK_DIALOG (window->confirmation_dialog),
311       _("_Reject"), GTK_RESPONSE_CANCEL);
312   image = gtk_image_new_from_icon_name (GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
313   gtk_button_set_image (GTK_BUTTON (button), image);
314
315   button = gtk_dialog_add_button (GTK_DIALOG (window->confirmation_dialog),
316       _("_Answer"), GTK_RESPONSE_OK);
317   image = gtk_image_new_from_icon_name (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
318   gtk_button_set_image (GTK_BUTTON (button), image);
319
320   g_signal_connect (window->confirmation_dialog, "response",
321       G_CALLBACK (call_window_confirmation_dialog_response_cb),
322       window);
323
324   gtk_widget_show (window->confirmation_dialog);
325   g_object_unref (contact);
326 }
327
328 static void
329 call_window_update (EmpathyCallWindow *window)
330 {
331   EmpathyContact *contact;
332   guint stream_state;
333   EmpathyTpCallStream *audio_stream;
334   EmpathyTpCallStream *video_stream;
335   gboolean is_incoming;
336   gchar *title;
337
338   g_object_get (window->call,
339       "status", &window->status,
340       "audio-stream", &audio_stream,
341       "video-stream", &video_stream,
342       "contact", &contact,
343       "is-incoming", &is_incoming,
344       NULL);
345
346   if (video_stream->state > audio_stream->state)
347       stream_state = video_stream->state;
348   else
349       stream_state = audio_stream->state;
350
351   DEBUG ("Status changed - status: %d, stream state: %d, "
352       "is-incoming: %d video-stream direction: %d",
353       window->status, stream_state, is_incoming, video_stream->direction);
354
355   /* Depending on the status we have to set:
356    * - window's title
357    * - status's label
358    * - sensibility of all buttons
359    * */
360   if (window->status == EMPATHY_TP_CALL_STATUS_READYING)
361     {
362       gtk_window_set_title (GTK_WINDOW (window->window), _("Empathy Call"));
363       gtk_label_set_text (GTK_LABEL (window->status_label), _("Readying"));
364       gtk_widget_set_sensitive (window->video_button, FALSE);
365       gtk_widget_set_sensitive (window->output_volume_button, FALSE);
366       gtk_widget_set_sensitive (window->input_volume_button, FALSE);
367       gtk_widget_set_sensitive (window->hang_up_button, FALSE);
368     }
369   else if (window->status == EMPATHY_TP_CALL_STATUS_PENDING)
370     {
371       title = g_strdup_printf (_("%s - Empathy Call"),
372           empathy_contact_get_name (contact));
373
374       gtk_window_set_title (GTK_WINDOW (window->window), title);
375       gtk_label_set_text (GTK_LABEL (window->status_label), _("Ringing"));
376       gtk_widget_set_sensitive (window->hang_up_button, TRUE);
377       if (is_incoming)
378           call_window_show_confirmation_dialog (window);
379     }
380   else if (window->status == EMPATHY_TP_CALL_STATUS_ACCEPTED)
381     {
382       gboolean receiving_video;
383       gboolean sending_video;
384
385       if (stream_state == TP_MEDIA_STREAM_STATE_DISCONNECTED)
386           gtk_label_set_text (GTK_LABEL (window->status_label), _("Disconnected"));
387       if (stream_state == TP_MEDIA_STREAM_STATE_CONNECTING)
388           gtk_label_set_text (GTK_LABEL (window->status_label), _("Connecting"));
389       else if (stream_state == TP_MEDIA_STREAM_STATE_CONNECTED &&
390                window->timeout_event_id == 0)
391         {
392           /* The call started, launch the timer */
393           g_get_current_time (&(window->start_time));
394           window->timeout_event_id = g_timeout_add_seconds (1,
395               call_window_update_timer, window);
396           call_window_update_timer (window);
397         }
398
399       receiving_video = video_stream->direction & TP_MEDIA_STREAM_DIRECTION_RECEIVE;
400       sending_video = video_stream->direction & TP_MEDIA_STREAM_DIRECTION_SEND;
401       call_window_set_output_video_is_drawing (window, receiving_video);
402       g_signal_handlers_block_by_func (window->video_button,
403           call_window_video_button_toggled_cb, window);
404       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (window->video_button),
405           sending_video);
406       g_signal_handlers_unblock_by_func (window->video_button,
407           call_window_video_button_toggled_cb, window);
408
409       gtk_widget_set_sensitive (window->video_button, TRUE);
410       gtk_widget_set_sensitive (window->output_volume_button, TRUE);
411       gtk_widget_set_sensitive (window->input_volume_button, TRUE);
412       gtk_widget_set_sensitive (window->hang_up_button, TRUE);
413     }
414   else if (window->status == EMPATHY_TP_CALL_STATUS_CLOSED)
415       call_window_finalize (window);
416
417   if (contact)
418       g_object_unref (contact);
419 }
420
421 GtkWidget *
422 empathy_call_window_new (EmpathyTpCall *call)
423 {
424   EmpathyCallWindow *window;
425   GladeXML *glade;
426   gchar *filename;
427   const gchar *icons[] = {"audio-input-microphone", NULL};
428
429   g_return_val_if_fail (EMPATHY_IS_TP_CALL (call), NULL);
430
431   window = g_slice_new0 (EmpathyCallWindow);
432   window->call = g_object_ref (call);
433
434   filename = empathy_file_lookup ("empathy-call-window.glade", "src");
435   glade = empathy_glade_get_file (filename,
436       "window",
437       NULL,
438       "window", &window->window,
439       "main_hbox", &window->main_hbox,
440       "controls_vbox", &window->controls_vbox,
441       "volume_hbox", &window->volume_hbox,
442       "status_label", &window->status_label,
443       "video_button", &window->video_button,
444       "hang_up_button", &window->hang_up_button,
445       NULL);
446   g_free (filename);
447
448   empathy_glade_connect (glade,
449       window,
450       "window", "destroy", call_window_destroy_cb,
451       "window", "delete_event", call_window_delete_event_cb,
452       "hang_up_button", "clicked", call_window_hang_up_button_clicked_cb,
453       "video_button", "toggled", call_window_video_button_toggled_cb,
454       NULL);
455
456   g_object_unref (glade);
457
458   /* Output volume button */
459   window->output_volume_button = gtk_volume_button_new ();
460   gtk_scale_button_set_value (GTK_SCALE_BUTTON (window->output_volume_button), 1);
461   gtk_box_pack_start (GTK_BOX (window->volume_hbox),
462       window->output_volume_button, FALSE, FALSE, 0);
463   gtk_widget_show (window->output_volume_button);
464   g_signal_connect (window->output_volume_button, "value-changed",
465       G_CALLBACK (call_window_output_volume_changed_cb), window);
466
467   /* Input volume button */
468   window->input_volume_button = gtk_volume_button_new ();
469   gtk_scale_button_set_icons (GTK_SCALE_BUTTON (window->input_volume_button),
470       icons);
471   gtk_scale_button_set_value (GTK_SCALE_BUTTON (window->input_volume_button), 1);
472   gtk_box_pack_start (GTK_BOX (window->volume_hbox),
473       window->input_volume_button, FALSE, FALSE, 0);
474   gtk_widget_show (window->input_volume_button);
475   g_signal_connect (window->input_volume_button, "value-changed",
476       G_CALLBACK (call_window_input_volume_changed_cb), window);
477
478   /* Output video socket */
479   window->output_video_socket = g_object_ref (gtk_socket_new ());
480   gtk_widget_set_size_request (window->output_video_socket, 400, 300);
481   g_signal_connect (GTK_OBJECT (window->output_video_socket), "realize",
482       G_CALLBACK (call_window_socket_realized_cb), window);
483   gtk_widget_show (window->output_video_socket);
484
485   /* Preview video socket */
486   window->preview_video_socket = g_object_ref (gtk_socket_new ());
487   gtk_widget_set_size_request (window->preview_video_socket, 176, 144);
488   g_signal_connect (GTK_OBJECT (window->preview_video_socket), "realize",
489       G_CALLBACK (call_window_socket_realized_cb), window);
490   gtk_box_pack_start (GTK_BOX (window->controls_vbox),
491       window->preview_video_socket, TRUE, TRUE, 0);
492   gtk_box_reorder_child (GTK_BOX (window->controls_vbox),
493       window->preview_video_socket, 0);
494   gtk_widget_show (window->preview_video_socket);
495
496   g_signal_connect_swapped (G_OBJECT (window->call), "notify",
497       G_CALLBACK (call_window_update),
498       window);
499
500   call_window_update (window);
501   gtk_widget_show (window->window);
502
503   return window->window;
504 }
505