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