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