]> git.0d.be Git - empathy.git/blob - src/empathy-call-window.c
Add Marco Barisione to CONTRIBUTORS
[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-tp-group.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_FLAG EMPATHY_DEBUG_OTHER
40 #include <libempathy/empathy-debug.h>
41
42 typedef struct
43 {
44   EmpathyTpCall *call;
45   GTimeVal start_time;
46   guint timeout_event_id;
47   gboolean is_drawing;
48   guint status; 
49
50   GtkWidget *window;
51   GtkWidget *main_hbox;
52   GtkWidget *controls_vbox;
53   GtkWidget *volume_hbox;
54   GtkWidget *status_label;
55   GtkWidget *input_volume_button;
56   GtkWidget *output_volume_button;
57   GtkWidget *preview_video_socket;
58   GtkWidget *output_video_socket;
59   GtkWidget *video_button;
60   GtkWidget *hang_up_button;
61   GtkWidget *confirmation_dialog;
62   GtkWidget *keypad_expander;
63 } EmpathyCallWindow;
64
65 static GSList *windows = NULL;
66
67 static gboolean
68 call_window_update_timer (gpointer data)
69 {
70   EmpathyCallWindow *window = data;
71   GTimeVal current;
72   gchar *str;
73   glong now, then;
74   glong time, seconds, minutes, hours;
75
76   g_get_current_time (&current);
77
78   now = current.tv_sec;
79   then = (window->start_time).tv_sec;
80
81   time = now - then;
82
83   seconds = time % 60;
84   time /= 60;
85   minutes = time % 60;
86   time /= 60;
87   hours = time % 60;
88
89   if (hours > 0)
90       str = g_strdup_printf ("Connected  -  %02ld : %02ld : %02ld", hours,
91           minutes, seconds);
92   else
93       str = g_strdup_printf ("Connected  -  %02ld : %02ld", minutes, seconds);
94
95   gtk_label_set_text (GTK_LABEL (window->status_label), str);
96
97   g_free (str);
98
99   return TRUE;
100 }
101
102 static void
103 call_window_stop_timeout (EmpathyCallWindow *window)
104 {
105   DEBUG ("Timer stopped");
106
107   if (window->timeout_event_id)
108     {
109       g_source_remove (window->timeout_event_id);
110       window->timeout_event_id = 0;
111     }
112 }
113
114 static void
115 call_window_set_output_video_is_drawing (EmpathyCallWindow *window,
116                                          gboolean is_drawing)
117 {
118   DEBUG ("Setting output video is drawing - %d", is_drawing);
119
120   if (is_drawing && !window->is_drawing)
121     {
122       gtk_window_set_resizable (GTK_WINDOW (window->window), TRUE);
123       gtk_box_pack_end (GTK_BOX (window->main_hbox),
124           window->output_video_socket, TRUE, TRUE, 0);
125       empathy_tp_call_add_output_video (window->call,
126           gtk_socket_get_id (GTK_SOCKET (window->output_video_socket)));
127     }
128   if (!is_drawing && window->is_drawing)
129     {
130       gtk_window_set_resizable (GTK_WINDOW (window->window), FALSE);
131       empathy_tp_call_add_output_video (window->call, 0);
132       gtk_container_remove (GTK_CONTAINER (window->main_hbox),
133           window->output_video_socket);
134     }
135
136   window->is_drawing = is_drawing;
137 }
138
139 static void
140 call_window_finalize (EmpathyCallWindow *window)
141 {
142   gtk_label_set_text (GTK_LABEL (window->status_label), _("Closed"));
143   gtk_widget_set_sensitive (window->hang_up_button, FALSE);
144   gtk_widget_set_sensitive (window->video_button, FALSE);
145   gtk_widget_set_sensitive (window->output_volume_button, FALSE);
146   gtk_widget_set_sensitive (window->input_volume_button, FALSE);
147   gtk_widget_set_sensitive (window->keypad_expander, FALSE);
148
149   if (window->call)
150     { 
151       call_window_stop_timeout (window);
152       call_window_set_output_video_is_drawing (window, FALSE);
153       empathy_tp_call_remove_preview_video (window->call,
154           gtk_socket_get_id (GTK_SOCKET (window->preview_video_socket)));
155       g_object_unref (window->call);
156       window->call = NULL;
157     }
158
159   if (window->confirmation_dialog)
160       gtk_widget_destroy (window->confirmation_dialog);
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       DEBUG ("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       DEBUG ("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   DEBUG ("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_hang_up_button_clicked_cb (GtkWidget *widget,
195                                        EmpathyCallWindow *window)
196 {
197   DEBUG ("Call clicked, end call");
198   call_window_finalize (window);
199 }
200
201 static void
202 call_window_output_volume_changed_cb (GtkScaleButton *button,
203                                       gdouble value,
204                                       EmpathyCallWindow *window)
205 {
206   if (!window->call)
207       return;
208
209   if (value <= 0)
210       empathy_tp_call_mute_output (window->call, TRUE);
211   else
212     {
213       empathy_tp_call_mute_output (window->call, FALSE);
214       empathy_tp_call_set_output_volume (window->call, value * 100);
215     }
216 }
217
218 static void
219 call_window_input_volume_changed_cb (GtkScaleButton    *button,
220                                      gdouble            value,
221                                      EmpathyCallWindow *window)
222 {
223   if (!window->call)
224       return;
225
226   if (value <= 0)
227       empathy_tp_call_mute_input (window->call, TRUE);
228   else
229     {
230       empathy_tp_call_mute_input (window->call, FALSE);
231       /* FIXME: Not implemented?
232       empathy_tp_call_set_input_volume (window->call, value * 100);*/
233     }
234 }
235
236 static gboolean
237 call_window_delete_event_cb (GtkWidget *widget,
238                              GdkEvent *event,
239                              EmpathyCallWindow *window)
240 {
241   GtkWidget *dialog;
242   gint result;
243   guint status = EMPATHY_TP_CALL_STATUS_CLOSED;
244
245   DEBUG ("Delete event occurred");
246
247   if (window->call)
248       g_object_get (window->call, "status", &status, NULL);
249
250   if (status == EMPATHY_TP_CALL_STATUS_ACCEPTED)
251     {
252       dialog = gtk_message_dialog_new (GTK_WINDOW (window->window),
253           GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
254           GTK_MESSAGE_WARNING, GTK_BUTTONS_CANCEL, _("End this call?"));
255       gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
256           _("Closing this window will end the call in progress."));
257       gtk_dialog_add_button (GTK_DIALOG (dialog), _("_End Call"), GTK_RESPONSE_OK);
258       gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
259
260       result = gtk_dialog_run (GTK_DIALOG (dialog));
261       gtk_widget_destroy (dialog);
262
263       if (result != GTK_RESPONSE_OK)
264           return TRUE;
265     }
266
267   return FALSE;
268 }
269
270 static void
271 call_window_destroy_cb (GtkWidget *widget,
272                         EmpathyCallWindow *window)
273 {
274   call_window_finalize (window);
275
276   g_object_unref (window->output_video_socket);
277   g_object_unref (window->preview_video_socket);
278
279   windows = g_slist_remove (windows, window);
280   g_slice_free (EmpathyCallWindow, window);
281 }
282
283 static void
284 call_window_confirmation_dialog_response_cb (GtkDialog *dialog,
285                                              gint response,
286                                              EmpathyCallWindow *window)
287 {
288   if (response == GTK_RESPONSE_OK && window->call)
289       empathy_tp_call_accept_incoming_call (window->call);
290   else
291       call_window_finalize (window);
292
293   gtk_widget_destroy (window->confirmation_dialog);
294   window->confirmation_dialog = NULL;
295 }
296
297 static void
298 call_window_show_confirmation_dialog (EmpathyCallWindow *window)
299 {
300   EmpathyContact *contact;
301   GtkWidget *button;
302   GtkWidget *image;
303
304   if (window->confirmation_dialog)
305       return;
306
307   g_object_get (window->call, "contact", &contact, NULL);
308
309   window->confirmation_dialog = gtk_message_dialog_new (GTK_WINDOW (window->window),
310       GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
311       GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("Incoming call"));
312   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (window->confirmation_dialog),
313       _("%s is calling you, do you want to answer?"),
314       empathy_contact_get_name (contact));
315   gtk_dialog_set_default_response (GTK_DIALOG (window->confirmation_dialog),
316       GTK_RESPONSE_OK);
317
318   button = gtk_dialog_add_button (GTK_DIALOG (window->confirmation_dialog),
319       _("_Reject"), GTK_RESPONSE_CANCEL);
320   image = gtk_image_new_from_icon_name (GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
321   gtk_button_set_image (GTK_BUTTON (button), image);
322
323   button = gtk_dialog_add_button (GTK_DIALOG (window->confirmation_dialog),
324       _("_Answer"), GTK_RESPONSE_OK);
325   image = gtk_image_new_from_icon_name (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
326   gtk_button_set_image (GTK_BUTTON (button), image);
327
328   g_signal_connect (window->confirmation_dialog, "response",
329       G_CALLBACK (call_window_confirmation_dialog_response_cb),
330       window);
331
332   gtk_widget_show (window->confirmation_dialog);
333   g_object_unref (contact);
334 }
335
336 static void
337 call_window_update (EmpathyCallWindow *window)
338 {
339   EmpathyContact *contact;
340   guint stream_state;
341   EmpathyTpCallStream *audio_stream;
342   EmpathyTpCallStream *video_stream;
343   gboolean is_incoming;
344   gchar *title;
345
346   g_object_get (window->call,
347       "status", &window->status,
348       "audio-stream", &audio_stream,
349       "video-stream", &video_stream,
350       "contact", &contact,
351       "is-incoming", &is_incoming,
352       NULL);
353
354   if (video_stream->state > audio_stream->state)
355       stream_state = video_stream->state;
356   else
357       stream_state = audio_stream->state;
358
359   DEBUG ("Status changed - status: %d, stream state: %d, "
360       "is-incoming: %d video-stream direction: %d",
361       window->status, stream_state, is_incoming, video_stream->direction);
362
363   if (empathy_tp_call_has_dtmf (window->call))
364     {
365       gtk_widget_show (window->keypad_expander);
366     }
367   else
368     {
369       gtk_widget_hide (window->keypad_expander);
370     }
371
372   /* Depending on the status we have to set:
373    * - window's title
374    * - status's label
375    * - sensibility of all buttons
376    * */
377   if (window->status == EMPATHY_TP_CALL_STATUS_READYING)
378     {
379       gtk_window_set_title (GTK_WINDOW (window->window), _("Empathy Call"));
380       /* To translators: Readying is the first state of the call, it is
381        * preparing the connection and it does not yet ring. */
382       gtk_label_set_text (GTK_LABEL (window->status_label), _("Readying"));
383       gtk_widget_set_sensitive (window->video_button, FALSE);
384       gtk_widget_set_sensitive (window->output_volume_button, FALSE);
385       gtk_widget_set_sensitive (window->input_volume_button, FALSE);
386       gtk_widget_set_sensitive (window->hang_up_button, FALSE);
387       gtk_widget_set_sensitive (window->keypad_expander, FALSE);
388     }
389   else if (window->status == EMPATHY_TP_CALL_STATUS_PENDING)
390     {
391       title = g_strdup_printf (_("%s - Empathy Call"),
392           empathy_contact_get_name (contact));
393
394       gtk_window_set_title (GTK_WINDOW (window->window), title);
395       gtk_label_set_text (GTK_LABEL (window->status_label), _("Ringing"));
396       gtk_widget_set_sensitive (window->hang_up_button, TRUE);
397       if (is_incoming)
398           call_window_show_confirmation_dialog (window);
399     }
400   else if (window->status == EMPATHY_TP_CALL_STATUS_ACCEPTED)
401     {
402       gboolean receiving_video;
403       gboolean sending_video;
404
405       if (stream_state == TP_MEDIA_STREAM_STATE_DISCONNECTED)
406           gtk_label_set_text (GTK_LABEL (window->status_label), _("Disconnected"));
407       if (stream_state == TP_MEDIA_STREAM_STATE_CONNECTING)
408           gtk_label_set_text (GTK_LABEL (window->status_label), _("Connecting"));
409       else if (stream_state == TP_MEDIA_STREAM_STATE_CONNECTED &&
410                window->timeout_event_id == 0)
411         {
412           /* The call started, launch the timer */
413           g_get_current_time (&(window->start_time));
414           window->timeout_event_id = g_timeout_add_seconds (1,
415               call_window_update_timer, window);
416           call_window_update_timer (window);
417         }
418
419       receiving_video = video_stream->direction & TP_MEDIA_STREAM_DIRECTION_RECEIVE;
420       sending_video = video_stream->direction & TP_MEDIA_STREAM_DIRECTION_SEND;
421       call_window_set_output_video_is_drawing (window, receiving_video);
422       g_signal_handlers_block_by_func (window->video_button,
423           call_window_video_button_toggled_cb, window);
424       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (window->video_button),
425           sending_video);
426       g_signal_handlers_unblock_by_func (window->video_button,
427           call_window_video_button_toggled_cb, window);
428
429       gtk_widget_set_sensitive (window->video_button, TRUE);
430       gtk_widget_set_sensitive (window->output_volume_button, TRUE);
431       gtk_widget_set_sensitive (window->input_volume_button, TRUE);
432       gtk_widget_set_sensitive (window->hang_up_button, TRUE);
433       gtk_widget_set_sensitive (window->keypad_expander, TRUE);
434     }
435   else if (window->status == EMPATHY_TP_CALL_STATUS_CLOSED)
436       call_window_finalize (window);
437
438   if (contact)
439       g_object_unref (contact);
440 }
441
442 static gboolean
443 call_window_dtmf_button_release_event_cb (GtkWidget *widget,
444                                           GdkEventButton *event,
445                                           EmpathyCallWindow *window)
446 {
447   empathy_tp_call_stop_tone (window->call);
448   return FALSE;
449 }
450
451 static gboolean
452 call_window_dtmf_button_press_event_cb (GtkWidget *widget,
453                                         GdkEventButton *event,
454                                         EmpathyCallWindow *window)
455 {
456   TpDTMFEvent dtmf_event;
457
458   dtmf_event = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), "code"));
459   empathy_tp_call_start_tone (window->call, dtmf_event);
460   return FALSE;
461 }
462
463 static void
464 call_window_dtmf_connect (GladeXML *glade,
465                           EmpathyCallWindow *window,
466                           const gchar *name,
467                           TpDTMFEvent event)
468 {
469   GtkWidget *widget;
470
471   widget = glade_xml_get_widget (glade, name);
472   g_object_set_data (G_OBJECT (widget), "code", GUINT_TO_POINTER (event));
473   g_signal_connect (widget, "button-press-event",
474       G_CALLBACK (call_window_dtmf_button_press_event_cb), window);
475   g_signal_connect (widget, "button-release-event",
476       G_CALLBACK (call_window_dtmf_button_release_event_cb), window);
477   /* FIXME: Connect "key-[press/release]-event" to*/
478 }
479
480 GtkWidget *
481 empathy_call_window_new (TpChannel *channel)
482 {
483   EmpathyCallWindow *window;
484   GladeXML *glade;
485   gchar *filename;
486   const gchar *icons[] = {"audio-input-microphone", NULL};
487
488   g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL);
489
490   if (windows)
491     {
492       window = (EmpathyCallWindow*) windows->data;
493       if (!window->call)
494         {
495           window->call = empathy_tp_call_new (channel);
496           g_signal_connect_swapped (G_OBJECT (window->call), "notify",
497               G_CALLBACK (call_window_update), window);
498           call_window_update (window);
499
500           if (GTK_WIDGET_REALIZED (GTK_WIDGET (window->preview_video_socket)))
501             {
502               call_window_socket_realized_cb (window->preview_video_socket,
503                   window);
504             }
505         }
506       else
507         {
508           GtkWidget *dialog;
509           EmpathyContact *contact;
510           EmpathyTpGroup *tp_group;
511
512           tp_group = empathy_tp_group_new (channel);
513           empathy_run_until_ready (tp_group);
514           empathy_tp_group_get_invitation (tp_group, &contact);
515           empathy_contact_run_until_ready (contact, EMPATHY_CONTACT_READY_NAME,
516               NULL);
517
518           /* We don't want to have multiple calls running.
519            * FIXME: We should use the hold interface... */
520           tp_cli_channel_call_close (channel, -1, NULL, NULL, NULL, NULL);
521
522           dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_INFO,
523               GTK_BUTTONS_CLOSE,
524               _("Incoming call from %s rejected because there is already a"
525                 " running call."), empathy_contact_get_name (contact));
526
527           g_object_unref (contact);
528           g_object_unref (tp_group);
529
530           g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy),
531               NULL);
532           gtk_widget_show (dialog);
533         }
534
535       gtk_window_present (GTK_WINDOW (window->window));
536       return window->window;
537     }
538
539   window = g_slice_new0 (EmpathyCallWindow);
540   windows = g_slist_prepend (windows, window);
541   window->call = empathy_tp_call_new (channel);
542
543   filename = empathy_file_lookup ("empathy-call-window.glade", "src");
544   glade = empathy_glade_get_file (filename,
545       "window",
546       NULL,
547       "window", &window->window,
548       "main_hbox", &window->main_hbox,
549       "controls_vbox", &window->controls_vbox,
550       "volume_hbox", &window->volume_hbox,
551       "status_label", &window->status_label,
552       "video_button", &window->video_button,
553       "hang_up_button", &window->hang_up_button,
554       "keypad_expander", &window->keypad_expander,
555       NULL);
556   g_free (filename);
557
558   empathy_glade_connect (glade,
559       window,
560       "window", "destroy", call_window_destroy_cb,
561       "window", "delete_event", call_window_delete_event_cb,
562       "hang_up_button", "clicked", call_window_hang_up_button_clicked_cb,
563       "video_button", "toggled", call_window_video_button_toggled_cb,
564       NULL);
565
566   /* Setup DTMF buttons */
567   call_window_dtmf_connect (glade, window, "button_0", TP_DTMF_EVENT_DIGIT_0);
568   call_window_dtmf_connect (glade, window, "button_1", TP_DTMF_EVENT_DIGIT_1);
569   call_window_dtmf_connect (glade, window, "button_2", TP_DTMF_EVENT_DIGIT_2);
570   call_window_dtmf_connect (glade, window, "button_3", TP_DTMF_EVENT_DIGIT_3);
571   call_window_dtmf_connect (glade, window, "button_4", TP_DTMF_EVENT_DIGIT_4);
572   call_window_dtmf_connect (glade, window, "button_5", TP_DTMF_EVENT_DIGIT_5);
573   call_window_dtmf_connect (glade, window, "button_6", TP_DTMF_EVENT_DIGIT_6);
574   call_window_dtmf_connect (glade, window, "button_7", TP_DTMF_EVENT_DIGIT_7);
575   call_window_dtmf_connect (glade, window, "button_8", TP_DTMF_EVENT_DIGIT_8);
576   call_window_dtmf_connect (glade, window, "button_9", TP_DTMF_EVENT_DIGIT_9);
577   call_window_dtmf_connect (glade, window, "button_asterisk", TP_DTMF_EVENT_ASTERISK);
578   call_window_dtmf_connect (glade, window, "button_hash", TP_DTMF_EVENT_HASH);
579
580   g_object_unref (glade);
581
582   /* Output volume button */
583   window->output_volume_button = gtk_volume_button_new ();
584   gtk_scale_button_set_value (GTK_SCALE_BUTTON (window->output_volume_button), 1);
585   gtk_box_pack_start (GTK_BOX (window->volume_hbox),
586       window->output_volume_button, FALSE, FALSE, 0);
587   gtk_widget_show (window->output_volume_button);
588   g_signal_connect (window->output_volume_button, "value-changed",
589       G_CALLBACK (call_window_output_volume_changed_cb), window);
590
591   /* Input volume button */
592   window->input_volume_button = gtk_volume_button_new ();
593   gtk_scale_button_set_icons (GTK_SCALE_BUTTON (window->input_volume_button),
594       icons);
595   gtk_scale_button_set_value (GTK_SCALE_BUTTON (window->input_volume_button), 1);
596   gtk_box_pack_start (GTK_BOX (window->volume_hbox),
597       window->input_volume_button, FALSE, FALSE, 0);
598   gtk_widget_show (window->input_volume_button);
599   g_signal_connect (window->input_volume_button, "value-changed",
600       G_CALLBACK (call_window_input_volume_changed_cb), window);
601
602   /* Output video socket */
603   window->output_video_socket = g_object_ref (gtk_socket_new ());
604   gtk_widget_set_size_request (window->output_video_socket, 400, 300);
605   g_signal_connect (GTK_OBJECT (window->output_video_socket), "realize",
606       G_CALLBACK (call_window_socket_realized_cb), window);
607   gtk_widget_show (window->output_video_socket);
608
609   /* Preview video socket */
610   window->preview_video_socket = g_object_ref (gtk_socket_new ());
611   gtk_widget_set_size_request (window->preview_video_socket, 176, 144);
612   g_signal_connect (GTK_OBJECT (window->preview_video_socket), "realize",
613       G_CALLBACK (call_window_socket_realized_cb), window);
614   gtk_widget_show (window->preview_video_socket);
615
616   /* FIXME: We shouldn't do this if there is no video input */
617   gtk_box_pack_start (GTK_BOX (window->controls_vbox),
618       window->preview_video_socket, FALSE, FALSE, 0);
619   gtk_box_reorder_child (GTK_BOX (window->controls_vbox),
620       window->preview_video_socket, 0);
621
622   g_signal_connect_swapped (G_OBJECT (window->call), "notify",
623       G_CALLBACK (call_window_update),
624       window);
625
626   call_window_update (window);
627   gtk_widget_show (window->window);
628
629   return window->window;
630 }
631