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