]> sjero.net Git - linphone/blob - gtk/main.c
implement call recording.
[linphone] / gtk / main.c
1 /*
2 linphone, gtk-glade interface.
3 Copyright (C) 2008  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
21 #define VIDEOSELFVIEW_DEFAULT 0
22
23 #include "linphone.h"
24 #include "lpconfig.h"
25
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30
31 #ifdef HAVE_GTK_OSX
32 #include <gtkosxapplication.h>
33 #endif
34
35 #ifdef WIN32
36 #define chdir _chdir
37 #endif
38
39 #if defined(HAVE_NOTIFY1) || defined(HAVE_NOTIFY4)
40 #define HAVE_NOTIFY
41 #endif
42
43 #ifdef HAVE_NOTIFY
44 #include <libnotify/notify.h>
45 #endif
46
47 #define LINPHONE_ICON "linphone.png"
48
49 const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION;
50
51 static LinphoneCore *the_core=NULL;
52 static GtkWidget *the_ui=NULL;
53
54 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg);
55 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
56 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
57 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username);
58 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
59 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
60 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
61 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
62 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
63 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
64 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token);
65 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate);
66 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
67 void linphone_gtk_status_icon_set_blinking(gboolean val);
68 void _linphone_gtk_enable_video(gboolean val);
69
70
71
72 static gboolean verbose=0;
73 static gboolean auto_answer = 0;
74 static gchar * addr_to_call = NULL;
75 static gboolean no_video=FALSE;
76 static gboolean iconified=FALSE;
77 static gchar *workingdir=NULL;
78 static char *progpath=NULL;
79 gchar *linphone_logfile=NULL;
80 static gboolean workaround_gtk_entry_chinese_bug=FALSE;
81
82 static GOptionEntry linphone_options[]={
83         {
84                 .long_name="verbose",
85                 .short_name= '\0',
86                 .arg=G_OPTION_ARG_NONE,
87                 .arg_data= (gpointer)&verbose,
88                 .description=N_("log to stdout some debug information while running.")
89         },
90         {
91             .long_name = "logfile",
92             .short_name = 'l',
93             .arg = G_OPTION_ARG_STRING,
94             .arg_data = &linphone_logfile,
95             .description = N_("path to a file to write logs into.")
96         },
97         {
98             .long_name = "no-video",
99             .short_name = '\0',
100             .arg = G_OPTION_ARG_NONE,
101             .arg_data = (gpointer)&no_video,
102             .description = N_("Start linphone with video disabled.")
103         },
104         {
105                 .long_name="iconified",
106                 .short_name= '\0',
107                 .arg=G_OPTION_ARG_NONE,
108                 .arg_data= (gpointer)&iconified,
109                 .description=N_("Start only in the system tray, do not show the main interface.")
110         },
111         {
112             .long_name = "call",
113             .short_name = 'c',
114             .arg = G_OPTION_ARG_STRING,
115             .arg_data = &addr_to_call,
116             .description = N_("address to call right now")
117         },
118         {
119             .long_name = "auto-answer",
120             .short_name = 'a',
121             .arg = G_OPTION_ARG_NONE,
122             .arg_data = (gpointer) & auto_answer,
123             .description = N_("if set automatically answer incoming calls")
124         },
125         {
126             .long_name = "workdir",
127             .short_name = '\0',
128             .arg = G_OPTION_ARG_STRING,
129             .arg_data = (gpointer) & workingdir,
130             .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
131         },
132         {0}
133 };
134
135 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
136 #define RELATIVE_XML_DIR
137 #define BUILD_TREE_XML_DIR "gtk"
138
139 #ifndef WIN32
140 #define CONFIG_FILE ".linphonerc"
141 #define SECRETS_FILE ".linphone-zidcache"
142 #else
143 #define CONFIG_FILE "linphonerc"
144 #define SECRETS_FILE "linphone-zidcache"
145 #endif
146
147 char *linphone_gtk_get_config_file(const char *filename){
148         const int path_max=1024;
149         char *config_file=g_malloc0(path_max);
150         if (filename==NULL) filename=CONFIG_FILE;
151         /*try accessing a local file first if exists*/
152         if (access(CONFIG_FILE,F_OK)==0){
153                 snprintf(config_file,path_max,"%s",filename);
154         }else{
155 #ifdef WIN32
156                 const char *appdata=getenv("APPDATA");
157                 if (appdata){
158                         snprintf(config_file,path_max,"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
159                         CreateDirectory(config_file,NULL);
160                         snprintf(config_file,path_max,"%s\\%s\\%s",appdata,LINPHONE_CONFIG_DIR,filename);
161                 }
162 #else
163                 const char *home=getenv("HOME");
164                 if (home==NULL) home=".";
165                 snprintf(config_file,path_max,"%s/%s",home,filename);
166 #endif
167         }
168         return config_file;
169 }
170
171 #define FACTORY_CONFIG_FILE "linphonerc.factory"
172 static char _factory_config_file[1024];
173 static const char *linphone_gtk_get_factory_config_file(){
174         /*try accessing a local file first if exists*/
175         if (access(FACTORY_CONFIG_FILE,F_OK)==0){
176                 snprintf(_factory_config_file,sizeof(_factory_config_file),
177                                                  "%s",FACTORY_CONFIG_FILE);
178         } else {
179                 char *progdir;
180
181                 if (progpath != NULL) {
182                         char *basename;
183                         progdir = strdup(progpath);
184 #ifdef WIN32
185                         basename = strrchr(progdir, '\\');
186                         if (basename != NULL) {
187                                 basename ++;
188                                 *basename = '\0';
189                                 snprintf(_factory_config_file, sizeof(_factory_config_file),
190                                                                  "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
191                         } else {
192                                 if (workingdir!=NULL) {
193                                         snprintf(_factory_config_file, sizeof(_factory_config_file),
194                                                                          "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
195                                 } else {
196                                         free(progdir);
197                                         return NULL;
198                                 }
199                         }
200 #else
201                         basename = strrchr(progdir, '/');
202                         if (basename != NULL) {
203                                 basename ++;
204                                 *basename = '\0';
205                                 snprintf(_factory_config_file, sizeof(_factory_config_file),
206                                                                  "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
207                         } else {
208                                 free(progdir);
209                                 return NULL;
210                         }
211 #endif
212                         free(progdir);
213                 }
214         }
215         return _factory_config_file;
216 }
217
218 static void linphone_gtk_init_liblinphone(const char *config_file,
219                 const char *factory_config_file) {
220         LinphoneCoreVTable vtable={0};
221         gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
222
223         vtable.call_state_changed=linphone_gtk_call_state_changed;
224         vtable.registration_state_changed=linphone_gtk_registration_state_changed;
225         vtable.notify_presence_recv=linphone_gtk_notify_recv;
226         vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
227         vtable.auth_info_requested=linphone_gtk_auth_info_requested;
228         vtable.display_status=linphone_gtk_display_status;
229         vtable.display_message=linphone_gtk_display_message;
230         vtable.display_warning=linphone_gtk_display_warning;
231         vtable.display_url=linphone_gtk_display_url;
232         vtable.call_log_updated=linphone_gtk_call_log_updated;
233         //vtable.text_received=linphone_gtk_text_received;
234         vtable.message_received=linphone_gtk_text_received;
235         vtable.refer_received=linphone_gtk_refer_received;
236         vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
237         vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
238         vtable.transfer_state_changed=linphone_gtk_transfer_state_changed;
239
240         the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
241         //lp_config_set_int(linphone_core_get_config(the_core), "sip", "store_auth_info", 0);
242         linphone_core_set_user_agent(the_core,"Linphone", LINPHONE_VERSION);
243         linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
244         linphone_core_set_zrtp_secrets_file(the_core,secrets_file);
245         g_free(secrets_file);
246         linphone_core_enable_video(the_core,TRUE,TRUE);
247         if (no_video) {
248                 _linphone_gtk_enable_video(FALSE);
249                 linphone_gtk_set_ui_config_int("videoselfview",0);
250         }
251 }
252
253 LinphoneCore *linphone_gtk_get_core(void){
254         return the_core;
255 }
256
257 GtkWidget *linphone_gtk_get_main_window(){
258         return the_ui;
259 }
260
261 void linphone_gtk_destroy_main_window() {
262         linphone_gtk_destroy_window(the_ui);    
263         the_ui = NULL;
264 }
265
266 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
267         static const char *icon_path=NULL;
268         static const char *hiddens=NULL;
269         static const char *shown=NULL;
270         static bool_t config_loaded=FALSE;
271         if (linphone_gtk_get_core()==NULL) return;
272         if (config_loaded==FALSE){
273                 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
274                 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
275                 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
276                 config_loaded=TRUE;
277         }
278         if (hiddens)
279                 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
280         if (shown)
281                 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
282         if (icon_path) {
283                 GdkPixbuf *pbuf=create_pixbuf(icon_path);
284                 if(pbuf != NULL) {
285                         gtk_window_set_icon(GTK_WINDOW(w),pbuf);
286                         g_object_unref(G_OBJECT(pbuf));
287                 }
288         }
289 }
290
291 static int get_ui_file(const char *name, char *path, int pathsize){
292         snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
293         if (access(path,F_OK)!=0){
294                 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
295                 if (access(path,F_OK)!=0){
296                         g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
297                                 INSTALLED_XML_DIR,name);
298                         return -1;
299                 }
300         }
301         return 0;
302 }
303
304 void linphone_gtk_destroy_window(GtkWidget *widget) {
305         GtkBuilder* builder = g_object_get_data(G_OBJECT(widget), "builder");
306         gtk_widget_destroy(widget);
307         g_object_unref (G_OBJECT (builder));
308 }
309
310 GtkWidget *linphone_gtk_create_window(const char *window_name){
311         GError* error = NULL;
312         GtkBuilder* builder = gtk_builder_new ();
313         char path[512];
314         GtkWidget *w;
315
316         if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
317
318         if (!gtk_builder_add_from_file (builder, path, &error)){
319                 g_error("Couldn't load builder file: %s", error->message);
320                 g_error_free (error);
321                 return NULL;
322         }
323         w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
324         if (w==NULL){
325                 g_error("Could not retrieve '%s' window from xml file",window_name);
326                 return NULL;
327         }
328         g_object_set_data(G_OBJECT(w), "builder",builder);
329         gtk_builder_connect_signals(builder,w);
330         linphone_gtk_configure_window(w,window_name);
331         return w;
332 }
333
334 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
335         char path[2048];
336         GtkWidget *w;
337         GtkBuilder* builder = gtk_builder_new ();
338         GError *error=NULL;
339         gchar *object_ids[2];
340         object_ids[0]=g_strdup(widget_name);
341         object_ids[1]=NULL;
342
343         if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
344         if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
345                 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
346                 g_error_free (error);
347                 g_free(object_ids[0]);
348                 return NULL;
349         }
350         g_free(object_ids[0]);
351         w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
352         if (w==NULL){
353                 g_error("Could not retrieve '%s' window from xml file",widget_name);
354                 return NULL;
355         }
356         g_object_set_data(G_OBJECT(w),"builder",builder);
357         g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
358         gtk_builder_connect_signals(builder,w);
359         return w;
360 }
361
362 static void entry_unmapped(GtkWidget *entry){
363         g_message("Entry is unmapped, calling unrealize to workaround chinese bug.");
364         gtk_widget_unrealize(entry);
365 }
366
367 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
368         GtkBuilder *builder;
369         GObject *w;
370         if (window==NULL) return NULL;
371         builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
372         if (builder==NULL){
373                 g_error("Fail to retrieve builder from window !");
374                 return NULL;
375         }
376         w=gtk_builder_get_object(builder,name);
377         if (w==NULL){
378                 g_error("No widget named %s found in xml interface.",name);
379         }
380         if (workaround_gtk_entry_chinese_bug){
381                 if (strcmp(G_OBJECT_TYPE_NAME(w),"GtkEntry")==0){
382                         if (g_object_get_data(G_OBJECT(w),"entry_bug_workaround")==NULL){
383                                 g_object_set_data(G_OBJECT(w),"entry_bug_workaround",GINT_TO_POINTER(1));
384                                 g_message("%s is a GtkEntry",name);
385                                 g_signal_connect(G_OBJECT(w),"unmap",(GCallback)entry_unmapped,NULL);
386                         }
387                 }
388         }
389         return GTK_WIDGET(w);
390 }
391
392
393 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
394         GtkWidget *dialog;
395         GtkWidget *main_window=linphone_gtk_get_main_window();
396
397         gtk_widget_show(main_window);
398         if (type==GTK_MESSAGE_QUESTION)
399         {
400                 /* draw a question box. link to dialog_click callback */
401                 dialog = gtk_message_dialog_new (
402                                 GTK_WINDOW(main_window),
403                                 GTK_DIALOG_DESTROY_WITH_PARENT,
404                                 GTK_MESSAGE_QUESTION,
405                                 GTK_BUTTONS_YES_NO,
406                                 "%s",
407                                 (const gchar*)message);
408                 /* connect to some callback : REVISIT */
409                 /*
410                 g_signal_connect_swapped (G_OBJECT (dialog), "response",
411                            G_CALLBACK (dialog_click),
412                            G_OBJECT (dialog));
413                 */
414                 /* actually show the box */
415                 gtk_widget_show(dialog);
416         }
417         else
418         {
419                 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
420                                   GTK_DIALOG_DESTROY_WITH_PARENT,
421                                   type,
422                                   GTK_BUTTONS_CLOSE,
423                                   "%s",
424                                   (const gchar*)message);
425                 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
426                 g_signal_connect_swapped (G_OBJECT (dialog), "response",
427                            G_CALLBACK (gtk_widget_destroy),
428                            G_OBJECT (dialog));
429                 gtk_widget_show(dialog);
430         }
431 }
432
433 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
434         if (id==GTK_RESPONSE_CANCEL){
435                 gtk_widget_destroy(GTK_WIDGET(dialog));
436         }
437 }
438
439 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
440         g_message("About url clicked");
441         linphone_gtk_open_browser(url);
442 }
443
444 void linphone_gtk_show_about(){
445         struct stat filestat;
446         const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
447         GtkWidget *about;
448         const char *tmp;
449         GdkPixbuf *logo=create_pixbuf(
450             linphone_gtk_get_ui_config("logo","linphone-banner.png"));
451         static const char *defcfg="defcfg";
452
453         about=linphone_gtk_create_window("about");
454         gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
455         memset(&filestat,0,sizeof(filestat));
456         if (stat(license_file,&filestat)!=0){
457                 license_file="COPYING";
458                 stat(license_file,&filestat);
459         }
460         if (filestat.st_size>0){
461                 char *license=g_malloc(filestat.st_size+1);
462                 FILE *f=fopen(license_file,"r");
463                 if (f && fread(license,filestat.st_size,1,f)==1){
464                         license[filestat.st_size]='\0';
465                         gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
466                 }
467                 g_free(license);
468         }
469         gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
470         gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
471         gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
472         if (logo)       gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
473         tmp=linphone_gtk_get_ui_config("artists",defcfg);
474         if (tmp!=defcfg){
475                 const char *tmp2[2];
476                 tmp2[0]=tmp;
477                 tmp2[1]=NULL;
478                 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
479         }
480         tmp=linphone_gtk_get_ui_config("translators",defcfg);
481         if (tmp!=defcfg)
482                 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
483         tmp=linphone_gtk_get_ui_config("comments",defcfg);
484         if (tmp!=defcfg)
485                 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
486         gtk_widget_show(about);
487 }
488
489 static void set_video_window_decorations(GdkWindow *w){
490         const char *title=linphone_gtk_get_ui_config("title","Linphone");
491         const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
492         char video_title[256];
493         GdkPixbuf *pbuf=create_pixbuf(icon_path);
494
495         if (!linphone_core_in_call(linphone_gtk_get_core())){
496                 snprintf(video_title,sizeof(video_title),"%s video",title);
497                 /* When not in call, treat the video as a normal window */
498                 gdk_window_set_keep_above(w, FALSE);
499         }else{
500                 LinphoneAddress *uri =
501                         linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
502                 char *display_name;
503
504                 linphone_address_clean(uri);
505                 if (linphone_address_get_display_name(uri)!=NULL){
506                         display_name=ms_strdup(linphone_address_get_display_name(uri));
507                 }else{
508                         display_name=linphone_address_as_string(uri);
509                 }
510                 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
511                 linphone_address_destroy(uri);
512                 ms_free(display_name);
513
514                 /* During calls, bring up the video window, arrange so that
515                 it is above all the other windows */
516                 gdk_window_deiconify(w);
517                 gdk_window_set_keep_above(w,TRUE);
518                 /* Maybe we should have the following, but then we want to
519                 have a timer that turns it off after a little while. */
520                 /* gdk_window_set_urgency_hint(w,TRUE); */
521         }
522         gdk_window_set_title(w,video_title);
523         /* Refrain the video window to be closed at all times. */
524         gdk_window_set_functions(w,
525                                  GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
526                                  GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
527         if (pbuf){
528                 GList *l=NULL;
529                 l=g_list_append(l,pbuf);
530                 gdk_window_set_icon_list(w,l);
531                 g_list_free(l);
532                 g_object_unref(G_OBJECT(pbuf));
533         }
534 }
535
536 static gboolean video_needs_update=FALSE;
537
538 static void update_video_title(){
539         video_needs_update=TRUE;
540 }
541
542 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
543         static gboolean first_time=TRUE;
544         unsigned long id;
545         static unsigned long previd=0;
546         static unsigned long preview_previd=0;
547         static gboolean in_iterate=FALSE;
548
549         /*avoid reentrancy*/
550         if (in_iterate) return TRUE;
551         in_iterate=TRUE;
552         linphone_core_iterate(lc);
553         if (first_time){
554                 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
555                 linphone_gtk_show_directory_search();
556                 first_time=FALSE;
557         }
558
559         id=linphone_core_get_native_video_window_id(lc);
560         if (id!=previd || video_needs_update){
561                 GdkWindow *w;
562                 previd=id;
563                 if (id!=0){
564                         ms_message("Updating window decorations");
565 #ifndef WIN32
566                         w=gdk_window_foreign_new(id);
567 #else
568                         w=gdk_window_foreign_new((HANDLE)id);
569 #endif
570                         if (w) {
571                                 set_video_window_decorations(w);
572                                 g_object_unref(G_OBJECT(w));
573                         }
574                         else ms_error("gdk_window_foreign_new() failed");
575                         if (video_needs_update) video_needs_update=FALSE;
576                 }
577         }
578         id=linphone_core_get_native_preview_window_id (lc);
579         if (id!=preview_previd ){
580                 GdkWindow *w;
581                 preview_previd=id;
582                 if (id!=0){
583                         ms_message("Updating window decorations for preview");
584 #ifndef WIN32
585                         w=gdk_window_foreign_new(id);
586 #else
587                         w=gdk_window_foreign_new((HANDLE)id);
588 #endif
589                         if (w) {
590                                 set_video_window_decorations(w);
591                                 g_object_unref(G_OBJECT(w));
592                         }
593                         else ms_error("gdk_window_foreign_new() failed");
594                         if (video_needs_update) video_needs_update=FALSE;
595                 }
596         }
597         if (addr_to_call!=NULL){
598                 /*make sure we are not showing the login screen*/
599                 GtkWidget *mw=linphone_gtk_get_main_window();
600                 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
601                 if (!GTK_WIDGET_VISIBLE(login_frame)){
602                         GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
603                         gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
604                         addr_to_call=NULL;
605                         linphone_gtk_start_call(uri_bar);
606                 }
607         }
608         in_iterate=FALSE;
609         return TRUE;
610 }
611
612 static void load_uri_history(){
613         GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
614         char key[20];
615         int i;
616         GtkEntryCompletion *gep=gtk_entry_completion_new();
617         GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
618         for (i=0;;i++){
619                 const char *uri;
620                 snprintf(key,sizeof(key),"uri%i",i);
621                 uri=linphone_gtk_get_ui_config(key,NULL);
622                 if (uri!=NULL) {
623                         GtkTreeIter iter;
624                         gtk_list_store_append(model,&iter);
625                         gtk_list_store_set(model,&iter,0,uri,-1);
626                         if (i==0) gtk_entry_set_text(uribar,uri);
627                 }
628                 else break;
629         }
630         gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
631         gtk_entry_completion_set_text_column(gep,0);
632         gtk_entry_set_completion(uribar,gep);
633 }
634
635 static void save_uri_history(){
636         LinphoneCore *lc=linphone_gtk_get_core();
637         LpConfig *cfg=linphone_core_get_config(lc);
638         GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
639         char key[20];
640         int i=0;
641         char *uri=NULL;
642         GtkTreeIter iter;
643         GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
644
645         if (!gtk_tree_model_get_iter_first(model,&iter)) return;
646         do {
647                 gtk_tree_model_get(model,&iter,0,&uri,-1);
648                 if (uri) {
649                         snprintf(key,sizeof(key),"uri%i",i);
650                         lp_config_set_string(cfg,"GtkUi",key,uri);
651                         g_free(uri);
652                 }else break;
653                 i++;
654                 if (i>5) break;
655         }while(gtk_tree_model_iter_next(model,&iter));
656         lp_config_sync(cfg);
657 }
658
659 static void completion_add_text(GtkEntry *entry, const char *text){
660         GtkTreeIter iter;
661         GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
662
663         if (gtk_tree_model_get_iter_first(model,&iter)){
664                 do {
665                         gchar *uri=NULL;
666                         gtk_tree_model_get(model,&iter,0,&uri,-1);
667                         if (uri!=NULL){
668                                 if (strcmp(uri,text)==0) {
669                                         /*remove text */
670                                         gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
671                                         g_free(uri);
672                                         break;
673                                 }
674                                 g_free(uri);
675                         }
676                 }while (gtk_tree_model_iter_next(model,&iter));
677         }
678         /* and prepend it on top of the list */
679         gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
680         gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
681         save_uri_history();
682 }
683
684 bool_t linphone_gtk_video_enabled(void){
685         const LinphoneVideoPolicy *vpol=linphone_core_get_video_policy(linphone_gtk_get_core());
686         return vpol->automatically_accept && vpol->automatically_initiate;
687 }
688
689 void linphone_gtk_show_main_window(){
690         GtkWidget *w=linphone_gtk_get_main_window();
691         LinphoneCore *lc=linphone_gtk_get_core();
692         linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
693                 VIDEOSELFVIEW_DEFAULT));
694         gtk_widget_show(w);
695         gtk_window_present(GTK_WINDOW(w));
696 }
697
698 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
699         GtkWidget *mw=linphone_gtk_get_main_window();
700         if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
701             gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
702         }
703         if (linphone_gtk_use_in_call_view() && call)
704                 linphone_gtk_in_call_view_terminate(call,error);
705         update_video_title();
706 }
707
708 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
709         LinphoneCore *lc=linphone_gtk_get_core();
710         GtkWidget *mw=linphone_gtk_get_main_window();
711         const MSList *calls=linphone_core_get_calls(lc);
712         GtkWidget *button;
713         bool_t start_active=TRUE;
714         //bool_t stop_active=FALSE;
715         bool_t add_call=FALSE;
716         int call_list_size=ms_list_size(calls);
717
718         if (calls==NULL){
719                 start_active=TRUE;
720                 //stop_active=FALSE;
721         }else{
722                 //stop_active=TRUE;
723                 start_active=TRUE;
724                 add_call=TRUE;
725         }
726         button=linphone_gtk_get_widget(mw,"start_call");
727         gtk_widget_set_sensitive(button,start_active);
728         gtk_widget_set_visible(button,!add_call);
729
730         button=linphone_gtk_get_widget(mw,"add_call");
731         if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
732                 gtk_widget_set_sensitive(button,FALSE);
733         } else {
734                 gtk_widget_set_sensitive(button,start_active);
735         }
736         gtk_widget_set_visible(button,add_call);
737
738         //gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
739         GtkWidget *conf_frame=(GtkWidget *)g_object_get_data(G_OBJECT(mw),"conf_frame");
740         if(conf_frame==NULL){
741                 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
742                 linphone_gtk_enable_conference_button(lc,call_list_size>1);
743         } else {
744                 linphone_gtk_enable_transfer_button(lc,FALSE);
745                 linphone_gtk_enable_conference_button(lc,FALSE);
746         }
747         update_video_title();
748         if (call) {
749                 linphone_gtk_update_video_button(call);
750         }
751 }
752
753 gchar *linphone_gtk_get_call_record_path(LinphoneAddress *address){
754         const char *dir=g_get_user_special_dir(G_USER_DIRECTORY_MUSIC);
755         const char *id=linphone_address_get_username(address);
756         char filename[256]={0};
757         if (id==NULL) id=linphone_address_get_domain(address);
758         snprintf(filename,sizeof(filename)-1,"%s-%lu-%s-record.wav",
759                  linphone_gtk_get_ui_config("title","Linphone"),
760                  (unsigned long)time(NULL),id);
761         return g_build_filename(dir,filename,NULL);
762 }
763
764 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
765         const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
766         LinphoneCore *lc=linphone_gtk_get_core();
767         LinphoneAddress *addr=linphone_core_interpret_url(lc,entered);
768         
769         if (addr!=NULL){
770                 LinphoneCallParams *params=linphone_core_create_default_call_parameters(lc);
771                 gchar *record_file=linphone_gtk_get_call_record_path(addr);
772                 linphone_call_params_set_record_file(params,record_file);
773                 linphone_core_invite_address_with_params(lc,addr,params);
774                 completion_add_text(GTK_ENTRY(uri_bar),entered);
775                 linphone_address_destroy(addr);
776                 linphone_call_params_destroy(params);
777                 g_free(record_file);
778         }else{
779                 linphone_gtk_call_terminated(NULL,NULL);
780         }
781         return FALSE;
782 }
783
784 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
785         if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
786                 linphone_core_accept_call (linphone_gtk_get_core(),call);
787                 linphone_call_unref(call);
788         }
789         return FALSE;
790 }
791
792 void linphone_gtk_start_call(GtkWidget *w){
793         LinphoneCore *lc=linphone_gtk_get_core();
794         LinphoneCall *call;
795         /*change into in-call mode, then do the work later as it might block a bit */
796         GtkWidget *mw=gtk_widget_get_toplevel(w);
797         GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
798
799         call=linphone_gtk_get_currently_displayed_call(NULL);
800         if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
801                 linphone_core_accept_call(lc,call);
802         }else{
803                 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
804                 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
805                 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
806                 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
807         }
808
809 }
810
811 void linphone_gtk_uri_bar_activate(GtkWidget *w){
812         linphone_gtk_start_call(w);
813 }
814
815 void linphone_gtk_terminate_call(GtkWidget *button){
816         gboolean is_conf;
817         LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
818         if (call){
819                 linphone_core_terminate_call(linphone_gtk_get_core(),call);
820         }else if (is_conf){
821                 linphone_core_terminate_conference(linphone_gtk_get_core());
822         }
823 }
824
825 void linphone_gtk_decline_clicked(GtkWidget *button){
826         LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
827         if (call)
828                 linphone_core_terminate_call(linphone_gtk_get_core(),call);
829 }
830
831 void linphone_gtk_answer_clicked(GtkWidget *button){
832         LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
833         if (call){
834                 linphone_core_accept_call(linphone_gtk_get_core(),call);
835                 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
836         }
837 }
838
839 void _linphone_gtk_enable_video(gboolean val){
840         LinphoneVideoPolicy policy={0};
841         policy.automatically_initiate=policy.automatically_accept=val;
842         linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE);
843         linphone_core_set_video_policy(linphone_gtk_get_core(),&policy);
844
845         if (val){
846                 linphone_core_enable_video_preview(linphone_gtk_get_core(),
847                 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
848         }else{
849                 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
850         }
851 }
852
853 void linphone_gtk_enable_video(GtkWidget *w){
854         gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
855         //GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
856         _linphone_gtk_enable_video(val);
857 }
858
859 void linphone_gtk_enable_self_view(GtkWidget *w){
860         gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
861         LinphoneCore *lc=linphone_gtk_get_core();
862         linphone_core_enable_video_preview(lc,val);
863         linphone_core_enable_self_view(lc,val);
864         linphone_gtk_set_ui_config_int("videoselfview",val);
865 }
866
867 void linphone_gtk_used_identity_changed(GtkWidget *w){
868         int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
869         char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
870         if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
871                 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
872                 linphone_gtk_show_directory_search();
873         }
874         if (sel) g_free(sel);
875 }
876
877 void on_proxy_refresh_button_clicked(GtkWidget *w){
878         LinphoneCore *lc=linphone_gtk_get_core();
879         MSList const *item=linphone_core_get_proxy_config_list(lc);
880         while (item != NULL) {
881                 LinphoneProxyConfig *lpc=(LinphoneProxyConfig*)item->data;
882                 linphone_proxy_config_edit(lpc);
883                 linphone_proxy_config_done(lpc);
884                 item = item->next;
885         }
886 }
887
888 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
889         linphone_gtk_show_friends();
890 }
891
892 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
893         switch(response_id){
894                 case GTK_RESPONSE_YES:
895                         linphone_gtk_show_contact(lf);
896                 break;
897                 default:
898                         linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
899         }
900         gtk_widget_destroy(dialog);
901 }
902
903 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
904         GtkWidget *dialog;
905
906         if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
907                 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
908                 return;
909         }
910
911         gchar *message=g_strdup_printf(_("%s would like to add you to his contact list.\nWould you allow him to see your presence status or add him to your contact list ?\nIf you answer no, this person will be temporarily blacklisted."),url);
912         dialog = gtk_message_dialog_new (
913                                 GTK_WINDOW(linphone_gtk_get_main_window()),
914                                 GTK_DIALOG_DESTROY_WITH_PARENT,
915                                 GTK_MESSAGE_QUESTION,
916                                 GTK_BUTTONS_YES_NO,
917                                 "%s",
918                                 message);
919         g_free(message);
920         g_signal_connect(G_OBJECT (dialog), "response",
921                 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
922         /* actually show the box */
923         gtk_widget_show(dialog);
924 }
925
926 typedef struct _AuthTimeout{
927         GtkWidget *w;
928 } AuthTimeout;
929
930 static void auth_timeout_clean(AuthTimeout *tout){
931         tout->w=NULL;
932 }
933
934 static gboolean auth_timeout_destroy(AuthTimeout *tout){
935         if (tout->w)  {
936                 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
937                 gtk_widget_destroy(tout->w);
938         }
939         g_free(tout);
940         return FALSE;
941 }
942
943 static AuthTimeout * auth_timeout_new(GtkWidget *w){
944         AuthTimeout *tout=g_new(AuthTimeout,1);
945         tout->w=w;
946         /*so that the timeout no more references the widget when it is destroyed:*/
947         g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
948         /*so that the widget is automatically destroyed after some time */
949         g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
950         return tout;
951 }
952
953 void linphone_gtk_password_cancel(GtkWidget *w){
954         LinphoneAuthInfo *info;
955         GtkWidget *window=gtk_widget_get_toplevel(w);
956         info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
957         linphone_core_abort_authentication(linphone_gtk_get_core(),info);
958         gtk_widget_destroy(window);
959 }
960
961 void linphone_gtk_password_ok(GtkWidget *w){
962         GtkWidget *entry;
963         GtkWidget *window=gtk_widget_get_toplevel(w);
964         LinphoneAuthInfo *info;
965         info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
966         g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
967         entry=linphone_gtk_get_widget(window,"password_entry");
968         linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
969         linphone_auth_info_set_userid(info,
970                 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
971         linphone_core_add_auth_info(linphone_gtk_get_core(),info);
972         gtk_widget_destroy(window);
973 }
974
975 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
976         GtkWidget *w=linphone_gtk_create_window("password");
977         GtkWidget *label=linphone_gtk_get_widget(w,"message");
978         LinphoneAuthInfo *info;
979         gchar *msg;
980         GtkWidget *mw=linphone_gtk_get_main_window();
981
982         if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
983                 /*don't prompt for authentication when login frame is visible*/
984                 linphone_core_abort_authentication(lc,NULL);
985                 return;
986         }
987
988         msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
989                 username,realm);
990         gtk_label_set_markup(GTK_LABEL(label),msg);
991         g_free(msg);
992         gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
993         info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
994         g_object_set_data(G_OBJECT(w),"auth_info",info);
995         g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
996         gtk_widget_show(w);
997         auth_timeout_new(w);
998 }
999
1000 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
1001         GtkWidget *w=linphone_gtk_get_main_window();
1002         GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
1003
1004         gtk_statusbar_push(GTK_STATUSBAR(status_bar),
1005                         gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
1006                         status);
1007 }
1008
1009 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
1010         linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
1011 }
1012
1013 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
1014         linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
1015 }
1016
1017 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
1018         char richtext[4096];
1019         snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
1020         linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
1021 }
1022
1023 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
1024         GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
1025         if (w) linphone_gtk_call_log_update(w);
1026         linphone_gtk_call_log_update(linphone_gtk_get_main_window());
1027 }
1028
1029 #ifdef HAVE_NOTIFY
1030 static bool_t notify_actions_supported() {
1031         bool_t accepts_actions = FALSE;
1032         GList *capabilities = notify_get_server_caps();
1033         GList *c;
1034         if(capabilities != NULL) {
1035                 for(c = capabilities; c != NULL; c = c->next) {
1036                         if(strcmp((char*)c->data, "actions") == 0 ) {
1037                                 accepts_actions = TRUE;
1038                                 break;
1039                         }
1040                 }
1041                 g_list_foreach(capabilities, (GFunc)g_free, NULL);
1042                 g_list_free(capabilities);
1043         }
1044         return accepts_actions;
1045 }
1046
1047 static NotifyNotification* build_notification(const char *title, const char *body){
1048          return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
1049 #ifdef HAVE_NOTIFY1
1050         ,NULL
1051 #endif
1052         );
1053 }
1054
1055 static void show_notification(NotifyNotification* n){
1056         if (n && !notify_notification_show(n,NULL))
1057                 ms_error("Failed to send notification.");
1058 }
1059
1060 static void make_notification(const char *title, const char *body){
1061         show_notification(build_notification(title,body));
1062 }
1063
1064 #endif
1065
1066 void linphone_gtk_notify(LinphoneCall *call, const char *msg){
1067 #ifdef HAVE_NOTIFY
1068         if (!notify_is_initted())
1069                 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
1070 #endif
1071         if (!call) {
1072
1073 #ifdef HAVE_NOTIFY
1074                 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
1075 #ifdef HAVE_NOTIFY1
1076         ,NULL
1077 #endif
1078 ),NULL))
1079
1080                                 ms_error("Failed to send notification.");
1081 #else
1082                 linphone_gtk_show_main_window();
1083 #endif
1084         } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1085 #ifdef HAVE_NOTIFY
1086                 char *body=NULL;
1087                 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1088                 NotifyNotification *n;
1089                 switch(linphone_call_get_state(call)){
1090                         case LinphoneCallError:
1091                                 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1092                         break;
1093                         case LinphoneCallEnd:
1094                                 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1095                         break;
1096                         case LinphoneCallIncomingReceived:
1097                                 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1098                                 if (notify_actions_supported()) {
1099                                         notify_notification_add_action (n,"answer", _("Answer"),
1100                                                 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1101                                         notify_notification_add_action (n,"decline",_("Decline"),
1102                                                 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1103                                 }
1104                                 show_notification(n);
1105                         break;
1106                         case LinphoneCallPausedByRemote:
1107                                 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1108                         break;
1109                         default:
1110                         break;
1111                 }
1112                 if (body) g_free(body);
1113                 if (remote) g_free(remote);
1114 #endif
1115         }
1116 }
1117
1118 static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){
1119         if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1120                 LinphoneCore *lc=linphone_call_get_core(call);
1121                 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
1122                 linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES);
1123                 linphone_core_accept_call_update(lc,call,params);
1124                 linphone_call_params_destroy(params);
1125         }
1126         linphone_call_unref(call);
1127         g_source_remove_by_user_data(dialog);
1128         gtk_widget_destroy(dialog);
1129 }
1130
1131 static void on_call_updated_timeout(GtkWidget *dialog){
1132         gtk_widget_destroy(dialog);
1133 }
1134
1135 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1136         LinphoneCore *lc=linphone_call_get_core(call);
1137         const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1138         const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1139         const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1140         gboolean video_requested=linphone_call_params_video_enabled(rparams);
1141         gboolean video_used=linphone_call_params_video_enabled(current_params);
1142         g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1143                   video_used,video_requested,pol->automatically_accept);
1144         if (video_used==FALSE && video_requested && !pol->automatically_accept){
1145                 linphone_core_defer_call_update(lc,call);
1146                 {
1147                         const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1148                         GtkWidget *dialog;
1149                         const char *dname=linphone_address_get_display_name(addr);
1150                         if (dname==NULL) dname=linphone_address_get_username(addr);
1151                         if (dname==NULL) dname=linphone_address_get_domain(addr);
1152                         dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1153                                                                  GTK_DIALOG_DESTROY_WITH_PARENT,
1154                                                                  GTK_MESSAGE_WARNING,
1155                                                                  GTK_BUTTONS_YES_NO,
1156                                                                  _("%s proposed to start video. Do you accept ?"),dname);
1157                         g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1158                         g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1159                         gtk_widget_show(dialog);
1160                 }
1161         }
1162 }
1163
1164 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1165         switch(cs){
1166                 case LinphoneCallOutgoingInit:
1167                         linphone_gtk_create_in_call_view (call);
1168                 break;
1169                 case LinphoneCallOutgoingProgress:
1170                         linphone_gtk_in_call_view_set_calling (call);
1171                 break;
1172                 case LinphoneCallStreamsRunning:
1173                         linphone_gtk_in_call_view_set_in_call(call);
1174                 break;
1175                 case LinphoneCallUpdatedByRemote:
1176                         linphone_gtk_call_updated_by_remote(call);
1177                 break;
1178                 case LinphoneCallError:
1179                         linphone_gtk_in_call_view_terminate (call,msg);
1180                 break;
1181                 case LinphoneCallEnd:
1182                         linphone_gtk_in_call_view_terminate(call,NULL);
1183                         linphone_gtk_status_icon_set_blinking(FALSE);
1184                 break;
1185                 case LinphoneCallIncomingReceived:
1186                         linphone_gtk_create_in_call_view(call);
1187                         linphone_gtk_in_call_view_set_incoming(call);
1188                         linphone_gtk_status_icon_set_blinking(TRUE);
1189                         if (auto_answer)  {
1190                                 linphone_call_ref(call);
1191                                 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1192                         }
1193                 break;
1194                 case LinphoneCallResuming:
1195                         linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1196                         linphone_gtk_in_call_view_set_in_call (call);
1197                 break;
1198                 case LinphoneCallPausing:
1199                         linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1200                         update_tab_header(call,FALSE);
1201                 case LinphoneCallPausedByRemote:
1202                         linphone_gtk_in_call_view_set_paused(call);
1203                         update_tab_header(call,TRUE);
1204                 break;
1205                 case LinphoneCallConnected:
1206                         linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1207                         linphone_gtk_status_icon_set_blinking(FALSE);
1208                 break;
1209                 default:
1210                 break;
1211         }
1212         linphone_gtk_notify(call, msg);
1213         linphone_gtk_update_call_buttons (call);
1214 }
1215
1216 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1217         linphone_gtk_in_call_view_show_encryption(call);
1218 }
1219
1220 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){
1221         linphone_gtk_in_call_view_set_transfer_status(call,cstate);
1222 }
1223
1224 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1225         GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1226         GtkTreeModel *model=gtk_combo_box_get_model(box);
1227         GtkTreeIter iter;
1228         gboolean found=FALSE;
1229         const char *stock_id=NULL;
1230
1231         if (gtk_tree_model_get_iter_first(model,&iter)){
1232                 gpointer p;
1233                 do{
1234                         gtk_tree_model_get(model,&iter,2,&p,-1);
1235                         if (p==cfg) {
1236                                 found=TRUE;
1237                                 break;
1238                         }
1239                 }while(gtk_tree_model_iter_next(model,&iter));
1240         }
1241         if (!found) {
1242                 g_warning("Could not find proxy config in combo box of identities.");
1243                 return;
1244         }
1245         switch (rs){
1246                 case LinphoneRegistrationOk:
1247                         stock_id=GTK_STOCK_YES;
1248                 break;
1249                 case LinphoneRegistrationProgress:
1250                         stock_id=GTK_STOCK_REFRESH;
1251                 break;
1252                 case LinphoneRegistrationCleared:
1253                         stock_id=NULL;
1254                 break;
1255                 case LinphoneRegistrationFailed:
1256                         stock_id=GTK_STOCK_DIALOG_WARNING;
1257                 break;
1258                 default:
1259                 break;
1260         }
1261         gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1262 }
1263
1264 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1265                                                     LinphoneRegistrationState rs, const char *msg){
1266         switch (rs){
1267                 case LinphoneRegistrationOk:
1268                         if (cfg){
1269                                 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1270                                 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1271                                         linphone_gtk_exit_login_frame();
1272                                 }
1273                         }
1274                 break;
1275                 default:
1276                 break;
1277         }
1278         update_registration_status(cfg,rs);
1279 }
1280
1281 void linphone_gtk_open_browser(const char *url){
1282         /*in gtk 2.16, gtk_show_uri does not work...*/
1283 #ifndef WIN32
1284 #if GTK_CHECK_VERSION(2,18,3)
1285         gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1286 #else
1287         char cl[255];
1288         snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1289         g_spawn_command_line_async(cl,NULL);
1290 #endif
1291 #else /*WIN32*/
1292         ShellExecute(0,"open",url,NULL,NULL,1);
1293 #endif
1294 }
1295
1296 void linphone_gtk_link_to_website(GtkWidget *item){
1297         const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1298         linphone_gtk_open_browser(home);
1299 }
1300
1301 #ifndef HAVE_GTK_OSX
1302
1303 static GtkStatusIcon *icon=NULL;
1304
1305 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1306         GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1307         gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1308 }
1309
1310 static GtkWidget *create_icon_menu(){
1311         GtkWidget *menu=gtk_menu_new();
1312         GtkWidget *menu_item;
1313         GtkWidget *image;
1314         gchar *tmp;
1315         const gchar *homesite;
1316
1317         homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1318         menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1319         tmp=g_strdup(homesite);
1320         g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1321         g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1322
1323         image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1324         gtk_widget_show(image);
1325         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1326         //g_object_unref(G_OBJECT(image));
1327         gtk_widget_show(menu_item);
1328         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1329         g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1330
1331         menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1332         gtk_widget_show(menu_item);
1333         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1334         g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1335         menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1336         gtk_widget_show(menu_item);
1337         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1338         g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1339         gtk_widget_show(menu);
1340         return menu;
1341 }
1342
1343 static void handle_icon_click() {
1344         GtkWidget *mw=linphone_gtk_get_main_window();
1345         if (!gtk_window_is_active((GtkWindow*)mw)) {
1346                 linphone_gtk_show_main_window();
1347         } else {
1348                 gtk_widget_hide(mw);
1349         }
1350 }
1351
1352 static void linphone_gtk_init_status_icon(){
1353         const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1354         const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1355         GdkPixbuf *pbuf=create_pixbuf(icon_path);
1356         GtkWidget *menu=create_icon_menu();
1357         const char *title;
1358         title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1359         icon=gtk_status_icon_new_from_pixbuf(pbuf);
1360 #if GTK_CHECK_VERSION(2,20,0)
1361         gtk_status_icon_set_name(icon,title);
1362 #endif
1363         g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1364         g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1365         gtk_status_icon_set_tooltip(icon,title);
1366         gtk_status_icon_set_visible(icon,TRUE);
1367         g_object_set_data(G_OBJECT(icon),"menu",menu);
1368         g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1369         g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1370         g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1371         pbuf=create_pixbuf(call_icon_path);
1372         g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1373 }
1374
1375 static gboolean do_icon_blink(GtkStatusIcon *gi){
1376         GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1377         GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1378         GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1379         if (cur_icon==call_icon){
1380                 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1381         }else{
1382                 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1383         }
1384         return TRUE;
1385 }
1386
1387 #endif
1388
1389 void linphone_gtk_status_icon_set_blinking(gboolean val){
1390 #ifdef HAVE_GTK_OSX
1391         static gint attention_id;
1392         GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1393         if (val)
1394                 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1395         else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1396 #else
1397         if (icon!=NULL){
1398                 guint tout;
1399                 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1400                 if (val && tout==0){
1401                         tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1402                         g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1403                 }else if (!val && tout!=0){
1404                         GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1405                         g_source_remove(tout);
1406                         g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1407                         gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1408                 }
1409         }
1410 #endif
1411 }
1412
1413 void linphone_gtk_options_activate(GtkWidget *item){
1414 #ifndef HAVE_GTK_OSX
1415         gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1416                 TRUE);
1417 #endif
1418 }
1419
1420 static void init_identity_combo(GtkComboBox *box){
1421         GtkListStore *store;
1422         GtkCellRenderer *r1,*r2;
1423         store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1424         gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1425         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1426         gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1427         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1428         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1429         g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1430         gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1431 }
1432
1433 void linphone_gtk_load_identities(void){
1434         const MSList *elem;
1435         GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1436         char *def_identity;
1437         LinphoneProxyConfig *def=NULL;
1438         int def_index=0,i;
1439         GtkListStore *store;
1440         GtkTreeIter iter;
1441
1442         store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1443         if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1444                 /* model is empty, this is the first time we go here */
1445                 init_identity_combo(box);
1446                 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1447         }
1448         gtk_list_store_clear(store);
1449         linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1450         def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1451         gtk_list_store_append(store,&iter);
1452         gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1453         g_free(def_identity);
1454         for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1455                         elem!=NULL;
1456                         elem=ms_list_next(elem),i++){
1457                 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1458                 gtk_list_store_append(store,&iter);
1459                 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1460                                    linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1461                                    2,cfg,-1);
1462                 if (cfg==def) {
1463                         def_index=i;
1464                 }
1465         }
1466         gtk_combo_box_set_active(box,def_index);
1467 }
1468
1469 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1470         const char *label=(char *)g_object_get_data(G_OBJECT(button),"label");
1471         GtkWidget *uri_bar=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar");
1472         int pos=-1;
1473         gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1474         linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1475         if (linphone_core_in_call(linphone_gtk_get_core())){
1476                 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1477         }
1478 }
1479
1480 static void linphone_gtk_dtmf_released(GtkButton *button){
1481         linphone_core_stop_dtmf (linphone_gtk_get_core());
1482 }
1483
1484
1485 static void linphone_gtk_connect_digits(GtkWidget *w){
1486         GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(w,"dtmf_table"));
1487         GList *children=gtk_container_get_children(cont);
1488         GList *elem;
1489         for(elem=children;elem!=NULL;elem=elem->next){
1490                 GtkButton *button=GTK_BUTTON(elem->data);
1491                 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1492                 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1493         }
1494 }
1495
1496 static void linphone_gtk_check_menu_items(void){
1497         bool_t video_enabled=linphone_gtk_video_enabled();
1498         bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1499         GtkWidget *selfview_item=linphone_gtk_get_widget(
1500                                         linphone_gtk_get_main_window(),"selfview_item");
1501         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1502                                         linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1503         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1504 }
1505
1506 static gboolean linphone_gtk_can_manage_accounts(){
1507         LinphoneCore *lc=linphone_gtk_get_core();
1508         const MSList *elem;
1509         for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1510                 SipSetup *ss=(SipSetup*)elem->data;
1511                 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1512                         return TRUE;
1513                 }
1514         }
1515         return FALSE;
1516 }
1517
1518 static void linphone_gtk_configure_main_window(){
1519         static gboolean config_loaded=FALSE;
1520         static const char *title;
1521         static const char *home;
1522         static const char *start_call_icon;
1523         static const char *add_call_icon;
1524         static const char *search_icon;
1525         static gboolean update_check_menu;
1526         static gboolean buttons_have_borders;
1527         static gboolean show_abcd;
1528         GtkWidget *w=linphone_gtk_get_main_window();
1529         GHashTable *contacts_history;
1530
1531         contacts_history=g_hash_table_new_full(g_str_hash, g_str_equal,g_free, NULL);
1532         g_object_set_data(G_OBJECT(w),"history",(gpointer)contacts_history);
1533
1534         if (!config_loaded){
1535                 title=linphone_gtk_get_ui_config("title","Linphone");
1536                 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1537                 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1538                 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1539                 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1540                 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1541                 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1542                 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1543                 config_loaded=TRUE;
1544         }
1545         linphone_gtk_configure_window(w,"main_window");
1546         if (title) {
1547                 gtk_window_set_title(GTK_WINDOW(w),title);
1548         }
1549         if (start_call_icon){
1550                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1551                                     create_pixmap (start_call_icon));
1552                 if (!buttons_have_borders)
1553                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1554         }
1555         if (add_call_icon){
1556                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1557                                     create_pixmap (add_call_icon));
1558                 if (!buttons_have_borders)
1559                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1560         }
1561         if (search_icon){
1562                 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1563                 if(pbuf != NULL) {
1564                         gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1565                         g_object_unref(G_OBJECT(pbuf));
1566                 }
1567         }
1568         if (home){
1569                 gchar *tmp;
1570                 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1571                 tmp=g_strdup(home);
1572                 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1573         }
1574         {
1575                 /*
1576                 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1577                 if (pbuf) {
1578                         gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1579                         g_object_unref(G_OBJECT(pbuf));
1580                 }
1581                 */
1582         }
1583         {
1584                 GdkPixbuf *pbuf=create_pixbuf("dialer.png");
1585                 if (pbuf) {
1586                         GtkButton *button=GTK_BUTTON(linphone_gtk_get_widget(w,"keypad"));
1587                         gtk_button_set_image(button,gtk_image_new_from_pixbuf (pbuf));
1588                 }
1589         }
1590         if (linphone_gtk_can_manage_accounts()) {
1591                 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1592         }
1593         if (update_check_menu){
1594                 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1595         }
1596         g_object_set_data(G_OBJECT(w),"show_abcd",GINT_TO_POINTER(show_abcd));
1597 }
1598
1599 void linphone_gtk_manage_login(void){
1600         LinphoneCore *lc=linphone_gtk_get_core();
1601         LinphoneProxyConfig *cfg=NULL;
1602         linphone_core_get_default_proxy(lc,&cfg);
1603         if (cfg){
1604                 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1605                 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1606                         linphone_gtk_show_login_frame(cfg);
1607                 }
1608         }
1609 }
1610
1611 gboolean linphone_gtk_close(GtkWidget *mw){
1612         /*shutdown calls if any*/
1613         LinphoneCore *lc=linphone_gtk_get_core();
1614         if (linphone_core_in_call(lc)){
1615                 linphone_core_terminate_all_calls(lc);
1616         }
1617         linphone_core_enable_video_preview(lc,FALSE);
1618 #ifdef __APPLE__ /*until with have a better option*/
1619         gtk_window_iconify(GTK_WINDOW(mw));
1620 #else
1621         gtk_widget_hide(mw);
1622 #endif
1623         return TRUE;
1624 }
1625
1626 #ifdef HAVE_GTK_OSX
1627 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1628         bool_t video_enabled=linphone_gtk_video_enabled();
1629         if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1630                 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1631         }else{
1632                 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1633                 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1634         }
1635         return FALSE;
1636 }
1637 #endif
1638
1639 void linphone_gtk_init_dtmf_table(GtkWidget *mw){
1640         GtkWidget *dtmf_table=linphone_gtk_get_widget(mw,"dtmf_table");
1641         gtk_widget_set_direction(dtmf_table, GTK_TEXT_DIR_LTR);
1642
1643         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_A")),"label","A");
1644         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_B")),"label","B");
1645         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_C")),"label","C");
1646         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_D")),"label","D");
1647         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_1")),"label","1");
1648         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_2")),"label","2");
1649         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_3")),"label","3");
1650         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_4")),"label","4");
1651         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_5")),"label","5");
1652         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_6")),"label","6");
1653         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_7")),"label","7");
1654         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_8")),"label","8");
1655         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_9")),"label","9");
1656         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_0")),"label","0");
1657         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_#")),"label","#");
1658         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_*")),"label","*");
1659 }
1660
1661 void linphone_gtk_create_keypad(GtkWidget *button){
1662         GtkWidget *mw=linphone_gtk_get_main_window();
1663         GtkWidget *k=(GtkWidget *)g_object_get_data(G_OBJECT(mw),"keypad");
1664         if(k!=NULL){
1665                 gtk_widget_destroy(k);
1666         }
1667         GtkWidget *keypad=linphone_gtk_create_window("keypad");
1668         linphone_gtk_connect_digits(keypad);
1669         linphone_gtk_init_dtmf_table(keypad);
1670         g_object_set_data(G_OBJECT(mw),"keypad",(gpointer)keypad);
1671         if(!GPOINTER_TO_INT(g_object_get_data(G_OBJECT(mw),"show_abcd"))){
1672                 gtk_widget_hide(linphone_gtk_get_widget(keypad,"dtmf_A"));
1673                 gtk_widget_hide(linphone_gtk_get_widget(keypad,"dtmf_B"));
1674                 gtk_widget_hide(linphone_gtk_get_widget(keypad,"dtmf_C"));
1675                 gtk_widget_hide(linphone_gtk_get_widget(keypad,"dtmf_D"));
1676                 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(keypad,"dtmf_table")),4,3);
1677         }
1678         gtk_widget_show(keypad);
1679 }
1680
1681 static void linphone_gtk_init_main_window(){
1682         GtkWidget *main_window;
1683         linphone_gtk_configure_main_window();
1684         linphone_gtk_manage_login();
1685         load_uri_history();
1686         linphone_gtk_load_identities();
1687         linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1688         linphone_gtk_show_friends();
1689         main_window=linphone_gtk_get_main_window();
1690         linphone_gtk_call_log_update(main_window);
1691
1692         linphone_gtk_update_call_buttons (NULL);
1693         g_object_set_data(G_OBJECT(main_window),"keypad",NULL);
1694         g_object_set_data(G_OBJECT(main_window),"is_conf",GINT_TO_POINTER(FALSE));
1695         /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1696         g_signal_connect (G_OBJECT (main_window), "delete-event",
1697                 G_CALLBACK (linphone_gtk_close), main_window);
1698 #ifdef HAVE_GTK_OSX
1699         {
1700                 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1701                 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1702                 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1703                 gtk_widget_hide(menubar);
1704                 gtk_osxapplication_ready(theMacApp);
1705         }
1706         g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1707 #endif
1708         linphone_gtk_check_menu_items();
1709 }
1710
1711
1712 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1713         if (verbose){
1714                 const char *lname="undef";
1715                 char *msg;
1716 #if defined(__linux) || defined(__APPLE__)
1717                 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1718 #endif
1719                 switch(lev){
1720                         case ORTP_DEBUG:
1721                                 lname="debug";
1722                                 break;
1723                         case ORTP_MESSAGE:
1724                                 lname="message";
1725                                 break;
1726                         case ORTP_WARNING:
1727                                 lname="warning";
1728                                 break;
1729                         case ORTP_ERROR:
1730                                 lname="error";
1731                                 break;
1732                         case ORTP_FATAL:
1733                                 lname="fatal";
1734                                 break;
1735                         default:
1736                                 g_error("Bad level !");
1737                 }
1738 #if defined(__linux) || defined(__APPLE__)
1739                 va_copy(cap,args);
1740                 msg=g_strdup_vprintf(fmt,cap);
1741                 va_end(cap);
1742 #else
1743                 msg=g_strdup_vprintf(fmt,args);
1744 #endif
1745                 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1746                 ortp_free(msg);
1747         }
1748         linphone_gtk_log_push(lev,fmt,args);
1749 }
1750
1751
1752 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1753         GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1754                 linphone_gtk_get_main_window(), "uribar"));
1755         char *text;
1756         linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1757         g_free(text);
1758         gtk_entry_set_text(uri_bar, refer_to);
1759         linphone_gtk_start_call(linphone_gtk_get_main_window());
1760 }
1761
1762 static void linphone_gtk_check_soundcards(){
1763         const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1764         if (devices==NULL || devices[0]==NULL){
1765                 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1766                         _("No sound cards have been detected on this computer.\n"
1767                                 "You won't be able to send or receive audio calls."));
1768         }
1769 }
1770
1771 static void linphone_gtk_quit(void){
1772         static gboolean quit_done=FALSE;
1773         if (!quit_done){
1774                 quit_done=TRUE;
1775                 linphone_gtk_unmonitor_usb();
1776                 g_source_remove_by_user_data(linphone_gtk_get_core());
1777 #ifdef BUILD_WIZARD
1778                 linphone_gtk_close_assistant();
1779 #endif
1780                 linphone_gtk_uninit_instance();
1781                 linphone_gtk_destroy_log_window();
1782                 linphone_core_destroy(the_core);
1783                 linphone_gtk_log_uninit();
1784 #ifdef HAVE_NOTIFY
1785                 notify_uninit();
1786 #endif
1787                 gdk_threads_leave();
1788         }
1789 }
1790
1791 #ifdef HAVE_GTK_OSX
1792 /*
1793 This is not the correct way to implement block termination.
1794 The good way would be to call gtk_main_quit(), and return TRUE.
1795 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1796 As a result the program cannot exit at all.
1797 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1798 */
1799 static gboolean on_block_termination(void){
1800         gtk_main_quit();
1801         linphone_gtk_quit();
1802         return FALSE;
1803 }
1804 #endif
1805
1806 int main(int argc, char *argv[]){
1807 #ifdef ENABLE_NLS
1808         void *p;
1809 #endif
1810         char *config_file;
1811         const char *factory_config_file;
1812         const char *lang;
1813         GtkSettings *settings;
1814         GdkPixbuf *pbuf;
1815         const char *app_name="Linphone";
1816         LpConfig *factory;
1817
1818 #if !GLIB_CHECK_VERSION(2, 31, 0)
1819         g_thread_init(NULL);
1820 #endif
1821         gdk_threads_init();
1822
1823         progpath = strdup(argv[0]);
1824
1825         config_file=linphone_gtk_get_config_file(NULL);
1826
1827
1828 #ifdef WIN32
1829         /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1830         if ((lang=getenv("LANG"))!=NULL){
1831                 if (atoi(lang)!=0){
1832                         char tmp[128];
1833                         snprintf(tmp,sizeof(tmp),"LANG=",lang);
1834                         _putenv(tmp);
1835                 }
1836         }
1837 #else
1838         /*for pulseaudio:*/
1839         g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1840 #endif
1841
1842         if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1843 #ifdef WIN32
1844                 char tmp[128];
1845                 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1846                 _putenv(tmp);
1847                 if (strncmp(lang,"zh",2)==0){
1848                         workaround_gtk_entry_chinese_bug=TRUE;
1849                 }
1850 #elif __APPLE__
1851                 setenv("LANG",lang,1);
1852 #else
1853                 setenv("LANGUAGE",lang,1);
1854 #endif
1855         }
1856
1857 #ifdef ENABLE_NLS
1858         p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1859         if (p==NULL) perror("bindtextdomain failed");
1860         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1861         textdomain (GETTEXT_PACKAGE);
1862 #else
1863         g_message("NLS disabled.\n");
1864 #endif
1865 #ifdef WIN32
1866         gtk_rc_add_default_file("./gtkrc");
1867 #endif
1868         gdk_threads_enter();
1869
1870         if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1871                                 linphone_options,NULL,NULL)){
1872                 gdk_threads_leave();
1873                 return -1;
1874         }
1875
1876         settings=gtk_settings_get_default();
1877         g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1878         g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1879         g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1880         g_object_set(settings, "gtk-button-images", TRUE, NULL);
1881
1882         if (workingdir!=NULL){
1883                 if (chdir(workingdir)==-1){
1884                         g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1885                 }
1886         }
1887
1888         /* Now, look for the factory configuration file, we do it this late
1889                  since we want to have had time to change directory and to parse
1890                  the options, in case we needed to access the working directory */
1891         factory_config_file = linphone_gtk_get_factory_config_file();
1892         if (factory_config_file){
1893                 factory=lp_config_new(NULL);
1894                 lp_config_read_file(factory,factory_config_file);
1895                 app_name=lp_config_get_string(factory,"GtkUi","title","Linphone");
1896         }
1897
1898         if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1899                 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1900                 g_warning("This instance is going to exit now.");
1901                 gdk_threads_leave();
1902                 return 0;
1903         }
1904
1905         add_pixmap_directory("pixmaps");
1906         add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1907
1908 #ifdef HAVE_GTK_OSX
1909         GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1910         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1911         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1912         /*never block termination:*/
1913         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1914 #endif
1915
1916         the_ui=linphone_gtk_create_window("main");
1917
1918         g_object_set_data(G_OBJECT(the_ui),"is_created",GINT_TO_POINTER(FALSE));
1919
1920         linphone_gtk_create_log_window();
1921         linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1922
1923         linphone_gtk_init_liblinphone(config_file, factory_config_file);
1924
1925         g_set_application_name(app_name);
1926         pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1927         if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1928
1929         /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1930         gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1931         gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1932         linphone_gtk_init_main_window();
1933
1934 #ifdef BUILD_WIZARD
1935         // Veryfing if at least one sip account is configured. If not, show wizard
1936         if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1937                 linphone_gtk_show_assistant();
1938         }
1939 #endif
1940
1941 #ifndef HAVE_GTK_OSX
1942         linphone_gtk_init_status_icon();
1943 #endif
1944         if (!iconified){
1945                 linphone_gtk_show_main_window();
1946                 linphone_gtk_check_soundcards();
1947         }
1948         if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1949                 linphone_gtk_check_for_new_version();
1950         linphone_gtk_monitor_usb();
1951
1952         gtk_main();
1953         linphone_gtk_quit();
1954 #ifndef HAVE_GTK_OSX
1955         /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1956         gtk_status_icon_set_visible(icon,FALSE);
1957 #endif
1958         free(progpath);
1959         return 0;
1960 }
1961