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