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