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