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