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