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 1
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;
54 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg);
55 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
56 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
57 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username);
58 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
59 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
60 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
61 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
62 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
63 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
64 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token);
65 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
66 static void linphone_gtk_status_icon_set_blinking(gboolean val);
69 static gboolean verbose=0;
70 static gboolean auto_answer = 0;
71 static gchar * addr_to_call = NULL;
72 static gboolean iconified=FALSE;
73 static gchar *workingdir=NULL;
74 static char *progpath=NULL;
75 gchar *linphone_logfile=NULL;
77 static GOptionEntry linphone_options[]={
81 .arg=G_OPTION_ARG_NONE,
82 .arg_data= (gpointer)&verbose,
83 .description=N_("log to stdout some debug information while running.")
86 .long_name = "logfile",
88 .arg = G_OPTION_ARG_STRING,
89 .arg_data = &linphone_logfile,
90 .description = N_("path to a file to write logs into.")
93 .long_name="iconified",
95 .arg=G_OPTION_ARG_NONE,
96 .arg_data= (gpointer)&iconified,
97 .description=N_("Start only in the system tray, do not show the main interface.")
102 .arg = G_OPTION_ARG_STRING,
103 .arg_data = &addr_to_call,
104 .description = N_("address to call right now")
107 .long_name = "auto-answer",
109 .arg = G_OPTION_ARG_NONE,
110 .arg_data = (gpointer) & auto_answer,
111 .description = N_("if set automatically answer incoming calls")
114 .long_name = "workdir",
116 .arg = G_OPTION_ARG_STRING,
117 .arg_data = (gpointer) & workingdir,
118 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
123 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
124 #define RELATIVE_XML_DIR
125 #define BUILD_TREE_XML_DIR "gtk"
128 #define CONFIG_FILE ".linphonerc"
129 #define SECRETS_FILE ".linphone-zidcache"
131 #define CONFIG_FILE "linphonerc"
132 #define SECRETS_FILE "linphone-zidcache"
136 char *linphone_gtk_get_config_file(const char *filename){
137 const int path_max=1024;
138 char *config_file=g_malloc0(path_max);
139 if (filename==NULL) filename=CONFIG_FILE;
140 /*try accessing a local file first if exists*/
141 if (access(CONFIG_FILE,F_OK)==0){
142 snprintf(config_file,path_max,"%s",filename);
145 const char *appdata=getenv("APPDATA");
147 snprintf(config_file,path_max,"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
148 CreateDirectory(config_file,NULL);
149 snprintf(config_file,path_max,"%s\\%s\\%s",appdata,LINPHONE_CONFIG_DIR,filename);
152 const char *home=getenv("HOME");
153 if (home==NULL) home=".";
154 snprintf(config_file,path_max,"%s/%s",home,filename);
161 #define FACTORY_CONFIG_FILE "linphonerc.factory"
162 static char _factory_config_file[1024];
163 static const char *linphone_gtk_get_factory_config_file(){
164 /*try accessing a local file first if exists*/
165 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
166 snprintf(_factory_config_file,sizeof(_factory_config_file),
167 "%s",FACTORY_CONFIG_FILE);
171 if (progpath != NULL) {
173 progdir = strdup(progpath);
175 basename = strrchr(progdir, '\\');
176 if (basename != NULL) {
179 snprintf(_factory_config_file, sizeof(_factory_config_file),
180 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
182 if (workingdir!=NULL) {
183 snprintf(_factory_config_file, sizeof(_factory_config_file),
184 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
191 basename = strrchr(progdir, '/');
192 if (basename != NULL) {
195 snprintf(_factory_config_file, sizeof(_factory_config_file),
196 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
205 return _factory_config_file;
208 static void linphone_gtk_init_liblinphone(const char *config_file,
209 const char *factory_config_file) {
210 LinphoneCoreVTable vtable={0};
211 gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
213 vtable.call_state_changed=linphone_gtk_call_state_changed;
214 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
215 vtable.notify_presence_recv=linphone_gtk_notify_recv;
216 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
217 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
218 vtable.display_status=linphone_gtk_display_status;
219 vtable.display_message=linphone_gtk_display_message;
220 vtable.display_warning=linphone_gtk_display_warning;
221 vtable.display_url=linphone_gtk_display_url;
222 vtable.call_log_updated=linphone_gtk_call_log_updated;
223 vtable.text_received=linphone_gtk_text_received;
224 vtable.refer_received=linphone_gtk_refer_received;
225 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
226 vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
228 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
229 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
230 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
231 linphone_core_set_zrtp_secrets_file(the_core,secrets_file);
232 g_free(secrets_file);
237 LinphoneCore *linphone_gtk_get_core(void){
241 GtkWidget *linphone_gtk_get_main_window(){
245 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
246 static const char *icon_path=NULL;
247 static const char *hiddens=NULL;
248 static const char *shown=NULL;
249 static bool_t config_loaded=FALSE;
250 if (linphone_gtk_get_core()==NULL) return;
251 if (config_loaded==FALSE){
252 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
253 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
254 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
258 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
260 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
262 GdkPixbuf *pbuf=create_pixbuf(icon_path);
263 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
264 g_object_unref(G_OBJECT(pbuf));
268 static int get_ui_file(const char *name, char *path, int pathsize){
269 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
270 if (access(path,F_OK)!=0){
271 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
272 if (access(path,F_OK)!=0){
273 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
274 INSTALLED_XML_DIR,name);
281 GtkWidget *linphone_gtk_create_window(const char *window_name){
282 GError* error = NULL;
283 GtkBuilder* builder = gtk_builder_new ();
287 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
289 if (!gtk_builder_add_from_file (builder, path, &error)){
290 g_error("Couldn't load builder file: %s", error->message);
291 g_error_free (error);
294 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
296 g_error("Could not retrieve '%s' window from xml file",window_name);
299 g_object_set_data(G_OBJECT(w),"builder",builder);
300 gtk_builder_connect_signals(builder,w);
301 linphone_gtk_configure_window(w,window_name);
305 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
308 GtkBuilder* builder = gtk_builder_new ();
310 gchar *object_ids[2];
311 object_ids[0]=g_strdup(widget_name);
314 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
315 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
316 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
317 g_error_free (error);
318 g_free(object_ids[0]);
321 g_free(object_ids[0]);
322 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
324 g_error("Could not retrieve '%s' window from xml file",widget_name);
327 g_object_set_data(G_OBJECT(w),"builder",builder);
328 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
329 gtk_builder_connect_signals(builder,w);
333 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
334 GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
337 g_error("Fail to retrieve builder from window !");
340 w=gtk_builder_get_object(builder,name);
342 g_error("No widget named %s found in xml interface.",name);
344 return GTK_WIDGET(w);
348 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
350 GtkWidget *main_window=linphone_gtk_get_main_window();
352 gtk_widget_show(main_window);
353 if (type==GTK_MESSAGE_QUESTION)
355 /* draw a question box. link to dialog_click callback */
356 dialog = gtk_message_dialog_new (
357 GTK_WINDOW(main_window),
358 GTK_DIALOG_DESTROY_WITH_PARENT,
359 GTK_MESSAGE_QUESTION,
362 (const gchar*)message);
363 /* connect to some callback : REVISIT */
365 g_signal_connect_swapped (G_OBJECT (dialog), "response",
366 G_CALLBACK (dialog_click),
369 /* actually show the box */
370 gtk_widget_show(dialog);
374 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
375 GTK_DIALOG_DESTROY_WITH_PARENT,
379 (const gchar*)message);
380 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
381 g_signal_connect_swapped (G_OBJECT (dialog), "response",
382 G_CALLBACK (gtk_widget_destroy),
384 gtk_widget_show(dialog);
388 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
389 if (id==GTK_RESPONSE_CANCEL){
390 gtk_widget_destroy(GTK_WIDGET(dialog));
394 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
395 g_message("About url clicked");
396 linphone_gtk_open_browser(url);
399 void linphone_gtk_show_about(){
400 struct stat filestat;
401 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
404 GdkPixbuf *logo=create_pixbuf(
405 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
406 static const char *defcfg="defcfg";
408 about=linphone_gtk_create_window("about");
409 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
410 memset(&filestat,0,sizeof(filestat));
411 if (stat(license_file,&filestat)!=0){
412 license_file="COPYING";
413 stat(license_file,&filestat);
415 if (filestat.st_size>0){
416 char *license=g_malloc(filestat.st_size+1);
417 FILE *f=fopen(license_file,"r");
418 if (f && fread(license,filestat.st_size,1,f)==1){
419 license[filestat.st_size]='\0';
420 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
424 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
425 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
426 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
427 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
428 tmp=linphone_gtk_get_ui_config("artists",defcfg);
433 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
435 tmp=linphone_gtk_get_ui_config("translators",defcfg);
437 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
438 tmp=linphone_gtk_get_ui_config("comments",defcfg);
440 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
441 gtk_widget_show(about);
444 static void set_video_window_decorations(GdkWindow *w){
445 const char *title=linphone_gtk_get_ui_config("title","Linphone");
446 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
447 char video_title[256];
448 GdkPixbuf *pbuf=create_pixbuf(icon_path);
449 if (!linphone_core_in_call(linphone_gtk_get_core())){
450 snprintf(video_title,sizeof(video_title),"%s video",title);
451 /* When not in call, treat the video as a normal window */
452 gdk_window_set_keep_above(w, FALSE);
454 LinphoneAddress *uri =
455 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
458 linphone_address_clean(uri);
459 if (linphone_address_get_display_name(uri)!=NULL){
460 display_name=ms_strdup(linphone_address_get_display_name(uri));
462 display_name=linphone_address_as_string(uri);
464 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
465 linphone_address_destroy(uri);
466 ms_free(display_name);
468 /* During calls, bring up the video window, arrange so that
469 it is above all the other windows */
470 gdk_window_deiconify(w);
471 gdk_window_set_keep_above(w,TRUE);
472 /* Maybe we should have the following, but then we want to
473 have a timer that turns it off after a little while. */
474 /* gdk_window_set_urgency_hint(w,TRUE); */
476 gdk_window_set_title(w,video_title);
477 /* Refrain the video window to be closed at all times. */
478 gdk_window_set_functions(w,
479 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
480 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
483 l=g_list_append(l,pbuf);
484 gdk_window_set_icon_list(w,l);
486 g_object_unref(G_OBJECT(pbuf));
490 static gboolean video_needs_update=FALSE;
492 static void update_video_title(){
493 video_needs_update=TRUE;
496 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
497 static gboolean first_time=TRUE;
499 static unsigned long previd=0;
500 static unsigned long preview_previd=0;
501 static gboolean in_iterate=FALSE;
504 if (in_iterate) return TRUE;
506 linphone_core_iterate(lc);
508 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
509 linphone_gtk_show_directory_search();
513 id=linphone_core_get_native_video_window_id(lc);
514 if (id!=previd || video_needs_update){
518 ms_message("Updating window decorations");
520 w=gdk_window_foreign_new(id);
522 w=gdk_window_foreign_new((HANDLE)id);
525 set_video_window_decorations(w);
526 g_object_unref(G_OBJECT(w));
528 else ms_error("gdk_window_foreign_new() failed");
529 if (video_needs_update) video_needs_update=FALSE;
532 id=linphone_core_get_native_preview_window_id (lc);
533 if (id!=preview_previd ){
537 ms_message("Updating window decorations for preview");
539 w=gdk_window_foreign_new(id);
541 w=gdk_window_foreign_new((HANDLE)id);
544 set_video_window_decorations(w);
545 g_object_unref(G_OBJECT(w));
547 else ms_error("gdk_window_foreign_new() failed");
548 if (video_needs_update) video_needs_update=FALSE;
551 if (addr_to_call!=NULL){
552 /*make sure we are not showing the login screen*/
553 GtkWidget *mw=linphone_gtk_get_main_window();
554 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
555 if (!GTK_WIDGET_VISIBLE(login_frame)){
556 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
557 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
559 linphone_gtk_start_call(uri_bar);
566 static void load_uri_history(){
567 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
570 GtkEntryCompletion *gep=gtk_entry_completion_new();
571 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
574 snprintf(key,sizeof(key),"uri%i",i);
575 uri=linphone_gtk_get_ui_config(key,NULL);
578 gtk_list_store_append(model,&iter);
579 gtk_list_store_set(model,&iter,0,uri,-1);
580 if (i==0) gtk_entry_set_text(uribar,uri);
584 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
585 gtk_entry_completion_set_text_column(gep,0);
586 gtk_entry_set_completion(uribar,gep);
589 static void save_uri_history(){
590 LinphoneCore *lc=linphone_gtk_get_core();
591 LpConfig *cfg=linphone_core_get_config(lc);
592 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
597 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
599 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
601 gtk_tree_model_get(model,&iter,0,&uri,-1);
603 snprintf(key,sizeof(key),"uri%i",i);
604 lp_config_set_string(cfg,"GtkUi",key,uri);
609 }while(gtk_tree_model_iter_next(model,&iter));
613 static void completion_add_text(GtkEntry *entry, const char *text){
615 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
617 if (gtk_tree_model_get_iter_first(model,&iter)){
620 gtk_tree_model_get(model,&iter,0,&uri,-1);
622 if (strcmp(uri,text)==0) {
624 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
630 }while (gtk_tree_model_iter_next(model,&iter));
632 /* and prepend it on top of the list */
633 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
634 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
639 void linphone_gtk_show_main_window(){
640 GtkWidget *w=linphone_gtk_get_main_window();
641 LinphoneCore *lc=linphone_gtk_get_core();
642 if (linphone_core_video_enabled(lc)){
643 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
644 VIDEOSELFVIEW_DEFAULT));
647 gtk_window_present(GTK_WINDOW(w));
650 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
651 GtkWidget *mw=linphone_gtk_get_main_window();
652 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
653 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
654 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
656 if (linphone_gtk_use_in_call_view() && call)
657 linphone_gtk_in_call_view_terminate(call,error);
658 update_video_title();
661 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
662 LinphoneCore *lc=linphone_gtk_get_core();
663 GtkWidget *mw=linphone_gtk_get_main_window();
664 const MSList *calls=linphone_core_get_calls(lc);
666 bool_t start_active=TRUE;
667 bool_t stop_active=FALSE;
668 bool_t add_call=FALSE;
669 int call_list_size=ms_list_size(calls);
679 button=linphone_gtk_get_widget(mw,"start_call");
680 gtk_widget_set_sensitive(button,start_active);
681 gtk_widget_set_visible(button,!add_call);
683 button=linphone_gtk_get_widget(mw,"add_call");
684 if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
685 gtk_widget_set_sensitive(button,FALSE);
687 gtk_widget_set_sensitive(button,start_active);
689 gtk_widget_set_visible(button,add_call);
691 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
693 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
694 linphone_gtk_enable_conference_button(lc,call_list_size>1);
695 update_video_title();
698 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
699 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
700 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
701 completion_add_text(GTK_ENTRY(uri_bar),entered);
703 linphone_gtk_call_terminated(NULL,NULL);
708 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
709 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
710 linphone_core_accept_call (linphone_gtk_get_core(),call);
711 linphone_call_unref(call);
717 void linphone_gtk_start_call(GtkWidget *w){
718 LinphoneCore *lc=linphone_gtk_get_core();
720 /*change into in-call mode, then do the work later as it might block a bit */
721 GtkWidget *mw=gtk_widget_get_toplevel(w);
722 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
724 call=linphone_gtk_get_currently_displayed_call(NULL);
725 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
726 linphone_core_accept_call(lc,call);
728 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
729 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
730 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
731 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
736 void linphone_gtk_uri_bar_activate(GtkWidget *w){
737 linphone_gtk_start_call(w);
741 void linphone_gtk_terminate_call(GtkWidget *button){
743 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
745 linphone_core_terminate_call(linphone_gtk_get_core(),call);
747 linphone_core_terminate_conference(linphone_gtk_get_core());
751 void linphone_gtk_decline_clicked(GtkWidget *button){
752 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
754 linphone_core_terminate_call(linphone_gtk_get_core(),call);
757 void linphone_gtk_answer_clicked(GtkWidget *button){
758 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
760 linphone_core_accept_call(linphone_gtk_get_core(),call);
761 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
765 void linphone_gtk_enable_video(GtkWidget *w){
766 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
767 GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
768 linphone_core_enable_video(linphone_gtk_get_core(),val,val);
769 gtk_widget_set_sensitive(selfview_item,val);
771 linphone_core_enable_video_preview(linphone_gtk_get_core(),
772 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
774 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
778 void linphone_gtk_enable_self_view(GtkWidget *w){
779 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
780 LinphoneCore *lc=linphone_gtk_get_core();
781 linphone_core_enable_video_preview(lc,val);
782 linphone_core_enable_self_view(lc,val);
783 linphone_gtk_set_ui_config_int("videoselfview",val);
786 void linphone_gtk_used_identity_changed(GtkWidget *w){
787 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
788 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
789 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
790 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
791 linphone_gtk_show_directory_search();
793 if (sel) g_free(sel);
796 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
797 linphone_gtk_show_friends();
800 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
802 case GTK_RESPONSE_YES:
803 linphone_gtk_show_contact(lf);
806 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
808 gtk_widget_destroy(dialog);
811 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
814 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
815 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
819 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);
820 dialog = gtk_message_dialog_new (
821 GTK_WINDOW(linphone_gtk_get_main_window()),
822 GTK_DIALOG_DESTROY_WITH_PARENT,
823 GTK_MESSAGE_QUESTION,
828 g_signal_connect(G_OBJECT (dialog), "response",
829 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
830 /* actually show the box */
831 gtk_widget_show(dialog);
834 typedef struct _AuthTimeout{
839 static void auth_timeout_clean(AuthTimeout *tout){
843 static gboolean auth_timeout_destroy(AuthTimeout *tout){
845 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
846 gtk_widget_destroy(tout->w);
852 static AuthTimeout * auth_timeout_new(GtkWidget *w){
853 AuthTimeout *tout=g_new(AuthTimeout,1);
855 /*so that the timeout no more references the widget when it is destroyed:*/
856 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
857 /*so that the widget is automatically destroyed after some time */
858 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
862 void linphone_gtk_password_cancel(GtkWidget *w){
863 LinphoneAuthInfo *info;
864 GtkWidget *window=gtk_widget_get_toplevel(w);
865 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
866 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
867 gtk_widget_destroy(window);
870 void linphone_gtk_password_ok(GtkWidget *w){
872 GtkWidget *window=gtk_widget_get_toplevel(w);
873 LinphoneAuthInfo *info;
874 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
875 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
876 entry=linphone_gtk_get_widget(window,"password_entry");
877 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
878 linphone_auth_info_set_userid(info,
879 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
880 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
881 gtk_widget_destroy(window);
884 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
885 GtkWidget *w=linphone_gtk_create_window("password");
886 GtkWidget *label=linphone_gtk_get_widget(w,"message");
887 LinphoneAuthInfo *info;
889 GtkWidget *mw=linphone_gtk_get_main_window();
891 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
892 /*don't prompt for authentication when login frame is visible*/
893 linphone_core_abort_authentication(lc,NULL);
897 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
899 gtk_label_set_markup(GTK_LABEL(label),msg);
901 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
902 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
903 g_object_set_data(G_OBJECT(w),"auth_info",info);
904 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
909 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
910 GtkWidget *w=linphone_gtk_get_main_window();
911 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
912 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
913 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
917 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
918 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
921 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
922 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
925 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
927 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
928 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
931 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
932 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
933 if (w) linphone_gtk_call_log_update(w);
934 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
938 static bool_t notify_actions_supported() {
939 bool_t accepts_actions = FALSE;
940 GList *capabilities = notify_get_server_caps();
942 if(capabilities != NULL) {
943 for(c = capabilities; c != NULL; c = c->next) {
944 if(strcmp((char*)c->data, "actions") == 0 ) {
945 accepts_actions = TRUE;
949 g_list_foreach(capabilities, (GFunc)g_free, NULL);
950 g_list_free(capabilities);
952 return accepts_actions;
955 static NotifyNotification* build_notification(const char *title, const char *body){
956 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
963 static void show_notification(NotifyNotification* n){
964 if (n && !notify_notification_show(n,NULL))
965 ms_error("Failed to send notification.");
968 static void make_notification(const char *title, const char *body){
969 show_notification(build_notification(title,body));
974 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
976 if (!notify_is_initted())
977 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
981 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
986 ms_error("Failed to send notification.");
988 linphone_gtk_show_main_window();
990 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
993 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
994 NotifyNotification *n;
995 switch(linphone_call_get_state(call)){
996 case LinphoneCallError:
997 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
999 case LinphoneCallEnd:
1000 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1002 case LinphoneCallIncomingReceived:
1003 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1004 if (notify_actions_supported()) {
1005 notify_notification_add_action (n,"answer", _("Answer"),
1006 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1007 notify_notification_add_action (n,"decline",_("Decline"),
1008 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1010 show_notification(n);
1012 case LinphoneCallPausedByRemote:
1013 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1018 if (body) g_free(body);
1019 if (remote) g_free(remote);
1024 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1026 case LinphoneCallOutgoingInit:
1027 linphone_gtk_create_in_call_view (call);
1029 case LinphoneCallOutgoingProgress:
1030 linphone_gtk_in_call_view_set_calling (call);
1032 case LinphoneCallStreamsRunning:
1033 linphone_gtk_in_call_view_set_in_call(call);
1035 case LinphoneCallError:
1036 linphone_gtk_in_call_view_terminate (call,msg);
1038 case LinphoneCallEnd:
1039 linphone_gtk_in_call_view_terminate(call,NULL);
1040 linphone_gtk_status_icon_set_blinking(FALSE);
1042 case LinphoneCallIncomingReceived:
1043 linphone_gtk_create_in_call_view(call);
1044 linphone_gtk_in_call_view_set_incoming(call);
1045 linphone_gtk_status_icon_set_blinking(TRUE);
1047 linphone_call_ref(call);
1048 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1051 case LinphoneCallResuming:
1052 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1053 linphone_gtk_in_call_view_set_in_call (call);
1055 case LinphoneCallPausing:
1056 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1057 case LinphoneCallPausedByRemote:
1058 linphone_gtk_in_call_view_set_paused(call);
1060 case LinphoneCallConnected:
1061 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1062 linphone_gtk_status_icon_set_blinking(FALSE);
1067 linphone_gtk_notify(call, msg);
1068 linphone_gtk_update_call_buttons (call);
1071 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1072 linphone_gtk_in_call_view_show_encryption(call);
1075 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1076 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1077 GtkTreeModel *model=gtk_combo_box_get_model(box);
1079 gboolean found=FALSE;
1080 const char *stock_id=NULL;
1082 if (gtk_tree_model_get_iter_first(model,&iter)){
1085 gtk_tree_model_get(model,&iter,2,&p,-1);
1090 }while(gtk_tree_model_iter_next(model,&iter));
1093 g_warning("Could not find proxy config in combo box of identities.");
1097 case LinphoneRegistrationOk:
1098 stock_id=GTK_STOCK_YES;
1100 case LinphoneRegistrationProgress:
1101 stock_id=GTK_STOCK_REFRESH;
1103 case LinphoneRegistrationCleared:
1106 case LinphoneRegistrationFailed:
1107 stock_id=GTK_STOCK_DIALOG_WARNING;
1112 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1115 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1116 LinphoneRegistrationState rs, const char *msg){
1118 case LinphoneRegistrationOk:
1120 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1121 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1122 linphone_gtk_exit_login_frame();
1129 update_registration_status(cfg,rs);
1132 void linphone_gtk_open_browser(const char *url){
1133 /*in gtk 2.16, gtk_show_uri does not work...*/
1135 #if GTK_CHECK_VERSION(2,18,3)
1136 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1139 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1140 g_spawn_command_line_async(cl,NULL);
1143 ShellExecute(0,"open",url,NULL,NULL,1);
1147 void linphone_gtk_link_to_website(GtkWidget *item){
1148 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1149 linphone_gtk_open_browser(home);
1152 #ifndef HAVE_GTK_OSX
1154 static GtkStatusIcon *icon=NULL;
1156 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1157 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1158 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1161 static GtkWidget *create_icon_menu(){
1162 GtkWidget *menu=gtk_menu_new();
1163 GtkWidget *menu_item;
1166 const gchar *homesite;
1168 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1169 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1170 tmp=g_strdup(homesite);
1171 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1172 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1174 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1175 gtk_widget_show(image);
1176 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1177 //g_object_unref(G_OBJECT(image));
1178 gtk_widget_show(menu_item);
1179 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1180 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1182 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1183 gtk_widget_show(menu_item);
1184 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1185 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1186 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1187 gtk_widget_show(menu_item);
1188 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1189 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1190 gtk_widget_show(menu);
1194 static void handle_icon_click() {
1195 GtkWidget *mw=linphone_gtk_get_main_window();
1196 if (!gtk_window_is_active((GtkWindow*)mw)) {
1197 linphone_gtk_show_main_window();
1199 gtk_widget_hide(mw);
1203 static void linphone_gtk_init_status_icon(){
1204 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1205 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1206 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1207 GtkWidget *menu=create_icon_menu();
1209 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1210 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1211 gtk_status_icon_set_name(icon,title);
1212 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1213 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1214 gtk_status_icon_set_tooltip(icon,title);
1215 gtk_status_icon_set_visible(icon,TRUE);
1216 g_object_set_data(G_OBJECT(icon),"menu",menu);
1217 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1218 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1219 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1220 pbuf=create_pixbuf(call_icon_path);
1221 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1224 static gboolean do_icon_blink(GtkStatusIcon *gi){
1225 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1226 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1227 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1228 if (cur_icon==call_icon){
1229 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1231 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1238 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1240 static gint attention_id;
1241 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1243 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1244 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1248 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1249 if (val && tout==0){
1250 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1251 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1252 }else if (!val && tout!=0){
1253 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1254 g_source_remove(tout);
1255 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1256 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1262 void linphone_gtk_options_activate(GtkWidget *item){
1263 #ifndef HAVE_GTK_OSX
1264 gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1265 icon && !gtk_status_icon_is_embedded(icon));
1269 static void init_identity_combo(GtkComboBox *box){
1270 GtkListStore *store;
1271 GtkCellRenderer *r1,*r2;
1272 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1273 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1274 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1275 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1276 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1277 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1278 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1279 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1282 void linphone_gtk_load_identities(void){
1284 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1286 LinphoneProxyConfig *def=NULL;
1288 GtkListStore *store;
1291 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1292 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1293 /* model is empty, this is the first time we go here */
1294 init_identity_combo(box);
1295 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1297 gtk_list_store_clear(store);
1298 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1299 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1300 gtk_list_store_append(store,&iter);
1301 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1302 g_free(def_identity);
1303 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1305 elem=ms_list_next(elem),i++){
1306 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1307 gtk_list_store_append(store,&iter);
1308 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1309 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1315 gtk_combo_box_set_active(box,def_index);
1318 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1319 const char *label=gtk_button_get_label(button);
1320 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1322 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1323 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1324 if (linphone_core_in_call(linphone_gtk_get_core())){
1325 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1329 static void linphone_gtk_dtmf_released(GtkButton *button){
1330 linphone_core_stop_dtmf (linphone_gtk_get_core());
1333 static void linphone_gtk_connect_digits(void){
1334 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1335 GList *children=gtk_container_get_children(cont);
1337 for(elem=children;elem!=NULL;elem=elem->next){
1338 GtkButton *button=GTK_BUTTON(elem->data);
1339 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1340 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1344 static void linphone_gtk_check_menu_items(void){
1345 bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core());
1346 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1347 GtkWidget *selfview_item=linphone_gtk_get_widget(
1348 linphone_gtk_get_main_window(),"selfview_item");
1349 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1350 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1351 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1352 gtk_widget_set_sensitive(selfview_item,video_enabled);
1355 static gboolean linphone_gtk_can_manage_accounts(){
1356 LinphoneCore *lc=linphone_gtk_get_core();
1358 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1359 SipSetup *ss=(SipSetup*)elem->data;
1360 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1367 static void linphone_gtk_configure_main_window(){
1368 static gboolean config_loaded=FALSE;
1369 static const char *title;
1370 static const char *home;
1371 static const char *start_call_icon;
1372 static const char *add_call_icon;
1373 static const char *stop_call_icon;
1374 static const char *search_icon;
1375 static gboolean update_check_menu;
1376 static gboolean buttons_have_borders;
1377 static gboolean show_abcd;
1378 GtkWidget *w=linphone_gtk_get_main_window();
1379 if (!config_loaded){
1380 title=linphone_gtk_get_ui_config("title","Linphone");
1381 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1382 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1383 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1384 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1385 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1386 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1387 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1388 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1391 linphone_gtk_configure_window(w,"main_window");
1393 gtk_window_set_title(GTK_WINDOW(w),title);
1395 if (start_call_icon){
1396 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1397 create_pixmap (start_call_icon));
1398 if (!buttons_have_borders)
1399 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1402 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1403 create_pixmap (add_call_icon));
1404 if (!buttons_have_borders)
1405 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1407 if (stop_call_icon){
1408 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1409 create_pixmap (stop_call_icon));
1410 if (!buttons_have_borders)
1411 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1414 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1415 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1416 g_object_unref(G_OBJECT(pbuf));
1420 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1422 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1426 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1428 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1429 g_object_unref(G_OBJECT(pbuf));
1434 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1436 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon")),pbuf);
1437 g_object_unref(G_OBJECT(pbuf));
1440 if (linphone_gtk_can_manage_accounts())
1441 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1442 if (update_check_menu){
1443 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1446 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1447 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1448 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1449 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1450 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1454 void linphone_gtk_manage_login(void){
1455 LinphoneCore *lc=linphone_gtk_get_core();
1456 LinphoneProxyConfig *cfg=NULL;
1457 linphone_core_get_default_proxy(lc,&cfg);
1459 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1460 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1461 linphone_gtk_show_login_frame(cfg);
1467 gboolean linphone_gtk_close(GtkWidget *mw){
1468 /*shutdown calls if any*/
1469 LinphoneCore *lc=linphone_gtk_get_core();
1470 if (linphone_core_in_call(lc)){
1471 linphone_core_terminate_all_calls(lc);
1473 linphone_core_enable_video_preview(lc,FALSE);
1474 #ifdef __APPLE__ /*until with have a better option*/
1475 gtk_window_iconify(GTK_WINDOW(mw));
1477 gtk_widget_hide(mw);
1483 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1484 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1485 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1487 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1488 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1495 static void linphone_gtk_init_main_window(){
1496 GtkWidget *main_window;
1498 linphone_gtk_configure_main_window();
1499 linphone_gtk_manage_login();
1501 linphone_gtk_load_identities();
1502 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1503 linphone_gtk_show_friends();
1504 linphone_gtk_connect_digits();
1505 main_window=linphone_gtk_get_main_window();
1506 linphone_gtk_call_log_update(main_window);
1508 linphone_gtk_update_call_buttons (NULL);
1509 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1510 g_signal_connect (G_OBJECT (main_window), "delete-event",
1511 G_CALLBACK (linphone_gtk_close), main_window);
1514 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1515 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1516 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1517 gtk_widget_hide(menubar);
1518 gtk_osxapplication_ready(theMacApp);
1520 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1522 linphone_gtk_check_menu_items();
1526 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1528 const char *lname="undef";
1530 #if defined(__linux) || defined(__APPLE__)
1531 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1550 g_error("Bad level !");
1552 #if defined(__linux) || defined(__APPLE__)
1554 msg=g_strdup_vprintf(fmt,cap);
1557 msg=g_strdup_vprintf(fmt,args);
1559 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1562 linphone_gtk_log_push(lev,fmt,args);
1566 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1567 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1568 linphone_gtk_get_main_window(), "uribar"));
1570 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1572 gtk_entry_set_text(uri_bar, refer_to);
1573 linphone_gtk_start_call(linphone_gtk_get_main_window());
1576 static void linphone_gtk_check_soundcards(){
1577 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1578 if (devices==NULL || devices[0]==NULL){
1579 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1580 _("No sound cards have been detected on this computer.\n"
1581 "You won't be able to send or receive audio calls."));
1585 static void linphone_gtk_quit(void){
1586 static gboolean quit_done=FALSE;
1589 g_source_remove_by_user_data(linphone_gtk_get_core());
1590 linphone_gtk_uninit_instance();
1591 linphone_gtk_destroy_log_window();
1592 linphone_core_destroy(the_core);
1593 linphone_gtk_log_uninit();
1597 gdk_threads_leave();
1603 This is not the correct way to implement block termination.
1604 The good way would be to call gtk_main_quit(), and return TRUE.
1605 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1606 As a result the program cannot exit at all.
1607 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1609 static gboolean on_block_termination(void){
1611 linphone_gtk_quit();
1616 int main(int argc, char *argv[]){
1621 const char *factory_config_file;
1623 GtkSettings *settings;
1625 const char *app_name="Linphone";
1627 g_thread_init(NULL);
1630 progpath = strdup(argv[0]);
1632 config_file=linphone_gtk_get_config_file(NULL);
1636 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1637 if ((lang=getenv("LANG"))!=NULL){
1640 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1646 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1649 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1652 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1655 setenv("LANG",lang,1);
1660 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1661 if (p==NULL) perror("bindtextdomain failed");
1662 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1663 textdomain (GETTEXT_PACKAGE);
1665 g_message("NLS disabled.\n");
1668 gtk_rc_add_default_file("./gtkrc");
1670 gdk_threads_enter();
1672 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1673 linphone_options,NULL,NULL)){
1674 gdk_threads_leave();
1678 settings=gtk_settings_get_default();
1679 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1680 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1681 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1682 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1684 if (workingdir!=NULL){
1685 if (chdir(workingdir)==-1){
1686 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1690 /* Now, look for the factory configuration file, we do it this late
1691 since we want to have had time to change directory and to parse
1692 the options, in case we needed to access the working directory */
1693 factory_config_file = linphone_gtk_get_factory_config_file();
1695 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1696 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1697 g_warning("This instance is going to exit now.");
1698 gdk_threads_leave();
1702 add_pixmap_directory("pixmaps");
1703 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1706 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1707 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1708 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1709 /*never block termination:*/
1710 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1713 the_ui=linphone_gtk_create_window("main");
1715 linphone_gtk_create_log_window();
1716 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1718 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1720 g_set_application_name(app_name);
1721 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1722 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1724 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1725 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1726 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1727 linphone_gtk_init_main_window();
1728 #ifndef HAVE_GTK_OSX
1729 linphone_gtk_init_status_icon();
1732 linphone_gtk_show_main_window();
1733 linphone_gtk_check_soundcards();
1735 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1736 linphone_gtk_check_for_new_version();
1739 linphone_gtk_quit();
1740 #ifndef HAVE_GTK_OSX
1741 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1742 gtk_status_icon_set_visible(icon,FALSE);