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