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