]> sjero.net Git - linphone/blob - gtk/main.c
b35b8f4c49aa5a79148f4cac4e4b9aad62be7b28
[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 //#define USE_LIBGLADE 1
21
22 #define VIDEOSELFVIEW_DEFAULT 1
23
24 #include "linphone.h"
25 #include "lpconfig.h"
26
27
28
29 #ifdef USE_LIBGLADE
30 #include <glade/glade.h>
31 #endif
32
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36
37 #ifdef WIN32
38 #define chdir _chdir
39 #endif
40
41 #define LINPHONE_ICON "linphone.png"
42
43 const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION;
44
45 static LinphoneCore *the_core=NULL;
46 static GtkWidget *the_ui=NULL;
47
48 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg);
49 static void linphone_gtk_show(LinphoneCore *lc);
50 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
51 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
52 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username);
53 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
54 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
55 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
56 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
57 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
58 static void linphone_gtk_refer_received(LinphoneCore *lc, const char  *refer_to);
59 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
60 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
61
62
63 static gboolean verbose=0;
64 static gboolean auto_answer = 0;
65 static gchar * addr_to_call = NULL;
66 static gboolean iconified=FALSE;
67 static gchar *workingdir=NULL;
68 static char *progpath=NULL;
69
70 static GOptionEntry linphone_options[]={
71         {
72                 .long_name="verbose",
73                 .short_name= '\0',
74                 .arg=G_OPTION_ARG_NONE,
75                 .arg_data= (gpointer)&verbose,
76                 .description=N_("log to stdout some debug information while running.")
77         },
78         {
79                 .long_name="iconified",
80                 .short_name= '\0',
81                 .arg=G_OPTION_ARG_NONE,
82                 .arg_data= (gpointer)&iconified,
83                 .description=N_("Start only in the system tray, do not show the main interface.")
84         },
85         {
86             .long_name = "call",
87             .short_name = 'c',
88             .arg = G_OPTION_ARG_STRING,
89             .arg_data = &addr_to_call,
90             .description = N_("address to call right now")
91         },
92         {
93             .long_name = "auto-answer",
94             .short_name = 'a',
95             .arg = G_OPTION_ARG_NONE,
96             .arg_data = (gpointer) & auto_answer,
97             .description = N_("if set automatically answer incoming calls")
98         },
99         {
100             .long_name = "workdir",
101             .short_name = '\0',
102             .arg = G_OPTION_ARG_STRING,
103             .arg_data = (gpointer) & workingdir,
104             .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
105         },
106         {0}
107 };
108
109 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
110 #define BUILD_TREE_XML_DIR "gtk"
111
112 #ifndef WIN32
113 #define CONFIG_FILE ".linphonerc"
114 #else
115 #define CONFIG_FILE "linphonerc"
116 #endif
117
118
119
120 static char _config_file[1024];
121
122
123 const char *linphone_gtk_get_config_file(){
124         /*try accessing a local file first if exists*/
125         if (access(CONFIG_FILE,F_OK)==0){
126                 snprintf(_config_file,sizeof(_config_file),"%s",CONFIG_FILE);
127         }else{
128 #ifdef WIN32
129                 const char *appdata=getenv("APPDATA");
130                 if (appdata){
131                         snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
132                         CreateDirectory(_config_file,NULL);
133                         snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR "\\" CONFIG_FILE);
134                 }
135 #else
136                 const char *home=getenv("HOME");
137                 if (home==NULL) home=".";
138                 snprintf(_config_file,sizeof(_config_file),"%s/%s",home,CONFIG_FILE);
139 #endif
140         }
141         return _config_file;
142 }
143
144
145 #define FACTORY_CONFIG_FILE "linphonerc.factory"
146 static char _factory_config_file[1024];
147 static const char *linphone_gtk_get_factory_config_file(){
148         /*try accessing a local file first if exists*/
149         if (access(FACTORY_CONFIG_FILE,F_OK)==0){
150                 snprintf(_factory_config_file,sizeof(_factory_config_file),
151                                                  "%s",FACTORY_CONFIG_FILE);
152         } else {
153                 char *progdir;
154                 
155                 if (progpath != NULL) {
156                         char *basename;
157                         progdir = strdup(progpath);
158 #ifdef WIN32
159                         basename = strrchr(progdir, '\\');
160                         if (basename != NULL) {
161                                 basename ++;
162                                 *basename = '\0';
163                                 snprintf(_factory_config_file, sizeof(_factory_config_file),
164                                                                  "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
165                         } else {
166                                 if (workingdir!=NULL) {
167                                         snprintf(_factory_config_file, sizeof(_factory_config_file),
168                                                                          "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
169                                 } else {
170                                         free(progdir);
171                                         return NULL;
172                                 }
173                         }
174 #else
175                         basename = strrchr(progdir, '/');
176                         if (basename != NULL) {
177                                 basename ++;
178                                 *basename = '\0';
179                                 snprintf(_factory_config_file, sizeof(_factory_config_file),
180                                                                  "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
181                         } else {
182                                 free(progdir);
183                                 return NULL;
184                         }
185 #endif
186                         free(progdir);
187                 }
188         }
189         return _factory_config_file;
190 }
191
192 static void linphone_gtk_init_liblinphone(const char *config_file,
193                 const char *factory_config_file) {
194         LinphoneCoreVTable vtable={0};
195
196         vtable.call_state_changed=linphone_gtk_call_state_changed;
197         vtable.registration_state_changed=linphone_gtk_registration_state_changed;
198         vtable.show=linphone_gtk_show;
199         vtable.notify_presence_recv=linphone_gtk_notify_recv;
200         vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
201         vtable.auth_info_requested=linphone_gtk_auth_info_requested;
202         vtable.display_status=linphone_gtk_display_status;
203         vtable.display_message=linphone_gtk_display_message;
204         vtable.display_warning=linphone_gtk_display_warning;
205         vtable.display_url=linphone_gtk_display_url;
206         vtable.call_log_updated=linphone_gtk_call_log_updated;
207         vtable.text_received=linphone_gtk_text_received;
208         vtable.refer_received=linphone_gtk_refer_received;
209         vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
210
211         linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
212         the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
213         linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
214 }
215
216
217
218 LinphoneCore *linphone_gtk_get_core(void){
219         return the_core;
220 }
221
222 GtkWidget *linphone_gtk_get_main_window(){
223         return the_ui;
224 }
225
226 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
227         static const char *icon_path=NULL;
228         static const char *hiddens=NULL;
229         static const char *shown=NULL;
230         static bool_t config_loaded=FALSE;
231         if (linphone_gtk_get_core()==NULL) return;
232         if (config_loaded==FALSE){
233                 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
234                 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
235                 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
236                 config_loaded=TRUE;
237         }
238         if (hiddens)
239                 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
240         if (shown)
241                 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
242         if (icon_path) {
243                 GdkPixbuf *pbuf=create_pixbuf(icon_path);
244                 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
245                 g_object_unref(G_OBJECT(pbuf));
246         }
247 }
248
249 #ifdef USE_LIBGLADE
250
251 GtkWidget *linphone_gtk_create_window(const char *window_name){
252         GtkWidget *w;
253         GladeXML *gxml;
254         char path[2048];
255         snprintf(path,sizeof(path),"%s/%s.glade",BUILD_TREE_XML_DIR,window_name);
256         if (access(path,F_OK)!=0){
257                 snprintf(path,sizeof(path),"%s/%s.glade",INSTALLED_XML_DIR,window_name);
258                 if (access(path,F_OK)!=0){
259                         g_error("Could not locate neither %s/%s.glade and %s/%s.glade .",BUILD_TREE_XML_DIR,window_name,
260                                 INSTALLED_XML_DIR,window_name);
261                         return NULL;
262                 }
263         }
264         gxml=glade_xml_new(path,NULL,NULL);
265         glade_xml_signal_autoconnect(gxml);
266         w=glade_xml_get_widget(gxml,window_name);
267         if (w==NULL) g_error("Could not retrieve '%s' window from xml file",window_name);
268         linphone_gtk_configure_window(w,window_name);
269         return w;
270 }
271
272 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
273         GtkWidget *w;
274         GladeXML *gxml=glade_get_widget_tree(window);
275         if (gxml==NULL) g_error("Could not retrieve XML tree of window %s",name);
276         w=glade_xml_get_widget(gxml,name);
277         if (w==NULL) g_error("Could not retrieve widget %s",name);
278         return GTK_WIDGET(w);
279 }
280
281 #else
282
283 static int get_ui_file(const char *name, char *path, int pathsize){
284         snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
285         if (access(path,F_OK)!=0){
286                 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
287                 if (access(path,F_OK)!=0){
288                         g_error("Could not locate neither %s/%s.ui and %s/%s.ui .",BUILD_TREE_XML_DIR,name,
289                                 INSTALLED_XML_DIR,name);
290                         return -1;
291                 }
292         }
293         return 0;
294 }
295
296 GtkWidget *linphone_gtk_create_window(const char *window_name){
297         GError* error = NULL;
298         GtkBuilder* builder = gtk_builder_new ();
299         char path[512];
300         GtkWidget *w;
301
302         if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
303         
304         if (!gtk_builder_add_from_file (builder, path, &error)){
305                 g_error("Couldn't load builder file: %s", error->message);
306                 g_error_free (error);
307                 return NULL;
308         }
309         w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
310         if (w==NULL){
311                 g_error("Could not retrieve '%s' window from xml file",window_name);
312                 return NULL;
313         }
314         g_object_set_data(G_OBJECT(w),"builder",builder);
315         gtk_builder_connect_signals(builder,w);
316         linphone_gtk_configure_window(w,window_name);
317         return w;
318 }
319
320 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
321         char path[2048];
322         GtkWidget *w;
323         GtkBuilder* builder = gtk_builder_new ();
324         GError *error=NULL;
325         gchar *object_ids[2];
326         object_ids[0]=g_strdup(widget_name);
327         object_ids[1]=NULL;
328         
329         if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
330         if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
331                 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
332                 g_error_free (error);
333                 g_free(object_ids[0]);
334                 return NULL;
335         }
336         g_free(object_ids[0]);
337         w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
338         if (w==NULL){
339                 g_error("Could not retrieve '%s' window from xml file",widget_name);
340                 return NULL;
341         }
342         g_object_set_data(G_OBJECT(w),"builder",builder);
343         gtk_builder_connect_signals(builder,w);
344         return w;
345 }
346
347 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
348         GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
349         GObject *w;
350         if (builder==NULL){
351                 g_error("Fail to retrieve builder from window !");
352                 return NULL;
353         }
354         w=gtk_builder_get_object(builder,name);
355         if (w==NULL){
356                 g_error("No widget named %s found in xml interface.",name);
357         }
358         return GTK_WIDGET(w);
359 }
360
361 #endif
362
363 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
364         GtkWidget *dialog;
365         GtkWidget *main_window=linphone_gtk_get_main_window();
366         
367         gtk_widget_show(main_window);
368         if (type==GTK_MESSAGE_QUESTION)
369         {
370                 /* draw a question box. link to dialog_click callback */
371                 dialog = gtk_message_dialog_new (
372                                 GTK_WINDOW(main_window),
373                                 GTK_DIALOG_DESTROY_WITH_PARENT,
374                                 GTK_MESSAGE_QUESTION,
375                                 GTK_BUTTONS_YES_NO,
376                                 "%s",
377                                 (const gchar*)message);
378                 /* connect to some callback : REVISIT */
379                 /*
380                 g_signal_connect_swapped (G_OBJECT (dialog), "response",
381                            G_CALLBACK (dialog_click),
382                            G_OBJECT (dialog));
383                 */
384                 /* actually show the box */
385                 gtk_widget_show(dialog);
386         }
387         else
388         {
389                 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
390                                   GTK_DIALOG_DESTROY_WITH_PARENT,
391                                   type,
392                                   GTK_BUTTONS_CLOSE,
393                                   "%s",
394                                   (const gchar*)message);
395                 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
396                 g_signal_connect_swapped (G_OBJECT (dialog), "response",
397                            G_CALLBACK (gtk_widget_destroy),
398                            G_OBJECT (dialog));
399                 gtk_widget_show(dialog);
400         }
401 }
402
403 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
404         if (id==GTK_RESPONSE_CANCEL){
405                 gtk_widget_destroy(GTK_WIDGET(dialog));
406         }
407 }
408
409 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
410         g_message("About url clicked");
411         linphone_gtk_open_browser(url);
412 }
413
414 void linphone_gtk_show_about(){
415         struct stat filestat;
416         const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
417         GtkWidget *about;
418         const char *tmp;
419         GdkPixbuf *logo=create_pixbuf(
420             linphone_gtk_get_ui_config("logo","linphone-banner.png"));
421         static const char *defcfg="defcfg";
422         
423         about=linphone_gtk_create_window("about");
424         gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
425         memset(&filestat,0,sizeof(filestat));
426         if (stat(license_file,&filestat)!=0){
427                 license_file="COPYING";
428                 stat(license_file,&filestat);
429         }
430         if (filestat.st_size>0){
431                 char *license=g_malloc(filestat.st_size+1);
432                 FILE *f=fopen(license_file,"r");
433                 if (f && fread(license,filestat.st_size,1,f)==1){
434                         license[filestat.st_size]='\0';
435                         gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
436                 }
437                 g_free(license);
438         }
439         gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
440         gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
441         gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
442         if (logo)       gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
443         tmp=linphone_gtk_get_ui_config("artists",defcfg);
444         if (tmp!=defcfg){
445                 const char *tmp2[2];
446                 tmp2[0]=tmp;
447                 tmp2[1]=NULL;
448                 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
449         }
450         tmp=linphone_gtk_get_ui_config("translators",defcfg);
451         if (tmp!=defcfg)
452                 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
453         tmp=linphone_gtk_get_ui_config("comments",defcfg);
454         if (tmp!=defcfg)
455                 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
456         gtk_widget_show(about);
457 }
458
459 static void set_video_window_decorations(GdkWindow *w){
460         const char *title=linphone_gtk_get_ui_config("title","Linphone");
461         const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
462         char video_title[256];
463         GdkPixbuf *pbuf=create_pixbuf(icon_path);
464         if (!linphone_core_in_call(linphone_gtk_get_core())){
465                 snprintf(video_title,sizeof(video_title),"%s video",title);
466                 /* When not in call, treat the video as a normal window */
467                 gdk_window_set_keep_above(w, FALSE);
468         }else{
469                 LinphoneAddress *uri =
470                         linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
471                 char *display_name;
472
473                 linphone_address_clean(uri);
474                 if (linphone_address_get_display_name(uri)!=NULL){
475                         display_name=ms_strdup(linphone_address_get_display_name(uri));
476                 }else{
477                         display_name=linphone_address_as_string(uri);
478                 }
479                 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
480                 linphone_address_destroy(uri);
481                 ms_free(display_name);
482
483                 /* During calls, bring up the video window, arrange so that
484                 it is above all the other windows */
485                 gdk_window_deiconify(w);
486                 gdk_window_set_keep_above(w,TRUE);
487                 /* Maybe we should have the following, but then we want to
488                 have a timer that turns it off after a little while. */
489                 /* gdk_window_set_urgency_hint(w,TRUE); */
490         }
491         gdk_window_set_title(w,video_title);
492         /* Refrain the video window to be closed at all times. */
493         gdk_window_set_functions(w,
494                                  GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
495                                  GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
496         if (pbuf){
497                 GList *l=NULL;
498                 l=g_list_append(l,pbuf);
499                 gdk_window_set_icon_list(w,l);
500                 g_list_free(l);
501                 g_object_unref(G_OBJECT(pbuf));
502         }
503 }
504
505 static gboolean video_needs_update=FALSE;
506
507 static void update_video_title(){
508         video_needs_update=TRUE;
509 }
510
511 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
512         static gboolean first_time=TRUE;
513         unsigned long id;
514         static unsigned long previd=0;
515         static unsigned long preview_previd=0;
516         static gboolean in_iterate=FALSE;
517         
518         /*avoid reentrancy*/
519         if (in_iterate) return TRUE;
520         in_iterate=TRUE;
521         linphone_core_iterate(lc);
522         if (first_time){
523                 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
524                 linphone_gtk_show_directory_search();
525                 first_time=FALSE;
526         }
527
528         id=linphone_core_get_native_video_window_id(lc);
529         if (id!=previd || video_needs_update){
530                 GdkWindow *w;
531                 previd=id;
532                 if (id!=0){
533                         ms_message("Updating window decorations");
534 #ifndef WIN32
535                         w=gdk_window_foreign_new(id);
536 #else
537                         w=gdk_window_foreign_new((HANDLE)id);
538 #endif
539                         if (w) {
540                                 set_video_window_decorations(w);
541                                 g_object_unref(G_OBJECT(w));
542                         }
543                         else ms_error("gdk_window_foreign_new() failed");
544                         if (video_needs_update) video_needs_update=FALSE;
545                 }
546         }
547         id=linphone_core_get_native_preview_window_id (lc);
548         if (id!=preview_previd ){
549                 GdkWindow *w;
550                 preview_previd=id;
551                 if (id!=0){
552                         ms_message("Updating window decorations for preview");
553 #ifndef WIN32
554                         w=gdk_window_foreign_new(id);
555 #else
556                         w=gdk_window_foreign_new((HANDLE)id);
557 #endif
558                         if (w) {
559                                 set_video_window_decorations(w);
560                                 g_object_unref(G_OBJECT(w));
561                         }
562                         else ms_error("gdk_window_foreign_new() failed");
563                         if (video_needs_update) video_needs_update=FALSE;
564                 }
565         }
566         if (addr_to_call!=NULL){
567                 /*make sure we are not showing the login screen*/
568                 GtkWidget *mw=linphone_gtk_get_main_window();
569                 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
570                 if (!GTK_WIDGET_VISIBLE(login_frame)){
571                         GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
572                         gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
573                         addr_to_call=NULL;
574                         linphone_gtk_start_call(uri_bar);
575                 }
576         }
577         in_iterate=FALSE;
578         return TRUE;
579 }
580
581 static void load_uri_history(){
582         GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
583         char key[20];
584         int i;
585         GtkEntryCompletion *gep=gtk_entry_completion_new();
586         GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
587         for (i=0;;i++){
588                 const char *uri;
589                 snprintf(key,sizeof(key),"uri%i",i);
590                 uri=linphone_gtk_get_ui_config(key,NULL);
591                 if (uri!=NULL) {
592                         GtkTreeIter iter;
593                         gtk_list_store_append(model,&iter);
594                         gtk_list_store_set(model,&iter,0,uri,-1);
595                         if (i==0) gtk_entry_set_text(uribar,uri);
596                 }
597                 else break;
598         }
599         gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
600         gtk_entry_completion_set_text_column(gep,0);
601         gtk_entry_set_completion(uribar,gep);
602 }
603
604 static void save_uri_history(){
605         LinphoneCore *lc=linphone_gtk_get_core();
606         LpConfig *cfg=linphone_core_get_config(lc);
607         GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
608         char key[20];
609         int i=0;
610         char *uri=NULL;
611         GtkTreeIter iter;
612         GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
613
614         if (!gtk_tree_model_get_iter_first(model,&iter)) return;
615         do {
616                 gtk_tree_model_get(model,&iter,0,&uri,-1);
617                 if (uri) {
618                         snprintf(key,sizeof(key),"uri%i",i);
619                         lp_config_set_string(cfg,"GtkUi",key,uri);
620                         g_free(uri);
621                 }else break;
622                 i++;
623                 if (i>5) break;
624         }while(gtk_tree_model_iter_next(model,&iter));
625         lp_config_sync(cfg);
626 }
627
628 static void completion_add_text(GtkEntry *entry, const char *text){
629         GtkTreeIter iter;
630         GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
631         
632         if (gtk_tree_model_get_iter_first(model,&iter)){ 
633                 do {
634                         gchar *uri=NULL;
635                         gtk_tree_model_get(model,&iter,0,&uri,-1);
636                         if (uri!=NULL){
637                                 if (strcmp(uri,text)==0) {
638                                         /*remove text */
639                                         gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
640                                         g_free(uri);
641                                         break;
642                                 }
643                                 g_free(uri);
644                         }
645                 }while (gtk_tree_model_iter_next(model,&iter));
646         }
647         /* and prepend it on top of the list */
648         gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
649         gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
650         save_uri_history();
651 }
652
653 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
654         GtkWidget *mw=linphone_gtk_get_main_window();
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 gboolean in_call_timer(){
664         LinphoneCall *call=linphone_core_get_current_call(linphone_gtk_get_core());
665         if (call){
666                 linphone_gtk_in_call_view_update_duration(call);
667                 return TRUE;
668         }
669         return FALSE;
670 }
671
672 static bool_t all_other_calls_paused(LinphoneCall *refcall, const MSList *calls){
673         for(;calls!=NULL;calls=calls->next){
674                 LinphoneCall *call=(LinphoneCall*)calls->data;
675                 LinphoneCallState cs=linphone_call_get_state(call);
676                 if (refcall!=call){
677                         if (cs!=LinphoneCallPaused  && cs!=LinphoneCallPausing)
678                                 return FALSE;
679                 }
680         }
681         return TRUE;
682 }
683
684 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
685         LinphoneCore *lc=linphone_gtk_get_core();
686         GtkWidget *mw=linphone_gtk_get_main_window();
687         const MSList *calls=linphone_core_get_calls(lc);
688         GtkWidget *button;
689         bool_t start_active=TRUE;
690         bool_t stop_active=FALSE;
691         bool_t add_call=FALSE;
692         int call_list_size=ms_list_size(calls);
693         
694         if (calls==NULL){
695                 start_active=TRUE;
696                 stop_active=FALSE;
697         }else{
698                 stop_active=TRUE;
699                 if (all_other_calls_paused(NULL,calls)){
700                         start_active=TRUE;
701                         add_call=TRUE;
702                 }else if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived && all_other_calls_paused(call,calls)){
703                         if (call_list_size>1){
704                                 start_active=TRUE;
705                                 add_call=TRUE;
706                         }else{
707                                 start_active=TRUE;
708                                 add_call=FALSE;
709                         }
710                 }else{
711                         start_active=FALSE;
712                 }
713         }
714         button=linphone_gtk_get_widget(mw,"start_call");
715         gtk_widget_set_sensitive(button,start_active);
716         gtk_widget_set_visible(button,!add_call);
717         
718         button=linphone_gtk_get_widget(mw,"add_call");
719         gtk_widget_set_sensitive(button,start_active);
720         gtk_widget_set_visible(button,add_call);
721         
722         gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
723         if (linphone_core_get_calls(lc)==NULL){
724                 linphone_gtk_enable_mute_button(
725                                 GTK_BUTTON(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"main_mute")),
726                         FALSE);
727         }
728         linphone_gtk_enable_transfer_button(lc,call_list_size>1);
729         update_video_title();
730 }
731
732 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
733         const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
734         if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
735                 completion_add_text(GTK_ENTRY(uri_bar),entered);
736         }else{
737                 linphone_gtk_call_terminated(NULL,NULL);
738         }
739         return FALSE;
740 }
741
742 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
743         if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
744                 linphone_core_accept_call (linphone_gtk_get_core(),call);
745                 linphone_call_unref(call);
746         }
747         return FALSE;
748 }
749
750
751 void linphone_gtk_start_call(GtkWidget *w){
752         LinphoneCore *lc=linphone_gtk_get_core();
753         LinphoneCall *call;
754         /*change into in-call mode, then do the work later as it might block a bit */
755         GtkWidget *mw=gtk_widget_get_toplevel(w);
756         GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
757
758         call=linphone_gtk_get_currently_displayed_call ();
759         if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
760                 linphone_core_accept_call(lc,call);
761         }else{
762                 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
763                 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
764                 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
765                 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
766         }
767         
768 }
769
770 void linphone_gtk_uri_bar_activate(GtkWidget *w){
771         linphone_gtk_start_call(w);
772 }
773
774
775 void linphone_gtk_terminate_call(GtkWidget *button){
776         LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
777         if (call)
778                 linphone_core_terminate_call(linphone_gtk_get_core(),call);
779 }
780
781 void linphone_gtk_decline_clicked(GtkWidget *button){
782         LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
783         if (call)
784                 linphone_core_terminate_call(linphone_gtk_get_core(),call);
785 }
786
787 void linphone_gtk_answer_clicked(GtkWidget *button){
788         LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
789         if (call){
790                 linphone_core_pause_all_calls(linphone_gtk_get_core());
791                 linphone_core_accept_call(linphone_gtk_get_core(),call);
792         }
793 }
794
795 void linphone_gtk_set_audio_video(){
796         linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE);
797         linphone_core_enable_video_preview(linphone_gtk_get_core(),
798             linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
799 }
800
801 void linphone_gtk_set_audio_only(){
802         linphone_core_enable_video(linphone_gtk_get_core(),FALSE,FALSE);
803         linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
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 static void linphone_gtk_show_main_window(){
825         GtkWidget *w=linphone_gtk_get_main_window();
826         LinphoneCore *lc=linphone_gtk_get_core();
827         if (linphone_core_video_enabled(lc)){
828                 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
829                 VIDEOSELFVIEW_DEFAULT));
830         }
831         gtk_widget_show(w);
832         gtk_window_present(GTK_WINDOW(w));
833 }
834
835 static void linphone_gtk_show(LinphoneCore *lc){
836         linphone_gtk_show_main_window();
837 }
838
839 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
840         linphone_gtk_show_friends();
841 }
842
843 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
844         switch(response_id){
845                 case GTK_RESPONSE_YES:
846                         linphone_gtk_show_contact(lf);
847                 break;
848                 default:
849                         linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
850         }
851         gtk_widget_destroy(dialog);
852 }
853
854 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
855         GtkWidget *dialog;
856
857         if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
858                 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
859                 return;
860         }
861
862         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);
863         dialog = gtk_message_dialog_new (
864                                 GTK_WINDOW(linphone_gtk_get_main_window()),
865                                 GTK_DIALOG_DESTROY_WITH_PARENT,
866                                 GTK_MESSAGE_QUESTION,
867                                 GTK_BUTTONS_YES_NO,
868                                 "%s",
869                                 message);
870         g_free(message);
871         g_signal_connect(G_OBJECT (dialog), "response",
872                 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
873         /* actually show the box */
874         gtk_widget_show(dialog);
875 }
876
877 typedef struct _AuthTimeout{
878         GtkWidget *w;
879 } AuthTimeout;
880
881
882 static void auth_timeout_clean(AuthTimeout *tout){
883         tout->w=NULL;
884 }
885
886 static gboolean auth_timeout_destroy(AuthTimeout *tout){
887         if (tout->w)  {
888                 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
889                 gtk_widget_destroy(tout->w);
890         }
891         g_free(tout);
892         return FALSE;
893 }
894
895 static AuthTimeout * auth_timeout_new(GtkWidget *w){
896         AuthTimeout *tout=g_new(AuthTimeout,1);
897         tout->w=w;
898         /*so that the timeout no more references the widget when it is destroyed:*/
899         g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
900         /*so that the widget is automatically destroyed after some time */
901         g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
902         return tout;
903 }
904
905 void linphone_gtk_password_cancel(GtkWidget *w){
906         LinphoneAuthInfo *info;
907         GtkWidget *window=gtk_widget_get_toplevel(w);
908         info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
909         linphone_core_abort_authentication(linphone_gtk_get_core(),info);
910         gtk_widget_destroy(window);
911 }
912
913 void linphone_gtk_password_ok(GtkWidget *w){
914         GtkWidget *entry;
915         GtkWidget *window=gtk_widget_get_toplevel(w);
916         LinphoneAuthInfo *info;
917         info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
918         g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
919         entry=linphone_gtk_get_widget(window,"password_entry");
920         linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
921         linphone_auth_info_set_userid(info,
922                 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
923         linphone_core_add_auth_info(linphone_gtk_get_core(),info);
924         gtk_widget_destroy(window);
925 }
926
927 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
928         GtkWidget *w=linphone_gtk_create_window("password");
929         GtkWidget *label=linphone_gtk_get_widget(w,"message");
930         LinphoneAuthInfo *info;
931         gchar *msg;
932         GtkWidget *mw=linphone_gtk_get_main_window();
933         
934         if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
935                 /*don't prompt for authentication when login frame is visible*/
936                 linphone_core_abort_authentication(lc,NULL);
937                 return;
938         }
939
940         msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
941                 username,realm);
942         gtk_label_set_markup(GTK_LABEL(label),msg);
943         g_free(msg);
944         gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
945         info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
946         g_object_set_data(G_OBJECT(w),"auth_info",info);
947         g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
948         gtk_widget_show(w);
949         auth_timeout_new(w);
950 }
951
952 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
953         GtkWidget *w=linphone_gtk_get_main_window();
954         GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
955         gtk_statusbar_push(GTK_STATUSBAR(status_bar),
956                         gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
957                         status);
958 }
959
960 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
961         linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
962 }
963
964 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
965         linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
966 }
967
968 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
969         char richtext[4096];
970         snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
971         linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
972 }
973
974 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
975         GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
976         if (w) linphone_gtk_call_log_update(w);
977 }
978
979 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
980         switch(cs){
981                 case LinphoneCallOutgoingInit:
982                         linphone_gtk_create_in_call_view (call);
983                 break;
984                 case LinphoneCallOutgoingProgress:
985                         linphone_gtk_in_call_view_set_calling (call);
986                 break;
987                 case LinphoneCallStreamsRunning:
988                         linphone_gtk_in_call_view_set_in_call(call);
989                         linphone_gtk_enable_mute_button(
990                                 GTK_BUTTON(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"main_mute")),
991                         TRUE);
992                         g_timeout_add(250,(GSourceFunc)in_call_timer,NULL);
993                 break;
994                 case LinphoneCallError:
995                         linphone_gtk_in_call_view_terminate (call,msg);
996                 break;
997                 case LinphoneCallEnd:
998                         linphone_gtk_in_call_view_terminate(call,NULL);
999                 break;
1000                 case LinphoneCallIncomingReceived:
1001                         linphone_gtk_create_in_call_view (call);
1002                         linphone_gtk_in_call_view_set_incoming(call,!all_other_calls_paused (call,linphone_core_get_calls(lc)));
1003                         if (auto_answer)  {
1004                                 linphone_call_ref(call);
1005                                 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1006                         }               
1007                 break;
1008                 case LinphoneCallResuming:
1009                         linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1010                         linphone_gtk_in_call_view_set_in_call (call);
1011                 break;
1012                 case LinphoneCallPausing:
1013                         linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1014                 case LinphoneCallPausedByRemote:
1015                         linphone_gtk_in_call_view_set_paused(call);
1016                 break;
1017                 case LinphoneCallConnected:
1018                         linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1019                 break;
1020                 default:
1021                 break;
1022         }
1023         linphone_gtk_update_call_buttons (call);
1024 }
1025
1026 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, 
1027                                                     LinphoneRegistrationState rs, const char *msg){
1028         switch (rs){
1029                 case LinphoneRegistrationOk:
1030                         if (cfg){
1031                                 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1032                                 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1033                                         linphone_gtk_exit_login_frame();
1034                                 }
1035                         }
1036                 break;
1037                 default:
1038                 break;
1039         }
1040 }
1041
1042
1043 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1044         GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1045         gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1046 }
1047
1048 void linphone_gtk_open_browser(const char *url){
1049         /*in gtk 2.16, gtk_show_uri does not work...*/
1050 #ifndef WIN32
1051 #if GTK_CHECK_VERSION(2,18,3)
1052         gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1053 #else
1054         char cl[255];
1055         snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1056         g_spawn_command_line_async(cl,NULL);
1057 #endif
1058 #else /*WIN32*/
1059         ShellExecute(0,"open",url,NULL,NULL,1);
1060 #endif
1061 }
1062
1063 void linphone_gtk_link_to_website(GtkWidget *item){
1064         const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1065         linphone_gtk_open_browser(home);
1066 }
1067
1068 static GtkWidget *create_icon_menu(){
1069         GtkWidget *menu=gtk_menu_new();
1070         GtkWidget *menu_item;
1071         GtkWidget *image;
1072         gchar *tmp;
1073         const gchar *homesite;
1074         
1075         homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1076         menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1077         tmp=g_strdup(homesite);
1078         g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1079         g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1080         
1081         image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1082         gtk_widget_show(image);
1083         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1084         //g_object_unref(G_OBJECT(image));
1085         gtk_widget_show(menu_item);
1086         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1087         g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1088         
1089         menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1090         gtk_widget_show(menu_item);
1091         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1092         g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1093         menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1094         gtk_widget_show(menu_item);
1095         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1096         g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1097         gtk_widget_show(menu);
1098         return menu;
1099 }
1100
1101 static GtkStatusIcon *icon=NULL;
1102
1103 static void linphone_gtk_init_status_icon(){
1104         const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1105         GdkPixbuf *pbuf=create_pixbuf(icon_path);
1106         GtkWidget *menu=create_icon_menu();
1107         const char *title;
1108         icon=gtk_status_icon_new_from_pixbuf(pbuf);
1109         g_object_unref(G_OBJECT(pbuf));
1110         g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)linphone_gtk_show_main_window,linphone_gtk_get_main_window());
1111         g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1112         title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1113         gtk_status_icon_set_tooltip(icon,title);
1114         gtk_status_icon_set_visible(icon,TRUE);
1115         g_object_set_data(G_OBJECT(icon),"menu",menu);
1116         g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1117 }
1118
1119 void linphone_gtk_load_identities(void){
1120         const MSList *elem;
1121         GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1122         char *def_identity;
1123         LinphoneProxyConfig *def=NULL;
1124         int def_index=0,i;
1125         GtkListStore *store;
1126
1127         store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1128         gtk_list_store_clear(store);
1129
1130         linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1131         def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1132         gtk_combo_box_append_text(box,def_identity);
1133         g_free(def_identity);
1134         for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1135                         elem!=NULL;
1136                         elem=ms_list_next(elem),i++){
1137                 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1138                 gtk_combo_box_append_text(box,linphone_proxy_config_get_identity(cfg));
1139                 if (cfg==def) {
1140                         def_index=i;
1141                 }
1142         }
1143         gtk_combo_box_set_active(box,def_index);
1144 }
1145
1146 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1147         const char *label=gtk_button_get_label(button);
1148         GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1149         int pos=-1;
1150         gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1151         linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1152         if (linphone_core_in_call(linphone_gtk_get_core())){
1153                 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1154         }
1155 }
1156
1157 static void linphone_gtk_dtmf_released(GtkButton *button){
1158         linphone_core_stop_dtmf (linphone_gtk_get_core());
1159 }
1160
1161 static void linphone_gtk_connect_digits(void){
1162         GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1163         GList *children=gtk_container_get_children(cont);
1164         GList *elem;
1165         for(elem=children;elem!=NULL;elem=elem->next){
1166                 GtkButton *button=GTK_BUTTON(elem->data);
1167                 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1168                 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1169         }
1170 }
1171
1172 static void linphone_gtk_check_menu_items(void){
1173         bool_t audio_only=!linphone_core_video_enabled(linphone_gtk_get_core());
1174         bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1175         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1176                                         linphone_gtk_get_main_window(),
1177                                         audio_only ? "audio_only_item" : "video_item")), TRUE);
1178         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1179                                         linphone_gtk_get_main_window(),"selfview_item")),selfview);
1180 }
1181
1182 static gboolean linphone_gtk_can_manage_accounts(){
1183         LinphoneCore *lc=linphone_gtk_get_core();
1184         const MSList *elem;
1185         for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1186                 SipSetup *ss=(SipSetup*)elem->data;
1187                 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1188                         return TRUE;
1189                 }
1190         }
1191         return FALSE;
1192 }
1193
1194 static void linphone_gtk_configure_main_window(){
1195         static gboolean config_loaded=FALSE;
1196         static const char *title;
1197         static const char *home;
1198         static const char *start_call_icon;
1199         static const char *add_call_icon;
1200         static const char *stop_call_icon;
1201         static const char *search_icon;
1202         static gboolean update_check_menu;
1203         static gboolean buttons_have_borders;
1204         static gboolean show_abcd;
1205         GtkWidget *w=linphone_gtk_get_main_window();
1206         if (!config_loaded){
1207                 title=linphone_gtk_get_ui_config("title","Linphone");
1208                 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1209                 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1210                 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1211                 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1212                 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1213                 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1214                 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1215                 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1216                 config_loaded=TRUE;
1217         }
1218         linphone_gtk_configure_window(w,"main_window");
1219         if (title) {
1220                 gtk_window_set_title(GTK_WINDOW(w),title);
1221 #if GTK_CHECK_VERSION(2,16,0)
1222                 gtk_menu_item_set_label(GTK_MENU_ITEM(linphone_gtk_get_widget(w,"main_menu")),title);
1223 #endif
1224         }
1225         if (start_call_icon){
1226                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1227                                     create_pixmap (start_call_icon));
1228                 if (!buttons_have_borders)
1229                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1230         }
1231         if (add_call_icon){
1232                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1233                                     create_pixmap (add_call_icon));
1234                 if (!buttons_have_borders)
1235                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1236         }
1237         if (stop_call_icon){
1238                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1239                                     create_pixmap (stop_call_icon));
1240                 if (!buttons_have_borders)
1241                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1242         }
1243         if (search_icon){
1244                 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1245                 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1246                 g_object_unref(G_OBJECT(pbuf));
1247         }
1248         if (home){
1249                 gchar *tmp;
1250                 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1251                 tmp=g_strdup(home);
1252                 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1253         }
1254         {
1255                 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1256                 if (pbuf) {
1257                         gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1258                         g_object_unref(G_OBJECT(pbuf));
1259                 }
1260         }
1261         {
1262                 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1263                 if (pbuf) {
1264                         gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon")),pbuf);
1265                         g_object_unref(G_OBJECT(pbuf));
1266                 }
1267         }
1268         if (linphone_gtk_can_manage_accounts())
1269                 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1270         if (update_check_menu){
1271                 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1272         }
1273         if (!show_abcd){
1274                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1275                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1276                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1277                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1278                 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1279         }
1280 }
1281
1282 void linphone_gtk_manage_login(void){
1283         LinphoneCore *lc=linphone_gtk_get_core();
1284         LinphoneProxyConfig *cfg=NULL;
1285         linphone_core_get_default_proxy(lc,&cfg);
1286         if (cfg){
1287                 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1288                 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1289                         linphone_gtk_show_login_frame(cfg);
1290                 }
1291         }
1292 }
1293
1294
1295 gboolean linphone_gtk_close(GtkWidget *mw){
1296         /*shutdown calls if any*/
1297         LinphoneCore *lc=linphone_gtk_get_core();
1298         if (linphone_core_in_call(lc)){
1299                 linphone_core_terminate_all_calls(lc);
1300         }
1301         linphone_core_enable_video_preview(lc,FALSE);
1302         gtk_widget_hide(mw);
1303         return TRUE;
1304 }
1305
1306 static void linphone_gtk_init_main_window(){
1307         GtkWidget *main_window;
1308
1309         linphone_gtk_configure_main_window();
1310         linphone_gtk_manage_login();
1311         load_uri_history();
1312         linphone_gtk_load_identities();
1313         linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1314         linphone_gtk_show_friends();
1315         linphone_gtk_connect_digits();
1316         linphone_gtk_check_menu_items();
1317         main_window=linphone_gtk_get_main_window();
1318         linphone_gtk_enable_mute_button(GTK_BUTTON(linphone_gtk_get_widget(main_window,
1319                                         "main_mute")),FALSE);
1320         if (!linphone_gtk_use_in_call_view()) {
1321                 gtk_widget_show(linphone_gtk_get_widget(main_window, "main_mute"));
1322         }
1323         linphone_gtk_update_call_buttons (NULL);
1324         /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1325         g_signal_connect (G_OBJECT (main_window), "delete-event",
1326                 G_CALLBACK (linphone_gtk_close), main_window);
1327 }
1328
1329
1330 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1331         if (verbose){
1332                 const char *lname="undef";
1333                 char *msg;
1334 #if defined(__linux) || defined(__APPLE__)
1335                 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1336 #endif
1337                 switch(lev){
1338                         case ORTP_DEBUG:
1339                                 lname="debug";
1340                                 break;
1341                         case ORTP_MESSAGE:
1342                                 lname="message";
1343                                 break;
1344                         case ORTP_WARNING:
1345                                 lname="warning";
1346                                 break;
1347                         case ORTP_ERROR:
1348                                 lname="error";
1349                                 break;
1350                         case ORTP_FATAL:
1351                                 lname="fatal";
1352                                 break;
1353                         default:
1354                                 g_error("Bad level !");
1355                 }
1356 #if defined(__linux) || defined(__APPLE__)
1357                 va_copy(cap,args);
1358                 msg=g_strdup_vprintf(fmt,cap);
1359                 va_end(cap);
1360 #else
1361                 msg=g_strdup_vprintf(fmt,args);
1362 #endif
1363                 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1364                 ortp_free(msg);
1365         }
1366         linphone_gtk_log_push(lev,fmt,args);
1367 }
1368
1369
1370 static void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1371     GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1372                 linphone_gtk_get_main_window(), "uribar"));
1373         linphone_gtk_show_main_window();
1374         gtk_entry_set_text(uri_bar, refer_to);
1375         linphone_gtk_start_call(linphone_gtk_get_main_window());
1376 }
1377
1378 static void linphone_gtk_check_soundcards(){
1379         const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1380         if (devices==NULL || devices[0]==NULL){
1381                 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1382                     _("No sound cards have been detected on this computer.\n"
1383                             "You won't be able to send or receive audio calls."));
1384         }
1385 }
1386
1387 int main(int argc, char *argv[]){
1388 #ifdef ENABLE_NLS
1389         void *p;
1390 #endif
1391         const char *config_file;
1392         const char *factory_config_file;
1393         const char *lang;
1394         GtkSettings *settings;
1395         GdkPixbuf *pbuf;
1396
1397         g_thread_init(NULL);
1398         gdk_threads_init();
1399         
1400         progpath = strdup(argv[0]);
1401         
1402         config_file=linphone_gtk_get_config_file();
1403
1404 #ifdef WIN32
1405         /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1406         if ((lang=getenv("LANG"))!=NULL){
1407                 if (atoi(lang)!=0){
1408                         char tmp[128];
1409                         snprintf(tmp,sizeof(tmp),"LANG=",lang);
1410                         _putenv(tmp);
1411                 }
1412         }
1413 #else
1414         /*for pulseaudio:*/
1415         g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1416 #endif
1417
1418         if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1419 #ifdef WIN32
1420                 char tmp[128];
1421                 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1422                 _putenv(tmp);
1423 #else
1424                 setenv("LANG",lang,1);
1425 #endif
1426         }
1427
1428 #ifdef ENABLE_NLS
1429         p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1430         if (p==NULL) perror("bindtextdomain failed");
1431         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1432         textdomain (GETTEXT_PACKAGE);
1433 #else
1434         g_message("NLS disabled.\n");
1435 #endif
1436 #ifdef WIN32
1437         gtk_rc_add_default_file("./gtkrc");
1438 #endif
1439         gdk_threads_enter();
1440         
1441         if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1442                                 linphone_options,NULL,NULL)){
1443                 gdk_threads_leave();
1444                 return -1;
1445         }
1446         
1447         settings=gtk_settings_get_default();
1448         g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1449         g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1450         g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1451         g_object_set(settings, "gtk-button-images", TRUE, NULL);
1452
1453         if (workingdir!=NULL)
1454                 chdir(workingdir);
1455
1456         /* Now, look for the factory configuration file, we do it this late
1457                  since we want to have had time to change directory and to parse
1458                  the options, in case we needed to access the working directory */
1459         factory_config_file = linphone_gtk_get_factory_config_file();
1460
1461         if (linphone_core_wake_up_possible_already_running_instance(
1462                 config_file, addr_to_call) == 0){
1463                 g_message("addr_to_call=%s",addr_to_call);
1464                 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1465                 g_warning("This instance is going to exit now.");
1466                 gdk_threads_leave();
1467                 return 0;
1468         }
1469
1470         add_pixmap_directory("pixmaps");
1471         add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1472
1473         
1474         
1475         
1476         the_ui=linphone_gtk_create_window("main");
1477         
1478         linphone_gtk_create_log_window();
1479         linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1480
1481         linphone_gtk_init_liblinphone(config_file, factory_config_file);
1482         
1483         g_set_application_name(linphone_gtk_get_ui_config("title","Linphone"));
1484         pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1485         if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1486         
1487         /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1488         gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1489         gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1490         linphone_gtk_init_main_window();
1491         linphone_gtk_init_status_icon();
1492         if (!iconified){
1493                 linphone_gtk_show_main_window();
1494                 linphone_gtk_check_soundcards();
1495         }
1496         if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1497                 linphone_gtk_check_for_new_version();
1498
1499         gtk_main();
1500         gdk_threads_leave();
1501         linphone_gtk_destroy_log_window();
1502         linphone_core_destroy(the_core);
1503         /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1504         gtk_status_icon_set_visible(icon,FALSE);
1505         free(progpath);
1506         return 0;
1507 }
1508