]> sjero.net Git - linphone/blob - gtk/main.c
fix bad handling of config files
[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 void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1151         GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1152         gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1153 }
1154
1155 static GtkWidget *create_icon_menu(){
1156         GtkWidget *menu=gtk_menu_new();
1157         GtkWidget *menu_item;
1158         GtkWidget *image;
1159         gchar *tmp;
1160         const gchar *homesite;
1161         
1162         homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1163         menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1164         tmp=g_strdup(homesite);
1165         g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1166         g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1167         
1168         image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1169         gtk_widget_show(image);
1170         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1171         //g_object_unref(G_OBJECT(image));
1172         gtk_widget_show(menu_item);
1173         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1174         g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1175         
1176         menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1177         gtk_widget_show(menu_item);
1178         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1179         g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1180         menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1181         gtk_widget_show(menu_item);
1182         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1183         g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1184         gtk_widget_show(menu);
1185         return menu;
1186 }
1187
1188 static GtkStatusIcon *icon=NULL;
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 static void init_identity_combo(GtkComboBox *box){
1259         GtkListStore *store;
1260         GtkCellRenderer *r1,*r2;
1261         store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1262         gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1263         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1264         gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1265         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1266         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1267         g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1268         gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1269 }
1270
1271 void linphone_gtk_load_identities(void){
1272         const MSList *elem;
1273         GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1274         char *def_identity;
1275         LinphoneProxyConfig *def=NULL;
1276         int def_index=0,i;
1277         GtkListStore *store;
1278         GtkTreeIter iter;
1279
1280         store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1281         if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1282                 /* model is empty, this is the first time we go here */
1283                 init_identity_combo(box);
1284                 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1285         }
1286         gtk_list_store_clear(store);
1287         linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1288         def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1289         gtk_list_store_append(store,&iter);
1290         gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1291         g_free(def_identity);
1292         for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1293                         elem!=NULL;
1294                         elem=ms_list_next(elem),i++){
1295                 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1296                 gtk_list_store_append(store,&iter);
1297                 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1298                                    linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1299                                    2,cfg,-1);
1300                 if (cfg==def) {
1301                         def_index=i;
1302                 }
1303         }
1304         gtk_combo_box_set_active(box,def_index);
1305 }
1306
1307 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1308         const char *label=gtk_button_get_label(button);
1309         GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1310         int pos=-1;
1311         gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1312         linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1313         if (linphone_core_in_call(linphone_gtk_get_core())){
1314                 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1315         }
1316 }
1317
1318 static void linphone_gtk_dtmf_released(GtkButton *button){
1319         linphone_core_stop_dtmf (linphone_gtk_get_core());
1320 }
1321
1322 static void linphone_gtk_connect_digits(void){
1323         GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1324         GList *children=gtk_container_get_children(cont);
1325         GList *elem;
1326         for(elem=children;elem!=NULL;elem=elem->next){
1327                 GtkButton *button=GTK_BUTTON(elem->data);
1328                 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1329                 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1330         }
1331 }
1332
1333 static void linphone_gtk_check_menu_items(void){
1334         bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core());
1335         bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1336         GtkWidget *selfview_item=linphone_gtk_get_widget(
1337                                         linphone_gtk_get_main_window(),"selfview_item");
1338         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1339                                         linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1340         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1341         gtk_widget_set_sensitive(selfview_item,video_enabled);
1342 }
1343
1344 static gboolean linphone_gtk_can_manage_accounts(){
1345         LinphoneCore *lc=linphone_gtk_get_core();
1346         const MSList *elem;
1347         for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1348                 SipSetup *ss=(SipSetup*)elem->data;
1349                 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1350                         return TRUE;
1351                 }
1352         }
1353         return FALSE;
1354 }
1355
1356 static void linphone_gtk_configure_main_window(){
1357         static gboolean config_loaded=FALSE;
1358         static const char *title;
1359         static const char *home;
1360         static const char *start_call_icon;
1361         static const char *add_call_icon;
1362         static const char *stop_call_icon;
1363         static const char *search_icon;
1364         static gboolean update_check_menu;
1365         static gboolean buttons_have_borders;
1366         static gboolean show_abcd;
1367         GtkWidget *w=linphone_gtk_get_main_window();
1368         if (!config_loaded){
1369                 title=linphone_gtk_get_ui_config("title","Linphone");
1370                 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1371                 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1372                 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1373                 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1374                 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1375                 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1376                 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1377                 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1378                 config_loaded=TRUE;
1379         }
1380         linphone_gtk_configure_window(w,"main_window");
1381         if (title) {
1382                 gtk_window_set_title(GTK_WINDOW(w),title);
1383         }
1384         if (start_call_icon){
1385                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1386                                     create_pixmap (start_call_icon));
1387                 if (!buttons_have_borders)
1388                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1389         }
1390         if (add_call_icon){
1391                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1392                                     create_pixmap (add_call_icon));
1393                 if (!buttons_have_borders)
1394                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1395         }
1396         if (stop_call_icon){
1397                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1398                                     create_pixmap (stop_call_icon));
1399                 if (!buttons_have_borders)
1400                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1401         }
1402         if (search_icon){
1403                 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1404                 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1405                 g_object_unref(G_OBJECT(pbuf));
1406         }
1407         if (home){
1408                 gchar *tmp;
1409                 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1410                 tmp=g_strdup(home);
1411                 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1412         }
1413         {
1414                 /*
1415                 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1416                 if (pbuf) {
1417                         gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1418                         g_object_unref(G_OBJECT(pbuf));
1419                 }
1420                 */
1421         }
1422         {
1423                 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1424                 if (pbuf) {
1425                         gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon")),pbuf);
1426                         g_object_unref(G_OBJECT(pbuf));
1427                 }
1428         }
1429         if (linphone_gtk_can_manage_accounts())
1430                 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1431         if (update_check_menu){
1432                 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1433         }
1434         if (!show_abcd){
1435                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1436                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1437                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1438                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1439                 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1440         }
1441 }
1442
1443 void linphone_gtk_manage_login(void){
1444         LinphoneCore *lc=linphone_gtk_get_core();
1445         LinphoneProxyConfig *cfg=NULL;
1446         linphone_core_get_default_proxy(lc,&cfg);
1447         if (cfg){
1448                 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1449                 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1450                         linphone_gtk_show_login_frame(cfg);
1451                 }
1452         }
1453 }
1454
1455
1456 gboolean linphone_gtk_close(GtkWidget *mw){
1457         /*shutdown calls if any*/
1458         LinphoneCore *lc=linphone_gtk_get_core();
1459         if (linphone_core_in_call(lc)){
1460                 linphone_core_terminate_all_calls(lc);
1461         }
1462         linphone_core_enable_video_preview(lc,FALSE);
1463 #ifdef __APPLE__ /*until with have a better option*/
1464         gtk_window_iconify(GTK_WINDOW(mw));
1465 #else
1466         gtk_widget_hide(mw);
1467 #endif
1468         return TRUE;
1469 }
1470
1471 #ifdef HAVE_GTK_OSX
1472 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1473         if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1474                 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1475         }else{
1476                 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1477                 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1478         }
1479         return FALSE;
1480 }
1481 #endif
1482
1483
1484 static void linphone_gtk_init_main_window(){
1485         GtkWidget *main_window;
1486
1487         linphone_gtk_configure_main_window();
1488         linphone_gtk_manage_login();
1489         load_uri_history();
1490         linphone_gtk_load_identities();
1491         linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1492         linphone_gtk_show_friends();
1493         linphone_gtk_connect_digits();
1494         main_window=linphone_gtk_get_main_window();
1495         linphone_gtk_call_log_update(main_window);
1496         
1497         linphone_gtk_update_call_buttons (NULL);
1498         /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1499         g_signal_connect (G_OBJECT (main_window), "delete-event",
1500                 G_CALLBACK (linphone_gtk_close), main_window);
1501 #ifdef HAVE_GTK_OSX
1502         {
1503                 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1504                 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1505                 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1506                 gtk_widget_hide(menubar);
1507                 gtk_osxapplication_ready(theMacApp);
1508         }
1509         g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1510 #endif
1511         linphone_gtk_check_menu_items();
1512 }
1513
1514
1515 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1516         if (verbose){
1517                 const char *lname="undef";
1518                 char *msg;
1519 #if defined(__linux) || defined(__APPLE__)
1520                 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1521 #endif
1522                 switch(lev){
1523                         case ORTP_DEBUG:
1524                                 lname="debug";
1525                                 break;
1526                         case ORTP_MESSAGE:
1527                                 lname="message";
1528                                 break;
1529                         case ORTP_WARNING:
1530                                 lname="warning";
1531                                 break;
1532                         case ORTP_ERROR:
1533                                 lname="error";
1534                                 break;
1535                         case ORTP_FATAL:
1536                                 lname="fatal";
1537                                 break;
1538                         default:
1539                                 g_error("Bad level !");
1540                 }
1541 #if defined(__linux) || defined(__APPLE__)
1542                 va_copy(cap,args);
1543                 msg=g_strdup_vprintf(fmt,cap);
1544                 va_end(cap);
1545 #else
1546                 msg=g_strdup_vprintf(fmt,args);
1547 #endif
1548                 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1549                 ortp_free(msg);
1550         }
1551         linphone_gtk_log_push(lev,fmt,args);
1552 }
1553
1554
1555 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1556         GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1557                 linphone_gtk_get_main_window(), "uribar"));
1558         char *text;
1559         linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1560         g_free(text);
1561         gtk_entry_set_text(uri_bar, refer_to);
1562         linphone_gtk_start_call(linphone_gtk_get_main_window());
1563 }
1564
1565 static void linphone_gtk_check_soundcards(){
1566         const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1567         if (devices==NULL || devices[0]==NULL){
1568                 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1569                         _("No sound cards have been detected on this computer.\n"
1570                                 "You won't be able to send or receive audio calls."));
1571         }
1572 }
1573
1574 static void linphone_gtk_quit(void){
1575         linphone_gtk_uninit_instance();
1576         linphone_gtk_destroy_log_window();
1577         linphone_core_destroy(the_core);
1578         linphone_gtk_log_uninit();
1579 #ifdef HAVE_NOTIFY
1580         notify_uninit();
1581 #endif
1582         gdk_threads_leave();
1583 }
1584
1585 #ifdef HAVE_GTK_OSX
1586 /*
1587 This is not the correct way to implement block termination.
1588 The good way would be to call gtk_main_quit(), and return TRUE.
1589 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1590 As a result the program cannot exit at all.
1591 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1592 */
1593 static gboolean on_block_termination(void){
1594         gtk_main_quit();
1595         linphone_gtk_quit();
1596         return FALSE;
1597 }
1598 #endif
1599
1600 int main(int argc, char *argv[]){
1601 #ifdef ENABLE_NLS
1602         void *p;
1603 #endif
1604         char *config_file;
1605         const char *factory_config_file;
1606         const char *lang;
1607         GtkSettings *settings;
1608         GdkPixbuf *pbuf;
1609         const char *app_name="Linphone";
1610
1611         g_thread_init(NULL);
1612         gdk_threads_init();
1613         
1614         progpath = strdup(argv[0]);
1615         
1616         config_file=linphone_gtk_get_config_file(NULL);
1617         
1618
1619 #ifdef WIN32
1620         /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1621         if ((lang=getenv("LANG"))!=NULL){
1622                 if (atoi(lang)!=0){
1623                         char tmp[128];
1624                         snprintf(tmp,sizeof(tmp),"LANG=",lang);
1625                         _putenv(tmp);
1626                 }
1627         }
1628 #else
1629         /*for pulseaudio:*/
1630         g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1631 #endif
1632
1633         if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1634 #ifdef WIN32
1635                 char tmp[128];
1636                 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1637                 _putenv(tmp);
1638 #else
1639                 setenv("LANG",lang,1);
1640 #endif
1641         }
1642
1643 #ifdef ENABLE_NLS
1644         p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1645         if (p==NULL) perror("bindtextdomain failed");
1646         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1647         textdomain (GETTEXT_PACKAGE);
1648 #else
1649         g_message("NLS disabled.\n");
1650 #endif
1651 #ifdef WIN32
1652         gtk_rc_add_default_file("./gtkrc");
1653 #endif
1654         gdk_threads_enter();
1655         
1656         if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1657                                 linphone_options,NULL,NULL)){
1658                 gdk_threads_leave();
1659                 return -1;
1660         }
1661         
1662         settings=gtk_settings_get_default();
1663         g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1664         g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1665         g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1666         g_object_set(settings, "gtk-button-images", TRUE, NULL);
1667
1668         if (workingdir!=NULL){
1669                 if (chdir(workingdir)==-1){
1670                         g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1671                 }
1672         }
1673
1674         /* Now, look for the factory configuration file, we do it this late
1675                  since we want to have had time to change directory and to parse
1676                  the options, in case we needed to access the working directory */
1677         factory_config_file = linphone_gtk_get_factory_config_file();
1678
1679         if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1680                 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1681                 g_warning("This instance is going to exit now.");
1682                 gdk_threads_leave();
1683                 return 0;
1684         }
1685
1686         add_pixmap_directory("pixmaps");
1687         add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1688
1689 #ifdef HAVE_GTK_OSX
1690         GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1691         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1692         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1693         /*never block termination:*/
1694         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1695 #endif
1696         
1697         the_ui=linphone_gtk_create_window("main");
1698         
1699         linphone_gtk_create_log_window();
1700         linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1701
1702         linphone_gtk_init_liblinphone(config_file, factory_config_file);
1703         
1704         g_set_application_name(app_name);
1705         pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1706         if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1707         
1708         /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1709         gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1710         gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1711         linphone_gtk_init_main_window();
1712 #ifndef HAVE_GTK_OSX
1713         linphone_gtk_init_status_icon();
1714 #endif
1715         if (!iconified){
1716                 linphone_gtk_show_main_window();
1717                 linphone_gtk_check_soundcards();
1718         }
1719         if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1720                 linphone_gtk_check_for_new_version();
1721
1722         gtk_main();
1723         linphone_gtk_quit();
1724 #ifndef HAVE_GTK_OSX
1725         /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1726         gtk_status_icon_set_visible(icon,FALSE);
1727 #endif
1728         free(progpath);
1729         return 0;
1730 }
1731