]> sjero.net Git - linphone/blob - gtk/main.c
Defer acceptation of video activation to gather ICE candidates if ICE is enabled.
[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 static void linphone_gtk_status_icon_set_blinking(gboolean val);
69 void _linphone_gtk_enable_video(gboolean val);
70
71
72 static gboolean verbose=0;
73 static gboolean auto_answer = 0;
74 static gchar * addr_to_call = NULL;
75 static gboolean no_video=FALSE;
76 static gboolean iconified=FALSE;
77 static gchar *workingdir=NULL;
78 static char *progpath=NULL;
79 gchar *linphone_logfile=NULL;
80 static gboolean workaround_gtk_entry_chinese_bug=FALSE;
81
82 static GOptionEntry linphone_options[]={
83         {
84                 .long_name="verbose",
85                 .short_name= '\0',
86                 .arg=G_OPTION_ARG_NONE,
87                 .arg_data= (gpointer)&verbose,
88                 .description=N_("log to stdout some debug information while running.")
89         },
90         {
91             .long_name = "logfile",
92             .short_name = 'l',
93             .arg = G_OPTION_ARG_STRING,
94             .arg_data = &linphone_logfile,
95             .description = N_("path to a file to write logs into.")
96         },
97         {
98             .long_name = "no-video",
99             .short_name = '\0',
100             .arg = G_OPTION_ARG_NONE,
101             .arg_data = (gpointer)&no_video,
102             .description = N_("Start linphone with video disabled.")
103         },
104         {
105                 .long_name="iconified",
106                 .short_name= '\0',
107                 .arg=G_OPTION_ARG_NONE,
108                 .arg_data= (gpointer)&iconified,
109                 .description=N_("Start only in the system tray, do not show the main interface.")
110         },
111         {
112             .long_name = "call",
113             .short_name = 'c',
114             .arg = G_OPTION_ARG_STRING,
115             .arg_data = &addr_to_call,
116             .description = N_("address to call right now")
117         },
118         {
119             .long_name = "auto-answer",
120             .short_name = 'a',
121             .arg = G_OPTION_ARG_NONE,
122             .arg_data = (gpointer) & auto_answer,
123             .description = N_("if set automatically answer incoming calls")
124         },
125         {
126             .long_name = "workdir",
127             .short_name = '\0',
128             .arg = G_OPTION_ARG_STRING,
129             .arg_data = (gpointer) & workingdir,
130             .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
131         },
132         {0}
133 };
134
135 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
136 #define RELATIVE_XML_DIR 
137 #define BUILD_TREE_XML_DIR "gtk"
138
139 #ifndef WIN32
140 #define CONFIG_FILE ".linphonerc"
141 #define SECRETS_FILE ".linphone-zidcache"
142 #else
143 #define CONFIG_FILE "linphonerc"
144 #define SECRETS_FILE "linphone-zidcache"
145 #endif
146
147
148 char *linphone_gtk_get_config_file(const char *filename){
149         const int path_max=1024;
150         char *config_file=g_malloc0(path_max);
151         if (filename==NULL) filename=CONFIG_FILE;
152         /*try accessing a local file first if exists*/
153         if (access(CONFIG_FILE,F_OK)==0){
154                 snprintf(config_file,path_max,"%s",filename);
155         }else{
156 #ifdef WIN32
157                 const char *appdata=getenv("APPDATA");
158                 if (appdata){
159                         snprintf(config_file,path_max,"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
160                         CreateDirectory(config_file,NULL);
161                         snprintf(config_file,path_max,"%s\\%s\\%s",appdata,LINPHONE_CONFIG_DIR,filename);
162                 }
163 #else
164                 const char *home=getenv("HOME");
165                 if (home==NULL) home=".";
166                 snprintf(config_file,path_max,"%s/%s",home,filename);
167 #endif
168         }
169         return config_file;
170 }
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         if (!linphone_core_in_call(linphone_gtk_get_core())){
484                 snprintf(video_title,sizeof(video_title),"%s video",title);
485                 /* When not in call, treat the video as a normal window */
486                 gdk_window_set_keep_above(w, FALSE);
487         }else{
488                 LinphoneAddress *uri =
489                         linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
490                 char *display_name;
491
492                 linphone_address_clean(uri);
493                 if (linphone_address_get_display_name(uri)!=NULL){
494                         display_name=ms_strdup(linphone_address_get_display_name(uri));
495                 }else{
496                         display_name=linphone_address_as_string(uri);
497                 }
498                 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
499                 linphone_address_destroy(uri);
500                 ms_free(display_name);
501
502                 /* During calls, bring up the video window, arrange so that
503                 it is above all the other windows */
504                 gdk_window_deiconify(w);
505                 gdk_window_set_keep_above(w,TRUE);
506                 /* Maybe we should have the following, but then we want to
507                 have a timer that turns it off after a little while. */
508                 /* gdk_window_set_urgency_hint(w,TRUE); */
509         }
510         gdk_window_set_title(w,video_title);
511         /* Refrain the video window to be closed at all times. */
512         gdk_window_set_functions(w,
513                                  GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
514                                  GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
515         if (pbuf){
516                 GList *l=NULL;
517                 l=g_list_append(l,pbuf);
518                 gdk_window_set_icon_list(w,l);
519                 g_list_free(l);
520                 g_object_unref(G_OBJECT(pbuf));
521         }
522 }
523
524 static gboolean video_needs_update=FALSE;
525
526 static void update_video_title(){
527         video_needs_update=TRUE;
528 }
529
530 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
531         static gboolean first_time=TRUE;
532         unsigned long id;
533         static unsigned long previd=0;
534         static unsigned long preview_previd=0;
535         static gboolean in_iterate=FALSE;
536
537         /*avoid reentrancy*/
538         if (in_iterate) return TRUE;
539         in_iterate=TRUE;
540         linphone_core_iterate(lc);
541         if (first_time){
542                 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
543                 linphone_gtk_show_directory_search();
544                 first_time=FALSE;
545         }
546
547         id=linphone_core_get_native_video_window_id(lc);
548         if (id!=previd || video_needs_update){
549                 GdkWindow *w;
550                 previd=id;
551                 if (id!=0){
552                         ms_message("Updating window decorations");
553 #ifndef WIN32
554                         w=gdk_window_foreign_new(id);
555 #else
556                         w=gdk_window_foreign_new((HANDLE)id);
557 #endif
558                         if (w) {
559                                 set_video_window_decorations(w);
560                                 g_object_unref(G_OBJECT(w));
561                         }
562                         else ms_error("gdk_window_foreign_new() failed");
563                         if (video_needs_update) video_needs_update=FALSE;
564                 }
565         }
566         id=linphone_core_get_native_preview_window_id (lc);
567         if (id!=preview_previd ){
568                 GdkWindow *w;
569                 preview_previd=id;
570                 if (id!=0){
571                         ms_message("Updating window decorations for preview");
572 #ifndef WIN32
573                         w=gdk_window_foreign_new(id);
574 #else
575                         w=gdk_window_foreign_new((HANDLE)id);
576 #endif
577                         if (w) {
578                                 set_video_window_decorations(w);
579                                 g_object_unref(G_OBJECT(w));
580                         }
581                         else ms_error("gdk_window_foreign_new() failed");
582                         if (video_needs_update) video_needs_update=FALSE;
583                 }
584         }
585         if (addr_to_call!=NULL){
586                 /*make sure we are not showing the login screen*/
587                 GtkWidget *mw=linphone_gtk_get_main_window();
588                 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
589                 if (!GTK_WIDGET_VISIBLE(login_frame)){
590                         GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
591                         gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
592                         addr_to_call=NULL;
593                         linphone_gtk_start_call(uri_bar);
594                 }
595         }
596         in_iterate=FALSE;
597         return TRUE;
598 }
599
600 static void load_uri_history(){
601         GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
602         char key[20];
603         int i;
604         GtkEntryCompletion *gep=gtk_entry_completion_new();
605         GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
606         for (i=0;;i++){
607                 const char *uri;
608                 snprintf(key,sizeof(key),"uri%i",i);
609                 uri=linphone_gtk_get_ui_config(key,NULL);
610                 if (uri!=NULL) {
611                         GtkTreeIter iter;
612                         gtk_list_store_append(model,&iter);
613                         gtk_list_store_set(model,&iter,0,uri,-1);
614                         if (i==0) gtk_entry_set_text(uribar,uri);
615                 }
616                 else break;
617         }
618         gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
619         gtk_entry_completion_set_text_column(gep,0);
620         gtk_entry_set_completion(uribar,gep);
621 }
622
623 static void save_uri_history(){
624         LinphoneCore *lc=linphone_gtk_get_core();
625         LpConfig *cfg=linphone_core_get_config(lc);
626         GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
627         char key[20];
628         int i=0;
629         char *uri=NULL;
630         GtkTreeIter iter;
631         GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
632
633         if (!gtk_tree_model_get_iter_first(model,&iter)) return;
634         do {
635                 gtk_tree_model_get(model,&iter,0,&uri,-1);
636                 if (uri) {
637                         snprintf(key,sizeof(key),"uri%i",i);
638                         lp_config_set_string(cfg,"GtkUi",key,uri);
639                         g_free(uri);
640                 }else break;
641                 i++;
642                 if (i>5) break;
643         }while(gtk_tree_model_iter_next(model,&iter));
644         lp_config_sync(cfg);
645 }
646
647 static void completion_add_text(GtkEntry *entry, const char *text){
648         GtkTreeIter iter;
649         GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
650         
651         if (gtk_tree_model_get_iter_first(model,&iter)){ 
652                 do {
653                         gchar *uri=NULL;
654                         gtk_tree_model_get(model,&iter,0,&uri,-1);
655                         if (uri!=NULL){
656                                 if (strcmp(uri,text)==0) {
657                                         /*remove text */
658                                         gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
659                                         g_free(uri);
660                                         break;
661                                 }
662                                 g_free(uri);
663                         }
664                 }while (gtk_tree_model_iter_next(model,&iter));
665         }
666         /* and prepend it on top of the list */
667         gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
668         gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
669         save_uri_history();
670 }
671
672
673 bool_t linphone_gtk_video_enabled(void){
674         const LinphoneVideoPolicy *vpol=linphone_core_get_video_policy(linphone_gtk_get_core());
675         return vpol->automatically_accept && vpol->automatically_initiate;
676 }
677
678 void linphone_gtk_show_main_window(){
679         GtkWidget *w=linphone_gtk_get_main_window();
680         LinphoneCore *lc=linphone_gtk_get_core();
681         linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
682                 VIDEOSELFVIEW_DEFAULT));
683         gtk_widget_show(w);
684         gtk_window_present(GTK_WINDOW(w));
685 }
686
687 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
688         GtkWidget *mw=linphone_gtk_get_main_window();
689         if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
690             gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
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         gtk_statusbar_push(GTK_STATUSBAR(status_bar),
970                         gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
971                         status);
972 }
973
974 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
975         linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
976 }
977
978 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
979         linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
980 }
981
982 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
983         char richtext[4096];
984         snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
985         linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
986 }
987
988 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
989         GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
990         if (w) linphone_gtk_call_log_update(w);
991         linphone_gtk_call_log_update(linphone_gtk_get_main_window());
992 }
993
994 #ifdef HAVE_NOTIFY
995 static bool_t notify_actions_supported() {
996         bool_t accepts_actions = FALSE;
997         GList *capabilities = notify_get_server_caps();
998         GList *c;
999         if(capabilities != NULL) {
1000                 for(c = capabilities; c != NULL; c = c->next) {
1001                         if(strcmp((char*)c->data, "actions") == 0 ) {
1002                                 accepts_actions = TRUE;
1003                                 break;
1004                         }
1005                 }
1006                 g_list_foreach(capabilities, (GFunc)g_free, NULL);
1007                 g_list_free(capabilities);
1008         }
1009         return accepts_actions;
1010 }
1011
1012 static NotifyNotification* build_notification(const char *title, const char *body){
1013          return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
1014 #ifdef HAVE_NOTIFY1
1015         ,NULL
1016 #endif
1017         );
1018 }
1019
1020 static void show_notification(NotifyNotification* n){
1021         if (n && !notify_notification_show(n,NULL))
1022                 ms_error("Failed to send notification.");
1023 }
1024
1025 static void make_notification(const char *title, const char *body){
1026         show_notification(build_notification(title,body));
1027 }
1028
1029 #endif
1030
1031 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
1032 #ifdef HAVE_NOTIFY
1033         if (!notify_is_initted())
1034                 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
1035 #endif
1036         if (!call) {
1037 #ifdef HAVE_NOTIFY
1038                 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
1039 #ifdef HAVE_NOTIFY1
1040         ,NULL
1041 #endif
1042 ),NULL))
1043                                 ms_error("Failed to send notification.");
1044 #else
1045                 linphone_gtk_show_main_window();
1046 #endif
1047         } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1048 #ifdef HAVE_NOTIFY
1049                 char *body=NULL;
1050                 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1051                 NotifyNotification *n;
1052                 switch(linphone_call_get_state(call)){
1053                         case LinphoneCallError:
1054                                 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1055                         break;
1056                         case LinphoneCallEnd:
1057                                 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1058                         break;
1059                         case LinphoneCallIncomingReceived:
1060                                 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1061                                 if (notify_actions_supported()) {
1062                                         notify_notification_add_action (n,"answer", _("Answer"),
1063                                                 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1064                                         notify_notification_add_action (n,"decline",_("Decline"),
1065                                                 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1066                                 }
1067                                 show_notification(n);
1068                         break;
1069                         case LinphoneCallPausedByRemote:
1070                                 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1071                         break;
1072                         default:
1073                         break;
1074                 }
1075                 if (body) g_free(body);
1076                 if (remote) g_free(remote);
1077 #endif
1078         }
1079 }
1080
1081 static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){
1082         if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1083                 linphone_call_enable_video(call, responseid==GTK_RESPONSE_YES);
1084         }
1085         linphone_call_unref(call);
1086         g_source_remove_by_user_data(dialog);
1087         gtk_widget_destroy(dialog);
1088 }
1089
1090 static void on_call_updated_timeout(GtkWidget *dialog){
1091         gtk_widget_destroy(dialog);
1092 }
1093
1094 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1095         LinphoneCore *lc=linphone_call_get_core(call);
1096         const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1097         const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1098         const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1099         gboolean video_requested=linphone_call_params_video_enabled(rparams);
1100         gboolean video_used=linphone_call_params_video_enabled(current_params);
1101         g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1102                   video_used,video_requested,pol->automatically_accept);
1103         if (video_used==FALSE && video_requested && !pol->automatically_accept){
1104                 linphone_core_defer_call_update(lc,call);
1105                 {
1106                         const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1107                         GtkWidget *dialog;
1108                         const char *dname=linphone_address_get_display_name(addr);
1109                         if (dname==NULL) dname=linphone_address_get_username(addr);
1110                         if (dname==NULL) dname=linphone_address_get_domain(addr);
1111                         dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1112                                                                  GTK_DIALOG_DESTROY_WITH_PARENT,
1113                                                                  GTK_MESSAGE_WARNING,
1114                                                                  GTK_BUTTONS_YES_NO,
1115                                                                  _("%s proposed to start video. Do you accept ?"),dname);
1116                         g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1117                         g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1118                         gtk_widget_show(dialog);
1119                 }
1120         }
1121 }
1122
1123 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1124         switch(cs){
1125                 case LinphoneCallOutgoingInit:
1126                         linphone_gtk_create_in_call_view (call);
1127                 break;
1128                 case LinphoneCallOutgoingProgress:
1129                         linphone_gtk_in_call_view_set_calling (call);
1130                 break;
1131                 case LinphoneCallStreamsRunning:
1132                         linphone_gtk_in_call_view_set_in_call(call);
1133                 break;
1134                 case LinphoneCallUpdatedByRemote:
1135                         linphone_gtk_call_updated_by_remote(call);
1136                 break;
1137                 case LinphoneCallError:
1138                         linphone_gtk_in_call_view_terminate (call,msg);
1139                 break;
1140                 case LinphoneCallEnd:
1141                         linphone_gtk_in_call_view_terminate(call,NULL);
1142                         linphone_gtk_status_icon_set_blinking(FALSE);
1143                 break;
1144                 case LinphoneCallIncomingReceived:
1145                         linphone_gtk_create_in_call_view(call);
1146                         linphone_gtk_in_call_view_set_incoming(call);
1147                         linphone_gtk_status_icon_set_blinking(TRUE);
1148                         if (auto_answer)  {
1149                                 linphone_call_ref(call);
1150                                 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1151                         }               
1152                 break;
1153                 case LinphoneCallResuming:
1154                         linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1155                         linphone_gtk_in_call_view_set_in_call (call);
1156                 break;
1157                 case LinphoneCallPausing:
1158                         linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1159                 case LinphoneCallPausedByRemote:
1160                         linphone_gtk_in_call_view_set_paused(call);
1161                 break;
1162                 case LinphoneCallConnected:
1163                         linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1164                         linphone_gtk_status_icon_set_blinking(FALSE);
1165                 break;
1166                 default:
1167                 break;
1168         }
1169         linphone_gtk_notify(call, msg);
1170         linphone_gtk_update_call_buttons (call);
1171 }
1172
1173 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1174         linphone_gtk_in_call_view_show_encryption(call);
1175 }
1176
1177 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){
1178         linphone_gtk_in_call_view_set_transfer_status(call,cstate);
1179 }
1180
1181 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1182         GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1183         GtkTreeModel *model=gtk_combo_box_get_model(box);
1184         GtkTreeIter iter;
1185         gboolean found=FALSE;
1186         const char *stock_id=NULL;
1187         
1188         if (gtk_tree_model_get_iter_first(model,&iter)){
1189                 gpointer p;
1190                 do{
1191                         gtk_tree_model_get(model,&iter,2,&p,-1);
1192                         if (p==cfg) {
1193                                 found=TRUE;
1194                                 break;
1195                         }
1196                 }while(gtk_tree_model_iter_next(model,&iter));
1197         }
1198         if (!found) {
1199                 g_warning("Could not find proxy config in combo box of identities.");
1200                 return;
1201         }
1202         switch (rs){
1203                 case LinphoneRegistrationOk:
1204                         stock_id=GTK_STOCK_YES;
1205                 break;
1206                 case LinphoneRegistrationProgress:
1207                         stock_id=GTK_STOCK_REFRESH;
1208                 break;
1209                 case LinphoneRegistrationCleared:
1210                         stock_id=NULL;
1211                 break;
1212                 case LinphoneRegistrationFailed:
1213                         stock_id=GTK_STOCK_DIALOG_WARNING;
1214                 break;
1215                 default:
1216                 break;
1217         }
1218         gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1219 }
1220
1221 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, 
1222                                                     LinphoneRegistrationState rs, const char *msg){
1223         switch (rs){
1224                 case LinphoneRegistrationOk:
1225                         if (cfg){
1226                                 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1227                                 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1228                                         linphone_gtk_exit_login_frame();
1229                                 }
1230                         }
1231                 break;
1232                 default:
1233                 break;
1234         }
1235         update_registration_status(cfg,rs);
1236 }
1237
1238 void linphone_gtk_open_browser(const char *url){
1239         /*in gtk 2.16, gtk_show_uri does not work...*/
1240 #ifndef WIN32
1241 #if GTK_CHECK_VERSION(2,18,3)
1242         gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1243 #else
1244         char cl[255];
1245         snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1246         g_spawn_command_line_async(cl,NULL);
1247 #endif
1248 #else /*WIN32*/
1249         ShellExecute(0,"open",url,NULL,NULL,1);
1250 #endif
1251 }
1252
1253 void linphone_gtk_link_to_website(GtkWidget *item){
1254         const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1255         linphone_gtk_open_browser(home);
1256 }
1257
1258 #ifndef HAVE_GTK_OSX
1259
1260 static GtkStatusIcon *icon=NULL;
1261
1262 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1263         GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1264         gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1265 }
1266
1267 static GtkWidget *create_icon_menu(){
1268         GtkWidget *menu=gtk_menu_new();
1269         GtkWidget *menu_item;
1270         GtkWidget *image;
1271         gchar *tmp;
1272         const gchar *homesite;
1273         
1274         homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1275         menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1276         tmp=g_strdup(homesite);
1277         g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1278         g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1279         
1280         image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1281         gtk_widget_show(image);
1282         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1283         //g_object_unref(G_OBJECT(image));
1284         gtk_widget_show(menu_item);
1285         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1286         g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1287         
1288         menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1289         gtk_widget_show(menu_item);
1290         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1291         g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1292         menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1293         gtk_widget_show(menu_item);
1294         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1295         g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1296         gtk_widget_show(menu);
1297         return menu;
1298 }
1299
1300 static void handle_icon_click() {
1301         GtkWidget *mw=linphone_gtk_get_main_window();
1302         if (!gtk_window_is_active((GtkWindow*)mw)) {
1303                 linphone_gtk_show_main_window();
1304         } else {
1305                 gtk_widget_hide(mw);
1306         }
1307 }
1308
1309 static void linphone_gtk_init_status_icon(){
1310         const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1311         const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1312         GdkPixbuf *pbuf=create_pixbuf(icon_path);
1313         GtkWidget *menu=create_icon_menu();
1314         const char *title;
1315         title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1316         icon=gtk_status_icon_new_from_pixbuf(pbuf);
1317 #if GTK_CHECK_VERSION(2,20,0)
1318         gtk_status_icon_set_name(icon,title);
1319 #endif
1320         g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1321         g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1322         gtk_status_icon_set_tooltip(icon,title);
1323         gtk_status_icon_set_visible(icon,TRUE);
1324         g_object_set_data(G_OBJECT(icon),"menu",menu);
1325         g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1326         g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1327         g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1328         pbuf=create_pixbuf(call_icon_path);
1329         g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1330 }
1331
1332 static gboolean do_icon_blink(GtkStatusIcon *gi){
1333         GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1334         GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1335         GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1336         if (cur_icon==call_icon){
1337                 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1338         }else{
1339                 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1340         }
1341         return TRUE;
1342 }
1343
1344 #endif
1345
1346 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1347 #ifdef HAVE_GTK_OSX
1348         static gint attention_id;
1349         GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1350         if (val)
1351                 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1352         else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1353 #else
1354         if (icon!=NULL){
1355                 guint tout;
1356                 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1357                 if (val && tout==0){
1358                         tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1359                         g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1360                 }else if (!val && tout!=0){
1361                         GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1362                         g_source_remove(tout);
1363                         g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1364                         gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1365                 }
1366         }
1367 #endif
1368 }
1369
1370 void linphone_gtk_options_activate(GtkWidget *item){
1371 #ifndef HAVE_GTK_OSX
1372         gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1373                 TRUE);
1374 #endif
1375 }
1376
1377 static void init_identity_combo(GtkComboBox *box){
1378         GtkListStore *store;
1379         GtkCellRenderer *r1,*r2;
1380         store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1381         gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1382         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1383         gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1384         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1385         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1386         g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1387         gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1388 }
1389
1390 void linphone_gtk_load_identities(void){
1391         const MSList *elem;
1392         GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1393         char *def_identity;
1394         LinphoneProxyConfig *def=NULL;
1395         int def_index=0,i;
1396         GtkListStore *store;
1397         GtkTreeIter iter;
1398
1399         store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1400         if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1401                 /* model is empty, this is the first time we go here */
1402                 init_identity_combo(box);
1403                 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1404         }
1405         gtk_list_store_clear(store);
1406         linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1407         def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1408         gtk_list_store_append(store,&iter);
1409         gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1410         g_free(def_identity);
1411         for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1412                         elem!=NULL;
1413                         elem=ms_list_next(elem),i++){
1414                 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1415                 gtk_list_store_append(store,&iter);
1416                 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1417                                    linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1418                                    2,cfg,-1);
1419                 if (cfg==def) {
1420                         def_index=i;
1421                 }
1422         }
1423         gtk_combo_box_set_active(box,def_index);
1424 }
1425
1426 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1427         const char *label=gtk_button_get_label(button);
1428         GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1429         int pos=-1;
1430         gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1431         linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1432         if (linphone_core_in_call(linphone_gtk_get_core())){
1433                 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1434         }
1435 }
1436
1437 static void linphone_gtk_dtmf_released(GtkButton *button){
1438         linphone_core_stop_dtmf (linphone_gtk_get_core());
1439 }
1440
1441 static void linphone_gtk_connect_digits(void){
1442         GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1443         GList *children=gtk_container_get_children(cont);
1444         GList *elem;
1445         for(elem=children;elem!=NULL;elem=elem->next){
1446                 GtkButton *button=GTK_BUTTON(elem->data);
1447                 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1448                 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1449         }
1450 }
1451
1452 static void linphone_gtk_check_menu_items(void){
1453         bool_t video_enabled=linphone_gtk_video_enabled();
1454         bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1455         GtkWidget *selfview_item=linphone_gtk_get_widget(
1456                                         linphone_gtk_get_main_window(),"selfview_item");
1457         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1458                                         linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1459         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1460 }
1461
1462 static gboolean linphone_gtk_can_manage_accounts(){
1463         LinphoneCore *lc=linphone_gtk_get_core();
1464         const MSList *elem;
1465         for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1466                 SipSetup *ss=(SipSetup*)elem->data;
1467                 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1468                         return TRUE;
1469                 }
1470         }
1471         return FALSE;
1472 }
1473
1474 static void linphone_gtk_configure_main_window(){
1475         static gboolean config_loaded=FALSE;
1476         static const char *title;
1477         static const char *home;
1478         static const char *start_call_icon;
1479         static const char *add_call_icon;
1480         static const char *stop_call_icon;
1481         static const char *search_icon;
1482         static gboolean update_check_menu;
1483         static gboolean buttons_have_borders;
1484         static gboolean show_abcd;
1485         GtkWidget *w=linphone_gtk_get_main_window();
1486         if (!config_loaded){
1487                 title=linphone_gtk_get_ui_config("title","Linphone");
1488                 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1489                 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1490                 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1491                 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1492                 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1493                 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1494                 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1495                 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1496                 config_loaded=TRUE;
1497         }
1498         linphone_gtk_configure_window(w,"main_window");
1499         if (title) {
1500                 gtk_window_set_title(GTK_WINDOW(w),title);
1501         }
1502         if (start_call_icon){
1503                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1504                                     create_pixmap (start_call_icon));
1505                 if (!buttons_have_borders)
1506                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1507         }
1508         if (add_call_icon){
1509                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1510                                     create_pixmap (add_call_icon));
1511                 if (!buttons_have_borders)
1512                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1513         }
1514         if (stop_call_icon){
1515                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1516                                     create_pixmap (stop_call_icon));
1517                 if (!buttons_have_borders)
1518                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1519         }
1520         if (search_icon){
1521                 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1522                 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1523                 g_object_unref(G_OBJECT(pbuf));
1524         }
1525         if (home){
1526                 gchar *tmp;
1527                 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1528                 tmp=g_strdup(home);
1529                 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1530         }
1531         {
1532                 /*
1533                 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1534                 if (pbuf) {
1535                         gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1536                         g_object_unref(G_OBJECT(pbuf));
1537                 }
1538                 */
1539         }
1540         {
1541                 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1542                 if (pbuf) {
1543                         GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1544                         int w,h;
1545                         GdkPixbuf *scaled;
1546                         gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1547                         scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1548                         gtk_image_set_from_pixbuf(img,scaled);
1549                         g_object_unref(G_OBJECT(scaled));
1550                         g_object_unref(G_OBJECT(pbuf));
1551                 }
1552         }
1553         if (linphone_gtk_can_manage_accounts()) {
1554                 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1555         }
1556         if (update_check_menu){
1557                 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1558         }
1559         if (!show_abcd){
1560                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1561                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1562                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1563                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1564                 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1565         }
1566 }
1567
1568 void linphone_gtk_manage_login(void){
1569         LinphoneCore *lc=linphone_gtk_get_core();
1570         LinphoneProxyConfig *cfg=NULL;
1571         linphone_core_get_default_proxy(lc,&cfg);
1572         if (cfg){
1573                 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1574                 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1575                         linphone_gtk_show_login_frame(cfg);
1576                 }
1577         }
1578 }
1579
1580
1581 gboolean linphone_gtk_close(GtkWidget *mw){
1582         /*shutdown calls if any*/
1583         LinphoneCore *lc=linphone_gtk_get_core();
1584         if (linphone_core_in_call(lc)){
1585                 linphone_core_terminate_all_calls(lc);
1586         }
1587         linphone_core_enable_video_preview(lc,FALSE);
1588 #ifdef __APPLE__ /*until with have a better option*/
1589         gtk_window_iconify(GTK_WINDOW(mw));
1590 #else
1591         gtk_widget_hide(mw);
1592 #endif
1593         return TRUE;
1594 }
1595
1596 #ifdef HAVE_GTK_OSX
1597 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1598         bool_t video_enabled=linphone_gtk_video_enabled();
1599         if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1600                 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1601         }else{
1602                 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1603                 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1604         }
1605         return FALSE;
1606 }
1607 #endif
1608
1609
1610 static void linphone_gtk_init_main_window(){
1611         GtkWidget *main_window;
1612
1613         linphone_gtk_configure_main_window();
1614         linphone_gtk_manage_login();
1615         load_uri_history();
1616         linphone_gtk_load_identities();
1617         linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1618         linphone_gtk_show_friends();
1619         linphone_gtk_connect_digits();
1620         main_window=linphone_gtk_get_main_window();
1621         linphone_gtk_call_log_update(main_window);
1622         
1623         linphone_gtk_update_call_buttons (NULL);
1624         /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1625         g_signal_connect (G_OBJECT (main_window), "delete-event",
1626                 G_CALLBACK (linphone_gtk_close), main_window);
1627 #ifdef HAVE_GTK_OSX
1628         {
1629                 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1630                 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1631                 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1632                 gtk_widget_hide(menubar);
1633                 gtk_osxapplication_ready(theMacApp);
1634         }
1635         g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1636 #endif
1637         linphone_gtk_check_menu_items();
1638 }
1639
1640
1641 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1642         if (verbose){
1643                 const char *lname="undef";
1644                 char *msg;
1645 #if defined(__linux) || defined(__APPLE__)
1646                 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1647 #endif
1648                 switch(lev){
1649                         case ORTP_DEBUG:
1650                                 lname="debug";
1651                                 break;
1652                         case ORTP_MESSAGE:
1653                                 lname="message";
1654                                 break;
1655                         case ORTP_WARNING:
1656                                 lname="warning";
1657                                 break;
1658                         case ORTP_ERROR:
1659                                 lname="error";
1660                                 break;
1661                         case ORTP_FATAL:
1662                                 lname="fatal";
1663                                 break;
1664                         default:
1665                                 g_error("Bad level !");
1666                 }
1667 #if defined(__linux) || defined(__APPLE__)
1668                 va_copy(cap,args);
1669                 msg=g_strdup_vprintf(fmt,cap);
1670                 va_end(cap);
1671 #else
1672                 msg=g_strdup_vprintf(fmt,args);
1673 #endif
1674                 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1675                 ortp_free(msg);
1676         }
1677         linphone_gtk_log_push(lev,fmt,args);
1678 }
1679
1680
1681 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1682         GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1683                 linphone_gtk_get_main_window(), "uribar"));
1684         char *text;
1685         linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1686         g_free(text);
1687         gtk_entry_set_text(uri_bar, refer_to);
1688         linphone_gtk_start_call(linphone_gtk_get_main_window());
1689 }
1690
1691 static void linphone_gtk_check_soundcards(){
1692         const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1693         if (devices==NULL || devices[0]==NULL){
1694                 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1695                         _("No sound cards have been detected on this computer.\n"
1696                                 "You won't be able to send or receive audio calls."));
1697         }
1698 }
1699
1700 #ifdef BUILD_WIZARD
1701 // Display the account wizard
1702 void linphone_gtk_display_wizard() {
1703         if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1704                 the_wizard = linphone_gtk_create_assistant();
1705         }
1706 }
1707 #endif
1708
1709 static void linphone_gtk_quit(void){
1710         static gboolean quit_done=FALSE;
1711         if (!quit_done){
1712                 quit_done=TRUE;
1713                 linphone_gtk_unmonitor_usb();
1714                 g_source_remove_by_user_data(linphone_gtk_get_core());
1715                 linphone_gtk_uninit_instance();
1716                 linphone_gtk_destroy_log_window();
1717                 linphone_core_destroy(the_core);
1718                 linphone_gtk_log_uninit();
1719 #ifdef HAVE_NOTIFY
1720                 notify_uninit();
1721 #endif
1722                 gdk_threads_leave();
1723         }
1724 }
1725
1726 #ifdef HAVE_GTK_OSX
1727 /*
1728 This is not the correct way to implement block termination.
1729 The good way would be to call gtk_main_quit(), and return TRUE.
1730 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1731 As a result the program cannot exit at all.
1732 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1733 */
1734 static gboolean on_block_termination(void){
1735         gtk_main_quit();
1736         linphone_gtk_quit();
1737         return FALSE;
1738 }
1739 #endif
1740
1741 int main(int argc, char *argv[]){
1742 #ifdef ENABLE_NLS
1743         void *p;
1744 #endif
1745         char *config_file;
1746         const char *factory_config_file;
1747         const char *lang;
1748         GtkSettings *settings;
1749         GdkPixbuf *pbuf;
1750         const char *app_name="Linphone";
1751
1752 #if !GLIB_CHECK_VERSION(2, 31, 0)
1753         g_thread_init(NULL);
1754 #endif
1755         gdk_threads_init();
1756         
1757         progpath = strdup(argv[0]);
1758         
1759         config_file=linphone_gtk_get_config_file(NULL);
1760         
1761
1762 #ifdef WIN32
1763         /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1764         if ((lang=getenv("LANG"))!=NULL){
1765                 if (atoi(lang)!=0){
1766                         char tmp[128];
1767                         snprintf(tmp,sizeof(tmp),"LANG=",lang);
1768                         _putenv(tmp);
1769                 }
1770         }
1771 #else
1772         /*for pulseaudio:*/
1773         g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1774 #endif
1775
1776         if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1777 #ifdef WIN32
1778                 char tmp[128];
1779                 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1780                 _putenv(tmp);
1781                 if (strncmp(lang,"zh",2)==0){
1782                         workaround_gtk_entry_chinese_bug=TRUE;
1783                 }
1784 #else
1785                 setenv("LANG",lang,1);
1786 #endif
1787         }
1788
1789 #ifdef ENABLE_NLS
1790         p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1791         if (p==NULL) perror("bindtextdomain failed");
1792         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1793         textdomain (GETTEXT_PACKAGE);
1794 #else
1795         g_message("NLS disabled.\n");
1796 #endif
1797 #ifdef WIN32
1798         gtk_rc_add_default_file("./gtkrc");
1799 #endif
1800         gdk_threads_enter();
1801         
1802         if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1803                                 linphone_options,NULL,NULL)){
1804                 gdk_threads_leave();
1805                 return -1;
1806         }
1807         
1808         settings=gtk_settings_get_default();
1809         g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1810         g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1811         g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1812         g_object_set(settings, "gtk-button-images", TRUE, NULL);
1813
1814         if (workingdir!=NULL){
1815                 if (chdir(workingdir)==-1){
1816                         g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1817                 }
1818         }
1819
1820         /* Now, look for the factory configuration file, we do it this late
1821                  since we want to have had time to change directory and to parse
1822                  the options, in case we needed to access the working directory */
1823         factory_config_file = linphone_gtk_get_factory_config_file();
1824
1825         if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1826                 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1827                 g_warning("This instance is going to exit now.");
1828                 gdk_threads_leave();
1829                 return 0;
1830         }
1831
1832         add_pixmap_directory("pixmaps");
1833         add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1834
1835 #ifdef HAVE_GTK_OSX
1836         GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1837         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1838         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1839         /*never block termination:*/
1840         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1841 #endif
1842         
1843         the_ui=linphone_gtk_create_window("main");
1844         
1845         linphone_gtk_create_log_window();
1846         linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1847
1848         linphone_gtk_init_liblinphone(config_file, factory_config_file);
1849         
1850         g_set_application_name(app_name);
1851         pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1852         if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1853         
1854         /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1855         gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1856         gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1857         linphone_gtk_init_main_window();
1858
1859 #ifdef BUILD_WIZARD
1860         // Veryfing if at least one sip account is configured. If not, show wizard
1861         if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1862                 linphone_gtk_display_wizard();
1863         }
1864 #endif
1865
1866 #ifndef HAVE_GTK_OSX
1867         linphone_gtk_init_status_icon();
1868 #endif
1869         if (!iconified){
1870                 linphone_gtk_show_main_window();
1871                 linphone_gtk_check_soundcards();
1872         }
1873         if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1874                 linphone_gtk_check_for_new_version();
1875         linphone_gtk_monitor_usb();
1876
1877         gtk_main();
1878         linphone_gtk_quit();
1879 #ifndef HAVE_GTK_OSX
1880         /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1881         gtk_status_icon_set_visible(icon,FALSE);
1882 #endif
1883         free(progpath);
1884         return 0;
1885 }
1886