]> sjero.net Git - linphone/blob - gtk/main.c
add call recording feature to conference
[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_record_path(const LinphoneAddress *address, gboolean is_conference){
754         const char *dir=g_get_user_special_dir(G_USER_DIRECTORY_MUSIC);
755         const char *id="unknown";
756         char filename[256]={0};
757         char date[64]={0};
758         time_t curtime=time(NULL);
759         struct tm loctime;
760         
761 #ifdef WIN32
762         loctime=*localtime(&curtime);
763 #else
764         localtime_r(&curtime,&loctime);
765 #endif
766         snprintf(date,sizeof(date)-1,"%i%02i%02i-%02i%02i",loctime.tm_year+1900,loctime.tm_mon+1,loctime.tm_mday, loctime.tm_hour, loctime.tm_min);
767         
768         if (address){
769                 id=linphone_address_get_username(address);
770                 if (id==NULL) id=linphone_address_get_domain(address);
771         }
772         if (is_conference){
773                 snprintf(filename,sizeof(filename)-1,"%s-conference-%s.wav",
774                         linphone_gtk_get_ui_config("title","Linphone"),
775                         date);
776         }else{
777                 snprintf(filename,sizeof(filename)-1,"%s-call-%s-%s.wav",
778                         linphone_gtk_get_ui_config("title","Linphone"),
779                         date,
780                         id);
781         }
782         return g_build_filename(dir,filename,NULL);
783 }
784
785 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
786         const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
787         LinphoneCore *lc=linphone_gtk_get_core();
788         LinphoneAddress *addr=linphone_core_interpret_url(lc,entered);
789         
790         if (addr!=NULL){
791                 LinphoneCallParams *params=linphone_core_create_default_call_parameters(lc);
792                 gchar *record_file=linphone_gtk_get_record_path(addr,FALSE);
793                 linphone_call_params_set_record_file(params,record_file);
794                 linphone_core_invite_address_with_params(lc,addr,params);
795                 completion_add_text(GTK_ENTRY(uri_bar),entered);
796                 linphone_address_destroy(addr);
797                 linphone_call_params_destroy(params);
798                 g_free(record_file);
799         }else{
800                 linphone_gtk_call_terminated(NULL,NULL);
801         }
802         return FALSE;
803 }
804
805
806 static void accept_incoming_call(LinphoneCall *call){
807         LinphoneCore *lc=linphone_gtk_get_core();
808         LinphoneCallParams *params=linphone_core_create_default_call_parameters(lc);
809         gchar *record_file=linphone_gtk_get_record_path(linphone_call_get_remote_address(call),FALSE);
810         linphone_call_params_set_record_file(params,record_file);
811         linphone_core_accept_call_with_params(lc,call,params);
812         linphone_call_params_destroy(params);
813 }
814
815 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
816         LinphoneCallState state=linphone_call_get_state(call);
817         if (state==LinphoneCallIncomingReceived || state==LinphoneCallIncomingEarlyMedia){
818                 accept_incoming_call(call);
819                 linphone_call_unref(call);
820         }
821         return FALSE;
822 }
823
824 void linphone_gtk_start_call(GtkWidget *w){
825         LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
826         /*change into in-call mode, then do the work later as it might block a bit */
827         GtkWidget *mw=gtk_widget_get_toplevel(w);
828         GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
829         LinphoneCallState state= call ? linphone_call_get_state(call) : LinphoneCallIdle;
830
831         if (state == LinphoneCallIncomingReceived || state == LinphoneCallIncomingEarlyMedia){
832                 accept_incoming_call(call);
833         }else{
834                 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
835                 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
836                 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
837                 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
838         }
839
840 }
841
842 void linphone_gtk_uri_bar_activate(GtkWidget *w){
843         linphone_gtk_start_call(w);
844 }
845
846 void linphone_gtk_terminate_call(GtkWidget *button){
847         gboolean is_conf;
848         LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
849         if (call){
850                 linphone_core_terminate_call(linphone_gtk_get_core(),call);
851         }else if (is_conf){
852                 linphone_core_terminate_conference(linphone_gtk_get_core());
853         }
854 }
855
856 void linphone_gtk_decline_clicked(GtkWidget *button){
857         LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
858         if (call)
859                 linphone_core_terminate_call(linphone_gtk_get_core(),call);
860 }
861
862 void linphone_gtk_answer_clicked(GtkWidget *button){
863         LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
864         if (call){
865                 accept_incoming_call(call);
866                 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
867         }
868 }
869
870 void _linphone_gtk_enable_video(gboolean val){
871         LinphoneVideoPolicy policy={0};
872         policy.automatically_initiate=policy.automatically_accept=val;
873         linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE);
874         linphone_core_set_video_policy(linphone_gtk_get_core(),&policy);
875
876         if (val){
877                 linphone_core_enable_video_preview(linphone_gtk_get_core(),
878                 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
879         }else{
880                 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
881         }
882 }
883
884 void linphone_gtk_enable_video(GtkWidget *w){
885         gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
886         //GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
887         _linphone_gtk_enable_video(val);
888 }
889
890 void linphone_gtk_enable_self_view(GtkWidget *w){
891         gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
892         LinphoneCore *lc=linphone_gtk_get_core();
893         linphone_core_enable_video_preview(lc,val);
894         linphone_core_enable_self_view(lc,val);
895         linphone_gtk_set_ui_config_int("videoselfview",val);
896 }
897
898 void linphone_gtk_used_identity_changed(GtkWidget *w){
899         int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
900         char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
901         if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
902                 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
903                 linphone_gtk_show_directory_search();
904         }
905         if (sel) g_free(sel);
906 }
907
908 void on_proxy_refresh_button_clicked(GtkWidget *w){
909         LinphoneCore *lc=linphone_gtk_get_core();
910         MSList const *item=linphone_core_get_proxy_config_list(lc);
911         while (item != NULL) {
912                 LinphoneProxyConfig *lpc=(LinphoneProxyConfig*)item->data;
913                 linphone_proxy_config_edit(lpc);
914                 linphone_proxy_config_done(lpc);
915                 item = item->next;
916         }
917 }
918
919 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
920         linphone_gtk_show_friends();
921 }
922
923 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
924         switch(response_id){
925                 case GTK_RESPONSE_YES:
926                         linphone_gtk_show_contact(lf);
927                 break;
928                 default:
929                         linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
930         }
931         gtk_widget_destroy(dialog);
932 }
933
934 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
935         GtkWidget *dialog;
936
937         if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
938                 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
939                 return;
940         }
941
942         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);
943         dialog = gtk_message_dialog_new (
944                                 GTK_WINDOW(linphone_gtk_get_main_window()),
945                                 GTK_DIALOG_DESTROY_WITH_PARENT,
946                                 GTK_MESSAGE_QUESTION,
947                                 GTK_BUTTONS_YES_NO,
948                                 "%s",
949                                 message);
950         g_free(message);
951         g_signal_connect(G_OBJECT (dialog), "response",
952                 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
953         /* actually show the box */
954         gtk_widget_show(dialog);
955 }
956
957 typedef struct _AuthTimeout{
958         GtkWidget *w;
959 } AuthTimeout;
960
961 static void auth_timeout_clean(AuthTimeout *tout){
962         tout->w=NULL;
963 }
964
965 static gboolean auth_timeout_destroy(AuthTimeout *tout){
966         if (tout->w)  {
967                 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
968                 gtk_widget_destroy(tout->w);
969         }
970         g_free(tout);
971         return FALSE;
972 }
973
974 static AuthTimeout * auth_timeout_new(GtkWidget *w){
975         AuthTimeout *tout=g_new(AuthTimeout,1);
976         tout->w=w;
977         /*so that the timeout no more references the widget when it is destroyed:*/
978         g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
979         /*so that the widget is automatically destroyed after some time */
980         g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
981         return tout;
982 }
983
984 void linphone_gtk_password_cancel(GtkWidget *w){
985         LinphoneAuthInfo *info;
986         GtkWidget *window=gtk_widget_get_toplevel(w);
987         info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
988         linphone_core_abort_authentication(linphone_gtk_get_core(),info);
989         gtk_widget_destroy(window);
990 }
991
992 void linphone_gtk_password_ok(GtkWidget *w){
993         GtkWidget *entry;
994         GtkWidget *window=gtk_widget_get_toplevel(w);
995         LinphoneAuthInfo *info;
996         info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
997         g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
998         entry=linphone_gtk_get_widget(window,"password_entry");
999         linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
1000         linphone_auth_info_set_userid(info,
1001                 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
1002         linphone_core_add_auth_info(linphone_gtk_get_core(),info);
1003         gtk_widget_destroy(window);
1004 }
1005
1006 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
1007         GtkWidget *w=linphone_gtk_create_window("password");
1008         GtkWidget *label=linphone_gtk_get_widget(w,"message");
1009         LinphoneAuthInfo *info;
1010         gchar *msg;
1011         GtkWidget *mw=linphone_gtk_get_main_window();
1012
1013         if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
1014                 /*don't prompt for authentication when login frame is visible*/
1015                 linphone_core_abort_authentication(lc,NULL);
1016                 return;
1017         }
1018
1019         msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
1020                 username,realm);
1021         gtk_label_set_markup(GTK_LABEL(label),msg);
1022         g_free(msg);
1023         gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
1024         info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
1025         g_object_set_data(G_OBJECT(w),"auth_info",info);
1026         g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
1027         gtk_widget_show(w);
1028         auth_timeout_new(w);
1029 }
1030
1031 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
1032         GtkWidget *w=linphone_gtk_get_main_window();
1033         GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
1034
1035         gtk_statusbar_push(GTK_STATUSBAR(status_bar),
1036                         gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
1037                         status);
1038 }
1039
1040 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
1041         linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
1042 }
1043
1044 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
1045         linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
1046 }
1047
1048 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
1049         char richtext[4096];
1050         snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
1051         linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
1052 }
1053
1054 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
1055         GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
1056         if (w) linphone_gtk_call_log_update(w);
1057         linphone_gtk_call_log_update(linphone_gtk_get_main_window());
1058 }
1059
1060 #ifdef HAVE_NOTIFY
1061 static bool_t notify_actions_supported() {
1062         bool_t accepts_actions = FALSE;
1063         GList *capabilities = notify_get_server_caps();
1064         GList *c;
1065         if(capabilities != NULL) {
1066                 for(c = capabilities; c != NULL; c = c->next) {
1067                         if(strcmp((char*)c->data, "actions") == 0 ) {
1068                                 accepts_actions = TRUE;
1069                                 break;
1070                         }
1071                 }
1072                 g_list_foreach(capabilities, (GFunc)g_free, NULL);
1073                 g_list_free(capabilities);
1074         }
1075         return accepts_actions;
1076 }
1077
1078 static NotifyNotification* build_notification(const char *title, const char *body){
1079          return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
1080 #ifdef HAVE_NOTIFY1
1081         ,NULL
1082 #endif
1083         );
1084 }
1085
1086 static void show_notification(NotifyNotification* n){
1087         if (n && !notify_notification_show(n,NULL))
1088                 ms_error("Failed to send notification.");
1089 }
1090
1091 static void make_notification(const char *title, const char *body){
1092         show_notification(build_notification(title,body));
1093 }
1094
1095 #endif
1096
1097 void linphone_gtk_notify(LinphoneCall *call, const char *msg){
1098 #ifdef HAVE_NOTIFY
1099         if (!notify_is_initted())
1100                 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
1101 #endif
1102         if (!call) {
1103
1104 #ifdef HAVE_NOTIFY
1105                 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
1106 #ifdef HAVE_NOTIFY1
1107         ,NULL
1108 #endif
1109 ),NULL))
1110
1111                                 ms_error("Failed to send notification.");
1112 #else
1113                 linphone_gtk_show_main_window();
1114 #endif
1115         } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1116 #ifdef HAVE_NOTIFY
1117                 char *body=NULL;
1118                 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1119                 NotifyNotification *n;
1120                 switch(linphone_call_get_state(call)){
1121                         case LinphoneCallError:
1122                                 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1123                         break;
1124                         case LinphoneCallEnd:
1125                                 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1126                         break;
1127                         case LinphoneCallIncomingReceived:
1128                                 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1129                                 if (notify_actions_supported()) {
1130                                         notify_notification_add_action (n,"answer", _("Answer"),
1131                                                 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1132                                         notify_notification_add_action (n,"decline",_("Decline"),
1133                                                 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1134                                 }
1135                                 show_notification(n);
1136                         break;
1137                         case LinphoneCallPausedByRemote:
1138                                 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1139                         break;
1140                         default:
1141                         break;
1142                 }
1143                 if (body) g_free(body);
1144                 if (remote) g_free(remote);
1145 #endif
1146         }
1147 }
1148
1149 static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){
1150         if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1151                 LinphoneCore *lc=linphone_call_get_core(call);
1152                 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
1153                 linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES);
1154                 linphone_core_accept_call_update(lc,call,params);
1155                 linphone_call_params_destroy(params);
1156         }
1157         linphone_call_unref(call);
1158         g_source_remove_by_user_data(dialog);
1159         gtk_widget_destroy(dialog);
1160 }
1161
1162 static void on_call_updated_timeout(GtkWidget *dialog){
1163         gtk_widget_destroy(dialog);
1164 }
1165
1166 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1167         LinphoneCore *lc=linphone_call_get_core(call);
1168         const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1169         const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1170         const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1171         gboolean video_requested=linphone_call_params_video_enabled(rparams);
1172         gboolean video_used=linphone_call_params_video_enabled(current_params);
1173         g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1174                   video_used,video_requested,pol->automatically_accept);
1175         if (video_used==FALSE && video_requested && !pol->automatically_accept){
1176                 linphone_core_defer_call_update(lc,call);
1177                 {
1178                         const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1179                         GtkWidget *dialog;
1180                         const char *dname=linphone_address_get_display_name(addr);
1181                         if (dname==NULL) dname=linphone_address_get_username(addr);
1182                         if (dname==NULL) dname=linphone_address_get_domain(addr);
1183                         dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1184                                                                  GTK_DIALOG_DESTROY_WITH_PARENT,
1185                                                                  GTK_MESSAGE_WARNING,
1186                                                                  GTK_BUTTONS_YES_NO,
1187                                                                  _("%s proposed to start video. Do you accept ?"),dname);
1188                         g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1189                         g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1190                         gtk_widget_show(dialog);
1191                 }
1192         }
1193 }
1194
1195 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1196         switch(cs){
1197                 case LinphoneCallOutgoingInit:
1198                         linphone_gtk_create_in_call_view (call);
1199                 break;
1200                 case LinphoneCallOutgoingProgress:
1201                         linphone_gtk_in_call_view_set_calling (call);
1202                 break;
1203                 case LinphoneCallStreamsRunning:
1204                         linphone_gtk_in_call_view_set_in_call(call);
1205                 break;
1206                 case LinphoneCallUpdatedByRemote:
1207                         linphone_gtk_call_updated_by_remote(call);
1208                 break;
1209                 case LinphoneCallError:
1210                         linphone_gtk_in_call_view_terminate (call,msg);
1211                 break;
1212                 case LinphoneCallEnd:
1213                         linphone_gtk_in_call_view_terminate(call,NULL);
1214                         linphone_gtk_status_icon_set_blinking(FALSE);
1215                 break;
1216                 case LinphoneCallIncomingReceived:
1217                         linphone_gtk_create_in_call_view(call);
1218                         linphone_gtk_in_call_view_set_incoming(call);
1219                         linphone_gtk_status_icon_set_blinking(TRUE);
1220                         if (auto_answer)  {
1221                                 linphone_call_ref(call);
1222                                 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1223                         }
1224                 break;
1225                 case LinphoneCallResuming:
1226                         linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1227                         linphone_gtk_in_call_view_set_in_call (call);
1228                 break;
1229                 case LinphoneCallPausing:
1230                         linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1231                         update_tab_header(call,FALSE);
1232                 case LinphoneCallPausedByRemote:
1233                         linphone_gtk_in_call_view_set_paused(call);
1234                         update_tab_header(call,TRUE);
1235                 break;
1236                 case LinphoneCallConnected:
1237                         linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1238                         linphone_gtk_status_icon_set_blinking(FALSE);
1239                 break;
1240                 default:
1241                 break;
1242         }
1243         linphone_gtk_notify(call, msg);
1244         linphone_gtk_update_call_buttons (call);
1245 }
1246
1247 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1248         linphone_gtk_in_call_view_show_encryption(call);
1249 }
1250
1251 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){
1252         linphone_gtk_in_call_view_set_transfer_status(call,cstate);
1253 }
1254
1255 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1256         GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1257         GtkTreeModel *model=gtk_combo_box_get_model(box);
1258         GtkTreeIter iter;
1259         gboolean found=FALSE;
1260         const char *stock_id=NULL;
1261
1262         if (gtk_tree_model_get_iter_first(model,&iter)){
1263                 gpointer p;
1264                 do{
1265                         gtk_tree_model_get(model,&iter,2,&p,-1);
1266                         if (p==cfg) {
1267                                 found=TRUE;
1268                                 break;
1269                         }
1270                 }while(gtk_tree_model_iter_next(model,&iter));
1271         }
1272         if (!found) {
1273                 g_warning("Could not find proxy config in combo box of identities.");
1274                 return;
1275         }
1276         switch (rs){
1277                 case LinphoneRegistrationOk:
1278                         stock_id=GTK_STOCK_YES;
1279                 break;
1280                 case LinphoneRegistrationProgress:
1281                         stock_id=GTK_STOCK_REFRESH;
1282                 break;
1283                 case LinphoneRegistrationCleared:
1284                         stock_id=NULL;
1285                 break;
1286                 case LinphoneRegistrationFailed:
1287                         stock_id=GTK_STOCK_DIALOG_WARNING;
1288                 break;
1289                 default:
1290                 break;
1291         }
1292         gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1293 }
1294
1295 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1296                                                     LinphoneRegistrationState rs, const char *msg){
1297         switch (rs){
1298                 case LinphoneRegistrationOk:
1299                         if (cfg){
1300                                 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1301                                 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1302                                         linphone_gtk_exit_login_frame();
1303                                 }
1304                         }
1305                 break;
1306                 default:
1307                 break;
1308         }
1309         update_registration_status(cfg,rs);
1310 }
1311
1312 void linphone_gtk_open_browser(const char *url){
1313         /*in gtk 2.16, gtk_show_uri does not work...*/
1314 #ifndef WIN32
1315 #if GTK_CHECK_VERSION(2,18,3)
1316         gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1317 #else
1318         char cl[255];
1319         snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1320         g_spawn_command_line_async(cl,NULL);
1321 #endif
1322 #else /*WIN32*/
1323         ShellExecute(0,"open",url,NULL,NULL,1);
1324 #endif
1325 }
1326
1327 void linphone_gtk_link_to_website(GtkWidget *item){
1328         const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1329         linphone_gtk_open_browser(home);
1330 }
1331
1332 #ifndef HAVE_GTK_OSX
1333
1334 static GtkStatusIcon *icon=NULL;
1335
1336 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1337         GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1338         gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1339 }
1340
1341 static GtkWidget *create_icon_menu(){
1342         GtkWidget *menu=gtk_menu_new();
1343         GtkWidget *menu_item;
1344         GtkWidget *image;
1345         gchar *tmp;
1346         const gchar *homesite;
1347
1348         homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1349         menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1350         tmp=g_strdup(homesite);
1351         g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1352         g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1353
1354         image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1355         gtk_widget_show(image);
1356         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1357         //g_object_unref(G_OBJECT(image));
1358         gtk_widget_show(menu_item);
1359         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1360         g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1361
1362         menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1363         gtk_widget_show(menu_item);
1364         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1365         g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1366         menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1367         gtk_widget_show(menu_item);
1368         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1369         g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1370         gtk_widget_show(menu);
1371         return menu;
1372 }
1373
1374 static void handle_icon_click() {
1375         GtkWidget *mw=linphone_gtk_get_main_window();
1376         if (!gtk_window_is_active((GtkWindow*)mw)) {
1377                 linphone_gtk_show_main_window();
1378         } else {
1379                 gtk_widget_hide(mw);
1380         }
1381 }
1382
1383 static void linphone_gtk_init_status_icon(){
1384         const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1385         const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1386         GdkPixbuf *pbuf=create_pixbuf(icon_path);
1387         GtkWidget *menu=create_icon_menu();
1388         const char *title;
1389         title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1390         icon=gtk_status_icon_new_from_pixbuf(pbuf);
1391 #if GTK_CHECK_VERSION(2,20,0)
1392         gtk_status_icon_set_name(icon,title);
1393 #endif
1394         g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1395         g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1396         gtk_status_icon_set_tooltip(icon,title);
1397         gtk_status_icon_set_visible(icon,TRUE);
1398         g_object_set_data(G_OBJECT(icon),"menu",menu);
1399         g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1400         g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1401         g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1402         pbuf=create_pixbuf(call_icon_path);
1403         g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1404 }
1405
1406 static gboolean do_icon_blink(GtkStatusIcon *gi){
1407         GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1408         GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1409         GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1410         if (cur_icon==call_icon){
1411                 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1412         }else{
1413                 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1414         }
1415         return TRUE;
1416 }
1417
1418 #endif
1419
1420 void linphone_gtk_status_icon_set_blinking(gboolean val){
1421 #ifdef HAVE_GTK_OSX
1422         static gint attention_id;
1423         GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1424         if (val)
1425                 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1426         else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1427 #else
1428         if (icon!=NULL){
1429                 guint tout;
1430                 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1431                 if (val && tout==0){
1432                         tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1433                         g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1434                 }else if (!val && tout!=0){
1435                         GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1436                         g_source_remove(tout);
1437                         g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1438                         gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1439                 }
1440         }
1441 #endif
1442 }
1443
1444 void linphone_gtk_options_activate(GtkWidget *item){
1445 #ifndef HAVE_GTK_OSX
1446         gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1447                 TRUE);
1448 #endif
1449 }
1450
1451 static void init_identity_combo(GtkComboBox *box){
1452         GtkListStore *store;
1453         GtkCellRenderer *r1,*r2;
1454         store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1455         gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1456         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1457         gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1458         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1459         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1460         g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1461         gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1462 }
1463
1464 void linphone_gtk_load_identities(void){
1465         const MSList *elem;
1466         GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1467         char *def_identity;
1468         LinphoneProxyConfig *def=NULL;
1469         int def_index=0,i;
1470         GtkListStore *store;
1471         GtkTreeIter iter;
1472
1473         store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1474         if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1475                 /* model is empty, this is the first time we go here */
1476                 init_identity_combo(box);
1477                 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1478         }
1479         gtk_list_store_clear(store);
1480         linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1481         def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1482         gtk_list_store_append(store,&iter);
1483         gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1484         g_free(def_identity);
1485         for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1486                         elem!=NULL;
1487                         elem=ms_list_next(elem),i++){
1488                 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1489                 gtk_list_store_append(store,&iter);
1490                 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1491                                    linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1492                                    2,cfg,-1);
1493                 if (cfg==def) {
1494                         def_index=i;
1495                 }
1496         }
1497         gtk_combo_box_set_active(box,def_index);
1498 }
1499
1500 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1501         const char *label=(char *)g_object_get_data(G_OBJECT(button),"label");
1502         GtkWidget *uri_bar=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar");
1503         int pos=-1;
1504         gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1505         linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1506         if (linphone_core_in_call(linphone_gtk_get_core())){
1507                 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1508         }
1509 }
1510
1511 static void linphone_gtk_dtmf_released(GtkButton *button){
1512         linphone_core_stop_dtmf (linphone_gtk_get_core());
1513 }
1514
1515
1516 static void linphone_gtk_connect_digits(GtkWidget *w){
1517         GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(w,"dtmf_table"));
1518         GList *children=gtk_container_get_children(cont);
1519         GList *elem;
1520         for(elem=children;elem!=NULL;elem=elem->next){
1521                 GtkButton *button=GTK_BUTTON(elem->data);
1522                 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1523                 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1524         }
1525 }
1526
1527 static void linphone_gtk_check_menu_items(void){
1528         bool_t video_enabled=linphone_gtk_video_enabled();
1529         bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1530         GtkWidget *selfview_item=linphone_gtk_get_widget(
1531                                         linphone_gtk_get_main_window(),"selfview_item");
1532         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1533                                         linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1534         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1535 }
1536
1537 static gboolean linphone_gtk_can_manage_accounts(){
1538         LinphoneCore *lc=linphone_gtk_get_core();
1539         const MSList *elem;
1540         for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1541                 SipSetup *ss=(SipSetup*)elem->data;
1542                 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1543                         return TRUE;
1544                 }
1545         }
1546         return FALSE;
1547 }
1548
1549 static void linphone_gtk_configure_main_window(){
1550         static gboolean config_loaded=FALSE;
1551         static const char *title;
1552         static const char *home;
1553         static const char *start_call_icon;
1554         static const char *add_call_icon;
1555         static const char *search_icon;
1556         static gboolean update_check_menu;
1557         static gboolean buttons_have_borders;
1558         static gboolean show_abcd;
1559         GtkWidget *w=linphone_gtk_get_main_window();
1560         GHashTable *contacts_history;
1561
1562         contacts_history=g_hash_table_new_full(g_str_hash, g_str_equal,g_free, NULL);
1563         g_object_set_data(G_OBJECT(w),"history",(gpointer)contacts_history);
1564
1565         if (!config_loaded){
1566                 title=linphone_gtk_get_ui_config("title","Linphone");
1567                 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1568                 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1569                 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1570                 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1571                 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1572                 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1573                 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1574                 config_loaded=TRUE;
1575         }
1576         linphone_gtk_configure_window(w,"main_window");
1577         if (title) {
1578                 gtk_window_set_title(GTK_WINDOW(w),title);
1579         }
1580         if (start_call_icon){
1581                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1582                                     create_pixmap (start_call_icon));
1583                 if (!buttons_have_borders)
1584                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1585         }
1586         if (add_call_icon){
1587                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1588                                     create_pixmap (add_call_icon));
1589                 if (!buttons_have_borders)
1590                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1591         }
1592         if (search_icon){
1593                 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1594                 if(pbuf != NULL) {
1595                         gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1596                         g_object_unref(G_OBJECT(pbuf));
1597                 }
1598         }
1599         if (home){
1600                 gchar *tmp;
1601                 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1602                 tmp=g_strdup(home);
1603                 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1604         }
1605         {
1606                 /*
1607                 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1608                 if (pbuf) {
1609                         gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1610                         g_object_unref(G_OBJECT(pbuf));
1611                 }
1612                 */
1613         }
1614         {
1615                 GdkPixbuf *pbuf=create_pixbuf("dialer.png");
1616                 if (pbuf) {
1617                         GtkButton *button=GTK_BUTTON(linphone_gtk_get_widget(w,"keypad"));
1618                         gtk_button_set_image(button,gtk_image_new_from_pixbuf (pbuf));
1619                 }
1620         }
1621         if (linphone_gtk_can_manage_accounts()) {
1622                 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1623         }
1624         if (update_check_menu){
1625                 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1626         }
1627         g_object_set_data(G_OBJECT(w),"show_abcd",GINT_TO_POINTER(show_abcd));
1628 }
1629
1630 void linphone_gtk_manage_login(void){
1631         LinphoneCore *lc=linphone_gtk_get_core();
1632         LinphoneProxyConfig *cfg=NULL;
1633         linphone_core_get_default_proxy(lc,&cfg);
1634         if (cfg){
1635                 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1636                 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1637                         linphone_gtk_show_login_frame(cfg);
1638                 }
1639         }
1640 }
1641
1642 gboolean linphone_gtk_close(GtkWidget *mw){
1643         /*shutdown calls if any*/
1644         LinphoneCore *lc=linphone_gtk_get_core();
1645         if (linphone_core_in_call(lc)){
1646                 linphone_core_terminate_all_calls(lc);
1647         }
1648         linphone_core_enable_video_preview(lc,FALSE);
1649 #ifdef __APPLE__ /*until with have a better option*/
1650         gtk_window_iconify(GTK_WINDOW(mw));
1651 #else
1652         gtk_widget_hide(mw);
1653 #endif
1654         return TRUE;
1655 }
1656
1657 #ifdef HAVE_GTK_OSX
1658 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1659         bool_t video_enabled=linphone_gtk_video_enabled();
1660         if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1661                 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1662         }else{
1663                 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1664                 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1665         }
1666         return FALSE;
1667 }
1668 #endif
1669
1670 void linphone_gtk_init_dtmf_table(GtkWidget *mw){
1671         GtkWidget *dtmf_table=linphone_gtk_get_widget(mw,"dtmf_table");
1672         gtk_widget_set_direction(dtmf_table, GTK_TEXT_DIR_LTR);
1673
1674         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_A")),"label","A");
1675         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_B")),"label","B");
1676         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_C")),"label","C");
1677         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_D")),"label","D");
1678         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_1")),"label","1");
1679         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_2")),"label","2");
1680         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_3")),"label","3");
1681         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_4")),"label","4");
1682         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_5")),"label","5");
1683         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_6")),"label","6");
1684         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_7")),"label","7");
1685         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_8")),"label","8");
1686         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_9")),"label","9");
1687         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_0")),"label","0");
1688         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_#")),"label","#");
1689         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_*")),"label","*");
1690 }
1691
1692 void linphone_gtk_create_keypad(GtkWidget *button){
1693         GtkWidget *mw=linphone_gtk_get_main_window();
1694         GtkWidget *k=(GtkWidget *)g_object_get_data(G_OBJECT(mw),"keypad");
1695         if(k!=NULL){
1696                 gtk_widget_destroy(k);
1697         }
1698         GtkWidget *keypad=linphone_gtk_create_window("keypad");
1699         linphone_gtk_connect_digits(keypad);
1700         linphone_gtk_init_dtmf_table(keypad);
1701         g_object_set_data(G_OBJECT(mw),"keypad",(gpointer)keypad);
1702         if(!GPOINTER_TO_INT(g_object_get_data(G_OBJECT(mw),"show_abcd"))){
1703                 gtk_widget_hide(linphone_gtk_get_widget(keypad,"dtmf_A"));
1704                 gtk_widget_hide(linphone_gtk_get_widget(keypad,"dtmf_B"));
1705                 gtk_widget_hide(linphone_gtk_get_widget(keypad,"dtmf_C"));
1706                 gtk_widget_hide(linphone_gtk_get_widget(keypad,"dtmf_D"));
1707                 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(keypad,"dtmf_table")),4,3);
1708         }
1709         gtk_widget_show(keypad);
1710 }
1711
1712 static void linphone_gtk_init_main_window(){
1713         GtkWidget *main_window;
1714         linphone_gtk_configure_main_window();
1715         linphone_gtk_manage_login();
1716         load_uri_history();
1717         linphone_gtk_load_identities();
1718         linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1719         linphone_gtk_show_friends();
1720         main_window=linphone_gtk_get_main_window();
1721         linphone_gtk_call_log_update(main_window);
1722
1723         linphone_gtk_update_call_buttons (NULL);
1724         g_object_set_data(G_OBJECT(main_window),"keypad",NULL);
1725         g_object_set_data(G_OBJECT(main_window),"is_conf",GINT_TO_POINTER(FALSE));
1726         /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1727         g_signal_connect (G_OBJECT (main_window), "delete-event",
1728                 G_CALLBACK (linphone_gtk_close), main_window);
1729 #ifdef HAVE_GTK_OSX
1730         {
1731                 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1732                 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1733                 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1734                 gtk_widget_hide(menubar);
1735                 gtk_osxapplication_ready(theMacApp);
1736         }
1737         g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1738 #endif
1739         linphone_gtk_check_menu_items();
1740 }
1741
1742
1743 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1744         if (verbose){
1745                 const char *lname="undef";
1746                 char *msg;
1747 #if defined(__linux) || defined(__APPLE__)
1748                 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1749 #endif
1750                 switch(lev){
1751                         case ORTP_DEBUG:
1752                                 lname="debug";
1753                                 break;
1754                         case ORTP_MESSAGE:
1755                                 lname="message";
1756                                 break;
1757                         case ORTP_WARNING:
1758                                 lname="warning";
1759                                 break;
1760                         case ORTP_ERROR:
1761                                 lname="error";
1762                                 break;
1763                         case ORTP_FATAL:
1764                                 lname="fatal";
1765                                 break;
1766                         default:
1767                                 g_error("Bad level !");
1768                 }
1769 #if defined(__linux) || defined(__APPLE__)
1770                 va_copy(cap,args);
1771                 msg=g_strdup_vprintf(fmt,cap);
1772                 va_end(cap);
1773 #else
1774                 msg=g_strdup_vprintf(fmt,args);
1775 #endif
1776                 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1777                 ortp_free(msg);
1778         }
1779         linphone_gtk_log_push(lev,fmt,args);
1780 }
1781
1782
1783 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1784         GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1785                 linphone_gtk_get_main_window(), "uribar"));
1786         char *text;
1787         linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1788         g_free(text);
1789         gtk_entry_set_text(uri_bar, refer_to);
1790         linphone_gtk_start_call(linphone_gtk_get_main_window());
1791 }
1792
1793 static void linphone_gtk_check_soundcards(){
1794         const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1795         if (devices==NULL || devices[0]==NULL){
1796                 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1797                         _("No sound cards have been detected on this computer.\n"
1798                                 "You won't be able to send or receive audio calls."));
1799         }
1800 }
1801
1802 static void linphone_gtk_quit(void){
1803         static gboolean quit_done=FALSE;
1804         if (!quit_done){
1805                 quit_done=TRUE;
1806                 linphone_gtk_unmonitor_usb();
1807                 g_source_remove_by_user_data(linphone_gtk_get_core());
1808 #ifdef BUILD_WIZARD
1809                 linphone_gtk_close_assistant();
1810 #endif
1811                 linphone_gtk_uninit_instance();
1812                 linphone_gtk_destroy_log_window();
1813                 linphone_core_destroy(the_core);
1814                 linphone_gtk_log_uninit();
1815 #ifdef HAVE_NOTIFY
1816                 notify_uninit();
1817 #endif
1818                 gdk_threads_leave();
1819         }
1820 }
1821
1822 #ifdef HAVE_GTK_OSX
1823 /*
1824 This is not the correct way to implement block termination.
1825 The good way would be to call gtk_main_quit(), and return TRUE.
1826 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1827 As a result the program cannot exit at all.
1828 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1829 */
1830 static gboolean on_block_termination(void){
1831         gtk_main_quit();
1832         linphone_gtk_quit();
1833         return FALSE;
1834 }
1835 #endif
1836
1837 int main(int argc, char *argv[]){
1838 #ifdef ENABLE_NLS
1839         void *p;
1840 #endif
1841         char *config_file;
1842         const char *factory_config_file;
1843         const char *lang;
1844         GtkSettings *settings;
1845         GdkPixbuf *pbuf;
1846         const char *app_name="Linphone";
1847         LpConfig *factory;
1848
1849 #if !GLIB_CHECK_VERSION(2, 31, 0)
1850         g_thread_init(NULL);
1851 #endif
1852         gdk_threads_init();
1853
1854         progpath = strdup(argv[0]);
1855
1856         config_file=linphone_gtk_get_config_file(NULL);
1857
1858
1859 #ifdef WIN32
1860         /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1861         if ((lang=getenv("LANG"))!=NULL){
1862                 if (atoi(lang)!=0){
1863                         char tmp[128];
1864                         snprintf(tmp,sizeof(tmp),"LANG=",lang);
1865                         _putenv(tmp);
1866                 }
1867         }
1868 #else
1869         /*for pulseaudio:*/
1870         g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1871 #endif
1872
1873         if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1874 #ifdef WIN32
1875                 char tmp[128];
1876                 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1877                 _putenv(tmp);
1878                 if (strncmp(lang,"zh",2)==0){
1879                         workaround_gtk_entry_chinese_bug=TRUE;
1880                 }
1881 #elif __APPLE__
1882                 setenv("LANG",lang,1);
1883 #else
1884                 setenv("LANGUAGE",lang,1);
1885 #endif
1886         }
1887
1888 #ifdef ENABLE_NLS
1889         p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1890         if (p==NULL) perror("bindtextdomain failed");
1891         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1892         textdomain (GETTEXT_PACKAGE);
1893 #else
1894         g_message("NLS disabled.\n");
1895 #endif
1896 #ifdef WIN32
1897         gtk_rc_add_default_file("./gtkrc");
1898 #endif
1899         gdk_threads_enter();
1900
1901         if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1902                                 linphone_options,NULL,NULL)){
1903                 gdk_threads_leave();
1904                 return -1;
1905         }
1906
1907         settings=gtk_settings_get_default();
1908         g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1909         g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1910         g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1911         g_object_set(settings, "gtk-button-images", TRUE, NULL);
1912
1913         if (workingdir!=NULL){
1914                 if (chdir(workingdir)==-1){
1915                         g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1916                 }
1917         }
1918
1919         /* Now, look for the factory configuration file, we do it this late
1920                  since we want to have had time to change directory and to parse
1921                  the options, in case we needed to access the working directory */
1922         factory_config_file = linphone_gtk_get_factory_config_file();
1923         if (factory_config_file){
1924                 factory=lp_config_new(NULL);
1925                 lp_config_read_file(factory,factory_config_file);
1926                 app_name=lp_config_get_string(factory,"GtkUi","title","Linphone");
1927         }
1928
1929         if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1930                 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1931                 g_warning("This instance is going to exit now.");
1932                 gdk_threads_leave();
1933                 return 0;
1934         }
1935
1936         add_pixmap_directory("pixmaps");
1937         add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1938
1939 #ifdef HAVE_GTK_OSX
1940         GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1941         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1942         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1943         /*never block termination:*/
1944         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1945 #endif
1946
1947         the_ui=linphone_gtk_create_window("main");
1948
1949         g_object_set_data(G_OBJECT(the_ui),"is_created",GINT_TO_POINTER(FALSE));
1950
1951         linphone_gtk_create_log_window();
1952         linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1953
1954         linphone_gtk_init_liblinphone(config_file, factory_config_file);
1955
1956         g_set_application_name(app_name);
1957         pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1958         if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1959
1960         /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1961         gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1962         gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1963         linphone_gtk_init_main_window();
1964
1965 #ifdef BUILD_WIZARD
1966         // Veryfing if at least one sip account is configured. If not, show wizard
1967         if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1968                 linphone_gtk_show_assistant();
1969         }
1970 #endif
1971
1972 #ifndef HAVE_GTK_OSX
1973         linphone_gtk_init_status_icon();
1974 #endif
1975         if (!iconified){
1976                 linphone_gtk_show_main_window();
1977                 linphone_gtk_check_soundcards();
1978         }
1979         if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1980                 linphone_gtk_check_for_new_version();
1981         linphone_gtk_monitor_usb();
1982
1983         gtk_main();
1984         linphone_gtk_quit();
1985 #ifndef HAVE_GTK_OSX
1986         /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1987         gtk_status_icon_set_visible(icon,FALSE);
1988 #endif
1989         free(progpath);
1990         return 0;
1991 }
1992