]> sjero.net Git - linphone/blob - gtk/main.c
Disabled buttons in conf frame, callview improved, removing the search bar
[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 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 #define FACTORY_CONFIG_FILE "linphonerc.factory"
173 static char _factory_config_file[1024];
174 static const char *linphone_gtk_get_factory_config_file(){
175         /*try accessing a local file first if exists*/
176         if (access(FACTORY_CONFIG_FILE,F_OK)==0){
177                 snprintf(_factory_config_file,sizeof(_factory_config_file),
178                                                  "%s",FACTORY_CONFIG_FILE);
179         } else {
180                 char *progdir;
181
182                 if (progpath != NULL) {
183                         char *basename;
184                         progdir = strdup(progpath);
185 #ifdef WIN32
186                         basename = strrchr(progdir, '\\');
187                         if (basename != NULL) {
188                                 basename ++;
189                                 *basename = '\0';
190                                 snprintf(_factory_config_file, sizeof(_factory_config_file),
191                                                                  "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
192                         } else {
193                                 if (workingdir!=NULL) {
194                                         snprintf(_factory_config_file, sizeof(_factory_config_file),
195                                                                          "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
196                                 } else {
197                                         free(progdir);
198                                         return NULL;
199                                 }
200                         }
201 #else
202                         basename = strrchr(progdir, '/');
203                         if (basename != NULL) {
204                                 basename ++;
205                                 *basename = '\0';
206                                 snprintf(_factory_config_file, sizeof(_factory_config_file),
207                                                                  "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
208                         } else {
209                                 free(progdir);
210                                 return NULL;
211                         }
212 #endif
213                         free(progdir);
214                 }
215         }
216         return _factory_config_file;
217 }
218
219 static void linphone_gtk_init_liblinphone(const char *config_file,
220                 const char *factory_config_file) {
221         LinphoneCoreVTable vtable={0};
222         gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
223
224         vtable.call_state_changed=linphone_gtk_call_state_changed;
225         vtable.registration_state_changed=linphone_gtk_registration_state_changed;
226         vtable.notify_presence_recv=linphone_gtk_notify_recv;
227         vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
228         vtable.auth_info_requested=linphone_gtk_auth_info_requested;
229         vtable.display_status=linphone_gtk_display_status;
230         vtable.display_message=linphone_gtk_display_message;
231         vtable.display_warning=linphone_gtk_display_warning;
232         vtable.display_url=linphone_gtk_display_url;
233         vtable.call_log_updated=linphone_gtk_call_log_updated;
234         vtable.text_received=linphone_gtk_text_received;
235         vtable.refer_received=linphone_gtk_refer_received;
236         vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
237         vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
238         vtable.transfer_state_changed=linphone_gtk_transfer_state_changed;
239
240         the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
241         linphone_core_set_user_agent(the_core,"Linphone", LINPHONE_VERSION);
242         linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
243         linphone_core_set_zrtp_secrets_file(the_core,secrets_file);
244         g_free(secrets_file);
245         linphone_core_enable_video(the_core,TRUE,TRUE);
246         if (no_video) {
247                 _linphone_gtk_enable_video(FALSE);
248                 linphone_gtk_set_ui_config_int("videoselfview",0);
249         }
250 }
251
252 LinphoneCore *linphone_gtk_get_core(void){
253         return the_core;
254 }
255
256 GtkWidget *linphone_gtk_get_main_window(){
257         return the_ui;
258 }
259
260 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
261         static const char *icon_path=NULL;
262         static const char *hiddens=NULL;
263         static const char *shown=NULL;
264         static bool_t config_loaded=FALSE;
265         if (linphone_gtk_get_core()==NULL) return;
266         if (config_loaded==FALSE){
267                 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
268                 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
269                 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
270                 config_loaded=TRUE;
271         }
272         if (hiddens)
273                 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
274         if (shown)
275                 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
276         if (icon_path) {
277                 GdkPixbuf *pbuf=create_pixbuf(icon_path);
278                 if(pbuf != NULL) {
279                         gtk_window_set_icon(GTK_WINDOW(w),pbuf);
280                         g_object_unref(G_OBJECT(pbuf));
281                 }
282         }
283 }
284
285 static int get_ui_file(const char *name, char *path, int pathsize){
286         snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
287         if (access(path,F_OK)!=0){
288                 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
289                 if (access(path,F_OK)!=0){
290                         g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
291                                 INSTALLED_XML_DIR,name);
292                         return -1;
293                 }
294         }
295         return 0;
296 }
297
298 GtkWidget *linphone_gtk_create_window(const char *window_name){
299         GError* error = NULL;
300         GtkBuilder* builder = gtk_builder_new ();
301         char path[512];
302         GtkWidget *w;
303
304         if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
305
306         if (!gtk_builder_add_from_file (builder, path, &error)){
307                 g_error("Couldn't load builder file: %s", error->message);
308                 g_error_free (error);
309                 return NULL;
310         }
311         w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
312         if (w==NULL){
313                 g_error("Could not retrieve '%s' window from xml file",window_name);
314                 return NULL;
315         }
316         g_object_set_data(G_OBJECT(w),"builder",builder);
317         gtk_builder_connect_signals(builder,w);
318         linphone_gtk_configure_window(w,window_name);
319         return w;
320 }
321
322 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
323         char path[2048];
324         GtkWidget *w;
325         GtkBuilder* builder = gtk_builder_new ();
326         GError *error=NULL;
327         gchar *object_ids[2];
328         object_ids[0]=g_strdup(widget_name);
329         object_ids[1]=NULL;
330
331         if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
332         if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
333                 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
334                 g_error_free (error);
335                 g_free(object_ids[0]);
336                 return NULL;
337         }
338         g_free(object_ids[0]);
339         w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
340         if (w==NULL){
341                 g_error("Could not retrieve '%s' window from xml file",widget_name);
342                 return NULL;
343         }
344         g_object_set_data(G_OBJECT(w),"builder",builder);
345         g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
346         gtk_builder_connect_signals(builder,w);
347         return w;
348 }
349
350 static void entry_unmapped(GtkWidget *entry){
351         g_message("Entry is unmapped, calling unrealize to workaround chinese bug.");
352         gtk_widget_unrealize(entry);
353 }
354
355 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
356         GtkBuilder *builder;
357         GObject *w;
358         if (window==NULL) return NULL;
359         builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
360         if (builder==NULL){
361                 g_error("Fail to retrieve builder from window !");
362                 return NULL;
363         }
364         w=gtk_builder_get_object(builder,name);
365         if (w==NULL){
366                 g_error("No widget named %s found in xml interface.",name);
367         }
368         if (workaround_gtk_entry_chinese_bug){
369                 if (strcmp(G_OBJECT_TYPE_NAME(w),"GtkEntry")==0){
370                         if (g_object_get_data(G_OBJECT(w),"entry_bug_workaround")==NULL){
371                                 g_object_set_data(G_OBJECT(w),"entry_bug_workaround",GINT_TO_POINTER(1));
372                                 g_message("%s is a GtkEntry",name);
373                                 g_signal_connect(G_OBJECT(w),"unmap",(GCallback)entry_unmapped,NULL);
374                         }
375                 }
376         }
377         return GTK_WIDGET(w);
378 }
379
380
381 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
382         GtkWidget *dialog;
383         GtkWidget *main_window=linphone_gtk_get_main_window();
384
385         gtk_widget_show(main_window);
386         if (type==GTK_MESSAGE_QUESTION)
387         {
388                 /* draw a question box. link to dialog_click callback */
389                 dialog = gtk_message_dialog_new (
390                                 GTK_WINDOW(main_window),
391                                 GTK_DIALOG_DESTROY_WITH_PARENT,
392                                 GTK_MESSAGE_QUESTION,
393                                 GTK_BUTTONS_YES_NO,
394                                 "%s",
395                                 (const gchar*)message);
396                 /* connect to some callback : REVISIT */
397                 /*
398                 g_signal_connect_swapped (G_OBJECT (dialog), "response",
399                            G_CALLBACK (dialog_click),
400                            G_OBJECT (dialog));
401                 */
402                 /* actually show the box */
403                 gtk_widget_show(dialog);
404         }
405         else
406         {
407                 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
408                                   GTK_DIALOG_DESTROY_WITH_PARENT,
409                                   type,
410                                   GTK_BUTTONS_CLOSE,
411                                   "%s",
412                                   (const gchar*)message);
413                 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
414                 g_signal_connect_swapped (G_OBJECT (dialog), "response",
415                            G_CALLBACK (gtk_widget_destroy),
416                            G_OBJECT (dialog));
417                 gtk_widget_show(dialog);
418         }
419 }
420
421 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
422         if (id==GTK_RESPONSE_CANCEL){
423                 gtk_widget_destroy(GTK_WIDGET(dialog));
424         }
425 }
426
427 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
428         g_message("About url clicked");
429         linphone_gtk_open_browser(url);
430 }
431
432 void linphone_gtk_show_about(){
433         struct stat filestat;
434         const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
435         GtkWidget *about;
436         const char *tmp;
437         GdkPixbuf *logo=create_pixbuf(
438             linphone_gtk_get_ui_config("logo","linphone-banner.png"));
439         static const char *defcfg="defcfg";
440
441         about=linphone_gtk_create_window("about");
442         gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
443         memset(&filestat,0,sizeof(filestat));
444         if (stat(license_file,&filestat)!=0){
445                 license_file="COPYING";
446                 stat(license_file,&filestat);
447         }
448         if (filestat.st_size>0){
449                 char *license=g_malloc(filestat.st_size+1);
450                 FILE *f=fopen(license_file,"r");
451                 if (f && fread(license,filestat.st_size,1,f)==1){
452                         license[filestat.st_size]='\0';
453                         gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
454                 }
455                 g_free(license);
456         }
457         gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
458         gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
459         gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
460         if (logo)       gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
461         tmp=linphone_gtk_get_ui_config("artists",defcfg);
462         if (tmp!=defcfg){
463                 const char *tmp2[2];
464                 tmp2[0]=tmp;
465                 tmp2[1]=NULL;
466                 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
467         }
468         tmp=linphone_gtk_get_ui_config("translators",defcfg);
469         if (tmp!=defcfg)
470                 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
471         tmp=linphone_gtk_get_ui_config("comments",defcfg);
472         if (tmp!=defcfg)
473                 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
474         gtk_widget_show(about);
475 }
476
477 static void set_video_window_decorations(GdkWindow *w){
478         const char *title=linphone_gtk_get_ui_config("title","Linphone");
479         const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
480         char video_title[256];
481         GdkPixbuf *pbuf=create_pixbuf(icon_path);
482
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 bool_t linphone_gtk_video_enabled(void){
673         const LinphoneVideoPolicy *vpol=linphone_core_get_video_policy(linphone_gtk_get_core());
674         return vpol->automatically_accept && vpol->automatically_initiate;
675 }
676
677 void linphone_gtk_show_main_window(){
678         GtkWidget *w=linphone_gtk_get_main_window();
679         LinphoneCore *lc=linphone_gtk_get_core();
680         linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
681                 VIDEOSELFVIEW_DEFAULT));
682         gtk_widget_show(w);
683         gtk_window_present(GTK_WINDOW(w));
684 }
685
686 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
687         GtkWidget *mw=linphone_gtk_get_main_window();
688         if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
689             gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
690         }
691         if (linphone_gtk_use_in_call_view() && call)
692                 linphone_gtk_in_call_view_terminate(call,error);
693         update_video_title();
694 }
695
696 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
697         LinphoneCore *lc=linphone_gtk_get_core();
698         GtkWidget *mw=linphone_gtk_get_main_window();
699         const MSList *calls=linphone_core_get_calls(lc);
700         GtkWidget *button;
701         bool_t start_active=TRUE;
702         //bool_t stop_active=FALSE;
703         bool_t add_call=FALSE;
704         int call_list_size=ms_list_size(calls);
705
706         if (calls==NULL){
707                 start_active=TRUE;
708                 //stop_active=FALSE;
709         }else{
710                 //stop_active=TRUE;
711                 start_active=TRUE;
712                 add_call=TRUE;
713         }
714         button=linphone_gtk_get_widget(mw,"start_call");
715         gtk_widget_set_sensitive(button,start_active);
716         gtk_widget_set_visible(button,!add_call);
717
718         button=linphone_gtk_get_widget(mw,"add_call");
719         if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
720                 gtk_widget_set_sensitive(button,FALSE);
721         } else {
722                 gtk_widget_set_sensitive(button,start_active);
723         }
724         gtk_widget_set_visible(button,add_call);
725
726         //gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
727         GtkWidget *conf_frame=(GtkWidget *)g_object_get_data(G_OBJECT(mw),"conf_frame");
728         if(conf_frame==NULL){
729                 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
730                 linphone_gtk_enable_conference_button(lc,call_list_size>1);
731         } else {
732                 linphone_gtk_enable_transfer_button(lc,FALSE);
733                 linphone_gtk_enable_conference_button(lc,FALSE);
734         }
735         update_video_title();
736         if (call) linphone_gtk_update_video_button(call);
737 }
738
739 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
740         const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
741         if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
742                 completion_add_text(GTK_ENTRY(uri_bar),entered);
743         }else{
744                 linphone_gtk_call_terminated(NULL,NULL);
745         }
746         return FALSE;
747 }
748
749 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
750         if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
751                 linphone_core_accept_call (linphone_gtk_get_core(),call);
752                 linphone_call_unref(call);
753         }
754         return FALSE;
755 }
756
757 void linphone_gtk_start_call(GtkWidget *w){
758         LinphoneCore *lc=linphone_gtk_get_core();
759         LinphoneCall *call;
760         /*change into in-call mode, then do the work later as it might block a bit */
761         GtkWidget *mw=gtk_widget_get_toplevel(w);
762         GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
763
764         call=linphone_gtk_get_currently_displayed_call(NULL);
765         if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
766                 linphone_core_accept_call(lc,call);
767         }else{
768                 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
769                 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
770                 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
771                 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
772         }
773
774 }
775
776 void linphone_gtk_uri_bar_activate(GtkWidget *w){
777         linphone_gtk_start_call(w);
778 }
779
780 void linphone_gtk_terminate_call(GtkWidget *button){
781         gboolean is_conf;
782         LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
783         if (call){
784                 linphone_core_terminate_call(linphone_gtk_get_core(),call);
785         }else if (is_conf){
786                 linphone_core_terminate_conference(linphone_gtk_get_core());
787         }
788 }
789
790 void linphone_gtk_decline_clicked(GtkWidget *button){
791         LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
792         if (call)
793                 linphone_core_terminate_call(linphone_gtk_get_core(),call);
794 }
795
796 void linphone_gtk_answer_clicked(GtkWidget *button){
797         LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
798         if (call){
799                 linphone_core_accept_call(linphone_gtk_get_core(),call);
800                 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
801         }
802 }
803
804 void _linphone_gtk_enable_video(gboolean val){
805         LinphoneVideoPolicy policy={0};
806         policy.automatically_initiate=policy.automatically_accept=val;
807         linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE);
808         linphone_core_set_video_policy(linphone_gtk_get_core(),&policy);
809
810         if (val){
811                 linphone_core_enable_video_preview(linphone_gtk_get_core(),
812                 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
813         }else{
814                 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
815         }
816 }
817
818 void linphone_gtk_enable_video(GtkWidget *w){
819         gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
820         //GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
821         _linphone_gtk_enable_video(val);
822 }
823
824 void linphone_gtk_enable_self_view(GtkWidget *w){
825         gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
826         LinphoneCore *lc=linphone_gtk_get_core();
827         linphone_core_enable_video_preview(lc,val);
828         linphone_core_enable_self_view(lc,val);
829         linphone_gtk_set_ui_config_int("videoselfview",val);
830 }
831
832 void linphone_gtk_used_identity_changed(GtkWidget *w){
833         int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
834         char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
835         if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
836                 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
837                 linphone_gtk_show_directory_search();
838         }
839         if (sel) g_free(sel);
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 static void auth_timeout_clean(AuthTimeout *tout){
896         tout->w=NULL;
897 }
898
899 static gboolean auth_timeout_destroy(AuthTimeout *tout){
900         if (tout->w)  {
901                 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
902                 gtk_widget_destroy(tout->w);
903         }
904         g_free(tout);
905         return FALSE;
906 }
907
908 static AuthTimeout * auth_timeout_new(GtkWidget *w){
909         AuthTimeout *tout=g_new(AuthTimeout,1);
910         tout->w=w;
911         /*so that the timeout no more references the widget when it is destroyed:*/
912         g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
913         /*so that the widget is automatically destroyed after some time */
914         g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
915         return tout;
916 }
917
918 void linphone_gtk_password_cancel(GtkWidget *w){
919         LinphoneAuthInfo *info;
920         GtkWidget *window=gtk_widget_get_toplevel(w);
921         info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
922         linphone_core_abort_authentication(linphone_gtk_get_core(),info);
923         gtk_widget_destroy(window);
924 }
925
926 void linphone_gtk_password_ok(GtkWidget *w){
927         GtkWidget *entry;
928         GtkWidget *window=gtk_widget_get_toplevel(w);
929         LinphoneAuthInfo *info;
930         info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
931         g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
932         entry=linphone_gtk_get_widget(window,"password_entry");
933         linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
934         linphone_auth_info_set_userid(info,
935                 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
936         linphone_core_add_auth_info(linphone_gtk_get_core(),info);
937         gtk_widget_destroy(window);
938 }
939
940 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
941         GtkWidget *w=linphone_gtk_create_window("password");
942         GtkWidget *label=linphone_gtk_get_widget(w,"message");
943         LinphoneAuthInfo *info;
944         gchar *msg;
945         GtkWidget *mw=linphone_gtk_get_main_window();
946
947         if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
948                 /*don't prompt for authentication when login frame is visible*/
949                 linphone_core_abort_authentication(lc,NULL);
950                 return;
951         }
952
953         msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
954                 username,realm);
955         gtk_label_set_markup(GTK_LABEL(label),msg);
956         g_free(msg);
957         gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
958         info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
959         g_object_set_data(G_OBJECT(w),"auth_info",info);
960         g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
961         gtk_widget_show(w);
962         auth_timeout_new(w);
963 }
964
965 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
966         GtkWidget *w=linphone_gtk_get_main_window();
967         GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
968
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 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
1038 #ifdef HAVE_NOTIFY
1039                 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
1040 #ifdef HAVE_NOTIFY1
1041         ,NULL
1042 #endif
1043 ),NULL))
1044
1045                                 ms_error("Failed to send notification.");
1046 #else
1047                 linphone_gtk_show_main_window();
1048 #endif
1049         } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1050 #ifdef HAVE_NOTIFY
1051                 char *body=NULL;
1052                 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1053                 NotifyNotification *n;
1054                 switch(linphone_call_get_state(call)){
1055                         case LinphoneCallError:
1056                                 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1057                         break;
1058                         case LinphoneCallEnd:
1059                                 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1060                         break;
1061                         case LinphoneCallIncomingReceived:
1062                                 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1063                                 if (notify_actions_supported()) {
1064                                         notify_notification_add_action (n,"answer", _("Answer"),
1065                                                 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1066                                         notify_notification_add_action (n,"decline",_("Decline"),
1067                                                 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1068                                 }
1069                                 show_notification(n);
1070                         break;
1071                         case LinphoneCallPausedByRemote:
1072                                 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1073                         break;
1074                         default:
1075                         break;
1076                 }
1077                 if (body) g_free(body);
1078                 if (remote) g_free(remote);
1079 #endif
1080         }
1081 }
1082
1083 static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){
1084         if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1085                 LinphoneCore *lc=linphone_call_get_core(call);
1086                 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
1087                 linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES);
1088                 linphone_core_accept_call_update(lc,call,params);
1089                 linphone_call_params_destroy(params);
1090         }
1091         linphone_call_unref(call);
1092         g_source_remove_by_user_data(dialog);
1093         gtk_widget_destroy(dialog);
1094 }
1095
1096 static void on_call_updated_timeout(GtkWidget *dialog){
1097         gtk_widget_destroy(dialog);
1098 }
1099
1100 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1101         LinphoneCore *lc=linphone_call_get_core(call);
1102         const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1103         const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1104         const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1105         gboolean video_requested=linphone_call_params_video_enabled(rparams);
1106         gboolean video_used=linphone_call_params_video_enabled(current_params);
1107         g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1108                   video_used,video_requested,pol->automatically_accept);
1109         if (video_used==FALSE && video_requested && !pol->automatically_accept){
1110                 linphone_core_defer_call_update(lc,call);
1111                 {
1112                         const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1113                         GtkWidget *dialog;
1114                         const char *dname=linphone_address_get_display_name(addr);
1115                         if (dname==NULL) dname=linphone_address_get_username(addr);
1116                         if (dname==NULL) dname=linphone_address_get_domain(addr);
1117                         dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1118                                                                  GTK_DIALOG_DESTROY_WITH_PARENT,
1119                                                                  GTK_MESSAGE_WARNING,
1120                                                                  GTK_BUTTONS_YES_NO,
1121                                                                  _("%s proposed to start video. Do you accept ?"),dname);
1122                         g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1123                         g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1124                         gtk_widget_show(dialog);
1125                 }
1126         }
1127 }
1128
1129 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1130         switch(cs){
1131                 case LinphoneCallOutgoingInit:
1132                         linphone_gtk_create_in_call_view (call);
1133                 break;
1134                 case LinphoneCallOutgoingProgress:
1135                         linphone_gtk_in_call_view_set_calling (call);
1136                 break;
1137                 case LinphoneCallStreamsRunning:
1138                         linphone_gtk_in_call_view_set_in_call(call);
1139                 break;
1140                 case LinphoneCallUpdatedByRemote:
1141                         linphone_gtk_call_updated_by_remote(call);
1142                 break;
1143                 case LinphoneCallError:
1144                         linphone_gtk_in_call_view_terminate (call,msg);
1145                 break;
1146                 case LinphoneCallEnd:
1147                         linphone_gtk_in_call_view_terminate(call,NULL);
1148                         linphone_gtk_status_icon_set_blinking(FALSE);
1149                 break;
1150                 case LinphoneCallIncomingReceived:
1151                         linphone_gtk_create_in_call_view(call);
1152                         linphone_gtk_in_call_view_set_incoming(call);
1153                         linphone_gtk_status_icon_set_blinking(TRUE);
1154                         if (auto_answer)  {
1155                                 linphone_call_ref(call);
1156                                 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1157                         }
1158                 break;
1159                 case LinphoneCallResuming:
1160                         linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1161                         linphone_gtk_in_call_view_set_in_call (call);
1162                 break;
1163                 case LinphoneCallPausing:
1164                         linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1165                         update_tab_header(call,FALSE);
1166                 case LinphoneCallPausedByRemote:
1167                         linphone_gtk_in_call_view_set_paused(call);
1168                         update_tab_header(call,TRUE);
1169                 break;
1170                 case LinphoneCallConnected:
1171                         linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1172                         linphone_gtk_status_icon_set_blinking(FALSE);
1173                 break;
1174                 default:
1175                 break;
1176         }
1177         linphone_gtk_notify(call, msg);
1178         linphone_gtk_update_call_buttons (call);
1179 }
1180
1181 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1182         linphone_gtk_in_call_view_show_encryption(call);
1183 }
1184
1185 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){
1186         linphone_gtk_in_call_view_set_transfer_status(call,cstate);
1187 }
1188
1189 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1190         GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1191         GtkTreeModel *model=gtk_combo_box_get_model(box);
1192         GtkTreeIter iter;
1193         gboolean found=FALSE;
1194         const char *stock_id=NULL;
1195
1196         if (gtk_tree_model_get_iter_first(model,&iter)){
1197                 gpointer p;
1198                 do{
1199                         gtk_tree_model_get(model,&iter,2,&p,-1);
1200                         if (p==cfg) {
1201                                 found=TRUE;
1202                                 break;
1203                         }
1204                 }while(gtk_tree_model_iter_next(model,&iter));
1205         }
1206         if (!found) {
1207                 g_warning("Could not find proxy config in combo box of identities.");
1208                 return;
1209         }
1210         switch (rs){
1211                 case LinphoneRegistrationOk:
1212                         stock_id=GTK_STOCK_YES;
1213                 break;
1214                 case LinphoneRegistrationProgress:
1215                         stock_id=GTK_STOCK_REFRESH;
1216                 break;
1217                 case LinphoneRegistrationCleared:
1218                         stock_id=NULL;
1219                 break;
1220                 case LinphoneRegistrationFailed:
1221                         stock_id=GTK_STOCK_DIALOG_WARNING;
1222                 break;
1223                 default:
1224                 break;
1225         }
1226         gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1227 }
1228
1229 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1230                                                     LinphoneRegistrationState rs, const char *msg){
1231         switch (rs){
1232                 case LinphoneRegistrationOk:
1233                         if (cfg){
1234                                 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1235                                 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1236                                         linphone_gtk_exit_login_frame();
1237                                 }
1238                         }
1239                 break;
1240                 default:
1241                 break;
1242         }
1243         update_registration_status(cfg,rs);
1244 }
1245
1246 void linphone_gtk_open_browser(const char *url){
1247         /*in gtk 2.16, gtk_show_uri does not work...*/
1248 #ifndef WIN32
1249 #if GTK_CHECK_VERSION(2,18,3)
1250         gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1251 #else
1252         char cl[255];
1253         snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1254         g_spawn_command_line_async(cl,NULL);
1255 #endif
1256 #else /*WIN32*/
1257         ShellExecute(0,"open",url,NULL,NULL,1);
1258 #endif
1259 }
1260
1261 void linphone_gtk_link_to_website(GtkWidget *item){
1262         const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1263         linphone_gtk_open_browser(home);
1264 }
1265
1266 #ifndef HAVE_GTK_OSX
1267
1268 static GtkStatusIcon *icon=NULL;
1269
1270 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1271         GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1272         gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1273 }
1274
1275 static GtkWidget *create_icon_menu(){
1276         GtkWidget *menu=gtk_menu_new();
1277         GtkWidget *menu_item;
1278         GtkWidget *image;
1279         gchar *tmp;
1280         const gchar *homesite;
1281
1282         homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1283         menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1284         tmp=g_strdup(homesite);
1285         g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1286         g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1287
1288         image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1289         gtk_widget_show(image);
1290         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1291         //g_object_unref(G_OBJECT(image));
1292         gtk_widget_show(menu_item);
1293         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1294         g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1295
1296         menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1297         gtk_widget_show(menu_item);
1298         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1299         g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1300         menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1301         gtk_widget_show(menu_item);
1302         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1303         g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1304         gtk_widget_show(menu);
1305         return menu;
1306 }
1307
1308 static void handle_icon_click() {
1309         GtkWidget *mw=linphone_gtk_get_main_window();
1310         if (!gtk_window_is_active((GtkWindow*)mw)) {
1311                 linphone_gtk_show_main_window();
1312         } else {
1313                 gtk_widget_hide(mw);
1314         }
1315 }
1316
1317 static void linphone_gtk_init_status_icon(){
1318         const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1319         const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1320         GdkPixbuf *pbuf=create_pixbuf(icon_path);
1321         GtkWidget *menu=create_icon_menu();
1322         const char *title;
1323         title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1324         icon=gtk_status_icon_new_from_pixbuf(pbuf);
1325 #if GTK_CHECK_VERSION(2,20,0)
1326         gtk_status_icon_set_name(icon,title);
1327 #endif
1328         g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1329         g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1330         gtk_status_icon_set_tooltip(icon,title);
1331         gtk_status_icon_set_visible(icon,TRUE);
1332         g_object_set_data(G_OBJECT(icon),"menu",menu);
1333         g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1334         g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1335         g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1336         pbuf=create_pixbuf(call_icon_path);
1337         g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1338 }
1339
1340 static gboolean do_icon_blink(GtkStatusIcon *gi){
1341         GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1342         GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1343         GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1344         if (cur_icon==call_icon){
1345                 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1346         }else{
1347                 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1348         }
1349         return TRUE;
1350 }
1351
1352 #endif
1353
1354 void linphone_gtk_status_icon_set_blinking(gboolean val){
1355 #ifdef HAVE_GTK_OSX
1356         static gint attention_id;
1357         GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1358         if (val)
1359                 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1360         else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1361 #else
1362         if (icon!=NULL){
1363                 guint tout;
1364                 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1365                 if (val && tout==0){
1366                         tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1367                         g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1368                 }else if (!val && tout!=0){
1369                         GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1370                         g_source_remove(tout);
1371                         g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1372                         gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1373                 }
1374         }
1375 #endif
1376 }
1377
1378 void linphone_gtk_options_activate(GtkWidget *item){
1379 #ifndef HAVE_GTK_OSX
1380         gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1381                 TRUE);
1382 #endif
1383 }
1384
1385 static void init_identity_combo(GtkComboBox *box){
1386         GtkListStore *store;
1387         GtkCellRenderer *r1,*r2;
1388         store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1389         gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1390         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1391         gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1392         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1393         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1394         g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1395         gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1396 }
1397
1398 void linphone_gtk_load_identities(void){
1399         const MSList *elem;
1400         GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1401         char *def_identity;
1402         LinphoneProxyConfig *def=NULL;
1403         int def_index=0,i;
1404         GtkListStore *store;
1405         GtkTreeIter iter;
1406
1407         store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1408         if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1409                 /* model is empty, this is the first time we go here */
1410                 init_identity_combo(box);
1411                 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1412         }
1413         gtk_list_store_clear(store);
1414         linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1415         def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1416         gtk_list_store_append(store,&iter);
1417         gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1418         g_free(def_identity);
1419         for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1420                         elem!=NULL;
1421                         elem=ms_list_next(elem),i++){
1422                 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1423                 gtk_list_store_append(store,&iter);
1424                 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1425                                    linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1426                                    2,cfg,-1);
1427                 if (cfg==def) {
1428                         def_index=i;
1429                 }
1430         }
1431         gtk_combo_box_set_active(box,def_index);
1432 }
1433
1434 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1435         const char *label=(char *)g_object_get_data(G_OBJECT(button),"label");
1436         GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1437         int pos=-1;
1438         gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1439         linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1440         if (linphone_core_in_call(linphone_gtk_get_core())){
1441                 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1442         }
1443 }
1444
1445 static void linphone_gtk_dtmf_released(GtkButton *button){
1446         linphone_core_stop_dtmf (linphone_gtk_get_core());
1447 }
1448
1449
1450 static void linphone_gtk_connect_digits(void){
1451         GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1452         GList *children=gtk_container_get_children(cont);
1453         GList *elem;
1454         for(elem=children;elem!=NULL;elem=elem->next){
1455                 GtkButton *button=GTK_BUTTON(elem->data);
1456                 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1457                 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1458         }
1459 }
1460
1461 static void linphone_gtk_check_menu_items(void){
1462         bool_t video_enabled=linphone_gtk_video_enabled();
1463         bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1464         GtkWidget *selfview_item=linphone_gtk_get_widget(
1465                                         linphone_gtk_get_main_window(),"selfview_item");
1466         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1467                                         linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1468         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1469 }
1470
1471 static gboolean linphone_gtk_can_manage_accounts(){
1472         LinphoneCore *lc=linphone_gtk_get_core();
1473         const MSList *elem;
1474         for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1475                 SipSetup *ss=(SipSetup*)elem->data;
1476                 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1477                         return TRUE;
1478                 }
1479         }
1480         return FALSE;
1481 }
1482
1483 static void linphone_gtk_configure_main_window(){
1484         static gboolean config_loaded=FALSE;
1485         static const char *title;
1486         static const char *home;
1487         static const char *start_call_icon;
1488         static const char *add_call_icon;
1489         //static const char *stop_call_icon;
1490         static const char *search_icon;
1491         static gboolean update_check_menu;
1492         static gboolean buttons_have_borders;
1493         static gboolean show_abcd;
1494         GtkWidget *w=linphone_gtk_get_main_window();
1495         GHashTable *contacts_history;
1496
1497         contacts_history=g_hash_table_new_full(g_str_hash, g_str_equal,g_free, NULL);
1498         g_object_set_data(G_OBJECT(w),"history",(gpointer)contacts_history);
1499
1500         if (!config_loaded){
1501                 title=linphone_gtk_get_ui_config("title","Linphone");
1502                 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1503                 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1504                 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1505                 //stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-small.png");
1506                 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1507                 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1508                 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1509                 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1510                 config_loaded=TRUE;
1511         }
1512         linphone_gtk_configure_window(w,"main_window");
1513         if (title) {
1514                 gtk_window_set_title(GTK_WINDOW(w),title);
1515         }
1516         if (start_call_icon){
1517                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1518                                     create_pixmap (start_call_icon));
1519                 if (!buttons_have_borders)
1520                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1521         }
1522         if (add_call_icon){
1523                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1524                                     create_pixmap (add_call_icon));
1525                 if (!buttons_have_borders)
1526                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1527         }
1528         if (search_icon){
1529                 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1530                 if(pbuf != NULL) {
1531                         gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1532                         g_object_unref(G_OBJECT(pbuf));
1533                 }
1534         }
1535         if (home){
1536                 gchar *tmp;
1537                 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1538                 tmp=g_strdup(home);
1539                 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1540         }
1541         {
1542                 /*
1543                 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1544                 if (pbuf) {
1545                         gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1546                         g_object_unref(G_OBJECT(pbuf));
1547                 }
1548                 */
1549         }
1550         {
1551                 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1552                 if (pbuf) {
1553                         GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1554                         int w,h;
1555                         GdkPixbuf *scaled;
1556                         gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1557                         scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1558                         gtk_image_set_from_pixbuf(img,scaled);
1559                         g_object_unref(G_OBJECT(scaled));
1560                         g_object_unref(G_OBJECT(pbuf));
1561                 }
1562         }
1563         if (linphone_gtk_can_manage_accounts()) {
1564                 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1565         }
1566         if (update_check_menu){
1567                 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1568         }
1569         if (!show_abcd){
1570                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1571                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1572                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1573                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1574                 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1575         }
1576 }
1577
1578 void linphone_gtk_manage_login(void){
1579         LinphoneCore *lc=linphone_gtk_get_core();
1580         LinphoneProxyConfig *cfg=NULL;
1581         linphone_core_get_default_proxy(lc,&cfg);
1582         if (cfg){
1583                 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1584                 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1585                         linphone_gtk_show_login_frame(cfg);
1586                 }
1587         }
1588 }
1589
1590
1591 gboolean linphone_gtk_close(GtkWidget *mw){
1592         /*shutdown calls if any*/
1593         LinphoneCore *lc=linphone_gtk_get_core();
1594         if (linphone_core_in_call(lc)){
1595                 linphone_core_terminate_all_calls(lc);
1596         }
1597         linphone_core_enable_video_preview(lc,FALSE);
1598 #ifdef __APPLE__ /*until with have a better option*/
1599         gtk_window_iconify(GTK_WINDOW(mw));
1600 #else
1601         gtk_widget_hide(mw);
1602 #endif
1603         return TRUE;
1604 }
1605
1606 #ifdef HAVE_GTK_OSX
1607 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1608         bool_t video_enabled=linphone_gtk_video_enabled();
1609         if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1610                 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1611         }else{
1612                 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1613                 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1614         }
1615         return FALSE;
1616 }
1617 #endif
1618
1619 void linphone_gtk_init_dtmf_table(GtkWidget *mw){
1620         GtkWidget *dtmf_table=linphone_gtk_get_widget(mw,"dtmf_table");
1621         gtk_widget_set_direction(dtmf_table, GTK_TEXT_DIR_LTR);
1622
1623         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_A")),"label","A");
1624         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_B")),"label","B");
1625         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_C")),"label","C");
1626         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_D")),"label","D");
1627         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_1")),"label","1");
1628         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_2")),"label","2");
1629         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_3")),"label","3");
1630         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_4")),"label","4");
1631         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_5")),"label","5");
1632         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_6")),"label","6");
1633         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_7")),"label","7");
1634         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_8")),"label","8");
1635         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_9")),"label","9");
1636         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_0")),"label","0");
1637         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_#")),"label","#");
1638         g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_*")),"label","*");
1639         
1640 }
1641
1642 static void linphone_gtk_init_main_window(){
1643         GtkWidget *main_window;
1644
1645         linphone_gtk_configure_main_window();
1646         linphone_gtk_manage_login();
1647         load_uri_history();
1648         linphone_gtk_load_identities();
1649         linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1650         linphone_gtk_show_friends();
1651         linphone_gtk_connect_digits();
1652         main_window=linphone_gtk_get_main_window();
1653         linphone_gtk_call_log_update(main_window);
1654
1655         linphone_gtk_init_dtmf_table(main_window);
1656         linphone_gtk_update_call_buttons (NULL);
1657         g_object_set_data(G_OBJECT(main_window),"is_conf",GINT_TO_POINTER(FALSE));
1658         /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1659         g_signal_connect (G_OBJECT (main_window), "delete-event",
1660                 G_CALLBACK (linphone_gtk_close), main_window);
1661 #ifdef HAVE_GTK_OSX
1662         {
1663                 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1664                 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1665                 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1666                 gtk_widget_hide(menubar);
1667                 gtk_osxapplication_ready(theMacApp);
1668         }
1669         g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1670 #endif
1671         linphone_gtk_check_menu_items();
1672 }
1673
1674
1675 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1676         if (verbose){
1677                 const char *lname="undef";
1678                 char *msg;
1679 #if defined(__linux) || defined(__APPLE__)
1680                 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1681 #endif
1682                 switch(lev){
1683                         case ORTP_DEBUG:
1684                                 lname="debug";
1685                                 break;
1686                         case ORTP_MESSAGE:
1687                                 lname="message";
1688                                 break;
1689                         case ORTP_WARNING:
1690                                 lname="warning";
1691                                 break;
1692                         case ORTP_ERROR:
1693                                 lname="error";
1694                                 break;
1695                         case ORTP_FATAL:
1696                                 lname="fatal";
1697                                 break;
1698                         default:
1699                                 g_error("Bad level !");
1700                 }
1701 #if defined(__linux) || defined(__APPLE__)
1702                 va_copy(cap,args);
1703                 msg=g_strdup_vprintf(fmt,cap);
1704                 va_end(cap);
1705 #else
1706                 msg=g_strdup_vprintf(fmt,args);
1707 #endif
1708                 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1709                 ortp_free(msg);
1710         }
1711         linphone_gtk_log_push(lev,fmt,args);
1712 }
1713
1714
1715 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1716         GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1717                 linphone_gtk_get_main_window(), "uribar"));
1718         char *text;
1719         linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1720         g_free(text);
1721         gtk_entry_set_text(uri_bar, refer_to);
1722         linphone_gtk_start_call(linphone_gtk_get_main_window());
1723 }
1724
1725 static void linphone_gtk_check_soundcards(){
1726         const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1727         if (devices==NULL || devices[0]==NULL){
1728                 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1729                         _("No sound cards have been detected on this computer.\n"
1730                                 "You won't be able to send or receive audio calls."));
1731         }
1732 }
1733
1734 #ifdef BUILD_WIZARD
1735 // Display the account wizard
1736 void linphone_gtk_display_wizard() {
1737         if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1738                 the_wizard = linphone_gtk_create_assistant();
1739         }
1740 }
1741 #endif
1742
1743 static void linphone_gtk_quit(void){
1744         static gboolean quit_done=FALSE;
1745         if (!quit_done){
1746                 quit_done=TRUE;
1747                 linphone_gtk_unmonitor_usb();
1748                 g_source_remove_by_user_data(linphone_gtk_get_core());
1749                 linphone_gtk_uninit_instance();
1750                 linphone_gtk_destroy_log_window();
1751                 linphone_core_destroy(the_core);
1752                 linphone_gtk_log_uninit();
1753 #ifdef HAVE_NOTIFY
1754                 notify_uninit();
1755 #endif
1756                 gdk_threads_leave();
1757         }
1758 }
1759
1760 #ifdef HAVE_GTK_OSX
1761 /*
1762 This is not the correct way to implement block termination.
1763 The good way would be to call gtk_main_quit(), and return TRUE.
1764 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1765 As a result the program cannot exit at all.
1766 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1767 */
1768 static gboolean on_block_termination(void){
1769         gtk_main_quit();
1770         linphone_gtk_quit();
1771         return FALSE;
1772 }
1773 #endif
1774
1775 int main(int argc, char *argv[]){
1776 #ifdef ENABLE_NLS
1777         void *p;
1778 #endif
1779         char *config_file;
1780         const char *factory_config_file;
1781         const char *lang;
1782         GtkSettings *settings;
1783         GdkPixbuf *pbuf;
1784         const char *app_name="Linphone";
1785
1786 #if !GLIB_CHECK_VERSION(2, 31, 0)
1787         g_thread_init(NULL);
1788 #endif
1789         gdk_threads_init();
1790
1791         progpath = strdup(argv[0]);
1792
1793         config_file=linphone_gtk_get_config_file(NULL);
1794
1795
1796 #ifdef WIN32
1797         /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1798         if ((lang=getenv("LANG"))!=NULL){
1799                 if (atoi(lang)!=0){
1800                         char tmp[128];
1801                         snprintf(tmp,sizeof(tmp),"LANG=",lang);
1802                         _putenv(tmp);
1803                 }
1804         }
1805 #else
1806         /*for pulseaudio:*/
1807         g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1808 #endif
1809
1810         if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1811 #ifdef WIN32
1812                 char tmp[128];
1813                 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1814                 _putenv(tmp);
1815                 if (strncmp(lang,"zh",2)==0){
1816                         workaround_gtk_entry_chinese_bug=TRUE;
1817                 }
1818 #elif __APPLE__
1819                 setenv("LANG",lang,1);
1820 #else
1821                 setenv("LANGUAGE",lang,1);
1822 #endif
1823         }
1824
1825 #ifdef ENABLE_NLS
1826         p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1827         if (p==NULL) perror("bindtextdomain failed");
1828         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1829         textdomain (GETTEXT_PACKAGE);
1830 #else
1831         g_message("NLS disabled.\n");
1832 #endif
1833 #ifdef WIN32
1834         gtk_rc_add_default_file("./gtkrc");
1835 #endif
1836         gdk_threads_enter();
1837
1838         if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1839                                 linphone_options,NULL,NULL)){
1840                 gdk_threads_leave();
1841                 return -1;
1842         }
1843
1844         settings=gtk_settings_get_default();
1845         g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1846         g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1847         g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1848         g_object_set(settings, "gtk-button-images", TRUE, NULL);
1849
1850         if (workingdir!=NULL){
1851                 if (chdir(workingdir)==-1){
1852                         g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1853                 }
1854         }
1855
1856         /* Now, look for the factory configuration file, we do it this late
1857                  since we want to have had time to change directory and to parse
1858                  the options, in case we needed to access the working directory */
1859         factory_config_file = linphone_gtk_get_factory_config_file();
1860
1861         if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1862                 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1863                 g_warning("This instance is going to exit now.");
1864                 gdk_threads_leave();
1865                 return 0;
1866         }
1867
1868         add_pixmap_directory("pixmaps");
1869         add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1870
1871 #ifdef HAVE_GTK_OSX
1872         GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1873         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1874         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1875         /*never block termination:*/
1876         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1877 #endif
1878
1879         the_ui=linphone_gtk_create_window("main");
1880
1881         g_object_set_data(G_OBJECT(the_ui),"is_created",GINT_TO_POINTER(FALSE));
1882
1883         linphone_gtk_create_log_window();
1884         linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1885
1886         linphone_gtk_init_liblinphone(config_file, factory_config_file);
1887
1888         g_set_application_name(app_name);
1889         pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1890         if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1891
1892         /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1893         gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1894         gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1895         linphone_gtk_init_main_window();
1896
1897 #ifdef BUILD_WIZARD
1898         // Veryfing if at least one sip account is configured. If not, show wizard
1899         if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1900                 linphone_gtk_display_wizard();
1901         }
1902 #endif
1903
1904 #ifndef HAVE_GTK_OSX
1905         linphone_gtk_init_status_icon();
1906 #endif
1907         if (!iconified){
1908                 linphone_gtk_show_main_window();
1909                 linphone_gtk_check_soundcards();
1910         }
1911         if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1912                 linphone_gtk_check_for_new_version();
1913         linphone_gtk_monitor_usb();
1914
1915         gtk_main();
1916         linphone_gtk_quit();
1917 #ifndef HAVE_GTK_OSX
1918         /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1919         gtk_status_icon_set_visible(icon,FALSE);
1920 #endif
1921         free(progpath);
1922         return 0;
1923 }
1924