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