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_show(LinphoneCore *lc);
56 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
57 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
58 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username);
59 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
60 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
61 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
62 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
63 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
64 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
65 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
66 static void linphone_gtk_status_icon_set_blinking(gboolean val);
69 static gboolean verbose=0;
70 static gboolean auto_answer = 0;
71 static gchar * addr_to_call = NULL;
72 static gboolean iconified=FALSE;
73 static gchar *workingdir=NULL;
74 static char *progpath=NULL;
75 gchar *linphone_logfile=NULL;
77 static GOptionEntry linphone_options[]={
81 .arg=G_OPTION_ARG_NONE,
82 .arg_data= (gpointer)&verbose,
83 .description=N_("log to stdout some debug information while running.")
86 .long_name = "logfile",
88 .arg = G_OPTION_ARG_STRING,
89 .arg_data = &linphone_logfile,
90 .description = N_("path to a file to write logs into.")
93 .long_name="iconified",
95 .arg=G_OPTION_ARG_NONE,
96 .arg_data= (gpointer)&iconified,
97 .description=N_("Start only in the system tray, do not show the main interface.")
102 .arg = G_OPTION_ARG_STRING,
103 .arg_data = &addr_to_call,
104 .description = N_("address to call right now")
107 .long_name = "auto-answer",
109 .arg = G_OPTION_ARG_NONE,
110 .arg_data = (gpointer) & auto_answer,
111 .description = N_("if set automatically answer incoming calls")
114 .long_name = "workdir",
116 .arg = G_OPTION_ARG_STRING,
117 .arg_data = (gpointer) & workingdir,
118 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
123 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
124 #define RELATIVE_XML_DIR
125 #define BUILD_TREE_XML_DIR "gtk"
128 #define CONFIG_FILE ".linphonerc"
130 #define CONFIG_FILE "linphonerc"
135 static char _config_file[1024];
138 const char *linphone_gtk_get_config_file(){
139 /*try accessing a local file first if exists*/
140 if (access(CONFIG_FILE,F_OK)==0){
141 snprintf(_config_file,sizeof(_config_file),"%s",CONFIG_FILE);
144 const char *appdata=getenv("APPDATA");
146 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
147 CreateDirectory(_config_file,NULL);
148 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR "\\" CONFIG_FILE);
151 const char *home=getenv("HOME");
152 if (home==NULL) home=".";
153 snprintf(_config_file,sizeof(_config_file),"%s/%s",home,CONFIG_FILE);
160 #define FACTORY_CONFIG_FILE "linphonerc.factory"
161 static char _factory_config_file[1024];
162 static const char *linphone_gtk_get_factory_config_file(){
163 /*try accessing a local file first if exists*/
164 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
165 snprintf(_factory_config_file,sizeof(_factory_config_file),
166 "%s",FACTORY_CONFIG_FILE);
170 if (progpath != NULL) {
172 progdir = strdup(progpath);
174 basename = strrchr(progdir, '\\');
175 if (basename != NULL) {
178 snprintf(_factory_config_file, sizeof(_factory_config_file),
179 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
181 if (workingdir!=NULL) {
182 snprintf(_factory_config_file, sizeof(_factory_config_file),
183 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
190 basename = strrchr(progdir, '/');
191 if (basename != NULL) {
194 snprintf(_factory_config_file, sizeof(_factory_config_file),
195 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
204 return _factory_config_file;
207 static void linphone_gtk_init_liblinphone(const char *config_file,
208 const char *factory_config_file) {
209 LinphoneCoreVTable vtable={0};
211 vtable.call_state_changed=linphone_gtk_call_state_changed;
212 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
213 vtable.show=linphone_gtk_show;
214 vtable.notify_presence_recv=linphone_gtk_notify_recv;
215 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
216 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
217 vtable.display_status=linphone_gtk_display_status;
218 vtable.display_message=linphone_gtk_display_message;
219 vtable.display_warning=linphone_gtk_display_warning;
220 vtable.display_url=linphone_gtk_display_url;
221 vtable.call_log_updated=linphone_gtk_call_log_updated;
222 vtable.text_received=linphone_gtk_text_received;
223 vtable.refer_received=linphone_gtk_refer_received;
224 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
226 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
227 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
228 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
233 LinphoneCore *linphone_gtk_get_core(void){
237 GtkWidget *linphone_gtk_get_main_window(){
241 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
242 static const char *icon_path=NULL;
243 static const char *hiddens=NULL;
244 static const char *shown=NULL;
245 static bool_t config_loaded=FALSE;
246 if (linphone_gtk_get_core()==NULL) return;
247 if (config_loaded==FALSE){
248 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
249 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
250 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
254 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
256 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
258 GdkPixbuf *pbuf=create_pixbuf(icon_path);
259 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
260 g_object_unref(G_OBJECT(pbuf));
264 static int get_ui_file(const char *name, char *path, int pathsize){
265 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
266 if (access(path,F_OK)!=0){
267 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
268 if (access(path,F_OK)!=0){
269 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
270 INSTALLED_XML_DIR,name);
277 GtkWidget *linphone_gtk_create_window(const char *window_name){
278 GError* error = NULL;
279 GtkBuilder* builder = gtk_builder_new ();
283 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
285 if (!gtk_builder_add_from_file (builder, path, &error)){
286 g_error("Couldn't load builder file: %s", error->message);
287 g_error_free (error);
290 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
292 g_error("Could not retrieve '%s' window from xml file",window_name);
295 g_object_set_data(G_OBJECT(w),"builder",builder);
296 gtk_builder_connect_signals(builder,w);
297 linphone_gtk_configure_window(w,window_name);
301 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
304 GtkBuilder* builder = gtk_builder_new ();
306 gchar *object_ids[2];
307 object_ids[0]=g_strdup(widget_name);
310 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
311 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
312 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
313 g_error_free (error);
314 g_free(object_ids[0]);
317 g_free(object_ids[0]);
318 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
320 g_error("Could not retrieve '%s' window from xml file",widget_name);
323 g_object_set_data(G_OBJECT(w),"builder",builder);
324 g_signal_connect_swapped(G_OBJECT(w),"destroy",(GCallback)g_object_unref,builder);
325 gtk_builder_connect_signals(builder,w);
329 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
330 GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
333 g_error("Fail to retrieve builder from window !");
336 w=gtk_builder_get_object(builder,name);
338 g_error("No widget named %s found in xml interface.",name);
340 return GTK_WIDGET(w);
344 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
346 GtkWidget *main_window=linphone_gtk_get_main_window();
348 gtk_widget_show(main_window);
349 if (type==GTK_MESSAGE_QUESTION)
351 /* draw a question box. link to dialog_click callback */
352 dialog = gtk_message_dialog_new (
353 GTK_WINDOW(main_window),
354 GTK_DIALOG_DESTROY_WITH_PARENT,
355 GTK_MESSAGE_QUESTION,
358 (const gchar*)message);
359 /* connect to some callback : REVISIT */
361 g_signal_connect_swapped (G_OBJECT (dialog), "response",
362 G_CALLBACK (dialog_click),
365 /* actually show the box */
366 gtk_widget_show(dialog);
370 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
371 GTK_DIALOG_DESTROY_WITH_PARENT,
375 (const gchar*)message);
376 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
377 g_signal_connect_swapped (G_OBJECT (dialog), "response",
378 G_CALLBACK (gtk_widget_destroy),
380 gtk_widget_show(dialog);
384 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
385 if (id==GTK_RESPONSE_CANCEL){
386 gtk_widget_destroy(GTK_WIDGET(dialog));
390 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
391 g_message("About url clicked");
392 linphone_gtk_open_browser(url);
395 void linphone_gtk_show_about(){
396 struct stat filestat;
397 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
400 GdkPixbuf *logo=create_pixbuf(
401 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
402 static const char *defcfg="defcfg";
404 about=linphone_gtk_create_window("about");
405 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
406 memset(&filestat,0,sizeof(filestat));
407 if (stat(license_file,&filestat)!=0){
408 license_file="COPYING";
409 stat(license_file,&filestat);
411 if (filestat.st_size>0){
412 char *license=g_malloc(filestat.st_size+1);
413 FILE *f=fopen(license_file,"r");
414 if (f && fread(license,filestat.st_size,1,f)==1){
415 license[filestat.st_size]='\0';
416 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
420 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
421 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
422 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
423 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
424 tmp=linphone_gtk_get_ui_config("artists",defcfg);
429 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
431 tmp=linphone_gtk_get_ui_config("translators",defcfg);
433 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
434 tmp=linphone_gtk_get_ui_config("comments",defcfg);
436 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
437 gtk_widget_show(about);
440 static void set_video_window_decorations(GdkWindow *w){
441 const char *title=linphone_gtk_get_ui_config("title","Linphone");
442 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
443 char video_title[256];
444 GdkPixbuf *pbuf=create_pixbuf(icon_path);
445 if (!linphone_core_in_call(linphone_gtk_get_core())){
446 snprintf(video_title,sizeof(video_title),"%s video",title);
447 /* When not in call, treat the video as a normal window */
448 gdk_window_set_keep_above(w, FALSE);
450 LinphoneAddress *uri =
451 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
454 linphone_address_clean(uri);
455 if (linphone_address_get_display_name(uri)!=NULL){
456 display_name=ms_strdup(linphone_address_get_display_name(uri));
458 display_name=linphone_address_as_string(uri);
460 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
461 linphone_address_destroy(uri);
462 ms_free(display_name);
464 /* During calls, bring up the video window, arrange so that
465 it is above all the other windows */
466 gdk_window_deiconify(w);
467 gdk_window_set_keep_above(w,TRUE);
468 /* Maybe we should have the following, but then we want to
469 have a timer that turns it off after a little while. */
470 /* gdk_window_set_urgency_hint(w,TRUE); */
472 gdk_window_set_title(w,video_title);
473 /* Refrain the video window to be closed at all times. */
474 gdk_window_set_functions(w,
475 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
476 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
479 l=g_list_append(l,pbuf);
480 gdk_window_set_icon_list(w,l);
482 g_object_unref(G_OBJECT(pbuf));
486 static gboolean video_needs_update=FALSE;
488 static void update_video_title(){
489 video_needs_update=TRUE;
492 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
493 static gboolean first_time=TRUE;
495 static unsigned long previd=0;
496 static unsigned long preview_previd=0;
497 static gboolean in_iterate=FALSE;
500 if (in_iterate) return TRUE;
502 linphone_core_iterate(lc);
504 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
505 linphone_gtk_show_directory_search();
509 id=linphone_core_get_native_video_window_id(lc);
510 if (id!=previd || video_needs_update){
514 ms_message("Updating window decorations");
516 w=gdk_window_foreign_new(id);
518 w=gdk_window_foreign_new((HANDLE)id);
521 set_video_window_decorations(w);
522 g_object_unref(G_OBJECT(w));
524 else ms_error("gdk_window_foreign_new() failed");
525 if (video_needs_update) video_needs_update=FALSE;
528 id=linphone_core_get_native_preview_window_id (lc);
529 if (id!=preview_previd ){
533 ms_message("Updating window decorations for preview");
535 w=gdk_window_foreign_new(id);
537 w=gdk_window_foreign_new((HANDLE)id);
540 set_video_window_decorations(w);
541 g_object_unref(G_OBJECT(w));
543 else ms_error("gdk_window_foreign_new() failed");
544 if (video_needs_update) video_needs_update=FALSE;
547 if (addr_to_call!=NULL){
548 /*make sure we are not showing the login screen*/
549 GtkWidget *mw=linphone_gtk_get_main_window();
550 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
551 if (!GTK_WIDGET_VISIBLE(login_frame)){
552 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
553 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
555 linphone_gtk_start_call(uri_bar);
562 static void load_uri_history(){
563 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
566 GtkEntryCompletion *gep=gtk_entry_completion_new();
567 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
570 snprintf(key,sizeof(key),"uri%i",i);
571 uri=linphone_gtk_get_ui_config(key,NULL);
574 gtk_list_store_append(model,&iter);
575 gtk_list_store_set(model,&iter,0,uri,-1);
576 if (i==0) gtk_entry_set_text(uribar,uri);
580 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
581 gtk_entry_completion_set_text_column(gep,0);
582 gtk_entry_set_completion(uribar,gep);
585 static void save_uri_history(){
586 LinphoneCore *lc=linphone_gtk_get_core();
587 LpConfig *cfg=linphone_core_get_config(lc);
588 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
593 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
595 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
597 gtk_tree_model_get(model,&iter,0,&uri,-1);
599 snprintf(key,sizeof(key),"uri%i",i);
600 lp_config_set_string(cfg,"GtkUi",key,uri);
605 }while(gtk_tree_model_iter_next(model,&iter));
609 static void completion_add_text(GtkEntry *entry, const char *text){
611 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
613 if (gtk_tree_model_get_iter_first(model,&iter)){
616 gtk_tree_model_get(model,&iter,0,&uri,-1);
618 if (strcmp(uri,text)==0) {
620 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
626 }while (gtk_tree_model_iter_next(model,&iter));
628 /* and prepend it on top of the list */
629 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
630 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
635 void linphone_gtk_show_main_window(){
636 GtkWidget *w=linphone_gtk_get_main_window();
637 LinphoneCore *lc=linphone_gtk_get_core();
638 if (linphone_core_video_enabled(lc)){
639 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
640 VIDEOSELFVIEW_DEFAULT));
643 gtk_window_present(GTK_WINDOW(w));
646 static void linphone_gtk_show(LinphoneCore *lc){
648 linphone_gtk_show_main_window();
652 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
653 GtkWidget *mw=linphone_gtk_get_main_window();
654 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
655 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
656 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
658 if (linphone_gtk_use_in_call_view() && call)
659 linphone_gtk_in_call_view_terminate(call,error);
660 update_video_title();
663 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
664 LinphoneCore *lc=linphone_gtk_get_core();
665 GtkWidget *mw=linphone_gtk_get_main_window();
666 const MSList *calls=linphone_core_get_calls(lc);
668 bool_t start_active=TRUE;
669 bool_t stop_active=FALSE;
670 bool_t add_call=FALSE;
671 int call_list_size=ms_list_size(calls);
681 button=linphone_gtk_get_widget(mw,"start_call");
682 gtk_widget_set_sensitive(button,start_active);
683 gtk_widget_set_visible(button,!add_call);
685 button=linphone_gtk_get_widget(mw,"add_call");
686 gtk_widget_set_sensitive(button,start_active);
687 gtk_widget_set_visible(button,add_call);
689 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
691 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
692 linphone_gtk_enable_conference_button(lc,call_list_size>1);
693 update_video_title();
696 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
697 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
698 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
699 completion_add_text(GTK_ENTRY(uri_bar),entered);
701 linphone_gtk_call_terminated(NULL,NULL);
706 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
707 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
708 linphone_core_accept_call (linphone_gtk_get_core(),call);
709 linphone_call_unref(call);
715 void linphone_gtk_start_call(GtkWidget *w){
716 LinphoneCore *lc=linphone_gtk_get_core();
718 /*change into in-call mode, then do the work later as it might block a bit */
719 GtkWidget *mw=gtk_widget_get_toplevel(w);
720 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
722 call=linphone_gtk_get_currently_displayed_call(NULL);
723 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
724 linphone_core_accept_call(lc,call);
726 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
727 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
728 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
729 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
734 void linphone_gtk_uri_bar_activate(GtkWidget *w){
735 linphone_gtk_start_call(w);
739 void linphone_gtk_terminate_call(GtkWidget *button){
741 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
743 linphone_core_terminate_call(linphone_gtk_get_core(),call);
745 linphone_core_terminate_conference(linphone_gtk_get_core());
749 void linphone_gtk_decline_clicked(GtkWidget *button){
750 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
752 linphone_core_terminate_call(linphone_gtk_get_core(),call);
755 void linphone_gtk_answer_clicked(GtkWidget *button){
756 LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
758 linphone_core_pause_all_calls(linphone_gtk_get_core());
759 linphone_core_accept_call(linphone_gtk_get_core(),call);
760 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
764 void linphone_gtk_enable_video(GtkWidget *w){
765 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
766 GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
767 linphone_core_enable_video(linphone_gtk_get_core(),val,val);
768 gtk_widget_set_sensitive(selfview_item,val);
770 linphone_core_enable_video_preview(linphone_gtk_get_core(),
771 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
773 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
777 void linphone_gtk_enable_self_view(GtkWidget *w){
778 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
779 LinphoneCore *lc=linphone_gtk_get_core();
780 linphone_core_enable_video_preview(lc,val);
781 linphone_core_enable_self_view(lc,val);
782 linphone_gtk_set_ui_config_int("videoselfview",val);
785 void linphone_gtk_used_identity_changed(GtkWidget *w){
786 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
787 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
788 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
789 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
790 linphone_gtk_show_directory_search();
792 if (sel) g_free(sel);
795 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
796 linphone_gtk_show_friends();
799 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
801 case GTK_RESPONSE_YES:
802 linphone_gtk_show_contact(lf);
805 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
807 gtk_widget_destroy(dialog);
810 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
813 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
814 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
818 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);
819 dialog = gtk_message_dialog_new (
820 GTK_WINDOW(linphone_gtk_get_main_window()),
821 GTK_DIALOG_DESTROY_WITH_PARENT,
822 GTK_MESSAGE_QUESTION,
827 g_signal_connect(G_OBJECT (dialog), "response",
828 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
829 /* actually show the box */
830 gtk_widget_show(dialog);
833 typedef struct _AuthTimeout{
838 static void auth_timeout_clean(AuthTimeout *tout){
842 static gboolean auth_timeout_destroy(AuthTimeout *tout){
844 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
845 gtk_widget_destroy(tout->w);
851 static AuthTimeout * auth_timeout_new(GtkWidget *w){
852 AuthTimeout *tout=g_new(AuthTimeout,1);
854 /*so that the timeout no more references the widget when it is destroyed:*/
855 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
856 /*so that the widget is automatically destroyed after some time */
857 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
861 void linphone_gtk_password_cancel(GtkWidget *w){
862 LinphoneAuthInfo *info;
863 GtkWidget *window=gtk_widget_get_toplevel(w);
864 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
865 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
866 gtk_widget_destroy(window);
869 void linphone_gtk_password_ok(GtkWidget *w){
871 GtkWidget *window=gtk_widget_get_toplevel(w);
872 LinphoneAuthInfo *info;
873 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
874 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
875 entry=linphone_gtk_get_widget(window,"password_entry");
876 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
877 linphone_auth_info_set_userid(info,
878 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
879 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
880 gtk_widget_destroy(window);
883 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
884 GtkWidget *w=linphone_gtk_create_window("password");
885 GtkWidget *label=linphone_gtk_get_widget(w,"message");
886 LinphoneAuthInfo *info;
888 GtkWidget *mw=linphone_gtk_get_main_window();
890 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
891 /*don't prompt for authentication when login frame is visible*/
892 linphone_core_abort_authentication(lc,NULL);
896 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
898 gtk_label_set_markup(GTK_LABEL(label),msg);
900 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
901 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
902 g_object_set_data(G_OBJECT(w),"auth_info",info);
903 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
908 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
909 GtkWidget *w=linphone_gtk_get_main_window();
910 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
911 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
912 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
916 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
917 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
920 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
921 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
924 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
926 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
927 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
930 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
931 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
932 if (w) linphone_gtk_call_log_update(w);
933 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
937 static bool_t notify_actions_supported() {
938 bool_t accepts_actions = FALSE;
939 GList *capabilities = notify_get_server_caps();
941 if(capabilities != NULL) {
942 for(c = capabilities; c != NULL; c = c->next) {
943 if(strcmp((char*)c->data, "actions") == 0 ) {
944 accepts_actions = TRUE;
948 g_list_foreach(capabilities, (GFunc)g_free, NULL);
949 g_list_free(capabilities);
951 return accepts_actions;
954 static NotifyNotification* build_notification(const char *title, const char *body){
955 return notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON)
962 static void show_notification(NotifyNotification* n){
963 if (n && !notify_notification_show(n,NULL))
964 ms_error("Failed to send notification.");
967 static void make_notification(const char *title, const char *body){
968 show_notification(build_notification(title,body));
973 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
975 if (!notify_is_initted())
976 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
980 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
985 ms_error("Failed to send notification.");
987 linphone_gtk_show_main_window();
989 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
992 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
993 NotifyNotification *n;
994 switch(linphone_call_get_state(call)){
995 case LinphoneCallError:
996 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
998 case LinphoneCallEnd:
999 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1001 case LinphoneCallIncomingReceived:
1002 n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
1003 if (notify_actions_supported()) {
1004 notify_notification_add_action (n,"answer", _("Answer"),
1005 NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1006 notify_notification_add_action (n,"decline",_("Decline"),
1007 NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1009 show_notification(n);
1011 case LinphoneCallPausedByRemote:
1012 make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<span size=\"large\">by %s</span>"),remote));
1017 if (body) g_free(body);
1018 if (remote) g_free(remote);
1023 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1025 case LinphoneCallOutgoingInit:
1026 linphone_gtk_create_in_call_view (call);
1028 case LinphoneCallOutgoingProgress:
1029 linphone_gtk_in_call_view_set_calling (call);
1031 case LinphoneCallStreamsRunning:
1032 linphone_gtk_in_call_view_set_in_call(call);
1034 case LinphoneCallError:
1035 linphone_gtk_in_call_view_terminate (call,msg);
1037 case LinphoneCallEnd:
1038 linphone_gtk_in_call_view_terminate(call,NULL);
1039 linphone_gtk_status_icon_set_blinking(FALSE);
1041 case LinphoneCallIncomingReceived:
1042 linphone_gtk_create_in_call_view (call);
1043 linphone_gtk_in_call_view_set_incoming(call);
1044 linphone_gtk_status_icon_set_blinking(TRUE);
1046 linphone_call_ref(call);
1047 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1050 case LinphoneCallResuming:
1051 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1052 linphone_gtk_in_call_view_set_in_call (call);
1054 case LinphoneCallPausing:
1055 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1056 case LinphoneCallPausedByRemote:
1057 linphone_gtk_in_call_view_set_paused(call);
1059 case LinphoneCallConnected:
1060 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1061 linphone_gtk_status_icon_set_blinking(FALSE);
1066 linphone_gtk_notify(call, msg);
1067 linphone_gtk_update_call_buttons (call);
1070 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1071 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1072 GtkTreeModel *model=gtk_combo_box_get_model(box);
1074 gboolean found=FALSE;
1075 const char *stock_id=NULL;
1077 if (gtk_tree_model_get_iter_first(model,&iter)){
1080 gtk_tree_model_get(model,&iter,2,&p,-1);
1085 }while(gtk_tree_model_iter_next(model,&iter));
1088 g_warning("Could not find proxy config in combo box of identities.");
1092 case LinphoneRegistrationOk:
1093 stock_id=GTK_STOCK_YES;
1095 case LinphoneRegistrationProgress:
1096 stock_id=GTK_STOCK_REFRESH;
1098 case LinphoneRegistrationCleared:
1101 case LinphoneRegistrationFailed:
1102 stock_id=GTK_STOCK_DIALOG_WARNING;
1107 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1110 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1111 LinphoneRegistrationState rs, const char *msg){
1113 case LinphoneRegistrationOk:
1115 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1116 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1117 linphone_gtk_exit_login_frame();
1124 update_registration_status(cfg,rs);
1128 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1129 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1130 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1133 void linphone_gtk_open_browser(const char *url){
1134 /*in gtk 2.16, gtk_show_uri does not work...*/
1136 #if GTK_CHECK_VERSION(2,18,3)
1137 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1140 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1141 g_spawn_command_line_async(cl,NULL);
1144 ShellExecute(0,"open",url,NULL,NULL,1);
1148 void linphone_gtk_link_to_website(GtkWidget *item){
1149 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1150 linphone_gtk_open_browser(home);
1153 static GtkWidget *create_icon_menu(){
1154 GtkWidget *menu=gtk_menu_new();
1155 GtkWidget *menu_item;
1158 const gchar *homesite;
1160 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1161 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1162 tmp=g_strdup(homesite);
1163 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1164 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1166 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1167 gtk_widget_show(image);
1168 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1169 //g_object_unref(G_OBJECT(image));
1170 gtk_widget_show(menu_item);
1171 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1172 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1174 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1175 gtk_widget_show(menu_item);
1176 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1177 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1178 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1179 gtk_widget_show(menu_item);
1180 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1181 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1182 gtk_widget_show(menu);
1186 static GtkStatusIcon *icon=NULL;
1188 static void handle_icon_click() {
1189 GtkWidget *mw=linphone_gtk_get_main_window();
1190 if (!gtk_window_is_active((GtkWindow*)mw)) {
1191 linphone_gtk_show_main_window();
1193 gtk_widget_hide(mw);
1197 static void linphone_gtk_init_status_icon(){
1198 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1199 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1200 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1201 GtkWidget *menu=create_icon_menu();
1203 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1204 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1205 gtk_status_icon_set_name(icon,title);
1206 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL);
1207 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1208 gtk_status_icon_set_tooltip(icon,title);
1209 gtk_status_icon_set_visible(icon,TRUE);
1210 g_object_set_data(G_OBJECT(icon),"menu",menu);
1211 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1212 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1213 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1214 pbuf=create_pixbuf(call_icon_path);
1215 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1218 static gboolean do_icon_blink(GtkStatusIcon *gi){
1219 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1220 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1221 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1222 if (cur_icon==call_icon){
1223 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1225 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1230 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1232 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1233 if (val && tout==0){
1234 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1235 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1236 }else if (!val && tout!=0){
1237 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1238 g_source_remove(tout);
1239 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1240 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1244 static void init_identity_combo(GtkComboBox *box){
1245 GtkListStore *store;
1246 GtkCellRenderer *r1,*r2;
1247 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1248 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1249 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1250 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1251 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1252 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1253 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1254 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1257 void linphone_gtk_load_identities(void){
1259 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1261 LinphoneProxyConfig *def=NULL;
1263 GtkListStore *store;
1266 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1267 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1268 /* model is empty, this is the first time we go here */
1269 init_identity_combo(box);
1270 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1272 gtk_list_store_clear(store);
1273 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1274 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1275 gtk_list_store_append(store,&iter);
1276 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1277 g_free(def_identity);
1278 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1280 elem=ms_list_next(elem),i++){
1281 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1282 gtk_list_store_append(store,&iter);
1283 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1284 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1290 gtk_combo_box_set_active(box,def_index);
1293 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1294 const char *label=gtk_button_get_label(button);
1295 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1297 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1298 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1299 if (linphone_core_in_call(linphone_gtk_get_core())){
1300 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1304 static void linphone_gtk_dtmf_released(GtkButton *button){
1305 linphone_core_stop_dtmf (linphone_gtk_get_core());
1308 static void linphone_gtk_connect_digits(void){
1309 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1310 GList *children=gtk_container_get_children(cont);
1312 for(elem=children;elem!=NULL;elem=elem->next){
1313 GtkButton *button=GTK_BUTTON(elem->data);
1314 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1315 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1319 static void linphone_gtk_check_menu_items(void){
1320 bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core());
1321 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1322 GtkWidget *selfview_item=linphone_gtk_get_widget(
1323 linphone_gtk_get_main_window(),"selfview_item");
1324 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1325 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1326 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1327 gtk_widget_set_sensitive(selfview_item,video_enabled);
1330 static gboolean linphone_gtk_can_manage_accounts(){
1331 LinphoneCore *lc=linphone_gtk_get_core();
1333 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1334 SipSetup *ss=(SipSetup*)elem->data;
1335 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1342 static void linphone_gtk_configure_main_window(){
1343 static gboolean config_loaded=FALSE;
1344 static const char *title;
1345 static const char *home;
1346 static const char *start_call_icon;
1347 static const char *add_call_icon;
1348 static const char *stop_call_icon;
1349 static const char *search_icon;
1350 static gboolean update_check_menu;
1351 static gboolean buttons_have_borders;
1352 static gboolean show_abcd;
1353 GtkWidget *w=linphone_gtk_get_main_window();
1354 if (!config_loaded){
1355 title=linphone_gtk_get_ui_config("title","Linphone");
1356 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1357 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1358 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1359 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1360 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1361 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1362 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1363 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1366 linphone_gtk_configure_window(w,"main_window");
1368 gtk_window_set_title(GTK_WINDOW(w),title);
1370 if (start_call_icon){
1371 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1372 create_pixmap (start_call_icon));
1373 if (!buttons_have_borders)
1374 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1377 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1378 create_pixmap (add_call_icon));
1379 if (!buttons_have_borders)
1380 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1382 if (stop_call_icon){
1383 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1384 create_pixmap (stop_call_icon));
1385 if (!buttons_have_borders)
1386 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1389 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1390 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1391 g_object_unref(G_OBJECT(pbuf));
1395 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1397 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1401 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1403 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1404 g_object_unref(G_OBJECT(pbuf));
1409 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1411 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon")),pbuf);
1412 g_object_unref(G_OBJECT(pbuf));
1415 if (linphone_gtk_can_manage_accounts())
1416 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1417 if (update_check_menu){
1418 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1421 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1422 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1423 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1424 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1425 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1429 void linphone_gtk_manage_login(void){
1430 LinphoneCore *lc=linphone_gtk_get_core();
1431 LinphoneProxyConfig *cfg=NULL;
1432 linphone_core_get_default_proxy(lc,&cfg);
1434 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1435 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1436 linphone_gtk_show_login_frame(cfg);
1442 gboolean linphone_gtk_close(GtkWidget *mw){
1443 /*shutdown calls if any*/
1444 LinphoneCore *lc=linphone_gtk_get_core();
1445 if (linphone_core_in_call(lc)){
1446 linphone_core_terminate_all_calls(lc);
1448 linphone_core_enable_video_preview(lc,FALSE);
1449 #ifdef __APPLE__ /*until with have a better option*/
1450 gtk_window_iconify(GTK_WINDOW(mw));
1452 gtk_widget_hide(mw);
1458 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1459 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1460 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1462 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1463 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1470 static void linphone_gtk_init_main_window(){
1471 GtkWidget *main_window;
1473 linphone_gtk_configure_main_window();
1474 linphone_gtk_manage_login();
1476 linphone_gtk_load_identities();
1477 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1478 linphone_gtk_show_friends();
1479 linphone_gtk_connect_digits();
1480 main_window=linphone_gtk_get_main_window();
1481 linphone_gtk_call_log_update(main_window);
1483 linphone_gtk_update_call_buttons (NULL);
1484 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1485 g_signal_connect (G_OBJECT (main_window), "delete-event",
1486 G_CALLBACK (linphone_gtk_close), main_window);
1489 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1490 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1491 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1492 gtk_widget_hide(menubar);
1493 gtk_osxapplication_ready(theMacApp);
1495 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1497 linphone_gtk_check_menu_items();
1501 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1503 const char *lname="undef";
1505 #if defined(__linux) || defined(__APPLE__)
1506 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1525 g_error("Bad level !");
1527 #if defined(__linux) || defined(__APPLE__)
1529 msg=g_strdup_vprintf(fmt,cap);
1532 msg=g_strdup_vprintf(fmt,args);
1534 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1537 linphone_gtk_log_push(lev,fmt,args);
1541 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1542 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1543 linphone_gtk_get_main_window(), "uribar"));
1545 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1547 gtk_entry_set_text(uri_bar, refer_to);
1548 linphone_gtk_start_call(linphone_gtk_get_main_window());
1551 static void linphone_gtk_check_soundcards(){
1552 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1553 if (devices==NULL || devices[0]==NULL){
1554 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1555 _("No sound cards have been detected on this computer.\n"
1556 "You won't be able to send or receive audio calls."));
1560 static void linphone_gtk_quit(void){
1561 linphone_gtk_uninit_instance();
1562 linphone_gtk_destroy_log_window();
1563 linphone_core_destroy(the_core);
1564 linphone_gtk_log_uninit();
1568 gdk_threads_leave();
1573 This is not the correct way to implement block termination.
1574 The good way would be to call gtk_main_quit(), and return TRUE.
1575 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1576 As a result the program cannot exit at all.
1577 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1579 static gboolean on_block_termination(void){
1581 linphone_gtk_quit();
1586 int main(int argc, char *argv[]){
1590 const char *config_file;
1591 const char *factory_config_file;
1593 GtkSettings *settings;
1595 const char *app_name="Linphone";
1597 g_thread_init(NULL);
1600 progpath = strdup(argv[0]);
1602 config_file=linphone_gtk_get_config_file();
1606 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1607 if ((lang=getenv("LANG"))!=NULL){
1610 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1616 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1619 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1622 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1625 setenv("LANG",lang,1);
1630 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1631 if (p==NULL) perror("bindtextdomain failed");
1632 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1633 textdomain (GETTEXT_PACKAGE);
1635 g_message("NLS disabled.\n");
1638 gtk_rc_add_default_file("./gtkrc");
1640 gdk_threads_enter();
1642 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1643 linphone_options,NULL,NULL)){
1644 gdk_threads_leave();
1648 settings=gtk_settings_get_default();
1649 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1650 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1651 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1652 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1654 if (workingdir!=NULL){
1655 if (chdir(workingdir)==-1){
1656 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1660 /* Now, look for the factory configuration file, we do it this late
1661 since we want to have had time to change directory and to parse
1662 the options, in case we needed to access the working directory */
1663 factory_config_file = linphone_gtk_get_factory_config_file();
1665 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1666 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1667 g_warning("This instance is going to exit now.");
1668 gdk_threads_leave();
1672 add_pixmap_directory("pixmaps");
1673 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1676 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1677 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1678 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1679 /*never block termination:*/
1680 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1683 the_ui=linphone_gtk_create_window("main");
1685 linphone_gtk_create_log_window();
1686 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1688 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1690 g_set_application_name(app_name);
1691 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1692 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1694 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1695 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1696 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1697 linphone_gtk_init_main_window();
1698 #ifndef HAVE_GTK_OSX
1699 linphone_gtk_init_status_icon();
1702 linphone_gtk_show_main_window();
1703 linphone_gtk_check_soundcards();
1705 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1706 linphone_gtk_check_for_new_version();
1709 linphone_gtk_quit();
1710 #ifndef HAVE_GTK_OSX
1711 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1712 gtk_status_icon_set_visible(icon,FALSE);