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