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