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