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