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