]> git.0d.be Git - empathy.git/blob - src/empathy-call-window.c
Ask confirmation to close call only if it was accepted. Fixes bug #526927 (Frederic...
[empathy.git] / src / empathy-call-window.c
1 /*
2  *  Copyright (C) 2007 Elliot Fairweather
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: Elliot Fairweather <elliot.fairweather@collabora.co.uk>
19  */
20
21 #include <string.h>
22
23 #include <libtelepathy/tp-chan.h>
24
25 #include <libmissioncontrol/mc-account.h>
26 #include <libmissioncontrol/mc-account-monitor.h>
27 #include <libmissioncontrol/mission-control.h>
28
29 #include <libempathy/empathy-contact.h>
30 #include <libempathy/empathy-tp-call.h>
31 #include <libempathy/empathy-chandler.h>
32 #include <libempathy/empathy-debug.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_DOMAIN "CallWindow"
39
40 typedef struct 
41 {
42   GtkWidget *window;
43   GtkWidget *status_label;
44   GtkWidget *start_call_button;
45   GtkWidget *end_call_button;
46   GtkWidget *input_volume_scale;
47   GtkWidget *output_volume_scale;
48   GtkWidget *input_mute_button;
49   GtkWidget *output_mute_button;
50   GtkWidget *preview_video_frame;
51   GtkWidget *output_video_frame;
52   GtkWidget *preview_video_socket;
53   GtkWidget *output_video_socket;
54   GtkWidget *video_button;
55   GtkWidget *output_video_label;
56
57   EmpathyTpCall *call;
58
59   GTimeVal start_time;
60   guint timeout_event_id;
61
62   gboolean is_drawing;
63 } EmpathyCallWindow;
64
65 static gboolean
66 call_window_update_timer (gpointer data)
67 {
68   EmpathyCallWindow *window = data;
69   GTimeVal current;
70   gchar *str;
71   glong now, then;
72   glong time, seconds, minutes, hours;
73
74   g_get_current_time (&current);
75
76   now = current.tv_sec;
77   then = (window->start_time).tv_sec;
78
79   time = now - then;
80
81   seconds = time % 60;
82   time /= 60;
83   minutes = time % 60;
84   time /= 60;
85   hours = time % 60;
86
87   if (hours > 0)
88     {
89       str = g_strdup_printf ("Connected  -  %02ld : %02ld : %02ld", hours,
90           minutes, seconds);
91     }
92   else
93     {
94       str = g_strdup_printf ("Connected  -  %02ld : %02ld", minutes, seconds);
95     }
96
97   gtk_label_set_text (GTK_LABEL (window->status_label), str);
98
99   g_free (str);
100
101   return TRUE;
102 }
103
104 static void
105 call_window_stop_timeout (EmpathyCallWindow *window)
106 {
107   GMainContext *context;
108   GSource *source;
109
110   context = g_main_context_default ();
111
112   empathy_debug (DEBUG_DOMAIN, "Timer stopped");
113
114   if (window->timeout_event_id)
115     {
116       source = g_main_context_find_source_by_id (context,
117           window->timeout_event_id);
118       g_source_destroy (source);
119       window->timeout_event_id = 0;
120     }
121 }
122
123 static void
124 call_window_set_output_video_is_drawing (EmpathyCallWindow *window,
125                                          gboolean is_drawing)
126 {
127   GtkWidget* child;
128
129   child = gtk_bin_get_child (GTK_BIN (window->output_video_frame));
130
131   empathy_debug (DEBUG_DOMAIN,
132       "Setting output video is drawing - %d", is_drawing);
133
134   if (is_drawing)
135     {
136       if (!window->is_drawing)
137         {
138           if (child)
139             {
140               gtk_container_remove (GTK_CONTAINER (window->output_video_frame),
141                   child);
142             }
143           gtk_container_add (GTK_CONTAINER (window->output_video_frame),
144               window->output_video_socket);
145           gtk_widget_show (window->output_video_socket);
146           empathy_tp_call_add_output_video (window->call,
147               gtk_socket_get_id (GTK_SOCKET (window->output_video_socket)));
148           window->is_drawing = is_drawing;
149         }
150     }
151   else
152     {
153       if (window->is_drawing)
154         {
155           empathy_tp_call_add_output_video (window->call, 0);
156           if (child)
157             {
158               gtk_container_remove (GTK_CONTAINER (window->output_video_frame),
159                   child);
160             }
161           gtk_container_add (GTK_CONTAINER (window->output_video_frame),
162               window->output_video_label);
163           gtk_widget_show (window->output_video_label);
164           window->is_drawing = is_drawing;
165         }
166     }
167 }
168
169 static gboolean
170 call_window_delete_event_cb (GtkWidget *widget,
171                              GdkEvent *event,
172                              EmpathyCallWindow *window)
173 {
174   GtkWidget *dialog;
175   gint result;
176   guint status;
177
178   empathy_debug (DEBUG_DOMAIN, "Delete event occurred");
179
180   g_object_get (G_OBJECT (window->call), "status", &status, NULL);
181
182   if (status == EMPATHY_TP_CALL_STATUS_ACCEPTED)
183     {
184       dialog = gtk_message_dialog_new (GTK_WINDOW (window->window),
185           GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
186           GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
187           "This call will be ended. Continue?");
188
189       result = gtk_dialog_run (GTK_DIALOG (dialog));
190       gtk_widget_destroy (dialog);
191
192       switch (result)
193         {
194         case GTK_RESPONSE_YES:
195           call_window_stop_timeout (window);
196           call_window_set_output_video_is_drawing (window, FALSE);
197           empathy_tp_call_close_channel (window->call);
198           empathy_tp_call_remove_preview_video (window->call,
199               gtk_socket_get_id (GTK_SOCKET (window->preview_video_socket)));
200           return FALSE;
201         default:
202           return TRUE;
203         }
204     }
205   else
206     {
207       empathy_tp_call_remove_preview_video (window->call,
208           gtk_socket_get_id (GTK_SOCKET (window->preview_video_socket)));
209       return FALSE;
210     }
211 }
212
213 static void
214 call_window_video_button_toggled_cb (GtkWidget *button,
215                                      EmpathyCallWindow *window)
216 {
217   gboolean is_sending;
218
219   is_sending = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
220
221   empathy_debug (DEBUG_DOMAIN, "Send video toggled - %d", is_sending);
222
223   empathy_tp_call_request_video_stream_direction (window->call, is_sending);
224 }
225
226 static void
227 call_window_status_changed_cb (EmpathyTpCall *call,
228                                EmpathyCallWindow *window)
229 {
230   EmpathyContact *contact;
231   guint status;
232   guint stream_state;
233   EmpathyTpCallStream *audio_stream;
234   EmpathyTpCallStream *video_stream;
235   gboolean is_incoming;
236   gchar *title;
237
238   g_object_get (window->call,
239       "status", &status,
240       "audio-stream", &audio_stream,
241       "video-stream", &video_stream,
242       NULL);
243
244   if (video_stream->state > audio_stream->state)
245       stream_state = video_stream->state;
246   else
247       stream_state = audio_stream->state;
248
249   empathy_debug (DEBUG_DOMAIN, "Status changed - status: %d, stream state: %d",
250       status, stream_state);
251
252   if (window->timeout_event_id)
253       call_window_stop_timeout (window);
254
255   if (status == EMPATHY_TP_CALL_STATUS_CLOSED)
256     {
257       gtk_label_set_text (GTK_LABEL (window->status_label), "Closed");
258       gtk_widget_set_sensitive (window->end_call_button, FALSE);
259       gtk_widget_set_sensitive (window->start_call_button, FALSE);
260
261       call_window_set_output_video_is_drawing (window, FALSE);
262     }
263   else if (stream_state == TP_MEDIA_STREAM_STATE_DISCONNECTED)
264       gtk_label_set_text (GTK_LABEL (window->status_label), "Disconnected");
265   else if (status == EMPATHY_TP_CALL_STATUS_PENDING)
266     {
267       g_object_get (G_OBJECT (window->call), "contact", &contact, NULL);
268
269       title = g_strdup_printf ("%s - Empathy Call",
270           empathy_contact_get_name (contact));
271       gtk_window_set_title (GTK_WINDOW (window->window), title);
272
273       gtk_label_set_text (GTK_LABEL (window->status_label), "Ringing");
274       gtk_widget_set_sensitive (window->end_call_button, TRUE);
275       gtk_widget_set_sensitive (window->video_button, TRUE);
276
277       g_object_get (G_OBJECT (window->call), "is-incoming", &is_incoming, NULL);
278       if (is_incoming)
279           gtk_widget_set_sensitive (window->start_call_button, TRUE);
280       else
281           g_signal_connect (GTK_OBJECT (window->video_button), "toggled",
282               G_CALLBACK (call_window_video_button_toggled_cb),
283               window);
284     }
285   else if (status == EMPATHY_TP_CALL_STATUS_ACCEPTED)
286     {
287       if (stream_state == TP_MEDIA_STREAM_STATE_CONNECTING)
288           gtk_label_set_text (GTK_LABEL (window->status_label), "Connecting");
289       else if (stream_state == TP_MEDIA_STREAM_STATE_CONNECTED)
290         {
291           if ((window->start_time).tv_sec == 0)
292               g_get_current_time (&(window->start_time));
293           window->timeout_event_id = g_timeout_add (1000,
294               call_window_update_timer, window);
295           empathy_debug (DEBUG_DOMAIN, "Timer started");
296         }
297     }
298 }
299
300 static void
301 call_window_receiving_video_cb (EmpathyTpCall *call,
302                                 gboolean receiving_video,
303                                 EmpathyCallWindow *window)
304 {
305   empathy_debug (DEBUG_DOMAIN, "Receiving video signal received");
306
307   call_window_set_output_video_is_drawing (window, receiving_video);
308 }
309
310 static void
311 call_window_sending_video_cb (EmpathyTpCall *call,
312                               gboolean sending_video,
313                               EmpathyCallWindow *window)
314 {
315   empathy_debug (DEBUG_DOMAIN, "Sending video signal received");
316
317   g_signal_handlers_block_by_func (window->video_button,
318       call_window_video_button_toggled_cb, window);
319   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (window->video_button),
320       sending_video);
321   g_signal_handlers_unblock_by_func (window->video_button,
322       call_window_video_button_toggled_cb, window);
323 }
324
325 static void
326 call_window_socket_realized_cb (GtkWidget *widget,
327                                 EmpathyCallWindow *window)
328 {
329   if (widget == window->preview_video_socket)
330     {
331       empathy_debug (DEBUG_DOMAIN, "Preview socket realized");
332       empathy_tp_call_add_preview_video (window->call,
333           gtk_socket_get_id (GTK_SOCKET (window->preview_video_socket)));
334     }
335   else
336     {
337       empathy_debug (DEBUG_DOMAIN, "Output socket realized");
338     }
339 }
340
341 static void
342 call_window_start_call_button_clicked_cb (GtkWidget *widget,
343                                           EmpathyCallWindow *window)
344 {
345   gboolean send_video;
346   gboolean is_incoming;
347
348   empathy_debug (DEBUG_DOMAIN, "Start call clicked");
349
350   gtk_widget_set_sensitive (window->start_call_button, FALSE);
351   g_object_get (G_OBJECT (window->call), "is-incoming", &is_incoming, NULL);
352   if (is_incoming)
353     {
354       empathy_tp_call_accept_incoming_call (window->call);
355       send_video = gtk_toggle_button_get_active
356           (GTK_TOGGLE_BUTTON (window->video_button));
357       empathy_tp_call_request_video_stream_direction (window->call, send_video);
358       g_signal_connect (GTK_OBJECT (window->video_button), "toggled",
359           G_CALLBACK (call_window_video_button_toggled_cb), window);
360     }
361 }
362
363 static void
364 call_window_end_call_button_clicked_cb (GtkWidget *widget,
365                                         EmpathyCallWindow *window)
366 {
367   empathy_debug (DEBUG_DOMAIN, "End call clicked");
368
369   call_window_set_output_video_is_drawing (window, FALSE);
370   empathy_tp_call_close_channel (window->call);
371   gtk_widget_set_sensitive (window->end_call_button, FALSE);
372   gtk_widget_set_sensitive (window->start_call_button, FALSE);
373 }
374
375 static void
376 call_window_output_volume_changed_cb (GtkWidget *scale,
377                                       EmpathyCallWindow *window)
378 {
379   guint volume;
380
381   volume = (guint) gtk_range_get_value (GTK_RANGE (scale));
382
383   empathy_debug (DEBUG_DOMAIN, "Output volume changed - %u", volume);
384
385   empathy_tp_call_set_output_volume (window->call, volume);
386 }
387
388 static void
389 call_window_output_mute_button_toggled_cb (GtkWidget *button,
390                                            EmpathyCallWindow *window)
391 {
392   gboolean is_muted;
393
394   is_muted = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
395
396   empathy_debug (DEBUG_DOMAIN, "Mute output toggled - %d", is_muted);
397
398   empathy_tp_call_mute_output (window->call, is_muted);
399 }
400
401 static void
402 call_window_input_mute_button_toggled_cb (GtkWidget *button,
403                                           EmpathyCallWindow *window)
404 {
405   gboolean is_muted;
406
407   is_muted = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
408
409   empathy_debug (DEBUG_DOMAIN, "Mute input toggled - %d", is_muted);
410
411   empathy_tp_call_mute_input (window->call, is_muted);
412 }
413
414 static void
415 call_window_destroy_cb (GtkWidget *widget,
416                         EmpathyCallWindow *window)
417 {
418   g_signal_handlers_disconnect_by_func (window->call,
419       call_window_status_changed_cb, window);
420   g_signal_handlers_disconnect_by_func (window->call,
421       call_window_receiving_video_cb, window);
422   g_signal_handlers_disconnect_by_func (window->call,
423       call_window_sending_video_cb, window);
424
425   g_object_unref (window->call);
426   g_object_unref (window->output_video_socket);
427   g_object_unref (window->preview_video_socket);
428   g_object_unref (window->output_video_label);
429
430   g_slice_free (EmpathyCallWindow, window);
431 }
432
433 GtkWidget *
434 empathy_call_window_new (EmpathyTpCall *call)
435 {
436   EmpathyCallWindow *window;
437   GladeXML *glade;
438   guint status;
439   gchar *filename;
440
441   g_return_val_if_fail (EMPATHY_IS_TP_CALL (call), NULL);
442
443   window = g_slice_new0 (EmpathyCallWindow);
444   window->call = g_object_ref (call);
445
446   filename = empathy_file_lookup ("empathy-call-window.glade", "src");
447   glade = empathy_glade_get_file (filename,
448       "window",
449       NULL,
450       "window", &window->window,
451       "status_label", &window->status_label,
452       "start_call_button", &window->start_call_button,
453       "end_call_button", &window->end_call_button,
454       "input_volume_scale", &window->input_volume_scale,
455       "output_volume_scale", &window->output_volume_scale,
456       "input_mute_button", &window->input_mute_button,
457       "output_mute_button", &window->output_mute_button,
458       "preview_video_frame", &window->preview_video_frame,
459       "output_video_frame", &window->output_video_frame,
460       "video_button", &window->video_button,
461       NULL);
462   g_free (filename);
463
464   empathy_glade_connect (glade,
465       window,
466       "window", "destroy", call_window_destroy_cb,
467       "window", "delete_event", call_window_delete_event_cb,
468       "input_mute_button", "toggled", call_window_input_mute_button_toggled_cb,
469       "output_mute_button", "toggled", call_window_output_mute_button_toggled_cb,
470       "output_volume_scale", "value-changed", call_window_output_volume_changed_cb,
471       "start_call_button", "clicked", call_window_start_call_button_clicked_cb,
472       "end_call_button", "clicked", call_window_end_call_button_clicked_cb,
473       NULL);
474
475   g_object_unref (glade);
476
477   /* Output video label */
478   window->output_video_label = g_object_ref (gtk_label_new ("No video output"));
479   gtk_container_add (GTK_CONTAINER (window->output_video_frame),
480       window->output_video_label);
481   gtk_widget_show (window->output_video_label);
482
483   /* Output video socket */
484   window->output_video_socket = g_object_ref (gtk_socket_new ());
485   g_signal_connect (GTK_OBJECT (window->output_video_socket), "realize",
486       G_CALLBACK (call_window_socket_realized_cb), window);
487   gtk_widget_show (window->output_video_socket);
488
489   /* Preview video socket */
490   window->preview_video_socket = g_object_ref (gtk_socket_new ());
491   g_signal_connect (GTK_OBJECT (window->preview_video_socket), "realize",
492       G_CALLBACK (call_window_socket_realized_cb), window);
493   gtk_container_add (GTK_CONTAINER (window->preview_video_frame),
494       window->preview_video_socket);
495   gtk_widget_show (window->preview_video_socket);
496
497   g_signal_connect (G_OBJECT (window->call), "status-changed",
498       G_CALLBACK (call_window_status_changed_cb),
499       window);
500   g_signal_connect (G_OBJECT (window->call), "receiving-video",
501       G_CALLBACK (call_window_receiving_video_cb),
502       window);
503   g_signal_connect (G_OBJECT (window->call), "sending-video",
504       G_CALLBACK (call_window_sending_video_cb),
505       window);
506
507   window->is_drawing = FALSE;
508
509   g_object_get (G_OBJECT (window->call), "status", &status, NULL);
510
511   if (status == EMPATHY_TP_CALL_STATUS_READYING)
512     {
513       gtk_window_set_title (GTK_WINDOW (window->window), "Empathy Call");
514       gtk_label_set_text (GTK_LABEL (window->status_label), "Readying");
515     }
516
517   gtk_widget_show (window->window);
518
519   return window->window;
520 }
521