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