]> sjero.net Git - linphone/blob - gtk/main.c
implement notifications when doing transfers
[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         gtk_widget_set_sensitive(selfview_item,val);
784         if (val){
785                 linphone_core_enable_video_preview(linphone_gtk_get_core(),
786                 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
787         }else{
788                 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
789         }
790 }
791
792 void linphone_gtk_enable_self_view(GtkWidget *w){
793         gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
794         LinphoneCore *lc=linphone_gtk_get_core();
795         linphone_core_enable_video_preview(lc,val);
796         linphone_core_enable_self_view(lc,val);
797         linphone_gtk_set_ui_config_int("videoselfview",val);
798 }
799
800 void linphone_gtk_used_identity_changed(GtkWidget *w){
801         int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
802         char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
803         if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
804                 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
805                 linphone_gtk_show_directory_search();
806         }
807         if (sel) g_free(sel);
808 }
809
810 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
811         linphone_gtk_show_friends();
812 }
813
814 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
815         switch(response_id){
816                 case GTK_RESPONSE_YES:
817                         linphone_gtk_show_contact(lf);
818                 break;
819                 default:
820                         linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
821         }
822         gtk_widget_destroy(dialog);
823 }
824
825 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
826         GtkWidget *dialog;
827
828         if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
829                 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
830                 return;
831         }
832
833         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);
834         dialog = gtk_message_dialog_new (
835                                 GTK_WINDOW(linphone_gtk_get_main_window()),
836                                 GTK_DIALOG_DESTROY_WITH_PARENT,
837                                 GTK_MESSAGE_QUESTION,
838                                 GTK_BUTTONS_YES_NO,
839                                 "%s",
840                                 message);
841         g_free(message);
842         g_signal_connect(G_OBJECT (dialog), "response",
843                 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
844         /* actually show the box */
845         gtk_widget_show(dialog);
846 }
847
848 typedef struct _AuthTimeout{
849         GtkWidget *w;
850 } AuthTimeout;
851
852
853 static void auth_timeout_clean(AuthTimeout *tout){
854         tout->w=NULL;
855 }
856
857 static gboolean auth_timeout_destroy(AuthTimeout *tout){
858         if (tout->w)  {
859                 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
860                 gtk_widget_destroy(tout->w);
861         }
862         g_free(tout);
863         return FALSE;
864 }
865
866 static AuthTimeout * auth_timeout_new(GtkWidget *w){
867         AuthTimeout *tout=g_new(AuthTimeout,1);
868         tout->w=w;
869         /*so that the timeout no more references the widget when it is destroyed:*/
870         g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
871         /*so that the widget is automatically destroyed after some time */
872         g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
873         return tout;
874 }
875
876 void linphone_gtk_password_cancel(GtkWidget *w){
877         LinphoneAuthInfo *info;
878         GtkWidget *window=gtk_widget_get_toplevel(w);
879         info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
880         linphone_core_abort_authentication(linphone_gtk_get_core(),info);
881         gtk_widget_destroy(window);
882 }
883
884 void linphone_gtk_password_ok(GtkWidget *w){
885         GtkWidget *entry;
886         GtkWidget *window=gtk_widget_get_toplevel(w);
887         LinphoneAuthInfo *info;
888         info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
889         g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
890         entry=linphone_gtk_get_widget(window,"password_entry");
891         linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
892         linphone_auth_info_set_userid(info,
893                 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
894         linphone_core_add_auth_info(linphone_gtk_get_core(),info);
895         gtk_widget_destroy(window);
896 }
897
898 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
899         GtkWidget *w=linphone_gtk_create_window("password");
900         GtkWidget *label=linphone_gtk_get_widget(w,"message");
901         LinphoneAuthInfo *info;
902         gchar *msg;
903         GtkWidget *mw=linphone_gtk_get_main_window();
904         
905         if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
906                 /*don't prompt for authentication when login frame is visible*/
907                 linphone_core_abort_authentication(lc,NULL);
908                 return;
909         }
910
911         msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
912                 username,realm);
913         gtk_label_set_markup(GTK_LABEL(label),msg);
914         g_free(msg);
915         gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
916         info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
917         g_object_set_data(G_OBJECT(w),"auth_info",info);
918         g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
919         gtk_widget_show(w);
920         auth_timeout_new(w);
921 }
922
923 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
924         GtkWidget *w=linphone_gtk_get_main_window();
925         GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
926         gtk_statusbar_push(GTK_STATUSBAR(status_bar),
927                         gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
928                         status);
929 }
930
931 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
932         linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
933 }
934
935 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
936         linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
937 }
938
939 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
940         char richtext[4096];
941         snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
942         linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
943 }
944
945 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
946         GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
947         if (w) linphone_gtk_call_log_update(w);
948         linphone_gtk_call_log_update(linphone_gtk_get_main_window());
949 }
950
951 #ifdef HAVE_NOTIFY
952 static bool_t notify_actions_supported() {
953         bool_t accepts_actions = FALSE;
954         GList *capabilities = notify_get_server_caps();
955         GList *c;
956         if(capabilities != NULL) {
957                 for(c = capabilities; c != NULL; c = c->next) {
958                         if(strcmp((char*)c->data, "actions") == 0 ) {
959                                 accepts_actions = TRUE;
960                                 break;
961                         }
962                 }
963                 g_list_foreach(capabilities, (GFunc)g_free, NULL);
964                 g_list_free(capabilities);
965         }
966         return accepts_actions;
967 }
968
969 static NotifyNotification* build_notification(const char *title, const char *body){
970          return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
971 #ifdef HAVE_NOTIFY1
972         ,NULL
973 #endif
974         );
975 }
976
977 static void show_notification(NotifyNotification* n){
978         if (n && !notify_notification_show(n,NULL))
979                 ms_error("Failed to send notification.");
980 }
981
982 static void make_notification(const char *title, const char *body){
983         show_notification(build_notification(title,body));
984 }
985
986 #endif
987
988 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
989 #ifdef HAVE_NOTIFY
990         if (!notify_is_initted())
991                 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
992 #endif
993         if (!call) {
994 #ifdef HAVE_NOTIFY
995                 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
996 #ifdef HAVE_NOTIFY1
997         ,NULL
998 #endif
999 ),NULL))
1000                                 ms_error("Failed to send notification.");
1001 #else
1002                 linphone_gtk_show_main_window();
1003 #endif
1004         } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1005 #ifdef HAVE_NOTIFY
1006                 char *body=NULL;
1007                 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1008                 NotifyNotification *n;
1009                 switch(linphone_call_get_state(call)){
1010                         case LinphoneCallError:
1011                                 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1012                         break;
1013                         case LinphoneCallEnd:
1014                                 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1015                         break;
1016                         case LinphoneCallIncomingReceived:
1017                                 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1018                                 if (notify_actions_supported()) {
1019                                         notify_notification_add_action (n,"answer", _("Answer"),
1020                                                 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1021                                         notify_notification_add_action (n,"decline",_("Decline"),
1022                                                 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1023                                 }
1024                                 show_notification(n);
1025                         break;
1026                         case LinphoneCallPausedByRemote:
1027                                 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1028                         break;
1029                         default:
1030                         break;
1031                 }
1032                 if (body) g_free(body);
1033                 if (remote) g_free(remote);
1034 #endif
1035         }
1036 }
1037
1038 static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){
1039         if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1040                 LinphoneCore *lc=linphone_call_get_core(call);
1041                 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
1042                 linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES);
1043                 linphone_core_accept_call_update(lc,call,params);
1044                 linphone_call_params_destroy(params);
1045         }
1046         linphone_call_unref(call);
1047         g_source_remove_by_user_data(dialog);
1048         gtk_widget_destroy(dialog);
1049 }
1050
1051 static void on_call_updated_timeout(GtkWidget *dialog){
1052         gtk_widget_destroy(dialog);
1053 }
1054
1055 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1056         LinphoneCore *lc=linphone_call_get_core(call);
1057         const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1058         const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1059         const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1060         gboolean video_requested=linphone_call_params_video_enabled(rparams);
1061         gboolean video_used=linphone_call_params_video_enabled(current_params);
1062         g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1063                   video_used,video_requested,pol->automatically_accept);
1064         if (video_used==FALSE && video_requested && !pol->automatically_accept){
1065                 linphone_core_defer_call_update(lc,call);
1066                 {
1067                         const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1068                         GtkWidget *dialog;
1069                         const char *dname=linphone_address_get_display_name(addr);
1070                         if (dname==NULL) dname=linphone_address_get_username(addr);
1071                         if (dname==NULL) dname=linphone_address_get_domain(addr);
1072                         dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1073                                                                  GTK_DIALOG_DESTROY_WITH_PARENT,
1074                                                                  GTK_MESSAGE_WARNING,
1075                                                                  GTK_BUTTONS_YES_NO,
1076                                                                  _("%s proposed to start video. Do you accept ?"),dname);
1077                         g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1078                         g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1079                         gtk_widget_show(dialog);
1080                 }
1081         }
1082 }
1083
1084 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1085         switch(cs){
1086                 case LinphoneCallOutgoingInit:
1087                         linphone_gtk_create_in_call_view (call);
1088                 break;
1089                 case LinphoneCallOutgoingProgress:
1090                         linphone_gtk_in_call_view_set_calling (call);
1091                 break;
1092                 case LinphoneCallStreamsRunning:
1093                         linphone_gtk_in_call_view_set_in_call(call);
1094                 break;
1095                 case LinphoneCallUpdatedByRemote:
1096                         linphone_gtk_call_updated_by_remote(call);
1097                 break;
1098                 case LinphoneCallError:
1099                         linphone_gtk_in_call_view_terminate (call,msg);
1100                 break;
1101                 case LinphoneCallEnd:
1102                         linphone_gtk_in_call_view_terminate(call,NULL);
1103                         linphone_gtk_status_icon_set_blinking(FALSE);
1104                 break;
1105                 case LinphoneCallIncomingReceived:
1106                         linphone_gtk_create_in_call_view(call);
1107                         linphone_gtk_in_call_view_set_incoming(call);
1108                         linphone_gtk_status_icon_set_blinking(TRUE);
1109                         if (auto_answer)  {
1110                                 linphone_call_ref(call);
1111                                 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1112                         }               
1113                 break;
1114                 case LinphoneCallResuming:
1115                         linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1116                         linphone_gtk_in_call_view_set_in_call (call);
1117                 break;
1118                 case LinphoneCallPausing:
1119                         linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1120                 case LinphoneCallPausedByRemote:
1121                         linphone_gtk_in_call_view_set_paused(call);
1122                 break;
1123                 case LinphoneCallConnected:
1124                         linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1125                         linphone_gtk_status_icon_set_blinking(FALSE);
1126                 break;
1127                 default:
1128                 break;
1129         }
1130         linphone_gtk_notify(call, msg);
1131         linphone_gtk_update_call_buttons (call);
1132 }
1133
1134 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1135         linphone_gtk_in_call_view_show_encryption(call);
1136 }
1137
1138 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){
1139         linphone_gtk_in_call_view_set_transfer_status(call,cstate);
1140 }
1141
1142 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1143         GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1144         GtkTreeModel *model=gtk_combo_box_get_model(box);
1145         GtkTreeIter iter;
1146         gboolean found=FALSE;
1147         const char *stock_id=NULL;
1148         
1149         if (gtk_tree_model_get_iter_first(model,&iter)){
1150                 gpointer p;
1151                 do{
1152                         gtk_tree_model_get(model,&iter,2,&p,-1);
1153                         if (p==cfg) {
1154                                 found=TRUE;
1155                                 break;
1156                         }
1157                 }while(gtk_tree_model_iter_next(model,&iter));
1158         }
1159         if (!found) {
1160                 g_warning("Could not find proxy config in combo box of identities.");
1161                 return;
1162         }
1163         switch (rs){
1164                 case LinphoneRegistrationOk:
1165                         stock_id=GTK_STOCK_YES;
1166                 break;
1167                 case LinphoneRegistrationProgress:
1168                         stock_id=GTK_STOCK_REFRESH;
1169                 break;
1170                 case LinphoneRegistrationCleared:
1171                         stock_id=NULL;
1172                 break;
1173                 case LinphoneRegistrationFailed:
1174                         stock_id=GTK_STOCK_DIALOG_WARNING;
1175                 break;
1176                 default:
1177                 break;
1178         }
1179         gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1180 }
1181
1182 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, 
1183                                                     LinphoneRegistrationState rs, const char *msg){
1184         switch (rs){
1185                 case LinphoneRegistrationOk:
1186                         if (cfg){
1187                                 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1188                                 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1189                                         linphone_gtk_exit_login_frame();
1190                                 }
1191                         }
1192                 break;
1193                 default:
1194                 break;
1195         }
1196         update_registration_status(cfg,rs);
1197 }
1198
1199 void linphone_gtk_open_browser(const char *url){
1200         /*in gtk 2.16, gtk_show_uri does not work...*/
1201 #ifndef WIN32
1202 #if GTK_CHECK_VERSION(2,18,3)
1203         gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1204 #else
1205         char cl[255];
1206         snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1207         g_spawn_command_line_async(cl,NULL);
1208 #endif
1209 #else /*WIN32*/
1210         ShellExecute(0,"open",url,NULL,NULL,1);
1211 #endif
1212 }
1213
1214 void linphone_gtk_link_to_website(GtkWidget *item){
1215         const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1216         linphone_gtk_open_browser(home);
1217 }
1218
1219 #ifndef HAVE_GTK_OSX
1220
1221 static GtkStatusIcon *icon=NULL;
1222
1223 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1224         GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1225         gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1226 }
1227
1228 static GtkWidget *create_icon_menu(){
1229         GtkWidget *menu=gtk_menu_new();
1230         GtkWidget *menu_item;
1231         GtkWidget *image;
1232         gchar *tmp;
1233         const gchar *homesite;
1234         
1235         homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1236         menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1237         tmp=g_strdup(homesite);
1238         g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1239         g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1240         
1241         image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1242         gtk_widget_show(image);
1243         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1244         //g_object_unref(G_OBJECT(image));
1245         gtk_widget_show(menu_item);
1246         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1247         g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1248         
1249         menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1250         gtk_widget_show(menu_item);
1251         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1252         g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1253         menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1254         gtk_widget_show(menu_item);
1255         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1256         g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1257         gtk_widget_show(menu);
1258         return menu;
1259 }
1260
1261 static void handle_icon_click() {
1262         GtkWidget *mw=linphone_gtk_get_main_window();
1263         if (!gtk_window_is_active((GtkWindow*)mw)) {
1264                 linphone_gtk_show_main_window();
1265         } else {
1266                 gtk_widget_hide(mw);
1267         }
1268 }
1269
1270 static void linphone_gtk_init_status_icon(){
1271         const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1272         const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1273         GdkPixbuf *pbuf=create_pixbuf(icon_path);
1274         GtkWidget *menu=create_icon_menu();
1275         const char *title;
1276         title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1277         icon=gtk_status_icon_new_from_pixbuf(pbuf);
1278 #if GTK_CHECK_VERSION(2,20,0)
1279         gtk_status_icon_set_name(icon,title);
1280 #endif
1281         g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1282         g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1283         gtk_status_icon_set_tooltip(icon,title);
1284         gtk_status_icon_set_visible(icon,TRUE);
1285         g_object_set_data(G_OBJECT(icon),"menu",menu);
1286         g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1287         g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1288         g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1289         pbuf=create_pixbuf(call_icon_path);
1290         g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1291 }
1292
1293 static gboolean do_icon_blink(GtkStatusIcon *gi){
1294         GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1295         GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1296         GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1297         if (cur_icon==call_icon){
1298                 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1299         }else{
1300                 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1301         }
1302         return TRUE;
1303 }
1304
1305 #endif
1306
1307 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1308 #ifdef HAVE_GTK_OSX
1309         static gint attention_id;
1310         GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1311         if (val)
1312                 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1313         else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1314 #else
1315         if (icon!=NULL){
1316                 guint tout;
1317                 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1318                 if (val && tout==0){
1319                         tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1320                         g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1321                 }else if (!val && tout!=0){
1322                         GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1323                         g_source_remove(tout);
1324                         g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1325                         gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1326                 }
1327         }
1328 #endif
1329 }
1330
1331 void linphone_gtk_options_activate(GtkWidget *item){
1332 #ifndef HAVE_GTK_OSX
1333         gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1334                 TRUE);
1335 #endif
1336 }
1337
1338 static void init_identity_combo(GtkComboBox *box){
1339         GtkListStore *store;
1340         GtkCellRenderer *r1,*r2;
1341         store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1342         gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1343         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1344         gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1345         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1346         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1347         g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1348         gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1349 }
1350
1351 void linphone_gtk_load_identities(void){
1352         const MSList *elem;
1353         GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1354         char *def_identity;
1355         LinphoneProxyConfig *def=NULL;
1356         int def_index=0,i;
1357         GtkListStore *store;
1358         GtkTreeIter iter;
1359
1360         store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1361         if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1362                 /* model is empty, this is the first time we go here */
1363                 init_identity_combo(box);
1364                 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1365         }
1366         gtk_list_store_clear(store);
1367         linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1368         def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1369         gtk_list_store_append(store,&iter);
1370         gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1371         g_free(def_identity);
1372         for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1373                         elem!=NULL;
1374                         elem=ms_list_next(elem),i++){
1375                 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1376                 gtk_list_store_append(store,&iter);
1377                 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1378                                    linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1379                                    2,cfg,-1);
1380                 if (cfg==def) {
1381                         def_index=i;
1382                 }
1383         }
1384         gtk_combo_box_set_active(box,def_index);
1385 }
1386
1387 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1388         const char *label=gtk_button_get_label(button);
1389         GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1390         int pos=-1;
1391         gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1392         linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1393         if (linphone_core_in_call(linphone_gtk_get_core())){
1394                 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1395         }
1396 }
1397
1398 static void linphone_gtk_dtmf_released(GtkButton *button){
1399         linphone_core_stop_dtmf (linphone_gtk_get_core());
1400 }
1401
1402 static void linphone_gtk_connect_digits(void){
1403         GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1404         GList *children=gtk_container_get_children(cont);
1405         GList *elem;
1406         for(elem=children;elem!=NULL;elem=elem->next){
1407                 GtkButton *button=GTK_BUTTON(elem->data);
1408                 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1409                 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1410         }
1411 }
1412
1413 static void linphone_gtk_check_menu_items(void){
1414         bool_t video_enabled=linphone_gtk_video_enabled();
1415         bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1416         GtkWidget *selfview_item=linphone_gtk_get_widget(
1417                                         linphone_gtk_get_main_window(),"selfview_item");
1418         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1419                                         linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1420         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1421         gtk_widget_set_sensitive(selfview_item,video_enabled);
1422 }
1423
1424 static gboolean linphone_gtk_can_manage_accounts(){
1425         LinphoneCore *lc=linphone_gtk_get_core();
1426         const MSList *elem;
1427         for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1428                 SipSetup *ss=(SipSetup*)elem->data;
1429                 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1430                         return TRUE;
1431                 }
1432         }
1433         return FALSE;
1434 }
1435
1436 static void linphone_gtk_configure_main_window(){
1437         static gboolean config_loaded=FALSE;
1438         static const char *title;
1439         static const char *home;
1440         static const char *start_call_icon;
1441         static const char *add_call_icon;
1442         static const char *stop_call_icon;
1443         static const char *search_icon;
1444         static gboolean update_check_menu;
1445         static gboolean buttons_have_borders;
1446         static gboolean show_abcd;
1447         GtkWidget *w=linphone_gtk_get_main_window();
1448         if (!config_loaded){
1449                 title=linphone_gtk_get_ui_config("title","Linphone");
1450                 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1451                 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1452                 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1453                 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1454                 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1455                 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1456                 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1457                 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1458                 config_loaded=TRUE;
1459         }
1460         linphone_gtk_configure_window(w,"main_window");
1461         if (title) {
1462                 gtk_window_set_title(GTK_WINDOW(w),title);
1463         }
1464         if (start_call_icon){
1465                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1466                                     create_pixmap (start_call_icon));
1467                 if (!buttons_have_borders)
1468                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1469         }
1470         if (add_call_icon){
1471                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1472                                     create_pixmap (add_call_icon));
1473                 if (!buttons_have_borders)
1474                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1475         }
1476         if (stop_call_icon){
1477                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1478                                     create_pixmap (stop_call_icon));
1479                 if (!buttons_have_borders)
1480                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1481         }
1482         if (search_icon){
1483                 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1484                 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1485                 g_object_unref(G_OBJECT(pbuf));
1486         }
1487         if (home){
1488                 gchar *tmp;
1489                 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1490                 tmp=g_strdup(home);
1491                 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1492         }
1493         {
1494                 /*
1495                 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1496                 if (pbuf) {
1497                         gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1498                         g_object_unref(G_OBJECT(pbuf));
1499                 }
1500                 */
1501         }
1502         {
1503                 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1504                 if (pbuf) {
1505                         GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1506                         int w,h;
1507                         GdkPixbuf *scaled;
1508                         gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1509                         scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1510                         gtk_image_set_from_pixbuf(img,scaled);
1511                         g_object_unref(G_OBJECT(scaled));
1512                         g_object_unref(G_OBJECT(pbuf));
1513                 }
1514         }
1515         if (linphone_gtk_can_manage_accounts()) {
1516                 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1517         }
1518         if (update_check_menu){
1519                 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1520         }
1521         if (!show_abcd){
1522                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1523                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1524                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1525                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1526                 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1527         }
1528 }
1529
1530 void linphone_gtk_manage_login(void){
1531         LinphoneCore *lc=linphone_gtk_get_core();
1532         LinphoneProxyConfig *cfg=NULL;
1533         linphone_core_get_default_proxy(lc,&cfg);
1534         if (cfg){
1535                 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1536                 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1537                         linphone_gtk_show_login_frame(cfg);
1538                 }
1539         }
1540 }
1541
1542
1543 gboolean linphone_gtk_close(GtkWidget *mw){
1544         /*shutdown calls if any*/
1545         LinphoneCore *lc=linphone_gtk_get_core();
1546         if (linphone_core_in_call(lc)){
1547                 linphone_core_terminate_all_calls(lc);
1548         }
1549         linphone_core_enable_video_preview(lc,FALSE);
1550 #ifdef __APPLE__ /*until with have a better option*/
1551         gtk_window_iconify(GTK_WINDOW(mw));
1552 #else
1553         gtk_widget_hide(mw);
1554 #endif
1555         return TRUE;
1556 }
1557
1558 #ifdef HAVE_GTK_OSX
1559 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1560         bool_t video_enabled=linphone_gtk_video_enabled();
1561         if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1562                 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1563         }else{
1564                 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1565                 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1566         }
1567         return FALSE;
1568 }
1569 #endif
1570
1571
1572 static void linphone_gtk_init_main_window(){
1573         GtkWidget *main_window;
1574
1575         linphone_gtk_configure_main_window();
1576         linphone_gtk_manage_login();
1577         load_uri_history();
1578         linphone_gtk_load_identities();
1579         linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1580         linphone_gtk_show_friends();
1581         linphone_gtk_connect_digits();
1582         main_window=linphone_gtk_get_main_window();
1583         linphone_gtk_call_log_update(main_window);
1584         
1585         linphone_gtk_update_call_buttons (NULL);
1586         /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1587         g_signal_connect (G_OBJECT (main_window), "delete-event",
1588                 G_CALLBACK (linphone_gtk_close), main_window);
1589 #ifdef HAVE_GTK_OSX
1590         {
1591                 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1592                 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1593                 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1594                 gtk_widget_hide(menubar);
1595                 gtk_osxapplication_ready(theMacApp);
1596         }
1597         g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1598 #endif
1599         linphone_gtk_check_menu_items();
1600 }
1601
1602
1603 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1604         if (verbose){
1605                 const char *lname="undef";
1606                 char *msg;
1607 #if defined(__linux) || defined(__APPLE__)
1608                 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1609 #endif
1610                 switch(lev){
1611                         case ORTP_DEBUG:
1612                                 lname="debug";
1613                                 break;
1614                         case ORTP_MESSAGE:
1615                                 lname="message";
1616                                 break;
1617                         case ORTP_WARNING:
1618                                 lname="warning";
1619                                 break;
1620                         case ORTP_ERROR:
1621                                 lname="error";
1622                                 break;
1623                         case ORTP_FATAL:
1624                                 lname="fatal";
1625                                 break;
1626                         default:
1627                                 g_error("Bad level !");
1628                 }
1629 #if defined(__linux) || defined(__APPLE__)
1630                 va_copy(cap,args);
1631                 msg=g_strdup_vprintf(fmt,cap);
1632                 va_end(cap);
1633 #else
1634                 msg=g_strdup_vprintf(fmt,args);
1635 #endif
1636                 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1637                 ortp_free(msg);
1638         }
1639         linphone_gtk_log_push(lev,fmt,args);
1640 }
1641
1642
1643 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1644         GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1645                 linphone_gtk_get_main_window(), "uribar"));
1646         char *text;
1647         linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1648         g_free(text);
1649         gtk_entry_set_text(uri_bar, refer_to);
1650         linphone_gtk_start_call(linphone_gtk_get_main_window());
1651 }
1652
1653 static void linphone_gtk_check_soundcards(){
1654         const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1655         if (devices==NULL || devices[0]==NULL){
1656                 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1657                         _("No sound cards have been detected on this computer.\n"
1658                                 "You won't be able to send or receive audio calls."));
1659         }
1660 }
1661
1662 #ifdef BUILD_WIZARD
1663 // Display the account wizard
1664 void linphone_gtk_display_wizard() {
1665         if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1666                 the_wizard = linphone_gtk_create_assistant();
1667         }
1668 }
1669 #endif
1670
1671 static void linphone_gtk_quit(void){
1672         static gboolean quit_done=FALSE;
1673         if (!quit_done){
1674                 quit_done=TRUE;
1675                 linphone_gtk_unmonitor_usb();
1676                 g_source_remove_by_user_data(linphone_gtk_get_core());
1677                 linphone_gtk_uninit_instance();
1678                 linphone_gtk_destroy_log_window();
1679                 linphone_core_destroy(the_core);
1680                 linphone_gtk_log_uninit();
1681 #ifdef HAVE_NOTIFY
1682                 notify_uninit();
1683 #endif
1684                 gdk_threads_leave();
1685         }
1686 }
1687
1688 #ifdef HAVE_GTK_OSX
1689 /*
1690 This is not the correct way to implement block termination.
1691 The good way would be to call gtk_main_quit(), and return TRUE.
1692 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1693 As a result the program cannot exit at all.
1694 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1695 */
1696 static gboolean on_block_termination(void){
1697         gtk_main_quit();
1698         linphone_gtk_quit();
1699         return FALSE;
1700 }
1701 #endif
1702
1703 int main(int argc, char *argv[]){
1704 #ifdef ENABLE_NLS
1705         void *p;
1706 #endif
1707         char *config_file;
1708         const char *factory_config_file;
1709         const char *lang;
1710         GtkSettings *settings;
1711         GdkPixbuf *pbuf;
1712         const char *app_name="Linphone";
1713
1714 #if !GLIB_CHECK_VERSION(2, 31, 0)
1715         g_thread_init(NULL);
1716 #endif
1717         gdk_threads_init();
1718         
1719         progpath = strdup(argv[0]);
1720         
1721         config_file=linphone_gtk_get_config_file(NULL);
1722         
1723
1724 #ifdef WIN32
1725         /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1726         if ((lang=getenv("LANG"))!=NULL){
1727                 if (atoi(lang)!=0){
1728                         char tmp[128];
1729                         snprintf(tmp,sizeof(tmp),"LANG=",lang);
1730                         _putenv(tmp);
1731                 }
1732         }
1733 #else
1734         /*for pulseaudio:*/
1735         g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1736 #endif
1737
1738         if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1739 #ifdef WIN32
1740                 char tmp[128];
1741                 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1742                 _putenv(tmp);
1743 #else
1744                 setenv("LANG",lang,1);
1745 #endif
1746         }
1747
1748 #ifdef ENABLE_NLS
1749         p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1750         if (p==NULL) perror("bindtextdomain failed");
1751         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1752         textdomain (GETTEXT_PACKAGE);
1753 #else
1754         g_message("NLS disabled.\n");
1755 #endif
1756 #ifdef WIN32
1757         gtk_rc_add_default_file("./gtkrc");
1758 #endif
1759         gdk_threads_enter();
1760         
1761         if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1762                                 linphone_options,NULL,NULL)){
1763                 gdk_threads_leave();
1764                 return -1;
1765         }
1766         
1767         settings=gtk_settings_get_default();
1768         g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1769         g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1770         g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1771         g_object_set(settings, "gtk-button-images", TRUE, NULL);
1772
1773         if (workingdir!=NULL){
1774                 if (chdir(workingdir)==-1){
1775                         g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1776                 }
1777         }
1778
1779         /* Now, look for the factory configuration file, we do it this late
1780                  since we want to have had time to change directory and to parse
1781                  the options, in case we needed to access the working directory */
1782         factory_config_file = linphone_gtk_get_factory_config_file();
1783
1784         if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1785                 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1786                 g_warning("This instance is going to exit now.");
1787                 gdk_threads_leave();
1788                 return 0;
1789         }
1790
1791         add_pixmap_directory("pixmaps");
1792         add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1793
1794 #ifdef HAVE_GTK_OSX
1795         GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1796         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1797         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1798         /*never block termination:*/
1799         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1800 #endif
1801         
1802         the_ui=linphone_gtk_create_window("main");
1803         
1804         linphone_gtk_create_log_window();
1805         linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1806
1807         linphone_gtk_init_liblinphone(config_file, factory_config_file);
1808         
1809         g_set_application_name(app_name);
1810         pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1811         if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1812         
1813         /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1814         gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1815         gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1816         linphone_gtk_init_main_window();
1817
1818 #ifdef BUILD_WIZARD
1819         // Veryfing if at least one sip account is configured. If not, show wizard
1820         if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1821                 linphone_gtk_display_wizard();
1822         }
1823 #endif
1824
1825 #ifndef HAVE_GTK_OSX
1826         linphone_gtk_init_status_icon();
1827 #endif
1828         if (!iconified){
1829                 linphone_gtk_show_main_window();
1830                 linphone_gtk_check_soundcards();
1831         }
1832         if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1833                 linphone_gtk_check_for_new_version();
1834         linphone_gtk_monitor_usb();
1835
1836         gtk_main();
1837         linphone_gtk_quit();
1838 #ifndef HAVE_GTK_OSX
1839         /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1840         gtk_status_icon_set_visible(icon,FALSE);
1841 #endif
1842         free(progpath);
1843         return 0;
1844 }
1845