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>
40 #include <libnotify/notify.h>
43 #define LINPHONE_ICON "linphone.png"
45 const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION;
47 static LinphoneCore *the_core=NULL;
48 static GtkWidget *the_ui=NULL;
50 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg);
51 static void linphone_gtk_show(LinphoneCore *lc);
52 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
53 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
54 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username);
55 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
56 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
57 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
58 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
59 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
60 static void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to);
61 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
62 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
63 static void linphone_gtk_status_icon_set_blinking(gboolean val);
66 static gboolean verbose=0;
67 static gboolean auto_answer = 0;
68 static gchar * addr_to_call = NULL;
69 static gboolean iconified=FALSE;
70 static gchar *workingdir=NULL;
71 static char *progpath=NULL;
72 gchar *linphone_logfile=NULL;
74 static GOptionEntry linphone_options[]={
78 .arg=G_OPTION_ARG_NONE,
79 .arg_data= (gpointer)&verbose,
80 .description=N_("log to stdout some debug information while running.")
83 .long_name = "logfile",
85 .arg = G_OPTION_ARG_STRING,
86 .arg_data = &linphone_logfile,
87 .description = N_("path to a file to write logs into.")
90 .long_name="iconified",
92 .arg=G_OPTION_ARG_NONE,
93 .arg_data= (gpointer)&iconified,
94 .description=N_("Start only in the system tray, do not show the main interface.")
99 .arg = G_OPTION_ARG_STRING,
100 .arg_data = &addr_to_call,
101 .description = N_("address to call right now")
104 .long_name = "auto-answer",
106 .arg = G_OPTION_ARG_NONE,
107 .arg_data = (gpointer) & auto_answer,
108 .description = N_("if set automatically answer incoming calls")
111 .long_name = "workdir",
113 .arg = G_OPTION_ARG_STRING,
114 .arg_data = (gpointer) & workingdir,
115 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
120 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
121 #define RELATIVE_XML_DIR
122 #define BUILD_TREE_XML_DIR "gtk"
125 #define CONFIG_FILE ".linphonerc"
127 #define CONFIG_FILE "linphonerc"
132 static char _config_file[1024];
135 const char *linphone_gtk_get_config_file(){
136 /*try accessing a local file first if exists*/
137 if (access(CONFIG_FILE,F_OK)==0){
138 snprintf(_config_file,sizeof(_config_file),"%s",CONFIG_FILE);
141 const char *appdata=getenv("APPDATA");
143 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
144 CreateDirectory(_config_file,NULL);
145 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR "\\" CONFIG_FILE);
148 const char *home=getenv("HOME");
149 if (home==NULL) home=".";
150 snprintf(_config_file,sizeof(_config_file),"%s/%s",home,CONFIG_FILE);
157 #define FACTORY_CONFIG_FILE "linphonerc.factory"
158 static char _factory_config_file[1024];
159 static const char *linphone_gtk_get_factory_config_file(){
160 /*try accessing a local file first if exists*/
161 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
162 snprintf(_factory_config_file,sizeof(_factory_config_file),
163 "%s",FACTORY_CONFIG_FILE);
167 if (progpath != NULL) {
169 progdir = strdup(progpath);
171 basename = strrchr(progdir, '\\');
172 if (basename != NULL) {
175 snprintf(_factory_config_file, sizeof(_factory_config_file),
176 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
178 if (workingdir!=NULL) {
179 snprintf(_factory_config_file, sizeof(_factory_config_file),
180 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
187 basename = strrchr(progdir, '/');
188 if (basename != NULL) {
191 snprintf(_factory_config_file, sizeof(_factory_config_file),
192 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
201 return _factory_config_file;
204 static void linphone_gtk_init_liblinphone(const char *config_file,
205 const char *factory_config_file) {
206 LinphoneCoreVTable vtable={0};
208 vtable.call_state_changed=linphone_gtk_call_state_changed;
209 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
210 vtable.show=linphone_gtk_show;
211 vtable.notify_presence_recv=linphone_gtk_notify_recv;
212 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
213 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
214 vtable.display_status=linphone_gtk_display_status;
215 vtable.display_message=linphone_gtk_display_message;
216 vtable.display_warning=linphone_gtk_display_warning;
217 vtable.display_url=linphone_gtk_display_url;
218 vtable.call_log_updated=linphone_gtk_call_log_updated;
219 vtable.text_received=linphone_gtk_text_received;
220 vtable.refer_received=linphone_gtk_refer_received;
221 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
223 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
224 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
225 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
230 LinphoneCore *linphone_gtk_get_core(void){
234 GtkWidget *linphone_gtk_get_main_window(){
238 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
239 static const char *icon_path=NULL;
240 static const char *hiddens=NULL;
241 static const char *shown=NULL;
242 static bool_t config_loaded=FALSE;
243 if (linphone_gtk_get_core()==NULL) return;
244 if (config_loaded==FALSE){
245 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
246 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
247 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
251 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
253 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
255 GdkPixbuf *pbuf=create_pixbuf(icon_path);
256 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
257 g_object_unref(G_OBJECT(pbuf));
261 static int get_ui_file(const char *name, char *path, int pathsize){
262 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
263 if (access(path,F_OK)!=0){
264 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
265 if (access(path,F_OK)!=0){
266 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
267 INSTALLED_XML_DIR,name);
274 GtkWidget *linphone_gtk_create_window(const char *window_name){
275 GError* error = NULL;
276 GtkBuilder* builder = gtk_builder_new ();
280 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
282 if (!gtk_builder_add_from_file (builder, path, &error)){
283 g_error("Couldn't load builder file: %s", error->message);
284 g_error_free (error);
287 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
289 g_error("Could not retrieve '%s' window from xml file",window_name);
292 g_object_set_data(G_OBJECT(w),"builder",builder);
293 gtk_builder_connect_signals(builder,w);
294 linphone_gtk_configure_window(w,window_name);
298 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
301 GtkBuilder* builder = gtk_builder_new ();
303 gchar *object_ids[2];
304 object_ids[0]=g_strdup(widget_name);
307 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
308 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
309 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
310 g_error_free (error);
311 g_free(object_ids[0]);
314 g_free(object_ids[0]);
315 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
317 g_error("Could not retrieve '%s' window from xml file",widget_name);
320 g_object_set_data(G_OBJECT(w),"builder",builder);
321 gtk_builder_connect_signals(builder,w);
325 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
326 GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
329 g_error("Fail to retrieve builder from window !");
332 w=gtk_builder_get_object(builder,name);
334 g_error("No widget named %s found in xml interface.",name);
336 return GTK_WIDGET(w);
340 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
342 GtkWidget *main_window=linphone_gtk_get_main_window();
344 gtk_widget_show(main_window);
345 if (type==GTK_MESSAGE_QUESTION)
347 /* draw a question box. link to dialog_click callback */
348 dialog = gtk_message_dialog_new (
349 GTK_WINDOW(main_window),
350 GTK_DIALOG_DESTROY_WITH_PARENT,
351 GTK_MESSAGE_QUESTION,
354 (const gchar*)message);
355 /* connect to some callback : REVISIT */
357 g_signal_connect_swapped (G_OBJECT (dialog), "response",
358 G_CALLBACK (dialog_click),
361 /* actually show the box */
362 gtk_widget_show(dialog);
366 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
367 GTK_DIALOG_DESTROY_WITH_PARENT,
371 (const gchar*)message);
372 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
373 g_signal_connect_swapped (G_OBJECT (dialog), "response",
374 G_CALLBACK (gtk_widget_destroy),
376 gtk_widget_show(dialog);
380 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
381 if (id==GTK_RESPONSE_CANCEL){
382 gtk_widget_destroy(GTK_WIDGET(dialog));
386 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
387 g_message("About url clicked");
388 linphone_gtk_open_browser(url);
391 void linphone_gtk_show_about(){
392 struct stat filestat;
393 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
396 GdkPixbuf *logo=create_pixbuf(
397 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
398 static const char *defcfg="defcfg";
400 about=linphone_gtk_create_window("about");
401 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
402 memset(&filestat,0,sizeof(filestat));
403 if (stat(license_file,&filestat)!=0){
404 license_file="COPYING";
405 stat(license_file,&filestat);
407 if (filestat.st_size>0){
408 char *license=g_malloc(filestat.st_size+1);
409 FILE *f=fopen(license_file,"r");
410 if (f && fread(license,filestat.st_size,1,f)==1){
411 license[filestat.st_size]='\0';
412 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
416 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
417 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
418 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
419 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
420 tmp=linphone_gtk_get_ui_config("artists",defcfg);
425 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
427 tmp=linphone_gtk_get_ui_config("translators",defcfg);
429 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
430 tmp=linphone_gtk_get_ui_config("comments",defcfg);
432 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
433 gtk_widget_show(about);
436 static void set_video_window_decorations(GdkWindow *w){
437 const char *title=linphone_gtk_get_ui_config("title","Linphone");
438 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
439 char video_title[256];
440 GdkPixbuf *pbuf=create_pixbuf(icon_path);
441 if (!linphone_core_in_call(linphone_gtk_get_core())){
442 snprintf(video_title,sizeof(video_title),"%s video",title);
443 /* When not in call, treat the video as a normal window */
444 gdk_window_set_keep_above(w, FALSE);
446 LinphoneAddress *uri =
447 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
450 linphone_address_clean(uri);
451 if (linphone_address_get_display_name(uri)!=NULL){
452 display_name=ms_strdup(linphone_address_get_display_name(uri));
454 display_name=linphone_address_as_string(uri);
456 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
457 linphone_address_destroy(uri);
458 ms_free(display_name);
460 /* During calls, bring up the video window, arrange so that
461 it is above all the other windows */
462 gdk_window_deiconify(w);
463 gdk_window_set_keep_above(w,TRUE);
464 /* Maybe we should have the following, but then we want to
465 have a timer that turns it off after a little while. */
466 /* gdk_window_set_urgency_hint(w,TRUE); */
468 gdk_window_set_title(w,video_title);
469 /* Refrain the video window to be closed at all times. */
470 gdk_window_set_functions(w,
471 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
472 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
475 l=g_list_append(l,pbuf);
476 gdk_window_set_icon_list(w,l);
478 g_object_unref(G_OBJECT(pbuf));
482 static gboolean video_needs_update=FALSE;
484 static void update_video_title(){
485 video_needs_update=TRUE;
488 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
489 static gboolean first_time=TRUE;
491 static unsigned long previd=0;
492 static unsigned long preview_previd=0;
493 static gboolean in_iterate=FALSE;
496 if (in_iterate) return TRUE;
498 linphone_core_iterate(lc);
500 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
501 linphone_gtk_show_directory_search();
505 id=linphone_core_get_native_video_window_id(lc);
506 if (id!=previd || video_needs_update){
510 ms_message("Updating window decorations");
512 w=gdk_window_foreign_new(id);
514 w=gdk_window_foreign_new((HANDLE)id);
517 set_video_window_decorations(w);
518 g_object_unref(G_OBJECT(w));
520 else ms_error("gdk_window_foreign_new() failed");
521 if (video_needs_update) video_needs_update=FALSE;
524 id=linphone_core_get_native_preview_window_id (lc);
525 if (id!=preview_previd ){
529 ms_message("Updating window decorations for preview");
531 w=gdk_window_foreign_new(id);
533 w=gdk_window_foreign_new((HANDLE)id);
536 set_video_window_decorations(w);
537 g_object_unref(G_OBJECT(w));
539 else ms_error("gdk_window_foreign_new() failed");
540 if (video_needs_update) video_needs_update=FALSE;
543 if (addr_to_call!=NULL){
544 /*make sure we are not showing the login screen*/
545 GtkWidget *mw=linphone_gtk_get_main_window();
546 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
547 if (!GTK_WIDGET_VISIBLE(login_frame)){
548 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
549 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
551 linphone_gtk_start_call(uri_bar);
558 static void load_uri_history(){
559 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
562 GtkEntryCompletion *gep=gtk_entry_completion_new();
563 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
566 snprintf(key,sizeof(key),"uri%i",i);
567 uri=linphone_gtk_get_ui_config(key,NULL);
570 gtk_list_store_append(model,&iter);
571 gtk_list_store_set(model,&iter,0,uri,-1);
572 if (i==0) gtk_entry_set_text(uribar,uri);
576 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
577 gtk_entry_completion_set_text_column(gep,0);
578 gtk_entry_set_completion(uribar,gep);
581 static void save_uri_history(){
582 LinphoneCore *lc=linphone_gtk_get_core();
583 LpConfig *cfg=linphone_core_get_config(lc);
584 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
589 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
591 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
593 gtk_tree_model_get(model,&iter,0,&uri,-1);
595 snprintf(key,sizeof(key),"uri%i",i);
596 lp_config_set_string(cfg,"GtkUi",key,uri);
601 }while(gtk_tree_model_iter_next(model,&iter));
605 static void completion_add_text(GtkEntry *entry, const char *text){
607 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
609 if (gtk_tree_model_get_iter_first(model,&iter)){
612 gtk_tree_model_get(model,&iter,0,&uri,-1);
614 if (strcmp(uri,text)==0) {
616 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
622 }while (gtk_tree_model_iter_next(model,&iter));
624 /* and prepend it on top of the list */
625 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
626 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
631 static void linphone_gtk_show_main_window(){
632 GtkWidget *w=linphone_gtk_get_main_window();
633 LinphoneCore *lc=linphone_gtk_get_core();
634 if (linphone_core_video_enabled(lc)){
635 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
636 VIDEOSELFVIEW_DEFAULT));
639 gtk_window_present(GTK_WINDOW(w));
642 static void linphone_gtk_show(LinphoneCore *lc){
644 linphone_gtk_show_main_window();
648 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
649 GtkWidget *mw=linphone_gtk_get_main_window();
650 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
651 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
652 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
654 if (linphone_gtk_use_in_call_view() && call)
655 linphone_gtk_in_call_view_terminate(call,error);
656 update_video_title();
659 static bool_t all_other_calls_paused(LinphoneCall *refcall, const MSList *calls){
660 for(;calls!=NULL;calls=calls->next){
661 LinphoneCall *call=(LinphoneCall*)calls->data;
662 LinphoneCallState cs=linphone_call_get_state(call);
664 if (cs!=LinphoneCallPaused && cs!=LinphoneCallPausing)
671 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
672 LinphoneCore *lc=linphone_gtk_get_core();
673 GtkWidget *mw=linphone_gtk_get_main_window();
674 const MSList *calls=linphone_core_get_calls(lc);
676 bool_t start_active=TRUE;
677 bool_t stop_active=FALSE;
678 bool_t add_call=FALSE;
679 int call_list_size=ms_list_size(calls);
686 if (all_other_calls_paused(NULL,calls)){
689 }else if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived && all_other_calls_paused(call,calls)){
690 if (call_list_size>1){
701 button=linphone_gtk_get_widget(mw,"start_call");
702 gtk_widget_set_sensitive(button,start_active);
703 gtk_widget_set_visible(button,!add_call);
705 button=linphone_gtk_get_widget(mw,"add_call");
706 gtk_widget_set_sensitive(button,start_active);
707 gtk_widget_set_visible(button,add_call);
709 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
711 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
712 update_video_title();
715 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
716 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
717 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
718 completion_add_text(GTK_ENTRY(uri_bar),entered);
720 linphone_gtk_call_terminated(NULL,NULL);
725 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
726 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
727 linphone_core_accept_call (linphone_gtk_get_core(),call);
728 linphone_call_unref(call);
734 void linphone_gtk_start_call(GtkWidget *w){
735 LinphoneCore *lc=linphone_gtk_get_core();
737 /*change into in-call mode, then do the work later as it might block a bit */
738 GtkWidget *mw=gtk_widget_get_toplevel(w);
739 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
741 call=linphone_gtk_get_currently_displayed_call ();
742 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
743 linphone_core_accept_call(lc,call);
745 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
746 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
747 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
748 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
753 void linphone_gtk_uri_bar_activate(GtkWidget *w){
754 linphone_gtk_start_call(w);
758 void linphone_gtk_terminate_call(GtkWidget *button){
759 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
761 linphone_core_terminate_call(linphone_gtk_get_core(),call);
764 void linphone_gtk_decline_clicked(GtkWidget *button){
765 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
767 linphone_core_terminate_call(linphone_gtk_get_core(),call);
770 void linphone_gtk_answer_clicked(GtkWidget *button){
771 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
773 linphone_core_pause_all_calls(linphone_gtk_get_core());
774 linphone_core_accept_call(linphone_gtk_get_core(),call);
775 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
779 void linphone_gtk_enable_video(GtkWidget *w){
780 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
781 GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
782 linphone_core_enable_video(linphone_gtk_get_core(),val,val);
783 gtk_widget_set_sensitive(selfview_item,val);
785 linphone_core_enable_video_preview(linphone_gtk_get_core(),
786 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
788 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
792 void linphone_gtk_enable_self_view(GtkWidget *w){
793 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
794 LinphoneCore *lc=linphone_gtk_get_core();
795 linphone_core_enable_video_preview(lc,val);
796 linphone_core_enable_self_view(lc,val);
797 linphone_gtk_set_ui_config_int("videoselfview",val);
800 void linphone_gtk_used_identity_changed(GtkWidget *w){
801 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
802 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
803 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
804 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
805 linphone_gtk_show_directory_search();
807 if (sel) g_free(sel);
810 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
811 linphone_gtk_show_friends();
814 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
816 case GTK_RESPONSE_YES:
817 linphone_gtk_show_contact(lf);
820 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
822 gtk_widget_destroy(dialog);
825 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
828 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
829 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
833 gchar *message=g_strdup_printf(_("%s would like to add you to his contact list.\nWould you allow him to see your presence status or add him to your contact list ?\nIf you answer no, this person will be temporarily blacklisted."),url);
834 dialog = gtk_message_dialog_new (
835 GTK_WINDOW(linphone_gtk_get_main_window()),
836 GTK_DIALOG_DESTROY_WITH_PARENT,
837 GTK_MESSAGE_QUESTION,
842 g_signal_connect(G_OBJECT (dialog), "response",
843 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
844 /* actually show the box */
845 gtk_widget_show(dialog);
848 typedef struct _AuthTimeout{
853 static void auth_timeout_clean(AuthTimeout *tout){
857 static gboolean auth_timeout_destroy(AuthTimeout *tout){
859 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
860 gtk_widget_destroy(tout->w);
866 static AuthTimeout * auth_timeout_new(GtkWidget *w){
867 AuthTimeout *tout=g_new(AuthTimeout,1);
869 /*so that the timeout no more references the widget when it is destroyed:*/
870 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
871 /*so that the widget is automatically destroyed after some time */
872 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
876 void linphone_gtk_password_cancel(GtkWidget *w){
877 LinphoneAuthInfo *info;
878 GtkWidget *window=gtk_widget_get_toplevel(w);
879 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
880 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
881 gtk_widget_destroy(window);
884 void linphone_gtk_password_ok(GtkWidget *w){
886 GtkWidget *window=gtk_widget_get_toplevel(w);
887 LinphoneAuthInfo *info;
888 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
889 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
890 entry=linphone_gtk_get_widget(window,"password_entry");
891 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
892 linphone_auth_info_set_userid(info,
893 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
894 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
895 gtk_widget_destroy(window);
898 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
899 GtkWidget *w=linphone_gtk_create_window("password");
900 GtkWidget *label=linphone_gtk_get_widget(w,"message");
901 LinphoneAuthInfo *info;
903 GtkWidget *mw=linphone_gtk_get_main_window();
905 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
906 /*don't prompt for authentication when login frame is visible*/
907 linphone_core_abort_authentication(lc,NULL);
911 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
913 gtk_label_set_markup(GTK_LABEL(label),msg);
915 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
916 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
917 g_object_set_data(G_OBJECT(w),"auth_info",info);
918 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
923 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
924 GtkWidget *w=linphone_gtk_get_main_window();
925 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
926 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
927 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
931 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
932 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
935 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
936 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
939 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
941 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
942 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
945 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
946 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
947 if (w) linphone_gtk_call_log_update(w);
948 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
952 static void make_notification(const char *title, const char *body){
953 NotifyNotification *n;
954 n = notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
955 if (n && !notify_notification_show(n,NULL))
956 ms_error("Failed to send notification.");
961 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
963 if (!notify_is_initted())
964 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
968 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL),NULL))
969 ms_error("Failed to send notification.");
971 linphone_gtk_show_main_window();
973 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
976 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
977 switch(linphone_call_get_state(call)){
978 case LinphoneCallError:
979 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
981 case LinphoneCallEnd:
982 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
984 case LinphoneCallIncomingReceived:
985 make_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
987 case LinphoneCallPausedByRemote:
988 make_notification(_("Call paused"),body=g_markup_printf_escaped("<span size=\"large\">by %s</span>",remote));
993 if (body) g_free(body);
994 if (remote) g_free(remote);
999 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1001 case LinphoneCallOutgoingInit:
1002 linphone_gtk_create_in_call_view (call);
1004 case LinphoneCallOutgoingProgress:
1005 linphone_gtk_in_call_view_set_calling (call);
1007 case LinphoneCallStreamsRunning:
1008 linphone_gtk_in_call_view_set_in_call(call);
1010 case LinphoneCallError:
1011 linphone_gtk_in_call_view_terminate (call,msg);
1013 case LinphoneCallEnd:
1014 linphone_gtk_in_call_view_terminate(call,NULL);
1015 linphone_gtk_status_icon_set_blinking(FALSE);
1017 case LinphoneCallIncomingReceived:
1018 linphone_gtk_create_in_call_view (call);
1019 linphone_gtk_in_call_view_set_incoming(call,!all_other_calls_paused (call,linphone_core_get_calls(lc)));
1020 linphone_gtk_status_icon_set_blinking(TRUE);
1022 linphone_call_ref(call);
1023 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1026 case LinphoneCallResuming:
1027 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1028 linphone_gtk_in_call_view_set_in_call (call);
1030 case LinphoneCallPausing:
1031 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1032 case LinphoneCallPausedByRemote:
1033 linphone_gtk_in_call_view_set_paused(call);
1035 case LinphoneCallConnected:
1036 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1037 linphone_gtk_status_icon_set_blinking(FALSE);
1042 linphone_gtk_notify(call, msg);
1043 linphone_gtk_update_call_buttons (call);
1046 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1047 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1048 GtkTreeModel *model=gtk_combo_box_get_model(box);
1050 gboolean found=FALSE;
1051 const char *stock_id=NULL;
1053 if (gtk_tree_model_get_iter_first(model,&iter)){
1056 gtk_tree_model_get(model,&iter,2,&p,-1);
1061 }while(gtk_tree_model_iter_next(model,&iter));
1064 g_warning("Could not find proxy config in combo box of identities.");
1068 case LinphoneRegistrationOk:
1069 stock_id=GTK_STOCK_YES;
1071 case LinphoneRegistrationProgress:
1072 stock_id=GTK_STOCK_REFRESH;
1074 case LinphoneRegistrationCleared:
1077 case LinphoneRegistrationFailed:
1078 stock_id=GTK_STOCK_DIALOG_WARNING;
1083 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1086 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1087 LinphoneRegistrationState rs, const char *msg){
1089 case LinphoneRegistrationOk:
1091 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1092 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1093 linphone_gtk_exit_login_frame();
1100 update_registration_status(cfg,rs);
1104 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1105 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1106 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1109 void linphone_gtk_open_browser(const char *url){
1110 /*in gtk 2.16, gtk_show_uri does not work...*/
1112 #if GTK_CHECK_VERSION(2,18,3)
1113 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1116 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1117 g_spawn_command_line_async(cl,NULL);
1120 ShellExecute(0,"open",url,NULL,NULL,1);
1124 void linphone_gtk_link_to_website(GtkWidget *item){
1125 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1126 linphone_gtk_open_browser(home);
1129 static GtkWidget *create_icon_menu(){
1130 GtkWidget *menu=gtk_menu_new();
1131 GtkWidget *menu_item;
1134 const gchar *homesite;
1136 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1137 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1138 tmp=g_strdup(homesite);
1139 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1140 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1142 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1143 gtk_widget_show(image);
1144 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1145 //g_object_unref(G_OBJECT(image));
1146 gtk_widget_show(menu_item);
1147 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1148 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1150 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1151 gtk_widget_show(menu_item);
1152 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1153 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1154 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1155 gtk_widget_show(menu_item);
1156 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1157 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1158 gtk_widget_show(menu);
1162 static GtkStatusIcon *icon=NULL;
1164 static void linphone_gtk_init_status_icon(){
1165 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1166 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1167 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1168 GtkWidget *menu=create_icon_menu();
1170 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1171 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1172 gtk_status_icon_set_name(icon,title);
1173 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)linphone_gtk_show_main_window,linphone_gtk_get_main_window());
1174 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1175 gtk_status_icon_set_tooltip(icon,title);
1176 gtk_status_icon_set_visible(icon,TRUE);
1177 g_object_set_data(G_OBJECT(icon),"menu",menu);
1178 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1179 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1180 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1181 pbuf=create_pixbuf(call_icon_path);
1182 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1185 static gboolean do_icon_blink(GtkStatusIcon *gi){
1186 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1187 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1188 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1189 if (cur_icon==call_icon){
1190 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1192 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1197 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1199 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1200 if (val && tout==0){
1201 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1202 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1203 }else if (!val && tout!=0){
1204 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1205 g_source_remove(tout);
1206 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1207 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1211 static void init_identity_combo(GtkComboBox *box){
1212 GtkListStore *store;
1213 GtkCellRenderer *r1,*r2;
1214 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1215 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1216 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1217 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1218 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1219 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1220 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1221 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1224 void linphone_gtk_load_identities(void){
1226 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1228 LinphoneProxyConfig *def=NULL;
1230 GtkListStore *store;
1233 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1234 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1235 /* model is empty, this is the first time we go here */
1236 init_identity_combo(box);
1237 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1239 gtk_list_store_clear(store);
1240 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1241 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1242 gtk_list_store_append(store,&iter);
1243 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1244 g_free(def_identity);
1245 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1247 elem=ms_list_next(elem),i++){
1248 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1249 gtk_list_store_append(store,&iter);
1250 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1251 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1257 gtk_combo_box_set_active(box,def_index);
1260 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1261 const char *label=gtk_button_get_label(button);
1262 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1264 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1265 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1266 if (linphone_core_in_call(linphone_gtk_get_core())){
1267 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1271 static void linphone_gtk_dtmf_released(GtkButton *button){
1272 linphone_core_stop_dtmf (linphone_gtk_get_core());
1275 static void linphone_gtk_connect_digits(void){
1276 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1277 GList *children=gtk_container_get_children(cont);
1279 for(elem=children;elem!=NULL;elem=elem->next){
1280 GtkButton *button=GTK_BUTTON(elem->data);
1281 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1282 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1286 static void linphone_gtk_check_menu_items(void){
1287 bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core());
1288 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1289 GtkWidget *selfview_item=linphone_gtk_get_widget(
1290 linphone_gtk_get_main_window(),"selfview_item");
1291 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1292 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1293 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1294 gtk_widget_set_sensitive(selfview_item,video_enabled);
1297 static gboolean linphone_gtk_can_manage_accounts(){
1298 LinphoneCore *lc=linphone_gtk_get_core();
1300 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1301 SipSetup *ss=(SipSetup*)elem->data;
1302 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1309 static void linphone_gtk_configure_main_window(){
1310 static gboolean config_loaded=FALSE;
1311 static const char *title;
1312 static const char *home;
1313 static const char *start_call_icon;
1314 static const char *add_call_icon;
1315 static const char *stop_call_icon;
1316 static const char *search_icon;
1317 static gboolean update_check_menu;
1318 static gboolean buttons_have_borders;
1319 static gboolean show_abcd;
1320 GtkWidget *w=linphone_gtk_get_main_window();
1321 if (!config_loaded){
1322 title=linphone_gtk_get_ui_config("title","Linphone");
1323 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1324 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1325 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1326 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1327 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1328 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1329 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1330 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1333 linphone_gtk_configure_window(w,"main_window");
1335 gtk_window_set_title(GTK_WINDOW(w),title);
1337 if (start_call_icon){
1338 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1339 create_pixmap (start_call_icon));
1340 if (!buttons_have_borders)
1341 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1344 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1345 create_pixmap (add_call_icon));
1346 if (!buttons_have_borders)
1347 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1349 if (stop_call_icon){
1350 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1351 create_pixmap (stop_call_icon));
1352 if (!buttons_have_borders)
1353 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1356 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1357 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1358 g_object_unref(G_OBJECT(pbuf));
1362 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1364 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1368 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1370 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1371 g_object_unref(G_OBJECT(pbuf));
1376 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1378 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon")),pbuf);
1379 g_object_unref(G_OBJECT(pbuf));
1382 if (linphone_gtk_can_manage_accounts())
1383 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1384 if (update_check_menu){
1385 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1388 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1389 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1390 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1391 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1392 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1396 void linphone_gtk_manage_login(void){
1397 LinphoneCore *lc=linphone_gtk_get_core();
1398 LinphoneProxyConfig *cfg=NULL;
1399 linphone_core_get_default_proxy(lc,&cfg);
1401 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1402 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1403 linphone_gtk_show_login_frame(cfg);
1409 gboolean linphone_gtk_close(GtkWidget *mw){
1410 /*shutdown calls if any*/
1411 LinphoneCore *lc=linphone_gtk_get_core();
1412 if (linphone_core_in_call(lc)){
1413 linphone_core_terminate_all_calls(lc);
1415 linphone_core_enable_video_preview(lc,FALSE);
1416 #ifdef __APPLE__ /*until with have a better option*/
1417 gtk_window_iconify(GTK_WINDOW(mw));
1419 gtk_widget_hide(mw);
1425 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1426 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1427 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1429 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1430 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1437 static void linphone_gtk_init_main_window(){
1438 GtkWidget *main_window;
1440 linphone_gtk_configure_main_window();
1441 linphone_gtk_manage_login();
1443 linphone_gtk_load_identities();
1444 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1445 linphone_gtk_show_friends();
1446 linphone_gtk_connect_digits();
1447 main_window=linphone_gtk_get_main_window();
1448 linphone_gtk_call_log_update(main_window);
1450 linphone_gtk_update_call_buttons (NULL);
1451 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1452 g_signal_connect (G_OBJECT (main_window), "delete-event",
1453 G_CALLBACK (linphone_gtk_close), main_window);
1456 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1457 gtk_widget_destroy(linphone_gtk_get_widget(main_window,"imagemenuitem5"));
1458 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1459 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1460 gtk_widget_hide(menubar);
1461 gtk_osxapplication_ready(theMacApp);
1463 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1465 linphone_gtk_check_menu_items();
1469 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1471 const char *lname="undef";
1473 #if defined(__linux) || defined(__APPLE__)
1474 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1493 g_error("Bad level !");
1495 #if defined(__linux) || defined(__APPLE__)
1497 msg=g_strdup_vprintf(fmt,cap);
1500 msg=g_strdup_vprintf(fmt,args);
1502 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1505 linphone_gtk_log_push(lev,fmt,args);
1509 static void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1510 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1511 linphone_gtk_get_main_window(), "uribar"));
1513 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1515 gtk_entry_set_text(uri_bar, refer_to);
1516 linphone_gtk_start_call(linphone_gtk_get_main_window());
1519 static void linphone_gtk_check_soundcards(){
1520 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1521 if (devices==NULL || devices[0]==NULL){
1522 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1523 _("No sound cards have been detected on this computer.\n"
1524 "You won't be able to send or receive audio calls."));
1528 static void linphone_gtk_quit(void){
1529 gdk_threads_leave();
1530 linphone_gtk_destroy_log_window();
1531 linphone_core_destroy(the_core);
1532 linphone_gtk_log_uninit();
1540 This is not the correct way to implement block termination.
1541 The good way would be to call gtk_main_quit(), and return TRUE.
1542 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1543 As a result the program cannot exit at all.
1544 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1546 static gboolean on_block_termination(void){
1548 linphone_gtk_quit();
1553 int main(int argc, char *argv[]){
1557 const char *config_file;
1558 const char *factory_config_file;
1560 GtkSettings *settings;
1563 g_thread_init(NULL);
1566 progpath = strdup(argv[0]);
1568 config_file=linphone_gtk_get_config_file();
1571 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1572 if ((lang=getenv("LANG"))!=NULL){
1575 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1581 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1584 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1587 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1590 setenv("LANG",lang,1);
1595 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1596 if (p==NULL) perror("bindtextdomain failed");
1597 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1598 textdomain (GETTEXT_PACKAGE);
1600 g_message("NLS disabled.\n");
1603 gtk_rc_add_default_file("./gtkrc");
1605 gdk_threads_enter();
1607 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1608 linphone_options,NULL,NULL)){
1609 gdk_threads_leave();
1613 settings=gtk_settings_get_default();
1614 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1615 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1616 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1617 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1619 if (workingdir!=NULL){
1620 if (chdir(workingdir)==-1){
1621 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1625 /* Now, look for the factory configuration file, we do it this late
1626 since we want to have had time to change directory and to parse
1627 the options, in case we needed to access the working directory */
1628 factory_config_file = linphone_gtk_get_factory_config_file();
1630 if (linphone_core_wake_up_possible_already_running_instance(
1631 config_file, addr_to_call) == 0){
1632 g_message("addr_to_call=%s",addr_to_call);
1633 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1634 g_warning("This instance is going to exit now.");
1635 gdk_threads_leave();
1639 add_pixmap_directory("pixmaps");
1640 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1643 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1644 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1645 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1646 /*never block termination:*/
1647 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1650 the_ui=linphone_gtk_create_window("main");
1652 linphone_gtk_create_log_window();
1653 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1655 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1657 g_set_application_name(linphone_gtk_get_ui_config("title","Linphone"));
1658 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1659 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1661 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1662 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1663 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1664 linphone_gtk_init_main_window();
1665 #ifndef HAVE_GTK_OSX
1666 linphone_gtk_init_status_icon();
1669 linphone_gtk_show_main_window();
1670 linphone_gtk_check_soundcards();
1672 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1673 linphone_gtk_check_for_new_version();
1676 linphone_gtk_quit();
1677 #ifndef HAVE_GTK_OSX
1678 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1679 gtk_status_icon_set_visible(icon,FALSE);