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