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 gboolean linphone_gtk_auto_answer(LinphoneCall *call);
65 static void linphone_gtk_status_icon_set_blinking(gboolean val);
68 static gboolean verbose=0;
69 static gboolean auto_answer = 0;
70 static gchar * addr_to_call = NULL;
71 static gboolean iconified=FALSE;
72 static gchar *workingdir=NULL;
73 static char *progpath=NULL;
74 gchar *linphone_logfile=NULL;
76 static GOptionEntry linphone_options[]={
80 .arg=G_OPTION_ARG_NONE,
81 .arg_data= (gpointer)&verbose,
82 .description=N_("log to stdout some debug information while running.")
85 .long_name = "logfile",
87 .arg = G_OPTION_ARG_STRING,
88 .arg_data = &linphone_logfile,
89 .description = N_("path to a file to write logs into.")
92 .long_name="iconified",
94 .arg=G_OPTION_ARG_NONE,
95 .arg_data= (gpointer)&iconified,
96 .description=N_("Start only in the system tray, do not show the main interface.")
101 .arg = G_OPTION_ARG_STRING,
102 .arg_data = &addr_to_call,
103 .description = N_("address to call right now")
106 .long_name = "auto-answer",
108 .arg = G_OPTION_ARG_NONE,
109 .arg_data = (gpointer) & auto_answer,
110 .description = N_("if set automatically answer incoming calls")
113 .long_name = "workdir",
115 .arg = G_OPTION_ARG_STRING,
116 .arg_data = (gpointer) & workingdir,
117 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
122 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
123 #define RELATIVE_XML_DIR
124 #define BUILD_TREE_XML_DIR "gtk"
127 #define CONFIG_FILE ".linphonerc"
129 #define CONFIG_FILE "linphonerc"
134 static char _config_file[1024];
137 const char *linphone_gtk_get_config_file(){
138 /*try accessing a local file first if exists*/
139 if (access(CONFIG_FILE,F_OK)==0){
140 snprintf(_config_file,sizeof(_config_file),"%s",CONFIG_FILE);
143 const char *appdata=getenv("APPDATA");
145 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
146 CreateDirectory(_config_file,NULL);
147 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR "\\" CONFIG_FILE);
150 const char *home=getenv("HOME");
151 if (home==NULL) home=".";
152 snprintf(_config_file,sizeof(_config_file),"%s/%s",home,CONFIG_FILE);
159 #define FACTORY_CONFIG_FILE "linphonerc.factory"
160 static char _factory_config_file[1024];
161 static const char *linphone_gtk_get_factory_config_file(){
162 /*try accessing a local file first if exists*/
163 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
164 snprintf(_factory_config_file,sizeof(_factory_config_file),
165 "%s",FACTORY_CONFIG_FILE);
169 if (progpath != NULL) {
171 progdir = strdup(progpath);
173 basename = strrchr(progdir, '\\');
174 if (basename != NULL) {
177 snprintf(_factory_config_file, sizeof(_factory_config_file),
178 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
180 if (workingdir!=NULL) {
181 snprintf(_factory_config_file, sizeof(_factory_config_file),
182 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
189 basename = strrchr(progdir, '/');
190 if (basename != NULL) {
193 snprintf(_factory_config_file, sizeof(_factory_config_file),
194 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
203 return _factory_config_file;
206 static void linphone_gtk_init_liblinphone(const char *config_file,
207 const char *factory_config_file) {
208 LinphoneCoreVTable vtable={0};
210 vtable.call_state_changed=linphone_gtk_call_state_changed;
211 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
212 vtable.notify_presence_recv=linphone_gtk_notify_recv;
213 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
214 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
215 vtable.display_status=linphone_gtk_display_status;
216 vtable.display_message=linphone_gtk_display_message;
217 vtable.display_warning=linphone_gtk_display_warning;
218 vtable.display_url=linphone_gtk_display_url;
219 vtable.call_log_updated=linphone_gtk_call_log_updated;
220 vtable.text_received=linphone_gtk_text_received;
221 vtable.refer_received=linphone_gtk_refer_received;
222 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
224 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
225 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
226 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
231 LinphoneCore *linphone_gtk_get_core(void){
235 GtkWidget *linphone_gtk_get_main_window(){
239 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
240 static const char *icon_path=NULL;
241 static const char *hiddens=NULL;
242 static const char *shown=NULL;
243 static bool_t config_loaded=FALSE;
244 if (linphone_gtk_get_core()==NULL) return;
245 if (config_loaded==FALSE){
246 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
247 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
248 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
252 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
254 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
256 GdkPixbuf *pbuf=create_pixbuf(icon_path);
257 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
258 g_object_unref(G_OBJECT(pbuf));
262 static int get_ui_file(const char *name, char *path, int pathsize){
263 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
264 if (access(path,F_OK)!=0){
265 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
266 if (access(path,F_OK)!=0){
267 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
268 INSTALLED_XML_DIR,name);
275 GtkWidget *linphone_gtk_create_window(const char *window_name){
276 GError* error = NULL;
277 GtkBuilder* builder = gtk_builder_new ();
281 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
283 if (!gtk_builder_add_from_file (builder, path, &error)){
284 g_error("Couldn't load builder file: %s", error->message);
285 g_error_free (error);
288 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
290 g_error("Could not retrieve '%s' window from xml file",window_name);
293 g_object_set_data(G_OBJECT(w),"builder",builder);
294 gtk_builder_connect_signals(builder,w);
295 linphone_gtk_configure_window(w,window_name);
299 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
302 GtkBuilder* builder = gtk_builder_new ();
304 gchar *object_ids[2];
305 object_ids[0]=g_strdup(widget_name);
308 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
309 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
310 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
311 g_error_free (error);
312 g_free(object_ids[0]);
315 g_free(object_ids[0]);
316 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
318 g_error("Could not retrieve '%s' window from xml file",widget_name);
321 g_object_set_data(G_OBJECT(w),"builder",builder);
322 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
323 gtk_builder_connect_signals(builder,w);
327 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
328 GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
331 g_error("Fail to retrieve builder from window !");
334 w=gtk_builder_get_object(builder,name);
336 g_error("No widget named %s found in xml interface.",name);
338 return GTK_WIDGET(w);
342 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
344 GtkWidget *main_window=linphone_gtk_get_main_window();
346 gtk_widget_show(main_window);
347 if (type==GTK_MESSAGE_QUESTION)
349 /* draw a question box. link to dialog_click callback */
350 dialog = gtk_message_dialog_new (
351 GTK_WINDOW(main_window),
352 GTK_DIALOG_DESTROY_WITH_PARENT,
353 GTK_MESSAGE_QUESTION,
356 (const gchar*)message);
357 /* connect to some callback : REVISIT */
359 g_signal_connect_swapped (G_OBJECT (dialog), "response",
360 G_CALLBACK (dialog_click),
363 /* actually show the box */
364 gtk_widget_show(dialog);
368 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
369 GTK_DIALOG_DESTROY_WITH_PARENT,
373 (const gchar*)message);
374 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
375 g_signal_connect_swapped (G_OBJECT (dialog), "response",
376 G_CALLBACK (gtk_widget_destroy),
378 gtk_widget_show(dialog);
382 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
383 if (id==GTK_RESPONSE_CANCEL){
384 gtk_widget_destroy(GTK_WIDGET(dialog));
388 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
389 g_message("About url clicked");
390 linphone_gtk_open_browser(url);
393 void linphone_gtk_show_about(){
394 struct stat filestat;
395 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
398 GdkPixbuf *logo=create_pixbuf(
399 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
400 static const char *defcfg="defcfg";
402 about=linphone_gtk_create_window("about");
403 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
404 memset(&filestat,0,sizeof(filestat));
405 if (stat(license_file,&filestat)!=0){
406 license_file="COPYING";
407 stat(license_file,&filestat);
409 if (filestat.st_size>0){
410 char *license=g_malloc(filestat.st_size+1);
411 FILE *f=fopen(license_file,"r");
412 if (f && fread(license,filestat.st_size,1,f)==1){
413 license[filestat.st_size]='\0';
414 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
418 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
419 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
420 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
421 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
422 tmp=linphone_gtk_get_ui_config("artists",defcfg);
427 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
429 tmp=linphone_gtk_get_ui_config("translators",defcfg);
431 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
432 tmp=linphone_gtk_get_ui_config("comments",defcfg);
434 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
435 gtk_widget_show(about);
438 static void set_video_window_decorations(GdkWindow *w){
439 const char *title=linphone_gtk_get_ui_config("title","Linphone");
440 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
441 char video_title[256];
442 GdkPixbuf *pbuf=create_pixbuf(icon_path);
443 if (!linphone_core_in_call(linphone_gtk_get_core())){
444 snprintf(video_title,sizeof(video_title),"%s video",title);
445 /* When not in call, treat the video as a normal window */
446 gdk_window_set_keep_above(w, FALSE);
448 LinphoneAddress *uri =
449 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
452 linphone_address_clean(uri);
453 if (linphone_address_get_display_name(uri)!=NULL){
454 display_name=ms_strdup(linphone_address_get_display_name(uri));
456 display_name=linphone_address_as_string(uri);
458 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
459 linphone_address_destroy(uri);
460 ms_free(display_name);
462 /* During calls, bring up the video window, arrange so that
463 it is above all the other windows */
464 gdk_window_deiconify(w);
465 gdk_window_set_keep_above(w,TRUE);
466 /* Maybe we should have the following, but then we want to
467 have a timer that turns it off after a little while. */
468 /* gdk_window_set_urgency_hint(w,TRUE); */
470 gdk_window_set_title(w,video_title);
471 /* Refrain the video window to be closed at all times. */
472 gdk_window_set_functions(w,
473 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
474 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
477 l=g_list_append(l,pbuf);
478 gdk_window_set_icon_list(w,l);
480 g_object_unref(G_OBJECT(pbuf));
484 static gboolean video_needs_update=FALSE;
486 static void update_video_title(){
487 video_needs_update=TRUE;
490 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
491 static gboolean first_time=TRUE;
493 static unsigned long previd=0;
494 static unsigned long preview_previd=0;
495 static gboolean in_iterate=FALSE;
498 if (in_iterate) return TRUE;
500 linphone_core_iterate(lc);
502 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
503 linphone_gtk_show_directory_search();
507 id=linphone_core_get_native_video_window_id(lc);
508 if (id!=previd || video_needs_update){
512 ms_message("Updating window decorations");
514 w=gdk_window_foreign_new(id);
516 w=gdk_window_foreign_new((HANDLE)id);
519 set_video_window_decorations(w);
520 g_object_unref(G_OBJECT(w));
522 else ms_error("gdk_window_foreign_new() failed");
523 if (video_needs_update) video_needs_update=FALSE;
526 id=linphone_core_get_native_preview_window_id (lc);
527 if (id!=preview_previd ){
531 ms_message("Updating window decorations for preview");
533 w=gdk_window_foreign_new(id);
535 w=gdk_window_foreign_new((HANDLE)id);
538 set_video_window_decorations(w);
539 g_object_unref(G_OBJECT(w));
541 else ms_error("gdk_window_foreign_new() failed");
542 if (video_needs_update) video_needs_update=FALSE;
545 if (addr_to_call!=NULL){
546 /*make sure we are not showing the login screen*/
547 GtkWidget *mw=linphone_gtk_get_main_window();
548 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
549 if (!GTK_WIDGET_VISIBLE(login_frame)){
550 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
551 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
553 linphone_gtk_start_call(uri_bar);
560 static void load_uri_history(){
561 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
564 GtkEntryCompletion *gep=gtk_entry_completion_new();
565 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
568 snprintf(key,sizeof(key),"uri%i",i);
569 uri=linphone_gtk_get_ui_config(key,NULL);
572 gtk_list_store_append(model,&iter);
573 gtk_list_store_set(model,&iter,0,uri,-1);
574 if (i==0) gtk_entry_set_text(uribar,uri);
578 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
579 gtk_entry_completion_set_text_column(gep,0);
580 gtk_entry_set_completion(uribar,gep);
583 static void save_uri_history(){
584 LinphoneCore *lc=linphone_gtk_get_core();
585 LpConfig *cfg=linphone_core_get_config(lc);
586 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
591 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
593 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
595 gtk_tree_model_get(model,&iter,0,&uri,-1);
597 snprintf(key,sizeof(key),"uri%i",i);
598 lp_config_set_string(cfg,"GtkUi",key,uri);
603 }while(gtk_tree_model_iter_next(model,&iter));
607 static void completion_add_text(GtkEntry *entry, const char *text){
609 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
611 if (gtk_tree_model_get_iter_first(model,&iter)){
614 gtk_tree_model_get(model,&iter,0,&uri,-1);
616 if (strcmp(uri,text)==0) {
618 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
624 }while (gtk_tree_model_iter_next(model,&iter));
626 /* and prepend it on top of the list */
627 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
628 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
633 void linphone_gtk_show_main_window(){
634 GtkWidget *w=linphone_gtk_get_main_window();
635 LinphoneCore *lc=linphone_gtk_get_core();
636 if (linphone_core_video_enabled(lc)){
637 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
638 VIDEOSELFVIEW_DEFAULT));
641 gtk_window_present(GTK_WINDOW(w));
644 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
645 GtkWidget *mw=linphone_gtk_get_main_window();
646 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
647 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
648 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
650 if (linphone_gtk_use_in_call_view() && call)
651 linphone_gtk_in_call_view_terminate(call,error);
652 update_video_title();
655 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
656 LinphoneCore *lc=linphone_gtk_get_core();
657 GtkWidget *mw=linphone_gtk_get_main_window();
658 const MSList *calls=linphone_core_get_calls(lc);
660 bool_t start_active=TRUE;
661 bool_t stop_active=FALSE;
662 bool_t add_call=FALSE;
663 int call_list_size=ms_list_size(calls);
673 button=linphone_gtk_get_widget(mw,"start_call");
674 gtk_widget_set_sensitive(button,start_active);
675 gtk_widget_set_visible(button,!add_call);
677 button=linphone_gtk_get_widget(mw,"add_call");
678 gtk_widget_set_sensitive(button,start_active);
679 gtk_widget_set_visible(button,add_call);
681 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
683 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
684 linphone_gtk_enable_conference_button(lc,call_list_size>1);
685 update_video_title();
688 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
689 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
690 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
691 completion_add_text(GTK_ENTRY(uri_bar),entered);
693 linphone_gtk_call_terminated(NULL,NULL);
698 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
699 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
700 linphone_core_accept_call (linphone_gtk_get_core(),call);
701 linphone_call_unref(call);
707 void linphone_gtk_start_call(GtkWidget *w){
708 LinphoneCore *lc=linphone_gtk_get_core();
710 /*change into in-call mode, then do the work later as it might block a bit */
711 GtkWidget *mw=gtk_widget_get_toplevel(w);
712 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
714 call=linphone_gtk_get_currently_displayed_call(NULL);
715 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
716 linphone_core_accept_call(lc,call);
718 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
719 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
720 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
721 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
726 void linphone_gtk_uri_bar_activate(GtkWidget *w){
727 linphone_gtk_start_call(w);
731 void linphone_gtk_terminate_call(GtkWidget *button){
733 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
735 linphone_core_terminate_call(linphone_gtk_get_core(),call);
737 linphone_core_terminate_conference(linphone_gtk_get_core());
741 void linphone_gtk_decline_clicked(GtkWidget *button){
742 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
744 linphone_core_terminate_call(linphone_gtk_get_core(),call);
747 void linphone_gtk_answer_clicked(GtkWidget *button){
748 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
750 linphone_core_accept_call(linphone_gtk_get_core(),call);
751 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
755 void linphone_gtk_enable_video(GtkWidget *w){
756 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
757 GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
758 linphone_core_enable_video(linphone_gtk_get_core(),val,val);
759 gtk_widget_set_sensitive(selfview_item,val);
761 linphone_core_enable_video_preview(linphone_gtk_get_core(),
762 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
764 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
768 void linphone_gtk_enable_self_view(GtkWidget *w){
769 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
770 LinphoneCore *lc=linphone_gtk_get_core();
771 linphone_core_enable_video_preview(lc,val);
772 linphone_core_enable_self_view(lc,val);
773 linphone_gtk_set_ui_config_int("videoselfview",val);
776 void linphone_gtk_used_identity_changed(GtkWidget *w){
777 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
778 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
779 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
780 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
781 linphone_gtk_show_directory_search();
783 if (sel) g_free(sel);
786 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
787 linphone_gtk_show_friends();
790 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
792 case GTK_RESPONSE_YES:
793 linphone_gtk_show_contact(lf);
796 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
798 gtk_widget_destroy(dialog);
801 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
804 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
805 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
809 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);
810 dialog = gtk_message_dialog_new (
811 GTK_WINDOW(linphone_gtk_get_main_window()),
812 GTK_DIALOG_DESTROY_WITH_PARENT,
813 GTK_MESSAGE_QUESTION,
818 g_signal_connect(G_OBJECT (dialog), "response",
819 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
820 /* actually show the box */
821 gtk_widget_show(dialog);
824 typedef struct _AuthTimeout{
829 static void auth_timeout_clean(AuthTimeout *tout){
833 static gboolean auth_timeout_destroy(AuthTimeout *tout){
835 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
836 gtk_widget_destroy(tout->w);
842 static AuthTimeout * auth_timeout_new(GtkWidget *w){
843 AuthTimeout *tout=g_new(AuthTimeout,1);
845 /*so that the timeout no more references the widget when it is destroyed:*/
846 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
847 /*so that the widget is automatically destroyed after some time */
848 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
852 void linphone_gtk_password_cancel(GtkWidget *w){
853 LinphoneAuthInfo *info;
854 GtkWidget *window=gtk_widget_get_toplevel(w);
855 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
856 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
857 gtk_widget_destroy(window);
860 void linphone_gtk_password_ok(GtkWidget *w){
862 GtkWidget *window=gtk_widget_get_toplevel(w);
863 LinphoneAuthInfo *info;
864 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
865 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
866 entry=linphone_gtk_get_widget(window,"password_entry");
867 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
868 linphone_auth_info_set_userid(info,
869 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
870 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
871 gtk_widget_destroy(window);
874 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
875 GtkWidget *w=linphone_gtk_create_window("password");
876 GtkWidget *label=linphone_gtk_get_widget(w,"message");
877 LinphoneAuthInfo *info;
879 GtkWidget *mw=linphone_gtk_get_main_window();
881 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
882 /*don't prompt for authentication when login frame is visible*/
883 linphone_core_abort_authentication(lc,NULL);
887 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
889 gtk_label_set_markup(GTK_LABEL(label),msg);
891 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
892 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
893 g_object_set_data(G_OBJECT(w),"auth_info",info);
894 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
899 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
900 GtkWidget *w=linphone_gtk_get_main_window();
901 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
902 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
903 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
907 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
908 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
911 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
912 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
915 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
917 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
918 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
921 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
922 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
923 if (w) linphone_gtk_call_log_update(w);
924 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
928 static bool_t notify_actions_supported() {
929 bool_t accepts_actions = FALSE;
930 GList *capabilities = notify_get_server_caps();
932 if(capabilities != NULL) {
933 for(c = capabilities; c != NULL; c = c->next) {
934 if(strcmp((char*)c->data, "actions") == 0 ) {
935 accepts_actions = TRUE;
939 g_list_foreach(capabilities, (GFunc)g_free, NULL);
940 g_list_free(capabilities);
942 return accepts_actions;
945 static NotifyNotification* build_notification(const char *title, const char *body){
946 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
953 static void show_notification(NotifyNotification* n){
954 if (n && !notify_notification_show(n,NULL))
955 ms_error("Failed to send notification.");
958 static void make_notification(const char *title, const char *body){
959 show_notification(build_notification(title,body));
964 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
966 if (!notify_is_initted())
967 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
971 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
976 ms_error("Failed to send notification.");
978 linphone_gtk_show_main_window();
980 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
983 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
984 NotifyNotification *n;
985 switch(linphone_call_get_state(call)){
986 case LinphoneCallError:
987 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
989 case LinphoneCallEnd:
990 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
992 case LinphoneCallIncomingReceived:
993 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
994 if (notify_actions_supported()) {
995 notify_notification_add_action (n,"answer", _("Answer"),
996 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
997 notify_notification_add_action (n,"decline",_("Decline"),
998 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1000 show_notification(n);
1002 case LinphoneCallPausedByRemote:
1003 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1008 if (body) g_free(body);
1009 if (remote) g_free(remote);
1014 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1016 case LinphoneCallOutgoingInit:
1017 linphone_gtk_create_in_call_view (call);
1019 case LinphoneCallOutgoingProgress:
1020 linphone_gtk_in_call_view_set_calling (call);
1022 case LinphoneCallStreamsRunning:
1023 linphone_gtk_in_call_view_set_in_call(call);
1025 case LinphoneCallError:
1026 linphone_gtk_in_call_view_terminate (call,msg);
1028 case LinphoneCallEnd:
1029 linphone_gtk_in_call_view_terminate(call,NULL);
1030 linphone_gtk_status_icon_set_blinking(FALSE);
1032 case LinphoneCallIncomingReceived:
1033 linphone_gtk_create_in_call_view(call);
1034 linphone_gtk_in_call_view_set_incoming(call);
1035 linphone_gtk_status_icon_set_blinking(TRUE);
1037 linphone_call_ref(call);
1038 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1041 case LinphoneCallResuming:
1042 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1043 linphone_gtk_in_call_view_set_in_call (call);
1045 case LinphoneCallPausing:
1046 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1047 case LinphoneCallPausedByRemote:
1048 linphone_gtk_in_call_view_set_paused(call);
1050 case LinphoneCallConnected:
1051 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1052 linphone_gtk_status_icon_set_blinking(FALSE);
1057 linphone_gtk_notify(call, msg);
1058 linphone_gtk_update_call_buttons (call);
1061 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1062 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1063 GtkTreeModel *model=gtk_combo_box_get_model(box);
1065 gboolean found=FALSE;
1066 const char *stock_id=NULL;
1068 if (gtk_tree_model_get_iter_first(model,&iter)){
1071 gtk_tree_model_get(model,&iter,2,&p,-1);
1076 }while(gtk_tree_model_iter_next(model,&iter));
1079 g_warning("Could not find proxy config in combo box of identities.");
1083 case LinphoneRegistrationOk:
1084 stock_id=GTK_STOCK_YES;
1086 case LinphoneRegistrationProgress:
1087 stock_id=GTK_STOCK_REFRESH;
1089 case LinphoneRegistrationCleared:
1092 case LinphoneRegistrationFailed:
1093 stock_id=GTK_STOCK_DIALOG_WARNING;
1098 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1101 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1102 LinphoneRegistrationState rs, const char *msg){
1104 case LinphoneRegistrationOk:
1106 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1107 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1108 linphone_gtk_exit_login_frame();
1115 update_registration_status(cfg,rs);
1118 void linphone_gtk_open_browser(const char *url){
1119 /*in gtk 2.16, gtk_show_uri does not work...*/
1121 #if GTK_CHECK_VERSION(2,18,3)
1122 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1125 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1126 g_spawn_command_line_async(cl,NULL);
1129 ShellExecute(0,"open",url,NULL,NULL,1);
1133 void linphone_gtk_link_to_website(GtkWidget *item){
1134 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1135 linphone_gtk_open_browser(home);
1138 #ifndef HAVE_GTK_OSX
1140 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1141 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1142 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1145 static GtkWidget *create_icon_menu(){
1146 GtkWidget *menu=gtk_menu_new();
1147 GtkWidget *menu_item;
1150 const gchar *homesite;
1152 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1153 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1154 tmp=g_strdup(homesite);
1155 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1156 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1158 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1159 gtk_widget_show(image);
1160 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1161 //g_object_unref(G_OBJECT(image));
1162 gtk_widget_show(menu_item);
1163 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1164 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1166 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1167 gtk_widget_show(menu_item);
1168 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1169 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1170 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1171 gtk_widget_show(menu_item);
1172 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1173 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1174 gtk_widget_show(menu);
1178 static GtkStatusIcon *icon=NULL;
1180 static void handle_icon_click() {
1181 GtkWidget *mw=linphone_gtk_get_main_window();
1182 if (!gtk_window_is_active((GtkWindow*)mw)) {
1183 linphone_gtk_show_main_window();
1185 gtk_widget_hide(mw);
1189 static void linphone_gtk_init_status_icon(){
1190 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1191 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1192 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1193 GtkWidget *menu=create_icon_menu();
1195 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1196 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1197 gtk_status_icon_set_name(icon,title);
1198 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1199 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1200 gtk_status_icon_set_tooltip(icon,title);
1201 gtk_status_icon_set_visible(icon,TRUE);
1202 g_object_set_data(G_OBJECT(icon),"menu",menu);
1203 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1204 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1205 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1206 pbuf=create_pixbuf(call_icon_path);
1207 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1210 static gboolean do_icon_blink(GtkStatusIcon *gi){
1211 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1212 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1213 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1214 if (cur_icon==call_icon){
1215 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1217 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1224 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1226 static gint attention_id;
1227 GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1229 attention_id=gtk_osxapplication_attention_request(theMacApp,CRITICAL_REQUEST);
1230 else gtk_osxapplication_cancel_attention_request(theMacApp,attention_id);
1234 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1235 if (val && tout==0){
1236 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1237 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1238 }else if (!val && tout!=0){
1239 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1240 g_source_remove(tout);
1241 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1242 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1248 static void init_identity_combo(GtkComboBox *box){
1249 GtkListStore *store;
1250 GtkCellRenderer *r1,*r2;
1251 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1252 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1253 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1254 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1255 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1256 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1257 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1258 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1261 void linphone_gtk_load_identities(void){
1263 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1265 LinphoneProxyConfig *def=NULL;
1267 GtkListStore *store;
1270 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1271 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1272 /* model is empty, this is the first time we go here */
1273 init_identity_combo(box);
1274 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1276 gtk_list_store_clear(store);
1277 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1278 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1279 gtk_list_store_append(store,&iter);
1280 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1281 g_free(def_identity);
1282 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1284 elem=ms_list_next(elem),i++){
1285 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1286 gtk_list_store_append(store,&iter);
1287 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1288 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1294 gtk_combo_box_set_active(box,def_index);
1297 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1298 const char *label=gtk_button_get_label(button);
1299 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1301 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1302 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1303 if (linphone_core_in_call(linphone_gtk_get_core())){
1304 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1308 static void linphone_gtk_dtmf_released(GtkButton *button){
1309 linphone_core_stop_dtmf (linphone_gtk_get_core());
1312 static void linphone_gtk_connect_digits(void){
1313 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1314 GList *children=gtk_container_get_children(cont);
1316 for(elem=children;elem!=NULL;elem=elem->next){
1317 GtkButton *button=GTK_BUTTON(elem->data);
1318 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1319 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1323 static void linphone_gtk_check_menu_items(void){
1324 bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core());
1325 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1326 GtkWidget *selfview_item=linphone_gtk_get_widget(
1327 linphone_gtk_get_main_window(),"selfview_item");
1328 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1329 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1330 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1331 gtk_widget_set_sensitive(selfview_item,video_enabled);
1334 static gboolean linphone_gtk_can_manage_accounts(){
1335 LinphoneCore *lc=linphone_gtk_get_core();
1337 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1338 SipSetup *ss=(SipSetup*)elem->data;
1339 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1346 static void linphone_gtk_configure_main_window(){
1347 static gboolean config_loaded=FALSE;
1348 static const char *title;
1349 static const char *home;
1350 static const char *start_call_icon;
1351 static const char *add_call_icon;
1352 static const char *stop_call_icon;
1353 static const char *search_icon;
1354 static gboolean update_check_menu;
1355 static gboolean buttons_have_borders;
1356 static gboolean show_abcd;
1357 GtkWidget *w=linphone_gtk_get_main_window();
1358 if (!config_loaded){
1359 title=linphone_gtk_get_ui_config("title","Linphone");
1360 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1361 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1362 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1363 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1364 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1365 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1366 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1367 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1370 linphone_gtk_configure_window(w,"main_window");
1372 gtk_window_set_title(GTK_WINDOW(w),title);
1374 if (start_call_icon){
1375 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1376 create_pixmap (start_call_icon));
1377 if (!buttons_have_borders)
1378 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1381 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1382 create_pixmap (add_call_icon));
1383 if (!buttons_have_borders)
1384 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1386 if (stop_call_icon){
1387 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1388 create_pixmap (stop_call_icon));
1389 if (!buttons_have_borders)
1390 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1393 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1394 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1395 g_object_unref(G_OBJECT(pbuf));
1399 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1401 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1405 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1407 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1408 g_object_unref(G_OBJECT(pbuf));
1413 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1415 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon")),pbuf);
1416 g_object_unref(G_OBJECT(pbuf));
1419 if (linphone_gtk_can_manage_accounts())
1420 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1421 if (update_check_menu){
1422 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1425 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1426 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1427 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1428 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1429 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1433 void linphone_gtk_manage_login(void){
1434 LinphoneCore *lc=linphone_gtk_get_core();
1435 LinphoneProxyConfig *cfg=NULL;
1436 linphone_core_get_default_proxy(lc,&cfg);
1438 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1439 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1440 linphone_gtk_show_login_frame(cfg);
1446 gboolean linphone_gtk_close(GtkWidget *mw){
1447 /*shutdown calls if any*/
1448 LinphoneCore *lc=linphone_gtk_get_core();
1449 if (linphone_core_in_call(lc)){
1450 linphone_core_terminate_all_calls(lc);
1452 linphone_core_enable_video_preview(lc,FALSE);
1453 #ifdef __APPLE__ /*until with have a better option*/
1454 gtk_window_iconify(GTK_WINDOW(mw));
1456 gtk_widget_hide(mw);
1462 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1463 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1464 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1466 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1467 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1474 static void linphone_gtk_init_main_window(){
1475 GtkWidget *main_window;
1477 linphone_gtk_configure_main_window();
1478 linphone_gtk_manage_login();
1480 linphone_gtk_load_identities();
1481 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1482 linphone_gtk_show_friends();
1483 linphone_gtk_connect_digits();
1484 main_window=linphone_gtk_get_main_window();
1485 linphone_gtk_call_log_update(main_window);
1487 linphone_gtk_update_call_buttons (NULL);
1488 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1489 g_signal_connect (G_OBJECT (main_window), "delete-event",
1490 G_CALLBACK (linphone_gtk_close), main_window);
1493 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1494 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1495 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1496 gtk_widget_hide(menubar);
1497 gtk_osxapplication_ready(theMacApp);
1499 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1501 linphone_gtk_check_menu_items();
1505 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1507 const char *lname="undef";
1509 #if defined(__linux) || defined(__APPLE__)
1510 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1529 g_error("Bad level !");
1531 #if defined(__linux) || defined(__APPLE__)
1533 msg=g_strdup_vprintf(fmt,cap);
1536 msg=g_strdup_vprintf(fmt,args);
1538 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1541 linphone_gtk_log_push(lev,fmt,args);
1545 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1546 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1547 linphone_gtk_get_main_window(), "uribar"));
1549 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1551 gtk_entry_set_text(uri_bar, refer_to);
1552 linphone_gtk_start_call(linphone_gtk_get_main_window());
1555 static void linphone_gtk_check_soundcards(){
1556 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1557 if (devices==NULL || devices[0]==NULL){
1558 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1559 _("No sound cards have been detected on this computer.\n"
1560 "You won't be able to send or receive audio calls."));
1564 static void linphone_gtk_quit(void){
1565 linphone_gtk_uninit_instance();
1566 linphone_gtk_destroy_log_window();
1567 linphone_core_destroy(the_core);
1568 linphone_gtk_log_uninit();
1572 gdk_threads_leave();
1577 This is not the correct way to implement block termination.
1578 The good way would be to call gtk_main_quit(), and return TRUE.
1579 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1580 As a result the program cannot exit at all.
1581 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1583 static gboolean on_block_termination(void){
1585 linphone_gtk_quit();
1590 int main(int argc, char *argv[]){
1594 const char *config_file;
1595 const char *factory_config_file;
1597 GtkSettings *settings;
1599 const char *app_name="Linphone";
1601 g_thread_init(NULL);
1604 progpath = strdup(argv[0]);
1606 config_file=linphone_gtk_get_config_file();
1610 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1611 if ((lang=getenv("LANG"))!=NULL){
1614 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1620 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1623 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1626 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1629 setenv("LANG",lang,1);
1634 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1635 if (p==NULL) perror("bindtextdomain failed");
1636 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1637 textdomain (GETTEXT_PACKAGE);
1639 g_message("NLS disabled.\n");
1642 gtk_rc_add_default_file("./gtkrc");
1644 gdk_threads_enter();
1646 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1647 linphone_options,NULL,NULL)){
1648 gdk_threads_leave();
1652 settings=gtk_settings_get_default();
1653 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1654 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1655 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1656 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1658 if (workingdir!=NULL){
1659 if (chdir(workingdir)==-1){
1660 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1664 /* Now, look for the factory configuration file, we do it this late
1665 since we want to have had time to change directory and to parse
1666 the options, in case we needed to access the working directory */
1667 factory_config_file = linphone_gtk_get_factory_config_file();
1669 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1670 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1671 g_warning("This instance is going to exit now.");
1672 gdk_threads_leave();
1676 add_pixmap_directory("pixmaps");
1677 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1680 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1681 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1682 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1683 /*never block termination:*/
1684 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1687 the_ui=linphone_gtk_create_window("main");
1689 linphone_gtk_create_log_window();
1690 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1692 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1694 g_set_application_name(app_name);
1695 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1696 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1698 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1699 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1700 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1701 linphone_gtk_init_main_window();
1702 #ifndef HAVE_GTK_OSX
1703 linphone_gtk_init_status_icon();
1706 linphone_gtk_show_main_window();
1707 linphone_gtk_check_soundcards();
1709 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1710 linphone_gtk_check_for_new_version();
1713 linphone_gtk_quit();
1714 #ifndef HAVE_GTK_OSX
1715 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1716 gtk_status_icon_set_visible(icon,FALSE);