]> sjero.net Git - linphone/blob - gtk/main.c
Merge branch 'dev_conference'
[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 }
949
950 #ifdef HAVE_NOTIFY
951 static void make_notification(const char *title, const char *body){
952         NotifyNotification *n;
953         n = notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
954         if (n && !notify_notification_show(n,NULL))
955                         ms_error("Failed to send notification.");
956 }
957
958 #endif
959
960 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
961 #ifdef HAVE_NOTIFY
962         if (!notify_is_initted())
963                 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
964 #endif
965         if (!call) {
966 #ifdef HAVE_NOTIFY
967                 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL),NULL))
968                                 ms_error("Failed to send notification.");
969 #else
970                 linphone_gtk_show_main_window();
971 #endif
972         } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
973 #ifdef HAVE_NOTIFY
974                 char *body=NULL;
975                 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
976                 switch(linphone_call_get_state(call)){
977                         case LinphoneCallError:
978                                 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
979                         break;
980                         case LinphoneCallEnd:
981                                 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
982                         break;
983                         case LinphoneCallIncomingReceived:
984                                 make_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
985                         break;
986                         case LinphoneCallPausedByRemote:
987                                 make_notification(_("Call paused"),body=g_markup_printf_escaped("<span size=\"large\">by %s</span>",remote));
988                         break;
989                         default:
990                         break;
991                 }
992                 if (body) g_free(body);
993                 if (remote) g_free(remote);
994 #endif
995         }
996 }
997
998 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
999         switch(cs){
1000                 case LinphoneCallOutgoingInit:
1001                         linphone_gtk_create_in_call_view (call);
1002                 break;
1003                 case LinphoneCallOutgoingProgress:
1004                         linphone_gtk_in_call_view_set_calling (call);
1005                 break;
1006                 case LinphoneCallStreamsRunning:
1007                         linphone_gtk_in_call_view_set_in_call(call);
1008                 break;
1009                 case LinphoneCallError:
1010                         linphone_gtk_in_call_view_terminate (call,msg);
1011                 break;
1012                 case LinphoneCallEnd:
1013                         linphone_gtk_in_call_view_terminate(call,NULL);
1014                         linphone_gtk_status_icon_set_blinking(FALSE);
1015                 break;
1016                 case LinphoneCallIncomingReceived:
1017                         linphone_gtk_create_in_call_view (call);
1018                         linphone_gtk_in_call_view_set_incoming(call,!all_other_calls_paused (call,linphone_core_get_calls(lc)));
1019                         linphone_gtk_status_icon_set_blinking(TRUE);
1020                         if (auto_answer)  {
1021                                 linphone_call_ref(call);
1022                                 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1023                         }               
1024                 break;
1025                 case LinphoneCallResuming:
1026                         linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1027                         linphone_gtk_in_call_view_set_in_call (call);
1028                 break;
1029                 case LinphoneCallPausing:
1030                         linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1031                 case LinphoneCallPausedByRemote:
1032                         linphone_gtk_in_call_view_set_paused(call);
1033                 break;
1034                 case LinphoneCallConnected:
1035                         linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1036                         linphone_gtk_status_icon_set_blinking(FALSE);
1037                 break;
1038                 default:
1039                 break;
1040         }
1041         linphone_gtk_notify(call, msg);
1042         linphone_gtk_update_call_buttons (call);
1043 }
1044
1045 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1046         GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1047         GtkTreeModel *model=gtk_combo_box_get_model(box);
1048         GtkTreeIter iter;
1049         gboolean found=FALSE;
1050         const char *stock_id=NULL;
1051         
1052         if (gtk_tree_model_get_iter_first(model,&iter)){
1053                 gpointer p;
1054                 do{
1055                         gtk_tree_model_get(model,&iter,2,&p,-1);
1056                         if (p==cfg) {
1057                                 found=TRUE;
1058                                 break;
1059                         }
1060                 }while(gtk_tree_model_iter_next(model,&iter));
1061         }
1062         if (!found) {
1063                 g_warning("Could not find proxy config in combo box of identities.");
1064                 return;
1065         }
1066         switch (rs){
1067                 case LinphoneRegistrationOk:
1068                         stock_id=GTK_STOCK_YES;
1069                 break;
1070                 case LinphoneRegistrationProgress:
1071                         stock_id=GTK_STOCK_REFRESH;
1072                 break;
1073                 case LinphoneRegistrationCleared:
1074                         stock_id=NULL;
1075                 break;
1076                 case LinphoneRegistrationFailed:
1077                         stock_id=GTK_STOCK_DIALOG_WARNING;
1078                 break;
1079                 default:
1080                 break;
1081         }
1082         gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1083 }
1084
1085 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, 
1086                                                     LinphoneRegistrationState rs, const char *msg){
1087         switch (rs){
1088                 case LinphoneRegistrationOk:
1089                         if (cfg){
1090                                 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1091                                 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1092                                         linphone_gtk_exit_login_frame();
1093                                 }
1094                         }
1095                 break;
1096                 default:
1097                 break;
1098         }
1099         update_registration_status(cfg,rs);
1100 }
1101
1102
1103 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1104         GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1105         gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1106 }
1107
1108 void linphone_gtk_open_browser(const char *url){
1109         /*in gtk 2.16, gtk_show_uri does not work...*/
1110 #ifndef WIN32
1111 #if GTK_CHECK_VERSION(2,18,3)
1112         gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1113 #else
1114         char cl[255];
1115         snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1116         g_spawn_command_line_async(cl,NULL);
1117 #endif
1118 #else /*WIN32*/
1119         ShellExecute(0,"open",url,NULL,NULL,1);
1120 #endif
1121 }
1122
1123 void linphone_gtk_link_to_website(GtkWidget *item){
1124         const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1125         linphone_gtk_open_browser(home);
1126 }
1127
1128 static GtkWidget *create_icon_menu(){
1129         GtkWidget *menu=gtk_menu_new();
1130         GtkWidget *menu_item;
1131         GtkWidget *image;
1132         gchar *tmp;
1133         const gchar *homesite;
1134         
1135         homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1136         menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1137         tmp=g_strdup(homesite);
1138         g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1139         g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1140         
1141         image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1142         gtk_widget_show(image);
1143         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1144         //g_object_unref(G_OBJECT(image));
1145         gtk_widget_show(menu_item);
1146         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1147         g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1148         
1149         menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1150         gtk_widget_show(menu_item);
1151         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1152         g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1153         menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1154         gtk_widget_show(menu_item);
1155         gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1156         g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1157         gtk_widget_show(menu);
1158         return menu;
1159 }
1160
1161 static GtkStatusIcon *icon=NULL;
1162
1163 static void linphone_gtk_init_status_icon(){
1164         const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1165         const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1166         GdkPixbuf *pbuf=create_pixbuf(icon_path);
1167         GtkWidget *menu=create_icon_menu();
1168         const char *title;
1169         title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1170         icon=gtk_status_icon_new_from_pixbuf(pbuf);
1171         gtk_status_icon_set_name(icon,title);
1172         g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)linphone_gtk_show_main_window,linphone_gtk_get_main_window());
1173         g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1174         gtk_status_icon_set_tooltip(icon,title);
1175         gtk_status_icon_set_visible(icon,TRUE);
1176         g_object_set_data(G_OBJECT(icon),"menu",menu);
1177         g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1178         g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1179         g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1180         pbuf=create_pixbuf(call_icon_path);
1181         g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1182 }
1183
1184 static gboolean do_icon_blink(GtkStatusIcon *gi){
1185         GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1186         GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1187         GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1188         if (cur_icon==call_icon){
1189                 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1190         }else{
1191                 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1192         }
1193         return TRUE;
1194 }
1195
1196 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1197         guint tout;
1198         tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1199         if (val && tout==0){
1200                 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1201                 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1202         }else if (!val && tout!=0){
1203                 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1204                 g_source_remove(tout);
1205                 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1206                 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1207         }
1208 }
1209
1210 static void init_identity_combo(GtkComboBox *box){
1211         GtkListStore *store;
1212         GtkCellRenderer *r1,*r2;
1213         store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1214         gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1215         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1216         gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1217         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1218         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1219         g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1220         gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1221 }
1222
1223 void linphone_gtk_load_identities(void){
1224         const MSList *elem;
1225         GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1226         char *def_identity;
1227         LinphoneProxyConfig *def=NULL;
1228         int def_index=0,i;
1229         GtkListStore *store;
1230         GtkTreeIter iter;
1231
1232         store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1233         if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1234                 /* model is empty, this is the first time we go here */
1235                 init_identity_combo(box);
1236                 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1237         }
1238         gtk_list_store_clear(store);
1239         linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1240         def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1241         gtk_list_store_append(store,&iter);
1242         gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1243         g_free(def_identity);
1244         for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1245                         elem!=NULL;
1246                         elem=ms_list_next(elem),i++){
1247                 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1248                 gtk_list_store_append(store,&iter);
1249                 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1250                                    linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1251                                    2,cfg,-1);
1252                 if (cfg==def) {
1253                         def_index=i;
1254                 }
1255         }
1256         gtk_combo_box_set_active(box,def_index);
1257 }
1258
1259 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1260         const char *label=gtk_button_get_label(button);
1261         GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1262         int pos=-1;
1263         gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1264         linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1265         if (linphone_core_in_call(linphone_gtk_get_core())){
1266                 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1267         }
1268 }
1269
1270 static void linphone_gtk_dtmf_released(GtkButton *button){
1271         linphone_core_stop_dtmf (linphone_gtk_get_core());
1272 }
1273
1274 static void linphone_gtk_connect_digits(void){
1275         GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1276         GList *children=gtk_container_get_children(cont);
1277         GList *elem;
1278         for(elem=children;elem!=NULL;elem=elem->next){
1279                 GtkButton *button=GTK_BUTTON(elem->data);
1280                 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1281                 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1282         }
1283 }
1284
1285 static void linphone_gtk_check_menu_items(void){
1286         bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core());
1287         bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1288         GtkWidget *selfview_item=linphone_gtk_get_widget(
1289                                         linphone_gtk_get_main_window(),"selfview_item");
1290         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1291                                         linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1292         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1293         gtk_widget_set_sensitive(selfview_item,video_enabled);
1294 }
1295
1296 static gboolean linphone_gtk_can_manage_accounts(){
1297         LinphoneCore *lc=linphone_gtk_get_core();
1298         const MSList *elem;
1299         for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1300                 SipSetup *ss=(SipSetup*)elem->data;
1301                 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1302                         return TRUE;
1303                 }
1304         }
1305         return FALSE;
1306 }
1307
1308 static void linphone_gtk_configure_main_window(){
1309         static gboolean config_loaded=FALSE;
1310         static const char *title;
1311         static const char *home;
1312         static const char *start_call_icon;
1313         static const char *add_call_icon;
1314         static const char *stop_call_icon;
1315         static const char *search_icon;
1316         static gboolean update_check_menu;
1317         static gboolean buttons_have_borders;
1318         static gboolean show_abcd;
1319         GtkWidget *w=linphone_gtk_get_main_window();
1320         if (!config_loaded){
1321                 title=linphone_gtk_get_ui_config("title","Linphone");
1322                 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1323                 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1324                 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1325                 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1326                 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1327                 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1328                 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1329                 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1330                 config_loaded=TRUE;
1331         }
1332         linphone_gtk_configure_window(w,"main_window");
1333         if (title) {
1334                 gtk_window_set_title(GTK_WINDOW(w),title);
1335         }
1336         if (start_call_icon){
1337                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1338                                     create_pixmap (start_call_icon));
1339                 if (!buttons_have_borders)
1340                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1341         }
1342         if (add_call_icon){
1343                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1344                                     create_pixmap (add_call_icon));
1345                 if (!buttons_have_borders)
1346                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1347         }
1348         if (stop_call_icon){
1349                 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1350                                     create_pixmap (stop_call_icon));
1351                 if (!buttons_have_borders)
1352                         gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1353         }
1354         if (search_icon){
1355                 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1356                 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1357                 g_object_unref(G_OBJECT(pbuf));
1358         }
1359         if (home){
1360                 gchar *tmp;
1361                 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1362                 tmp=g_strdup(home);
1363                 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1364         }
1365         {
1366                 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1367                 if (pbuf) {
1368                         gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1369                         g_object_unref(G_OBJECT(pbuf));
1370                 }
1371         }
1372         {
1373                 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1374                 if (pbuf) {
1375                         gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon")),pbuf);
1376                         g_object_unref(G_OBJECT(pbuf));
1377                 }
1378         }
1379         if (linphone_gtk_can_manage_accounts())
1380                 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1381         if (update_check_menu){
1382                 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1383         }
1384         if (!show_abcd){
1385                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1386                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1387                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1388                 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1389                 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1390         }
1391 }
1392
1393 void linphone_gtk_manage_login(void){
1394         LinphoneCore *lc=linphone_gtk_get_core();
1395         LinphoneProxyConfig *cfg=NULL;
1396         linphone_core_get_default_proxy(lc,&cfg);
1397         if (cfg){
1398                 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1399                 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1400                         linphone_gtk_show_login_frame(cfg);
1401                 }
1402         }
1403 }
1404
1405
1406 gboolean linphone_gtk_close(GtkWidget *mw){
1407         /*shutdown calls if any*/
1408         LinphoneCore *lc=linphone_gtk_get_core();
1409         if (linphone_core_in_call(lc)){
1410                 linphone_core_terminate_all_calls(lc);
1411         }
1412         linphone_core_enable_video_preview(lc,FALSE);
1413 #ifdef __APPLE__ /*until with have a better option*/
1414         gtk_window_iconify(GTK_WINDOW(mw));
1415 #else
1416         gtk_widget_hide(mw);
1417 #endif
1418         return TRUE;
1419 }
1420
1421 #ifdef HAVE_GTK_OSX
1422 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1423         if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1424                 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1425         }else{
1426                 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1427                 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1428         }
1429         return FALSE;
1430 }
1431 #endif
1432
1433
1434 static void linphone_gtk_init_main_window(){
1435         GtkWidget *main_window;
1436
1437         linphone_gtk_configure_main_window();
1438         linphone_gtk_manage_login();
1439         load_uri_history();
1440         linphone_gtk_load_identities();
1441         linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1442         linphone_gtk_show_friends();
1443         linphone_gtk_connect_digits();
1444         main_window=linphone_gtk_get_main_window();
1445
1446         linphone_gtk_update_call_buttons (NULL);
1447         /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1448         g_signal_connect (G_OBJECT (main_window), "delete-event",
1449                 G_CALLBACK (linphone_gtk_close), main_window);
1450 #ifdef HAVE_GTK_OSX
1451         {
1452                 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1453                 gtk_widget_destroy(linphone_gtk_get_widget(main_window,"imagemenuitem5"));
1454                 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1455                 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1456                 gtk_widget_hide(menubar);
1457                 gtk_osxapplication_ready(theMacApp);
1458         }
1459         g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1460 #endif
1461         linphone_gtk_check_menu_items();
1462 }
1463
1464
1465 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1466         if (verbose){
1467                 const char *lname="undef";
1468                 char *msg;
1469 #if defined(__linux) || defined(__APPLE__)
1470                 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1471 #endif
1472                 switch(lev){
1473                         case ORTP_DEBUG:
1474                                 lname="debug";
1475                                 break;
1476                         case ORTP_MESSAGE:
1477                                 lname="message";
1478                                 break;
1479                         case ORTP_WARNING:
1480                                 lname="warning";
1481                                 break;
1482                         case ORTP_ERROR:
1483                                 lname="error";
1484                                 break;
1485                         case ORTP_FATAL:
1486                                 lname="fatal";
1487                                 break;
1488                         default:
1489                                 g_error("Bad level !");
1490                 }
1491 #if defined(__linux) || defined(__APPLE__)
1492                 va_copy(cap,args);
1493                 msg=g_strdup_vprintf(fmt,cap);
1494                 va_end(cap);
1495 #else
1496                 msg=g_strdup_vprintf(fmt,args);
1497 #endif
1498                 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1499                 ortp_free(msg);
1500         }
1501         linphone_gtk_log_push(lev,fmt,args);
1502 }
1503
1504
1505 static void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1506         GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1507                 linphone_gtk_get_main_window(), "uribar"));
1508         char *text;
1509         linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1510         g_free(text);
1511         gtk_entry_set_text(uri_bar, refer_to);
1512         linphone_gtk_start_call(linphone_gtk_get_main_window());
1513 }
1514
1515 static void linphone_gtk_check_soundcards(){
1516         const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1517         if (devices==NULL || devices[0]==NULL){
1518                 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1519                         _("No sound cards have been detected on this computer.\n"
1520                                 "You won't be able to send or receive audio calls."));
1521         }
1522 }
1523
1524 static void linphone_gtk_quit(void){
1525         gdk_threads_leave();
1526         linphone_gtk_destroy_log_window();
1527         linphone_core_destroy(the_core);
1528         linphone_gtk_log_uninit();
1529 #ifdef HAVE_NOTIFY
1530         notify_uninit();
1531 #endif
1532 }
1533
1534 #ifdef HAVE_GTK_OSX
1535 /*
1536 This is not the correct way to implement block termination.
1537 The good way would be to call gtk_main_quit(), and return TRUE.
1538 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1539 As a result the program cannot exit at all.
1540 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1541 */
1542 static gboolean on_block_termination(void){
1543         gtk_main_quit();
1544         linphone_gtk_quit();
1545         return FALSE;
1546 }
1547 #endif
1548
1549 int main(int argc, char *argv[]){
1550 #ifdef ENABLE_NLS
1551         void *p;
1552 #endif
1553         const char *config_file;
1554         const char *factory_config_file;
1555         const char *lang;
1556         GtkSettings *settings;
1557         GdkPixbuf *pbuf;
1558
1559         g_thread_init(NULL);
1560         gdk_threads_init();
1561         
1562         progpath = strdup(argv[0]);
1563         
1564         config_file=linphone_gtk_get_config_file();
1565
1566 #ifdef WIN32
1567         /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1568         if ((lang=getenv("LANG"))!=NULL){
1569                 if (atoi(lang)!=0){
1570                         char tmp[128];
1571                         snprintf(tmp,sizeof(tmp),"LANG=",lang);
1572                         _putenv(tmp);
1573                 }
1574         }
1575 #else
1576         /*for pulseaudio:*/
1577         g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1578 #endif
1579
1580         if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1581 #ifdef WIN32
1582                 char tmp[128];
1583                 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1584                 _putenv(tmp);
1585 #else
1586                 setenv("LANG",lang,1);
1587 #endif
1588         }
1589
1590 #ifdef ENABLE_NLS
1591         p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1592         if (p==NULL) perror("bindtextdomain failed");
1593         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1594         textdomain (GETTEXT_PACKAGE);
1595 #else
1596         g_message("NLS disabled.\n");
1597 #endif
1598 #ifdef WIN32
1599         gtk_rc_add_default_file("./gtkrc");
1600 #endif
1601         gdk_threads_enter();
1602         
1603         if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1604                                 linphone_options,NULL,NULL)){
1605                 gdk_threads_leave();
1606                 return -1;
1607         }
1608         
1609         settings=gtk_settings_get_default();
1610         g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1611         g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1612         g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1613         g_object_set(settings, "gtk-button-images", TRUE, NULL);
1614
1615         if (workingdir!=NULL){
1616                 if (chdir(workingdir)==-1){
1617                         g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1618                 }
1619         }
1620
1621         /* Now, look for the factory configuration file, we do it this late
1622                  since we want to have had time to change directory and to parse
1623                  the options, in case we needed to access the working directory */
1624         factory_config_file = linphone_gtk_get_factory_config_file();
1625
1626         if (linphone_core_wake_up_possible_already_running_instance(
1627                 config_file, addr_to_call) == 0){
1628                 g_message("addr_to_call=%s",addr_to_call);
1629                 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1630                 g_warning("This instance is going to exit now.");
1631                 gdk_threads_leave();
1632                 return 0;
1633         }
1634
1635         add_pixmap_directory("pixmaps");
1636         add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1637
1638 #ifdef HAVE_GTK_OSX
1639         GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1640         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1641         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1642         /*never block termination:*/
1643         g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1644 #endif
1645         
1646         the_ui=linphone_gtk_create_window("main");
1647         
1648         linphone_gtk_create_log_window();
1649         linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1650
1651         linphone_gtk_init_liblinphone(config_file, factory_config_file);
1652         
1653         g_set_application_name(linphone_gtk_get_ui_config("title","Linphone"));
1654         pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1655         if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1656         
1657         /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1658         gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1659         gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1660         linphone_gtk_init_main_window();
1661 #ifndef HAVE_GTK_OSX
1662         linphone_gtk_init_status_icon();
1663 #endif
1664         if (!iconified){
1665                 linphone_gtk_show_main_window();
1666                 linphone_gtk_check_soundcards();
1667         }
1668         if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1669                 linphone_gtk_check_for_new_version();
1670
1671         gtk_main();
1672         linphone_gtk_quit();
1673 #ifndef HAVE_GTK_OSX
1674         /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1675         gtk_status_icon_set_visible(icon,FALSE);
1676 #endif
1677         free(progpath);
1678         return 0;
1679 }
1680