2 linphone, gtk-glade interface.
3 Copyright (C) 2008 Simon MORLAT (simon.morlat@linphone.org)
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.
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.
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.
21 #define VIDEOSELFVIEW_DEFAULT 0
27 #include <sys/types.h>
32 #include <gtkosxapplication.h>
39 #if defined(HAVE_NOTIFY1) || defined(HAVE_NOTIFY4)
44 #include <libnotify/notify.h>
47 #define LINPHONE_ICON "linphone.png"
49 const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION;
51 static LinphoneCore *the_core=NULL;
52 static GtkWidget *the_ui=NULL;
53 GtkWidget *the_wizard=NULL;
55 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg);
56 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
57 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
58 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username);
59 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
60 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
61 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
62 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
63 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
64 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
65 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token);
66 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate);
67 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
68 void linphone_gtk_status_icon_set_blinking(gboolean val);
69 void _linphone_gtk_enable_video(gboolean val);
73 static gboolean verbose=0;
74 static gboolean auto_answer = 0;
75 static gchar * addr_to_call = NULL;
76 static gboolean no_video=FALSE;
77 static gboolean iconified=FALSE;
78 static gchar *workingdir=NULL;
79 static char *progpath=NULL;
80 gchar *linphone_logfile=NULL;
81 static gboolean workaround_gtk_entry_chinese_bug=FALSE;
83 static GOptionEntry linphone_options[]={
87 .arg=G_OPTION_ARG_NONE,
88 .arg_data= (gpointer)&verbose,
89 .description=N_("log to stdout some debug information while running.")
92 .long_name = "logfile",
94 .arg = G_OPTION_ARG_STRING,
95 .arg_data = &linphone_logfile,
96 .description = N_("path to a file to write logs into.")
99 .long_name = "no-video",
101 .arg = G_OPTION_ARG_NONE,
102 .arg_data = (gpointer)&no_video,
103 .description = N_("Start linphone with video disabled.")
106 .long_name="iconified",
108 .arg=G_OPTION_ARG_NONE,
109 .arg_data= (gpointer)&iconified,
110 .description=N_("Start only in the system tray, do not show the main interface.")
115 .arg = G_OPTION_ARG_STRING,
116 .arg_data = &addr_to_call,
117 .description = N_("address to call right now")
120 .long_name = "auto-answer",
122 .arg = G_OPTION_ARG_NONE,
123 .arg_data = (gpointer) & auto_answer,
124 .description = N_("if set automatically answer incoming calls")
127 .long_name = "workdir",
129 .arg = G_OPTION_ARG_STRING,
130 .arg_data = (gpointer) & workingdir,
131 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
136 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
137 #define RELATIVE_XML_DIR
138 #define BUILD_TREE_XML_DIR "gtk"
141 #define CONFIG_FILE ".linphonerc"
142 #define SECRETS_FILE ".linphone-zidcache"
144 #define CONFIG_FILE "linphonerc"
145 #define SECRETS_FILE "linphone-zidcache"
148 char *linphone_gtk_get_config_file(const char *filename){
149 const int path_max=1024;
150 char *config_file=g_malloc0(path_max);
151 if (filename==NULL) filename=CONFIG_FILE;
152 /*try accessing a local file first if exists*/
153 if (access(CONFIG_FILE,F_OK)==0){
154 snprintf(config_file,path_max,"%s",filename);
157 const char *appdata=getenv("APPDATA");
159 snprintf(config_file,path_max,"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
160 CreateDirectory(config_file,NULL);
161 snprintf(config_file,path_max,"%s\\%s\\%s",appdata,LINPHONE_CONFIG_DIR,filename);
164 const char *home=getenv("HOME");
165 if (home==NULL) home=".";
166 snprintf(config_file,path_max,"%s/%s",home,filename);
172 #define FACTORY_CONFIG_FILE "linphonerc.factory"
173 static char _factory_config_file[1024];
174 static const char *linphone_gtk_get_factory_config_file(){
175 /*try accessing a local file first if exists*/
176 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
177 snprintf(_factory_config_file,sizeof(_factory_config_file),
178 "%s",FACTORY_CONFIG_FILE);
182 if (progpath != NULL) {
184 progdir = strdup(progpath);
186 basename = strrchr(progdir, '\\');
187 if (basename != NULL) {
190 snprintf(_factory_config_file, sizeof(_factory_config_file),
191 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
193 if (workingdir!=NULL) {
194 snprintf(_factory_config_file, sizeof(_factory_config_file),
195 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
202 basename = strrchr(progdir, '/');
203 if (basename != NULL) {
206 snprintf(_factory_config_file, sizeof(_factory_config_file),
207 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
216 return _factory_config_file;
219 static void linphone_gtk_init_liblinphone(const char *config_file,
220 const char *factory_config_file) {
221 LinphoneCoreVTable vtable={0};
222 gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
224 vtable.call_state_changed=linphone_gtk_call_state_changed;
225 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
226 vtable.notify_presence_recv=linphone_gtk_notify_recv;
227 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
228 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
229 vtable.display_status=linphone_gtk_display_status;
230 vtable.display_message=linphone_gtk_display_message;
231 vtable.display_warning=linphone_gtk_display_warning;
232 vtable.display_url=linphone_gtk_display_url;
233 vtable.call_log_updated=linphone_gtk_call_log_updated;
234 vtable.text_received=linphone_gtk_text_received;
235 vtable.refer_received=linphone_gtk_refer_received;
236 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
237 vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
238 vtable.transfer_state_changed=linphone_gtk_transfer_state_changed;
240 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
241 linphone_core_set_user_agent(the_core,"Linphone", LINPHONE_VERSION);
242 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
243 linphone_core_set_zrtp_secrets_file(the_core,secrets_file);
244 g_free(secrets_file);
245 linphone_core_enable_video(the_core,TRUE,TRUE);
247 _linphone_gtk_enable_video(FALSE);
248 linphone_gtk_set_ui_config_int("videoselfview",0);
252 LinphoneCore *linphone_gtk_get_core(void){
256 GtkWidget *linphone_gtk_get_main_window(){
260 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
261 static const char *icon_path=NULL;
262 static const char *hiddens=NULL;
263 static const char *shown=NULL;
264 static bool_t config_loaded=FALSE;
265 if (linphone_gtk_get_core()==NULL) return;
266 if (config_loaded==FALSE){
267 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
268 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
269 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
273 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
275 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
277 GdkPixbuf *pbuf=create_pixbuf(icon_path);
279 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
280 g_object_unref(G_OBJECT(pbuf));
285 static int get_ui_file(const char *name, char *path, int pathsize){
286 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
287 if (access(path,F_OK)!=0){
288 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
289 if (access(path,F_OK)!=0){
290 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
291 INSTALLED_XML_DIR,name);
298 GtkWidget *linphone_gtk_create_window(const char *window_name){
299 GError* error = NULL;
300 GtkBuilder* builder = gtk_builder_new ();
304 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
306 if (!gtk_builder_add_from_file (builder, path, &error)){
307 g_error("Couldn't load builder file: %s", error->message);
308 g_error_free (error);
311 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
313 g_error("Could not retrieve '%s' window from xml file",window_name);
316 g_object_set_data(G_OBJECT(w),"builder",builder);
317 gtk_builder_connect_signals(builder,w);
318 linphone_gtk_configure_window(w,window_name);
322 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
325 GtkBuilder* builder = gtk_builder_new ();
327 gchar *object_ids[2];
328 object_ids[0]=g_strdup(widget_name);
331 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
332 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
333 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
334 g_error_free (error);
335 g_free(object_ids[0]);
338 g_free(object_ids[0]);
339 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
341 g_error("Could not retrieve '%s' window from xml file",widget_name);
344 g_object_set_data(G_OBJECT(w),"builder",builder);
345 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
346 gtk_builder_connect_signals(builder,w);
350 static void entry_unmapped(GtkWidget *entry){
351 g_message("Entry is unmapped, calling unrealize to workaround chinese bug.");
352 gtk_widget_unrealize(entry);
355 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
358 if (window==NULL) return NULL;
359 builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
361 g_error("Fail to retrieve builder from window !");
364 w=gtk_builder_get_object(builder,name);
366 g_error("No widget named %s found in xml interface.",name);
368 if (workaround_gtk_entry_chinese_bug){
369 if (strcmp(G_OBJECT_TYPE_NAME(w),"GtkEntry")==0){
370 if (g_object_get_data(G_OBJECT(w),"entry_bug_workaround")==NULL){
371 g_object_set_data(G_OBJECT(w),"entry_bug_workaround",GINT_TO_POINTER(1));
372 g_message("%s is a GtkEntry",name);
373 g_signal_connect(G_OBJECT(w),"unmap",(GCallback)entry_unmapped,NULL);
377 return GTK_WIDGET(w);
381 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
383 GtkWidget *main_window=linphone_gtk_get_main_window();
385 gtk_widget_show(main_window);
386 if (type==GTK_MESSAGE_QUESTION)
388 /* draw a question box. link to dialog_click callback */
389 dialog = gtk_message_dialog_new (
390 GTK_WINDOW(main_window),
391 GTK_DIALOG_DESTROY_WITH_PARENT,
392 GTK_MESSAGE_QUESTION,
395 (const gchar*)message);
396 /* connect to some callback : REVISIT */
398 g_signal_connect_swapped (G_OBJECT (dialog), "response",
399 G_CALLBACK (dialog_click),
402 /* actually show the box */
403 gtk_widget_show(dialog);
407 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
408 GTK_DIALOG_DESTROY_WITH_PARENT,
412 (const gchar*)message);
413 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
414 g_signal_connect_swapped (G_OBJECT (dialog), "response",
415 G_CALLBACK (gtk_widget_destroy),
417 gtk_widget_show(dialog);
421 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
422 if (id==GTK_RESPONSE_CANCEL){
423 gtk_widget_destroy(GTK_WIDGET(dialog));
427 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
428 g_message("About url clicked");
429 linphone_gtk_open_browser(url);
432 void linphone_gtk_show_about(){
433 struct stat filestat;
434 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
437 GdkPixbuf *logo=create_pixbuf(
438 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
439 static const char *defcfg="defcfg";
441 about=linphone_gtk_create_window("about");
442 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
443 memset(&filestat,0,sizeof(filestat));
444 if (stat(license_file,&filestat)!=0){
445 license_file="COPYING";
446 stat(license_file,&filestat);
448 if (filestat.st_size>0){
449 char *license=g_malloc(filestat.st_size+1);
450 FILE *f=fopen(license_file,"r");
451 if (f && fread(license,filestat.st_size,1,f)==1){
452 license[filestat.st_size]='\0';
453 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
457 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
458 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
459 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
460 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
461 tmp=linphone_gtk_get_ui_config("artists",defcfg);
466 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
468 tmp=linphone_gtk_get_ui_config("translators",defcfg);
470 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
471 tmp=linphone_gtk_get_ui_config("comments",defcfg);
473 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
474 gtk_widget_show(about);
477 static void set_video_window_decorations(GdkWindow *w){
478 const char *title=linphone_gtk_get_ui_config("title","Linphone");
479 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
480 char video_title[256];
481 GdkPixbuf *pbuf=create_pixbuf(icon_path);
483 if (!linphone_core_in_call(linphone_gtk_get_core())){
484 snprintf(video_title,sizeof(video_title),"%s video",title);
485 /* When not in call, treat the video as a normal window */
486 gdk_window_set_keep_above(w, FALSE);
488 LinphoneAddress *uri =
489 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
492 linphone_address_clean(uri);
493 if (linphone_address_get_display_name(uri)!=NULL){
494 display_name=ms_strdup(linphone_address_get_display_name(uri));
496 display_name=linphone_address_as_string(uri);
498 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
499 linphone_address_destroy(uri);
500 ms_free(display_name);
502 /* During calls, bring up the video window, arrange so that
503 it is above all the other windows */
504 gdk_window_deiconify(w);
505 gdk_window_set_keep_above(w,TRUE);
506 /* Maybe we should have the following, but then we want to
507 have a timer that turns it off after a little while. */
508 /* gdk_window_set_urgency_hint(w,TRUE); */
510 gdk_window_set_title(w,video_title);
511 /* Refrain the video window to be closed at all times. */
512 gdk_window_set_functions(w,
513 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
514 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
517 l=g_list_append(l,pbuf);
518 gdk_window_set_icon_list(w,l);
520 g_object_unref(G_OBJECT(pbuf));
524 static gboolean video_needs_update=FALSE;
526 static void update_video_title(){
527 video_needs_update=TRUE;
530 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
531 static gboolean first_time=TRUE;
533 static unsigned long previd=0;
534 static unsigned long preview_previd=0;
535 static gboolean in_iterate=FALSE;
538 if (in_iterate) return TRUE;
540 linphone_core_iterate(lc);
542 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
543 linphone_gtk_show_directory_search();
547 id=linphone_core_get_native_video_window_id(lc);
548 if (id!=previd || video_needs_update){
552 ms_message("Updating window decorations");
554 w=gdk_window_foreign_new(id);
556 w=gdk_window_foreign_new((HANDLE)id);
559 set_video_window_decorations(w);
560 g_object_unref(G_OBJECT(w));
562 else ms_error("gdk_window_foreign_new() failed");
563 if (video_needs_update) video_needs_update=FALSE;
566 id=linphone_core_get_native_preview_window_id (lc);
567 if (id!=preview_previd ){
571 ms_message("Updating window decorations for preview");
573 w=gdk_window_foreign_new(id);
575 w=gdk_window_foreign_new((HANDLE)id);
578 set_video_window_decorations(w);
579 g_object_unref(G_OBJECT(w));
581 else ms_error("gdk_window_foreign_new() failed");
582 if (video_needs_update) video_needs_update=FALSE;
585 if (addr_to_call!=NULL){
586 /*make sure we are not showing the login screen*/
587 GtkWidget *mw=linphone_gtk_get_main_window();
588 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
589 if (!GTK_WIDGET_VISIBLE(login_frame)){
590 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
591 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
593 linphone_gtk_start_call(uri_bar);
600 static void load_uri_history(){
601 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
604 GtkEntryCompletion *gep=gtk_entry_completion_new();
605 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
608 snprintf(key,sizeof(key),"uri%i",i);
609 uri=linphone_gtk_get_ui_config(key,NULL);
612 gtk_list_store_append(model,&iter);
613 gtk_list_store_set(model,&iter,0,uri,-1);
614 if (i==0) gtk_entry_set_text(uribar,uri);
618 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
619 gtk_entry_completion_set_text_column(gep,0);
620 gtk_entry_set_completion(uribar,gep);
623 static void save_uri_history(){
624 LinphoneCore *lc=linphone_gtk_get_core();
625 LpConfig *cfg=linphone_core_get_config(lc);
626 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
631 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
633 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
635 gtk_tree_model_get(model,&iter,0,&uri,-1);
637 snprintf(key,sizeof(key),"uri%i",i);
638 lp_config_set_string(cfg,"GtkUi",key,uri);
643 }while(gtk_tree_model_iter_next(model,&iter));
647 static void completion_add_text(GtkEntry *entry, const char *text){
649 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
651 if (gtk_tree_model_get_iter_first(model,&iter)){
654 gtk_tree_model_get(model,&iter,0,&uri,-1);
656 if (strcmp(uri,text)==0) {
658 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
664 }while (gtk_tree_model_iter_next(model,&iter));
666 /* and prepend it on top of the list */
667 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
668 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
672 bool_t linphone_gtk_video_enabled(void){
673 const LinphoneVideoPolicy *vpol=linphone_core_get_video_policy(linphone_gtk_get_core());
674 return vpol->automatically_accept && vpol->automatically_initiate;
677 void linphone_gtk_show_main_window(){
678 GtkWidget *w=linphone_gtk_get_main_window();
679 LinphoneCore *lc=linphone_gtk_get_core();
680 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
681 VIDEOSELFVIEW_DEFAULT));
683 gtk_window_present(GTK_WINDOW(w));
686 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
687 GtkWidget *mw=linphone_gtk_get_main_window();
688 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
689 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
691 if (linphone_gtk_use_in_call_view() && call)
692 linphone_gtk_in_call_view_terminate(call,error);
693 update_video_title();
696 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
697 LinphoneCore *lc=linphone_gtk_get_core();
698 GtkWidget *mw=linphone_gtk_get_main_window();
699 const MSList *calls=linphone_core_get_calls(lc);
701 bool_t start_active=TRUE;
702 //bool_t stop_active=FALSE;
703 bool_t add_call=FALSE;
704 int call_list_size=ms_list_size(calls);
714 button=linphone_gtk_get_widget(mw,"start_call");
715 gtk_widget_set_sensitive(button,start_active);
716 gtk_widget_set_visible(button,!add_call);
718 button=linphone_gtk_get_widget(mw,"add_call");
719 if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
720 gtk_widget_set_sensitive(button,FALSE);
722 gtk_widget_set_sensitive(button,start_active);
724 gtk_widget_set_visible(button,add_call);
726 //gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
728 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
729 linphone_gtk_enable_conference_button(lc,call_list_size>1);
730 update_video_title();
731 if (call) linphone_gtk_update_video_button(call);
734 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
735 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
736 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
737 completion_add_text(GTK_ENTRY(uri_bar),entered);
739 linphone_gtk_call_terminated(NULL,NULL);
744 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
745 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
746 linphone_core_accept_call (linphone_gtk_get_core(),call);
747 linphone_call_unref(call);
752 void linphone_gtk_start_call(GtkWidget *w){
753 LinphoneCore *lc=linphone_gtk_get_core();
755 /*change into in-call mode, then do the work later as it might block a bit */
756 GtkWidget *mw=gtk_widget_get_toplevel(w);
757 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
759 call=linphone_gtk_get_currently_displayed_call(NULL);
760 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
761 linphone_core_accept_call(lc,call);
763 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
764 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
765 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
766 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
771 void linphone_gtk_uri_bar_activate(GtkWidget *w){
772 linphone_gtk_start_call(w);
775 void linphone_gtk_terminate_call(GtkWidget *button){
777 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
779 linphone_core_terminate_call(linphone_gtk_get_core(),call);
781 linphone_core_terminate_conference(linphone_gtk_get_core());
785 void linphone_gtk_decline_clicked(GtkWidget *button){
786 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
788 linphone_core_terminate_call(linphone_gtk_get_core(),call);
791 void linphone_gtk_answer_clicked(GtkWidget *button){
792 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
794 linphone_core_accept_call(linphone_gtk_get_core(),call);
795 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
799 void _linphone_gtk_enable_video(gboolean val){
800 LinphoneVideoPolicy policy={0};
801 policy.automatically_initiate=policy.automatically_accept=val;
802 linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE);
803 linphone_core_set_video_policy(linphone_gtk_get_core(),&policy);
806 linphone_core_enable_video_preview(linphone_gtk_get_core(),
807 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
809 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
813 void linphone_gtk_enable_video(GtkWidget *w){
814 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
815 //GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
816 _linphone_gtk_enable_video(val);
819 void linphone_gtk_enable_self_view(GtkWidget *w){
820 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
821 LinphoneCore *lc=linphone_gtk_get_core();
822 linphone_core_enable_video_preview(lc,val);
823 linphone_core_enable_self_view(lc,val);
824 linphone_gtk_set_ui_config_int("videoselfview",val);
827 void linphone_gtk_used_identity_changed(GtkWidget *w){
828 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
829 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
830 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
831 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
832 linphone_gtk_show_directory_search();
834 if (sel) g_free(sel);
837 void on_proxy_refresh_button_clicked(GtkWidget *w){
838 LinphoneCore *lc=linphone_gtk_get_core();
839 MSList const *item=linphone_core_get_proxy_config_list(lc);
840 while (item != NULL) {
841 LinphoneProxyConfig *lpc=(LinphoneProxyConfig*)item->data;
842 linphone_proxy_config_edit(lpc);
843 linphone_proxy_config_done(lpc);
848 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
849 linphone_gtk_show_friends();
852 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
854 case GTK_RESPONSE_YES:
855 linphone_gtk_show_contact(lf);
858 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
860 gtk_widget_destroy(dialog);
863 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
866 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
867 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
871 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);
872 dialog = gtk_message_dialog_new (
873 GTK_WINDOW(linphone_gtk_get_main_window()),
874 GTK_DIALOG_DESTROY_WITH_PARENT,
875 GTK_MESSAGE_QUESTION,
880 g_signal_connect(G_OBJECT (dialog), "response",
881 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
882 /* actually show the box */
883 gtk_widget_show(dialog);
886 typedef struct _AuthTimeout{
890 static void auth_timeout_clean(AuthTimeout *tout){
894 static gboolean auth_timeout_destroy(AuthTimeout *tout){
896 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
897 gtk_widget_destroy(tout->w);
903 static AuthTimeout * auth_timeout_new(GtkWidget *w){
904 AuthTimeout *tout=g_new(AuthTimeout,1);
906 /*so that the timeout no more references the widget when it is destroyed:*/
907 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
908 /*so that the widget is automatically destroyed after some time */
909 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
913 void linphone_gtk_password_cancel(GtkWidget *w){
914 LinphoneAuthInfo *info;
915 GtkWidget *window=gtk_widget_get_toplevel(w);
916 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
917 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
918 gtk_widget_destroy(window);
921 void linphone_gtk_password_ok(GtkWidget *w){
923 GtkWidget *window=gtk_widget_get_toplevel(w);
924 LinphoneAuthInfo *info;
925 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
926 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
927 entry=linphone_gtk_get_widget(window,"password_entry");
928 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
929 linphone_auth_info_set_userid(info,
930 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
931 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
932 gtk_widget_destroy(window);
935 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
936 GtkWidget *w=linphone_gtk_create_window("password");
937 GtkWidget *label=linphone_gtk_get_widget(w,"message");
938 LinphoneAuthInfo *info;
940 GtkWidget *mw=linphone_gtk_get_main_window();
942 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
943 /*don't prompt for authentication when login frame is visible*/
944 linphone_core_abort_authentication(lc,NULL);
948 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
950 gtk_label_set_markup(GTK_LABEL(label),msg);
952 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
953 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
954 g_object_set_data(G_OBJECT(w),"auth_info",info);
955 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
960 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
961 GtkWidget *w=linphone_gtk_get_main_window();
962 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
964 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
965 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
969 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
970 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
973 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
974 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
977 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
979 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
980 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
983 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
984 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
985 if (w) linphone_gtk_call_log_update(w);
986 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
990 static bool_t notify_actions_supported() {
991 bool_t accepts_actions = FALSE;
992 GList *capabilities = notify_get_server_caps();
994 if(capabilities != NULL) {
995 for(c = capabilities; c != NULL; c = c->next) {
996 if(strcmp((char*)c->data, "actions") == 0 ) {
997 accepts_actions = TRUE;
1001 g_list_foreach(capabilities, (GFunc)g_free, NULL);
1002 g_list_free(capabilities);
1004 return accepts_actions;
1007 static NotifyNotification* build_notification(const char *title, const char *body){
1008 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
1015 static void show_notification(NotifyNotification* n){
1016 if (n && !notify_notification_show(n,NULL))
1017 ms_error("Failed to send notification.");
1020 static void make_notification(const char *title, const char *body){
1021 show_notification(build_notification(title,body));
1026 void linphone_gtk_notify(LinphoneCall *call, const char *msg){
1028 if (!notify_is_initted())
1029 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
1034 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
1040 ms_error("Failed to send notification.");
1042 linphone_gtk_show_main_window();
1044 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1047 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1048 NotifyNotification *n;
1049 switch(linphone_call_get_state(call)){
1050 case LinphoneCallError:
1051 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
1053 case LinphoneCallEnd:
1054 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1056 case LinphoneCallIncomingReceived:
1057 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1058 if (notify_actions_supported()) {
1059 notify_notification_add_action (n,"answer", _("Answer"),
1060 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1061 notify_notification_add_action (n,"decline",_("Decline"),
1062 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1064 show_notification(n);
1066 case LinphoneCallPausedByRemote:
1067 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1072 if (body) g_free(body);
1073 if (remote) g_free(remote);
1078 static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){
1079 if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1080 LinphoneCore *lc=linphone_call_get_core(call);
1081 LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
1082 linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES);
1083 linphone_core_accept_call_update(lc,call,params);
1084 linphone_call_params_destroy(params);
1086 linphone_call_unref(call);
1087 g_source_remove_by_user_data(dialog);
1088 gtk_widget_destroy(dialog);
1091 static void on_call_updated_timeout(GtkWidget *dialog){
1092 gtk_widget_destroy(dialog);
1095 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1096 LinphoneCore *lc=linphone_call_get_core(call);
1097 const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1098 const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1099 const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1100 gboolean video_requested=linphone_call_params_video_enabled(rparams);
1101 gboolean video_used=linphone_call_params_video_enabled(current_params);
1102 g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1103 video_used,video_requested,pol->automatically_accept);
1104 if (video_used==FALSE && video_requested && !pol->automatically_accept){
1105 linphone_core_defer_call_update(lc,call);
1107 const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1109 const char *dname=linphone_address_get_display_name(addr);
1110 if (dname==NULL) dname=linphone_address_get_username(addr);
1111 if (dname==NULL) dname=linphone_address_get_domain(addr);
1112 dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1113 GTK_DIALOG_DESTROY_WITH_PARENT,
1114 GTK_MESSAGE_WARNING,
1116 _("%s proposed to start video. Do you accept ?"),dname);
1117 g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call));
1118 g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1119 gtk_widget_show(dialog);
1124 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1126 case LinphoneCallOutgoingInit:
1127 linphone_gtk_create_in_call_view (call);
1129 case LinphoneCallOutgoingProgress:
1130 linphone_gtk_in_call_view_set_calling (call);
1132 case LinphoneCallStreamsRunning:
1133 linphone_gtk_in_call_view_set_in_call(call);
1135 case LinphoneCallUpdatedByRemote:
1136 linphone_gtk_call_updated_by_remote(call);
1138 case LinphoneCallError:
1139 linphone_gtk_in_call_view_terminate (call,msg);
1141 case LinphoneCallEnd:
1142 linphone_gtk_in_call_view_terminate(call,NULL);
1143 linphone_gtk_status_icon_set_blinking(FALSE);
1145 case LinphoneCallIncomingReceived:
1146 linphone_gtk_create_in_call_view(call);
1147 linphone_gtk_in_call_view_set_incoming(call);
1148 linphone_gtk_status_icon_set_blinking(TRUE);
1150 linphone_call_ref(call);
1151 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1154 case LinphoneCallResuming:
1155 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1156 linphone_gtk_in_call_view_set_in_call (call);
1158 case LinphoneCallPausing:
1159 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1160 update_tab_header(call,FALSE);
1161 case LinphoneCallPausedByRemote:
1162 linphone_gtk_in_call_view_set_paused(call);
1163 update_tab_header(call,TRUE);
1165 case LinphoneCallConnected:
1166 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1167 linphone_gtk_status_icon_set_blinking(FALSE);
1172 linphone_gtk_notify(call, msg);
1173 linphone_gtk_update_call_buttons (call);
1176 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1177 linphone_gtk_in_call_view_show_encryption(call);
1180 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){
1181 linphone_gtk_in_call_view_set_transfer_status(call,cstate);
1184 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1185 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1186 GtkTreeModel *model=gtk_combo_box_get_model(box);
1188 gboolean found=FALSE;
1189 const char *stock_id=NULL;
1191 if (gtk_tree_model_get_iter_first(model,&iter)){
1194 gtk_tree_model_get(model,&iter,2,&p,-1);
1199 }while(gtk_tree_model_iter_next(model,&iter));
1202 g_warning("Could not find proxy config in combo box of identities.");
1206 case LinphoneRegistrationOk:
1207 stock_id=GTK_STOCK_YES;
1209 case LinphoneRegistrationProgress:
1210 stock_id=GTK_STOCK_REFRESH;
1212 case LinphoneRegistrationCleared:
1215 case LinphoneRegistrationFailed:
1216 stock_id=GTK_STOCK_DIALOG_WARNING;
1221 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1224 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1225 LinphoneRegistrationState rs, const char *msg){
1227 case LinphoneRegistrationOk:
1229 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1230 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1231 linphone_gtk_exit_login_frame();
1238 update_registration_status(cfg,rs);
1241 void linphone_gtk_open_browser(const char *url){
1242 /*in gtk 2.16, gtk_show_uri does not work...*/
1244 #if GTK_CHECK_VERSION(2,18,3)
1245 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1248 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1249 g_spawn_command_line_async(cl,NULL);
1252 ShellExecute(0,"open",url,NULL,NULL,1);
1256 void linphone_gtk_link_to_website(GtkWidget *item){
1257 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1258 linphone_gtk_open_browser(home);
1261 #ifndef HAVE_GTK_OSX
1263 static GtkStatusIcon *icon=NULL;
1265 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1266 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1267 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1270 static GtkWidget *create_icon_menu(){
1271 GtkWidget *menu=gtk_menu_new();
1272 GtkWidget *menu_item;
1275 const gchar *homesite;
1277 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1278 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1279 tmp=g_strdup(homesite);
1280 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1281 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1283 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1284 gtk_widget_show(image);
1285 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1286 //g_object_unref(G_OBJECT(image));
1287 gtk_widget_show(menu_item);
1288 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1289 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1291 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1292 gtk_widget_show(menu_item);
1293 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1294 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1295 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1296 gtk_widget_show(menu_item);
1297 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1298 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1299 gtk_widget_show(menu);
1303 static void handle_icon_click() {
1304 GtkWidget *mw=linphone_gtk_get_main_window();
1305 if (!gtk_window_is_active((GtkWindow*)mw)) {
1306 linphone_gtk_show_main_window();
1308 gtk_widget_hide(mw);
1312 static void linphone_gtk_init_status_icon(){
1313 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1314 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1315 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1316 GtkWidget *menu=create_icon_menu();
1318 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1319 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1320 #if GTK_CHECK_VERSION(2,20,0)
1321 gtk_status_icon_set_name(icon,title);
1323 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1324 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1325 gtk_status_icon_set_tooltip(icon,title);
1326 gtk_status_icon_set_visible(icon,TRUE);
1327 g_object_set_data(G_OBJECT(icon),"menu",menu);
1328 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1329 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1330 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1331 pbuf=create_pixbuf(call_icon_path);
1332 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1335 static gboolean do_icon_blink(GtkStatusIcon *gi){
1336 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1337 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1338 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1339 if (cur_icon==call_icon){
1340 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1342 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1349 void linphone_gtk_status_icon_set_blinking(gboolean val){
1351 static gint attention_id;
1352 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1354 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1355 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1359 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1360 if (val && tout==0){
1361 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1362 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1363 }else if (!val && tout!=0){
1364 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1365 g_source_remove(tout);
1366 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1367 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1373 void linphone_gtk_options_activate(GtkWidget *item){
1374 #ifndef HAVE_GTK_OSX
1375 gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1380 static void init_identity_combo(GtkComboBox *box){
1381 GtkListStore *store;
1382 GtkCellRenderer *r1,*r2;
1383 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1384 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1385 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1386 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1387 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1388 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1389 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1390 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1393 void linphone_gtk_load_identities(void){
1395 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1397 LinphoneProxyConfig *def=NULL;
1399 GtkListStore *store;
1402 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1403 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1404 /* model is empty, this is the first time we go here */
1405 init_identity_combo(box);
1406 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1408 gtk_list_store_clear(store);
1409 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1410 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1411 gtk_list_store_append(store,&iter);
1412 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1413 g_free(def_identity);
1414 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1416 elem=ms_list_next(elem),i++){
1417 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1418 gtk_list_store_append(store,&iter);
1419 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1420 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1426 gtk_combo_box_set_active(box,def_index);
1429 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1430 const char *label=gtk_button_get_label(button);
1431 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1433 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1434 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1435 if (linphone_core_in_call(linphone_gtk_get_core())){
1436 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1440 static void linphone_gtk_dtmf_released(GtkButton *button){
1441 linphone_core_stop_dtmf (linphone_gtk_get_core());
1445 static void linphone_gtk_connect_digits(void){
1446 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1447 GList *children=gtk_container_get_children(cont);
1449 for(elem=children;elem!=NULL;elem=elem->next){
1450 GtkButton *button=GTK_BUTTON(elem->data);
1451 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1452 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1456 static void linphone_gtk_check_menu_items(void){
1457 bool_t video_enabled=linphone_gtk_video_enabled();
1458 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1459 GtkWidget *selfview_item=linphone_gtk_get_widget(
1460 linphone_gtk_get_main_window(),"selfview_item");
1461 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1462 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1463 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1466 static gboolean linphone_gtk_can_manage_accounts(){
1467 LinphoneCore *lc=linphone_gtk_get_core();
1469 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1470 SipSetup *ss=(SipSetup*)elem->data;
1471 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1478 static void linphone_gtk_configure_main_window(){
1479 static gboolean config_loaded=FALSE;
1480 static const char *title;
1481 static const char *home;
1482 static const char *start_call_icon;
1483 static const char *add_call_icon;
1484 //static const char *stop_call_icon;
1485 static const char *search_icon;
1486 static gboolean update_check_menu;
1487 static gboolean buttons_have_borders;
1488 static gboolean show_abcd;
1489 GtkWidget *w=linphone_gtk_get_main_window();
1490 GHashTable *contacts_history;
1492 contacts_history=g_hash_table_new_full(g_str_hash, g_str_equal,g_free, NULL);
1493 g_object_set_data(G_OBJECT(w),"history",(gpointer)contacts_history);
1495 if (!config_loaded){
1496 title=linphone_gtk_get_ui_config("title","Linphone");
1497 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1498 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1499 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1500 //stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1501 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1502 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1503 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1504 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1507 linphone_gtk_configure_window(w,"main_window");
1509 gtk_window_set_title(GTK_WINDOW(w),title);
1511 if (start_call_icon){
1512 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1513 create_pixmap (start_call_icon));
1514 if (!buttons_have_borders)
1515 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1518 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1519 create_pixmap (add_call_icon));
1520 if (!buttons_have_borders)
1521 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1524 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1526 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1527 g_object_unref(G_OBJECT(pbuf));
1532 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1534 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1538 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1540 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1541 g_object_unref(G_OBJECT(pbuf));
1546 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1548 GtkImage *img=GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon"));
1551 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU,&w,&h);
1552 scaled=gdk_pixbuf_scale_simple(pbuf,w,h,GDK_INTERP_BILINEAR);
1553 gtk_image_set_from_pixbuf(img,scaled);
1554 g_object_unref(G_OBJECT(scaled));
1555 g_object_unref(G_OBJECT(pbuf));
1558 if (linphone_gtk_can_manage_accounts()) {
1559 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1561 if (update_check_menu){
1562 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1565 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1566 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1567 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1568 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1569 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1573 void linphone_gtk_manage_login(void){
1574 LinphoneCore *lc=linphone_gtk_get_core();
1575 LinphoneProxyConfig *cfg=NULL;
1576 linphone_core_get_default_proxy(lc,&cfg);
1578 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1579 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1580 linphone_gtk_show_login_frame(cfg);
1586 gboolean linphone_gtk_close(GtkWidget *mw){
1587 /*shutdown calls if any*/
1588 LinphoneCore *lc=linphone_gtk_get_core();
1589 if (linphone_core_in_call(lc)){
1590 linphone_core_terminate_all_calls(lc);
1592 linphone_core_enable_video_preview(lc,FALSE);
1593 #ifdef __APPLE__ /*until with have a better option*/
1594 gtk_window_iconify(GTK_WINDOW(mw));
1596 gtk_widget_hide(mw);
1602 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1603 bool_t video_enabled=linphone_gtk_video_enabled();
1604 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1605 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1607 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1608 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled);
1615 static void linphone_gtk_init_main_window(){
1616 GtkWidget *main_window;
1618 linphone_gtk_configure_main_window();
1619 linphone_gtk_manage_login();
1621 linphone_gtk_load_identities();
1622 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1623 linphone_gtk_show_friends();
1624 linphone_gtk_connect_digits();
1625 main_window=linphone_gtk_get_main_window();
1626 linphone_gtk_call_log_update(main_window);
1628 linphone_gtk_update_call_buttons (NULL);
1629 g_object_set_data(G_OBJECT(main_window),"is_conf",GINT_TO_POINTER(FALSE));
1630 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1631 g_signal_connect (G_OBJECT (main_window), "delete-event",
1632 G_CALLBACK (linphone_gtk_close), main_window);
1635 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1636 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1637 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1638 gtk_widget_hide(menubar);
1639 gtk_osxapplication_ready(theMacApp);
1641 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1643 linphone_gtk_check_menu_items();
1647 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1649 const char *lname="undef";
1651 #if defined(__linux) || defined(__APPLE__)
1652 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1671 g_error("Bad level !");
1673 #if defined(__linux) || defined(__APPLE__)
1675 msg=g_strdup_vprintf(fmt,cap);
1678 msg=g_strdup_vprintf(fmt,args);
1680 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1683 linphone_gtk_log_push(lev,fmt,args);
1687 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1688 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1689 linphone_gtk_get_main_window(), "uribar"));
1691 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1693 gtk_entry_set_text(uri_bar, refer_to);
1694 linphone_gtk_start_call(linphone_gtk_get_main_window());
1697 static void linphone_gtk_check_soundcards(){
1698 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1699 if (devices==NULL || devices[0]==NULL){
1700 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1701 _("No sound cards have been detected on this computer.\n"
1702 "You won't be able to send or receive audio calls."));
1707 // Display the account wizard
1708 void linphone_gtk_display_wizard() {
1709 if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time
1710 the_wizard = linphone_gtk_create_assistant();
1715 static void linphone_gtk_quit(void){
1716 static gboolean quit_done=FALSE;
1719 linphone_gtk_unmonitor_usb();
1720 g_source_remove_by_user_data(linphone_gtk_get_core());
1721 linphone_gtk_uninit_instance();
1722 linphone_gtk_destroy_log_window();
1723 linphone_core_destroy(the_core);
1724 linphone_gtk_log_uninit();
1728 gdk_threads_leave();
1734 This is not the correct way to implement block termination.
1735 The good way would be to call gtk_main_quit(), and return TRUE.
1736 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1737 As a result the program cannot exit at all.
1738 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1740 static gboolean on_block_termination(void){
1742 linphone_gtk_quit();
1747 int main(int argc, char *argv[]){
1752 const char *factory_config_file;
1754 GtkSettings *settings;
1756 const char *app_name="Linphone";
1758 #if !GLIB_CHECK_VERSION(2, 31, 0)
1759 g_thread_init(NULL);
1763 progpath = strdup(argv[0]);
1765 config_file=linphone_gtk_get_config_file(NULL);
1769 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1770 if ((lang=getenv("LANG"))!=NULL){
1773 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1779 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1782 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1785 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1787 if (strncmp(lang,"zh",2)==0){
1788 workaround_gtk_entry_chinese_bug=TRUE;
1791 setenv("LANG",lang,1);
1793 setenv("LANGUAGE",lang,1);
1798 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1799 if (p==NULL) perror("bindtextdomain failed");
1800 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1801 textdomain (GETTEXT_PACKAGE);
1803 g_message("NLS disabled.\n");
1806 gtk_rc_add_default_file("./gtkrc");
1808 gdk_threads_enter();
1810 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1811 linphone_options,NULL,NULL)){
1812 gdk_threads_leave();
1816 settings=gtk_settings_get_default();
1817 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1818 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1819 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1820 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1822 if (workingdir!=NULL){
1823 if (chdir(workingdir)==-1){
1824 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1828 /* Now, look for the factory configuration file, we do it this late
1829 since we want to have had time to change directory and to parse
1830 the options, in case we needed to access the working directory */
1831 factory_config_file = linphone_gtk_get_factory_config_file();
1833 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1834 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1835 g_warning("This instance is going to exit now.");
1836 gdk_threads_leave();
1840 add_pixmap_directory("pixmaps");
1841 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1844 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1845 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1846 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1847 /*never block termination:*/
1848 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1851 the_ui=linphone_gtk_create_window("main");
1853 g_object_set_data(G_OBJECT(the_ui),"is_created",GINT_TO_POINTER(FALSE));
1855 linphone_gtk_create_log_window();
1856 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1858 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1860 g_set_application_name(app_name);
1861 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1862 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1864 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1865 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1866 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1867 linphone_gtk_init_main_window();
1870 // Veryfing if at least one sip account is configured. If not, show wizard
1871 if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
1872 linphone_gtk_display_wizard();
1876 #ifndef HAVE_GTK_OSX
1877 linphone_gtk_init_status_icon();
1880 linphone_gtk_show_main_window();
1881 linphone_gtk_check_soundcards();
1883 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1884 linphone_gtk_check_for_new_version();
1885 linphone_gtk_monitor_usb();
1888 linphone_gtk_quit();
1889 #ifndef HAVE_GTK_OSX
1890 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1891 gtk_status_icon_set_visible(icon,FALSE);