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