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