]> sjero.net Git - linphone/blob - gtk/incall_view.c
implement call quality indicator
[linphone] / gtk / incall_view.c
1 /*
2 linphone, gtk-glade interface.
3 Copyright (C) 2009  Simon MORLAT (simon.morlat@linphone.org)
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 */
19 /*
20 *  C Implementation: incall_frame
21 *
22 * Description: 
23 *
24 *
25 * Author: Simon Morlat <simon.morlat@linphone.org>, (C) 2009
26 *
27 *
28 */
29
30 #include "linphone.h"
31
32
33 gboolean linphone_gtk_use_in_call_view(){
34         static int val=-1;
35         if (val==-1) val=linphone_gtk_get_ui_config_int("use_incall_view",1);
36         return val;
37 }
38
39 LinphoneCall *linphone_gtk_get_currently_displayed_call(){
40         LinphoneCore *lc=linphone_gtk_get_core();
41         GtkWidget *main_window=linphone_gtk_get_main_window ();
42         GtkNotebook *notebook=(GtkNotebook *)linphone_gtk_get_widget(main_window,"viewswitch");
43         const MSList *calls=linphone_core_get_calls(lc);
44         if (!linphone_gtk_use_in_call_view() || ms_list_size(calls)==1){
45                 if (calls) return (LinphoneCall*)calls->data;
46         }else{
47                 int idx=gtk_notebook_get_current_page (notebook);
48                 GtkWidget *page=gtk_notebook_get_nth_page(notebook,idx);
49                 if (page!=NULL){
50                         LinphoneCall *call=(LinphoneCall*)g_object_get_data(G_OBJECT(page),"call");
51                         return call;
52                 }
53         }
54         return NULL;
55 }
56
57 static GtkWidget *make_tab_header(int number){
58         GtkWidget *w=gtk_hbox_new (FALSE,0);
59         GtkWidget *i=create_pixmap ("status-green.png");
60         GtkWidget *l;
61         gchar *text=g_strdup_printf(_("Call #%i"),number);
62         l=gtk_label_new (text);
63         gtk_box_pack_start (GTK_BOX(w),i,FALSE,FALSE,0);
64         gtk_box_pack_end(GTK_BOX(w),l,TRUE,TRUE,0);
65         gtk_widget_show_all(w);
66         return w;
67 }
68
69 static void linphone_gtk_transfer_call(LinphoneCall *dest_call){
70         LinphoneCall *call=linphone_gtk_get_currently_displayed_call();
71         linphone_core_transfer_call_to_another (linphone_gtk_get_core(),call,dest_call);
72 }
73
74 static void transfer_button_clicked(GtkWidget *button, gpointer call_ref){
75         GtkWidget *menu_item;
76         GtkWidget *menu=gtk_menu_new();
77         LinphoneCall *call=(LinphoneCall*)call_ref;
78         LinphoneCore *lc=linphone_gtk_get_core();
79         const MSList *elem=linphone_core_get_calls(lc);
80         
81         for(;elem!=NULL;elem=elem->next){
82                 LinphoneCall *other_call=(LinphoneCall*)elem->data;
83                 GtkWidget *call_view=(GtkWidget*)linphone_call_get_user_pointer(other_call);
84                 if (other_call!=call){
85                         int call_index=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(call_view),"call_index"));
86                         char *remote_uri=linphone_call_get_remote_address_as_string (other_call);
87                         char *text=g_strdup_printf(_("Transfer to call #%i with %s"),call_index,remote_uri);
88                         menu_item=gtk_image_menu_item_new_with_label(text);
89                         ms_free(remote_uri);
90                         g_free(text);
91                         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),create_pixmap("status-green.png"));
92                         gtk_widget_show(menu_item);
93                         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
94                         g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_transfer_call,other_call);
95                 }
96         }
97         gtk_menu_popup(GTK_MENU(menu),NULL,NULL,NULL,NULL,0,
98                 gtk_get_current_event_time());
99         gtk_widget_show(menu);
100 }
101
102 void linphone_gtk_enable_transfer_button(LinphoneCore *lc, gboolean value){
103         const MSList *elem=linphone_core_get_calls(lc);
104         for(;elem!=NULL;elem=elem->next){
105                 LinphoneCall *call=(LinphoneCall*)elem->data;
106                 GtkWidget *call_view=(GtkWidget*)linphone_call_get_user_pointer(call);
107                 GtkWidget *box=linphone_gtk_get_widget (call_view,"mute_pause_buttons");
108                 GtkWidget *button=(GtkWidget*)g_object_get_data(G_OBJECT(box),"transfer");
109                 if (button && value==FALSE){
110                         gtk_widget_destroy(button);
111                         button=NULL;
112                 }else if (!button && value==TRUE){
113                         button=gtk_button_new_with_label (_("Transfer"));
114                         gtk_button_set_image(GTK_BUTTON(button),gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD,GTK_ICON_SIZE_BUTTON));
115                         g_signal_connect(G_OBJECT(button),"clicked",(GCallback)transfer_button_clicked,call);
116                         gtk_widget_show_all(button);
117                         gtk_container_add(GTK_CONTAINER(box),button);
118                 }
119                 g_object_set_data(G_OBJECT(box),"transfer",button);
120         }
121 }
122
123 void linphone_gtk_create_in_call_view(LinphoneCall *call){
124         GtkWidget *call_view=linphone_gtk_create_widget("main","in_call_frame");
125         GtkWidget *main_window=linphone_gtk_get_main_window ();
126         GtkNotebook *notebook=(GtkNotebook *)linphone_gtk_get_widget(main_window,"viewswitch");
127         static int call_index=1;
128         int idx;
129
130         if (ms_list_size(linphone_core_get_calls(linphone_gtk_get_core()))==1){
131                 /*this is the only call at this time */
132                 call_index=1;
133         }
134         g_object_set_data(G_OBJECT(call_view),"call",call);
135         g_object_set_data(G_OBJECT(call_view),"call_index",GINT_TO_POINTER(call_index));
136
137         linphone_call_set_user_pointer (call,call_view);
138         linphone_call_ref(call);
139         gtk_notebook_append_page (notebook,call_view,make_tab_header(call_index));
140         gtk_widget_show(call_view);
141         idx = gtk_notebook_page_num(notebook, call_view);
142         gtk_notebook_set_current_page(notebook, idx);
143         call_index++;
144         linphone_gtk_enable_hold_button (call,FALSE,TRUE);
145         linphone_gtk_enable_mute_button(
146                                         GTK_BUTTON(linphone_gtk_get_widget(call_view,"incall_mute")),FALSE);
147 }
148
149 void linphone_gtk_remove_in_call_view(LinphoneCall *call){
150         GtkWidget *w=(GtkWidget*)linphone_call_get_user_pointer (call);
151         GtkWidget *main_window=linphone_gtk_get_main_window ();
152         GtkWidget *nb=linphone_gtk_get_widget(main_window,"viewswitch");
153         int idx;
154         g_return_if_fail(w!=NULL);
155         idx=gtk_notebook_page_num(GTK_NOTEBOOK(nb),w);
156         gtk_notebook_remove_page (GTK_NOTEBOOK(nb),idx);
157         gtk_widget_destroy(w);
158         linphone_call_set_user_pointer (call,NULL);
159         linphone_call_unref(call);
160         gtk_notebook_set_current_page(GTK_NOTEBOOK(nb), 0);
161 }
162
163 static void display_peer_name_in_label(GtkWidget *label, const LinphoneAddress *from){
164         const char *displayname=NULL;
165         const char *id;
166         char *uri_label;
167         displayname=linphone_address_get_display_name(from);
168         id=linphone_address_as_string_uri_only(from);
169         
170         if (displayname!=NULL){
171                 uri_label=g_markup_printf_escaped("<span size=\"large\">%s</span>\n<i>%s</i>", 
172                         displayname,id);
173         }else
174                 uri_label=g_markup_printf_escaped("<span size=\"large\"><i>%s</i></span>\n",id);
175         gtk_label_set_markup(GTK_LABEL(label),uri_label);
176         g_free(uri_label);
177 }
178
179 void linphone_gtk_in_call_view_set_calling(LinphoneCall *call){
180         GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call);
181         GtkWidget *status=linphone_gtk_get_widget(callview,"in_call_status");
182         GtkWidget *callee=linphone_gtk_get_widget(callview,"in_call_uri");
183         GtkWidget *duration=linphone_gtk_get_widget(callview,"in_call_duration");
184         GtkWidget *animation=linphone_gtk_get_widget(callview,"in_call_animation");
185         GdkPixbufAnimation *pbuf=create_pixbuf_animation("calling_anim.gif");
186         
187         gtk_label_set_markup(GTK_LABEL(status),_("<b>Calling...</b>"));
188         display_peer_name_in_label(callee,linphone_call_get_remote_address (call));
189         
190         gtk_label_set_text(GTK_LABEL(duration),_("00::00::00"));
191         if (pbuf!=NULL){
192                 gtk_image_set_from_animation(GTK_IMAGE(animation),pbuf);
193                 g_object_unref(G_OBJECT(pbuf));
194         }else gtk_image_set_from_stock(GTK_IMAGE(animation),GTK_STOCK_FIND,GTK_ICON_SIZE_DIALOG);
195 }
196
197 void linphone_gtk_in_call_view_set_incoming(LinphoneCall *call, bool_t with_pause){
198         GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call);
199         GtkWidget *status=linphone_gtk_get_widget(callview,"in_call_status");
200         GtkWidget *callee=linphone_gtk_get_widget(callview,"in_call_uri");
201         GtkWidget *animation=linphone_gtk_get_widget(callview,"in_call_animation");
202         GdkPixbufAnimation *pbuf=create_pixbuf_animation("calling_anim.gif");
203         GtkWidget *answer_button;
204         GtkWidget *image;
205
206         gtk_label_set_markup(GTK_LABEL(status),_("<b>Incoming call</b>"));
207         gtk_widget_show_all(linphone_gtk_get_widget(callview,"answer_decline_panel"));
208         gtk_widget_hide(linphone_gtk_get_widget(callview,"mute_pause_buttons"));
209         display_peer_name_in_label(callee,linphone_call_get_remote_address (call));
210
211         answer_button=linphone_gtk_get_widget(callview,"accept_call");
212         image=create_pixmap (linphone_gtk_get_ui_config("start_call_icon","startcall-green.png"));
213         if (with_pause){
214                 gtk_button_set_label(GTK_BUTTON(answer_button),
215                                      _("Pause all calls\nand answer"));
216         }else gtk_button_set_label(GTK_BUTTON(answer_button),_("Answer"));
217         gtk_button_set_image(GTK_BUTTON(answer_button),image);
218         gtk_widget_show(image);
219         
220         image=create_pixmap (linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png"));
221         gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(callview,"decline_call")),image);
222         gtk_widget_show(image);
223         
224         if (pbuf!=NULL){
225                 gtk_image_set_from_animation(GTK_IMAGE(animation),pbuf);
226                 g_object_unref(G_OBJECT(pbuf));
227         }else gtk_image_set_from_stock(GTK_IMAGE(animation),GTK_STOCK_EXECUTE,GTK_ICON_SIZE_DIALOG);
228 }
229
230 static void rating_to_color(float rating, GdkColor *color){
231         const char *colorname="grey";
232         if (rating>=4.0)
233                 colorname="green";
234         else if (rating>=3.0)
235                 colorname="white";
236         else if (rating>=2.0)
237                 colorname="yellow";
238         else if (rating>=1.0)
239                 colorname="orange";
240         else if (rating>=0)
241                 colorname="red";
242         if (!gdk_color_parse(colorname,color)){
243                 g_warning("Fail to parse color %s",colorname);
244         }
245 }
246
247 static const char *rating_to_text(float rating){
248         if (rating>=4.0)
249                 return _("good");
250         if (rating>=3.0)
251                 return _("average");
252         if (rating>=2.0)
253                 return _("poor");
254         if (rating>=1.0)
255                 return _("very poor");
256         if (rating>=0)
257                 return _("too bad");
258         return _("unavailable");
259 }
260
261 static gboolean linphone_gtk_in_call_view_refresh(LinphoneCall *call){
262         GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call);
263         GtkWidget *qi=linphone_gtk_get_widget(callview,"quality_indicator");
264         float rating=linphone_call_get_current_quality(call);
265         GdkColor color;
266         gchar tmp[50];
267         linphone_gtk_in_call_view_update_duration(call);
268         if (rating>=0){
269                 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(qi),rating/5.0);
270                 snprintf(tmp,sizeof(tmp),"%.1f (%s)",rating,rating_to_text(rating));
271                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(qi),tmp);
272         }else{
273                 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(qi),0);
274                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(qi),_("unavailable"));
275         }
276         rating_to_color(rating,&color);
277         gtk_widget_modify_bg(qi,GTK_STATE_NORMAL,&color);
278         return TRUE;
279 }
280
281 void linphone_gtk_in_call_view_set_in_call(LinphoneCall *call){
282         GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call);
283         GtkWidget *status=linphone_gtk_get_widget(callview,"in_call_status");
284         GtkWidget *callee=linphone_gtk_get_widget(callview,"in_call_uri");
285         GtkWidget *duration=linphone_gtk_get_widget(callview,"in_call_duration");
286         GtkWidget *animation=linphone_gtk_get_widget(callview,"in_call_animation");
287         GdkPixbufAnimation *pbuf=create_pixbuf_animation("incall_anim.gif");
288         guint taskid=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(callview),"taskid"));
289         
290         display_peer_name_in_label(callee,linphone_call_get_remote_address (call));
291
292         gtk_widget_show(linphone_gtk_get_widget(callview,"mute_pause_buttons"));
293         gtk_widget_hide(linphone_gtk_get_widget(callview,"answer_decline_panel"));
294         gtk_label_set_markup(GTK_LABEL(status),_("<b>In call</b>"));
295
296         gtk_label_set_text(GTK_LABEL(duration),_("00::00::00"));
297         if (pbuf!=NULL){
298                 gtk_image_set_from_animation(GTK_IMAGE(animation),pbuf);
299                 g_object_unref(G_OBJECT(pbuf));
300         }else gtk_image_set_from_stock(GTK_IMAGE(animation),GTK_STOCK_EXECUTE,GTK_ICON_SIZE_DIALOG);
301         linphone_gtk_enable_mute_button(
302                                         GTK_BUTTON(linphone_gtk_get_widget(callview,"incall_mute")),TRUE);
303         if (taskid==0){
304                 taskid=g_timeout_add(250,(GSourceFunc)linphone_gtk_in_call_view_refresh,call);
305                 g_object_set_data(G_OBJECT(callview),"taskid",GINT_TO_POINTER(taskid));
306         }
307 }
308
309 void linphone_gtk_in_call_view_set_paused(LinphoneCall *call){
310         GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call);
311         GtkWidget *status=linphone_gtk_get_widget(callview,"in_call_status");
312         GtkWidget *animation=linphone_gtk_get_widget(callview,"in_call_animation");
313         gtk_widget_hide(linphone_gtk_get_widget(callview,"answer_decline_panel"));
314         gtk_label_set_markup(GTK_LABEL(status),_("<b>Paused call</b>"));
315         gtk_image_set_from_stock(GTK_IMAGE(animation),GTK_STOCK_MEDIA_PAUSE,GTK_ICON_SIZE_DIALOG);
316 }
317
318 void linphone_gtk_in_call_view_update_duration(LinphoneCall *call){
319         GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call);
320         GtkWidget *duration_label=linphone_gtk_get_widget(callview,"in_call_duration");
321         int duration=linphone_call_get_duration(call);
322         char tmp[256]={0};
323         int seconds=duration%60;
324         int minutes=(duration/60)%60;
325         int hours=duration/3600;
326         snprintf(tmp,sizeof(tmp)-1,_("%02i::%02i::%02i"),hours,minutes,seconds);
327         gtk_label_set_text(GTK_LABEL(duration_label),tmp);
328 }
329
330 static gboolean in_call_view_terminated(LinphoneCall *call){
331         linphone_gtk_remove_in_call_view(call);
332         return FALSE;
333 }
334
335 void linphone_gtk_in_call_view_terminate(LinphoneCall *call, const char *error_msg){
336         GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call);
337         GtkWidget *status=linphone_gtk_get_widget(callview,"in_call_status");
338         GtkWidget *animation=linphone_gtk_get_widget(callview,"in_call_animation");
339         GdkPixbuf *pbuf=create_pixbuf(linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png"));
340         guint taskid=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(callview),"taskid"));
341
342         if (error_msg==NULL)
343                 gtk_label_set_markup(GTK_LABEL(status),_("<b>Call ended.</b>"));
344         else{
345                 char *msg=g_markup_printf_escaped("<span color=\"red\"><b>%s</b></span>",error_msg);
346                 gtk_label_set_markup(GTK_LABEL(status),msg);
347                 g_free(msg);
348         }
349         if (pbuf!=NULL){
350                 gtk_image_set_from_pixbuf(GTK_IMAGE(animation),pbuf);
351                 g_object_unref(G_OBJECT(pbuf));
352         }
353         gtk_widget_hide(linphone_gtk_get_widget(callview,"answer_decline_panel"));
354         linphone_gtk_enable_mute_button(
355                 GTK_BUTTON(linphone_gtk_get_widget(callview,"incall_mute")),FALSE);
356         linphone_gtk_enable_hold_button(call,FALSE,TRUE);
357         if (taskid!=0) g_source_remove(taskid);
358         g_timeout_add_seconds(2,(GSourceFunc)in_call_view_terminated,call);
359 }
360
361 void linphone_gtk_draw_mute_button(GtkButton *button, gboolean active){
362         g_object_set_data(G_OBJECT(button),"active",GINT_TO_POINTER(active));
363         if (active){
364                 GtkWidget *image=create_pixmap("mic_muted.png");
365                 gtk_button_set_label(GTK_BUTTON(button),_("Unmute"));
366                 if (image!=NULL) {
367                         gtk_button_set_image(GTK_BUTTON(button),image);
368                         gtk_widget_show(image);
369                 }
370         }else{
371                 GtkWidget *image=create_pixmap("mic_active.png");
372                 gtk_button_set_label(GTK_BUTTON(button),_("Mute"));
373                 if (image!=NULL) {
374                         gtk_button_set_image(GTK_BUTTON(button),image);
375                         gtk_widget_show(image);
376                 }
377         }
378 }
379
380 void linphone_gtk_mute_clicked(GtkButton *button){
381         int active=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button),"active"));
382         linphone_core_mute_mic(linphone_gtk_get_core(),!active);
383         linphone_gtk_draw_mute_button(button,!active);
384 }
385
386 void linphone_gtk_enable_mute_button(GtkButton *button, gboolean sensitive)
387 {
388         gtk_widget_set_sensitive(GTK_WIDGET(button),sensitive);
389         linphone_gtk_draw_mute_button(button,FALSE);
390 }
391
392 void linphone_gtk_draw_hold_button(GtkButton *button, gboolean active){
393         g_object_set_data(G_OBJECT(button),"active",GINT_TO_POINTER(active));
394         if (active){
395                 GtkWidget *image=create_pixmap("hold_off.png");
396                 gtk_button_set_label(GTK_BUTTON(button),_("Resume"));
397                 if (image!=NULL) {
398                         gtk_button_set_image(GTK_BUTTON(button),image);
399                         gtk_widget_show(image);
400                 }
401         }else{
402                 GtkWidget *image=create_pixmap("hold_on.png");
403                 gtk_button_set_label(GTK_BUTTON(button),_("Pause"));
404                 if (image!=NULL) {
405                         gtk_button_set_image(GTK_BUTTON(button),image);
406                         gtk_widget_show(image);
407                 }
408         }
409 }
410
411 void linphone_gtk_hold_clicked(GtkButton *button){
412         int active=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button),"active"));
413         LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
414         if(!active)
415         {
416                 linphone_core_pause_call(linphone_gtk_get_core(),call);
417         }
418         else
419         {
420                 linphone_core_resume_call(linphone_gtk_get_core(),call);
421         }
422 }
423
424 void linphone_gtk_enable_hold_button(LinphoneCall *call, gboolean sensitive, gboolean holdon){
425         GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer (call);
426         GtkWidget *button;
427         g_return_if_fail(callview!=NULL);
428         button=linphone_gtk_get_widget(callview,"hold_call");
429         gtk_widget_set_sensitive(GTK_WIDGET(button),sensitive);
430         linphone_gtk_draw_hold_button(GTK_BUTTON(button),!holdon);
431 }