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