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_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
61 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
62 static void linphone_gtk_status_icon_set_blinking(gboolean val);
65 static gboolean verbose=0;
66 static gboolean auto_answer = 0;
67 static gchar * addr_to_call = NULL;
68 static gboolean iconified=FALSE;
69 static gchar *workingdir=NULL;
70 static char *progpath=NULL;
71 gchar *linphone_logfile=NULL;
73 static GOptionEntry linphone_options[]={
77 .arg=G_OPTION_ARG_NONE,
78 .arg_data= (gpointer)&verbose,
79 .description=N_("log to stdout some debug information while running.")
82 .long_name = "logfile",
84 .arg = G_OPTION_ARG_STRING,
85 .arg_data = &linphone_logfile,
86 .description = N_("path to a file to write logs into.")
89 .long_name="iconified",
91 .arg=G_OPTION_ARG_NONE,
92 .arg_data= (gpointer)&iconified,
93 .description=N_("Start only in the system tray, do not show the main interface.")
98 .arg = G_OPTION_ARG_STRING,
99 .arg_data = &addr_to_call,
100 .description = N_("address to call right now")
103 .long_name = "auto-answer",
105 .arg = G_OPTION_ARG_NONE,
106 .arg_data = (gpointer) & auto_answer,
107 .description = N_("if set automatically answer incoming calls")
110 .long_name = "workdir",
112 .arg = G_OPTION_ARG_STRING,
113 .arg_data = (gpointer) & workingdir,
114 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
119 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
120 #define RELATIVE_XML_DIR
121 #define BUILD_TREE_XML_DIR "gtk"
124 #define CONFIG_FILE ".linphonerc"
126 #define CONFIG_FILE "linphonerc"
131 static char _config_file[1024];
134 const char *linphone_gtk_get_config_file(){
135 /*try accessing a local file first if exists*/
136 if (access(CONFIG_FILE,F_OK)==0){
137 snprintf(_config_file,sizeof(_config_file),"%s",CONFIG_FILE);
140 const char *appdata=getenv("APPDATA");
142 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
143 CreateDirectory(_config_file,NULL);
144 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR "\\" CONFIG_FILE);
147 const char *home=getenv("HOME");
148 if (home==NULL) home=".";
149 snprintf(_config_file,sizeof(_config_file),"%s/%s",home,CONFIG_FILE);
156 #define FACTORY_CONFIG_FILE "linphonerc.factory"
157 static char _factory_config_file[1024];
158 static const char *linphone_gtk_get_factory_config_file(){
159 /*try accessing a local file first if exists*/
160 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
161 snprintf(_factory_config_file,sizeof(_factory_config_file),
162 "%s",FACTORY_CONFIG_FILE);
166 if (progpath != NULL) {
168 progdir = strdup(progpath);
170 basename = strrchr(progdir, '\\');
171 if (basename != NULL) {
174 snprintf(_factory_config_file, sizeof(_factory_config_file),
175 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
177 if (workingdir!=NULL) {
178 snprintf(_factory_config_file, sizeof(_factory_config_file),
179 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
186 basename = strrchr(progdir, '/');
187 if (basename != NULL) {
190 snprintf(_factory_config_file, sizeof(_factory_config_file),
191 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
200 return _factory_config_file;
203 static void linphone_gtk_init_liblinphone(const char *config_file,
204 const char *factory_config_file) {
205 LinphoneCoreVTable vtable={0};
207 vtable.call_state_changed=linphone_gtk_call_state_changed;
208 vtable.registration_state_changed=linphone_gtk_registration_state_changed;
209 vtable.show=linphone_gtk_show;
210 vtable.notify_presence_recv=linphone_gtk_notify_recv;
211 vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber;
212 vtable.auth_info_requested=linphone_gtk_auth_info_requested;
213 vtable.display_status=linphone_gtk_display_status;
214 vtable.display_message=linphone_gtk_display_message;
215 vtable.display_warning=linphone_gtk_display_warning;
216 vtable.display_url=linphone_gtk_display_url;
217 vtable.call_log_updated=linphone_gtk_call_log_updated;
218 vtable.text_received=linphone_gtk_text_received;
219 vtable.refer_received=linphone_gtk_refer_received;
220 vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
222 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
223 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
224 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
229 LinphoneCore *linphone_gtk_get_core(void){
233 GtkWidget *linphone_gtk_get_main_window(){
237 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
238 static const char *icon_path=NULL;
239 static const char *hiddens=NULL;
240 static const char *shown=NULL;
241 static bool_t config_loaded=FALSE;
242 if (linphone_gtk_get_core()==NULL) return;
243 if (config_loaded==FALSE){
244 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
245 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
246 icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
250 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
252 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
254 GdkPixbuf *pbuf=create_pixbuf(icon_path);
255 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
256 g_object_unref(G_OBJECT(pbuf));
260 static int get_ui_file(const char *name, char *path, int pathsize){
261 snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
262 if (access(path,F_OK)!=0){
263 snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
264 if (access(path,F_OK)!=0){
265 g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
266 INSTALLED_XML_DIR,name);
273 GtkWidget *linphone_gtk_create_window(const char *window_name){
274 GError* error = NULL;
275 GtkBuilder* builder = gtk_builder_new ();
279 if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL;
281 if (!gtk_builder_add_from_file (builder, path, &error)){
282 g_error("Couldn't load builder file: %s", error->message);
283 g_error_free (error);
286 w=GTK_WIDGET(gtk_builder_get_object (builder,window_name));
288 g_error("Could not retrieve '%s' window from xml file",window_name);
291 g_object_set_data(G_OBJECT(w),"builder",builder);
292 gtk_builder_connect_signals(builder,w);
293 linphone_gtk_configure_window(w,window_name);
297 GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){
300 GtkBuilder* builder = gtk_builder_new ();
302 gchar *object_ids[2];
303 object_ids[0]=g_strdup(widget_name);
306 if (get_ui_file(filename,path,sizeof(path))==-1) return NULL;
307 if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){
308 g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message);
309 g_error_free (error);
310 g_free(object_ids[0]);
313 g_free(object_ids[0]);
314 w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name));
316 g_error("Could not retrieve '%s' window from xml file",widget_name);
319 g_object_set_data(G_OBJECT(w),"builder",builder);
320 gtk_builder_connect_signals(builder,w);
324 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
325 GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
328 g_error("Fail to retrieve builder from window !");
331 w=gtk_builder_get_object(builder,name);
333 g_error("No widget named %s found in xml interface.",name);
335 return GTK_WIDGET(w);
339 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
341 GtkWidget *main_window=linphone_gtk_get_main_window();
343 gtk_widget_show(main_window);
344 if (type==GTK_MESSAGE_QUESTION)
346 /* draw a question box. link to dialog_click callback */
347 dialog = gtk_message_dialog_new (
348 GTK_WINDOW(main_window),
349 GTK_DIALOG_DESTROY_WITH_PARENT,
350 GTK_MESSAGE_QUESTION,
353 (const gchar*)message);
354 /* connect to some callback : REVISIT */
356 g_signal_connect_swapped (G_OBJECT (dialog), "response",
357 G_CALLBACK (dialog_click),
360 /* actually show the box */
361 gtk_widget_show(dialog);
365 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
366 GTK_DIALOG_DESTROY_WITH_PARENT,
370 (const gchar*)message);
371 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
372 g_signal_connect_swapped (G_OBJECT (dialog), "response",
373 G_CALLBACK (gtk_widget_destroy),
375 gtk_widget_show(dialog);
379 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
380 if (id==GTK_RESPONSE_CANCEL){
381 gtk_widget_destroy(GTK_WIDGET(dialog));
385 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
386 g_message("About url clicked");
387 linphone_gtk_open_browser(url);
390 void linphone_gtk_show_about(){
391 struct stat filestat;
392 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
395 GdkPixbuf *logo=create_pixbuf(
396 linphone_gtk_get_ui_config("logo","linphone-banner.png"));
397 static const char *defcfg="defcfg";
399 about=linphone_gtk_create_window("about");
400 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
401 memset(&filestat,0,sizeof(filestat));
402 if (stat(license_file,&filestat)!=0){
403 license_file="COPYING";
404 stat(license_file,&filestat);
406 if (filestat.st_size>0){
407 char *license=g_malloc(filestat.st_size+1);
408 FILE *f=fopen(license_file,"r");
409 if (f && fread(license,filestat.st_size,1,f)==1){
410 license[filestat.st_size]='\0';
411 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
415 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
416 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
417 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
418 if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo);
419 tmp=linphone_gtk_get_ui_config("artists",defcfg);
424 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
426 tmp=linphone_gtk_get_ui_config("translators",defcfg);
428 gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
429 tmp=linphone_gtk_get_ui_config("comments",defcfg);
431 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
432 gtk_widget_show(about);
435 static void set_video_window_decorations(GdkWindow *w){
436 const char *title=linphone_gtk_get_ui_config("title","Linphone");
437 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
438 char video_title[256];
439 GdkPixbuf *pbuf=create_pixbuf(icon_path);
440 if (!linphone_core_in_call(linphone_gtk_get_core())){
441 snprintf(video_title,sizeof(video_title),"%s video",title);
442 /* When not in call, treat the video as a normal window */
443 gdk_window_set_keep_above(w, FALSE);
445 LinphoneAddress *uri =
446 linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core()));
449 linphone_address_clean(uri);
450 if (linphone_address_get_display_name(uri)!=NULL){
451 display_name=ms_strdup(linphone_address_get_display_name(uri));
453 display_name=linphone_address_as_string(uri);
455 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
456 linphone_address_destroy(uri);
457 ms_free(display_name);
459 /* During calls, bring up the video window, arrange so that
460 it is above all the other windows */
461 gdk_window_deiconify(w);
462 gdk_window_set_keep_above(w,TRUE);
463 /* Maybe we should have the following, but then we want to
464 have a timer that turns it off after a little while. */
465 /* gdk_window_set_urgency_hint(w,TRUE); */
467 gdk_window_set_title(w,video_title);
468 /* Refrain the video window to be closed at all times. */
469 gdk_window_set_functions(w,
470 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
471 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
474 l=g_list_append(l,pbuf);
475 gdk_window_set_icon_list(w,l);
477 g_object_unref(G_OBJECT(pbuf));
481 static gboolean video_needs_update=FALSE;
483 static void update_video_title(){
484 video_needs_update=TRUE;
487 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
488 static gboolean first_time=TRUE;
490 static unsigned long previd=0;
491 static unsigned long preview_previd=0;
492 static gboolean in_iterate=FALSE;
495 if (in_iterate) return TRUE;
497 linphone_core_iterate(lc);
499 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
500 linphone_gtk_show_directory_search();
504 id=linphone_core_get_native_video_window_id(lc);
505 if (id!=previd || video_needs_update){
509 ms_message("Updating window decorations");
511 w=gdk_window_foreign_new(id);
513 w=gdk_window_foreign_new((HANDLE)id);
516 set_video_window_decorations(w);
517 g_object_unref(G_OBJECT(w));
519 else ms_error("gdk_window_foreign_new() failed");
520 if (video_needs_update) video_needs_update=FALSE;
523 id=linphone_core_get_native_preview_window_id (lc);
524 if (id!=preview_previd ){
528 ms_message("Updating window decorations for preview");
530 w=gdk_window_foreign_new(id);
532 w=gdk_window_foreign_new((HANDLE)id);
535 set_video_window_decorations(w);
536 g_object_unref(G_OBJECT(w));
538 else ms_error("gdk_window_foreign_new() failed");
539 if (video_needs_update) video_needs_update=FALSE;
542 if (addr_to_call!=NULL){
543 /*make sure we are not showing the login screen*/
544 GtkWidget *mw=linphone_gtk_get_main_window();
545 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
546 if (!GTK_WIDGET_VISIBLE(login_frame)){
547 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
548 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
550 linphone_gtk_start_call(uri_bar);
557 static void load_uri_history(){
558 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
561 GtkEntryCompletion *gep=gtk_entry_completion_new();
562 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
565 snprintf(key,sizeof(key),"uri%i",i);
566 uri=linphone_gtk_get_ui_config(key,NULL);
569 gtk_list_store_append(model,&iter);
570 gtk_list_store_set(model,&iter,0,uri,-1);
571 if (i==0) gtk_entry_set_text(uribar,uri);
575 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
576 gtk_entry_completion_set_text_column(gep,0);
577 gtk_entry_set_completion(uribar,gep);
580 static void save_uri_history(){
581 LinphoneCore *lc=linphone_gtk_get_core();
582 LpConfig *cfg=linphone_core_get_config(lc);
583 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
588 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
590 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
592 gtk_tree_model_get(model,&iter,0,&uri,-1);
594 snprintf(key,sizeof(key),"uri%i",i);
595 lp_config_set_string(cfg,"GtkUi",key,uri);
600 }while(gtk_tree_model_iter_next(model,&iter));
604 static void completion_add_text(GtkEntry *entry, const char *text){
606 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
608 if (gtk_tree_model_get_iter_first(model,&iter)){
611 gtk_tree_model_get(model,&iter,0,&uri,-1);
613 if (strcmp(uri,text)==0) {
615 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
621 }while (gtk_tree_model_iter_next(model,&iter));
623 /* and prepend it on top of the list */
624 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
625 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
630 void linphone_gtk_show_main_window(){
631 GtkWidget *w=linphone_gtk_get_main_window();
632 LinphoneCore *lc=linphone_gtk_get_core();
633 if (linphone_core_video_enabled(lc)){
634 linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview",
635 VIDEOSELFVIEW_DEFAULT));
638 gtk_window_present(GTK_WINDOW(w));
641 static void linphone_gtk_show(LinphoneCore *lc){
643 linphone_gtk_show_main_window();
647 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
648 GtkWidget *mw=linphone_gtk_get_main_window();
649 if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
650 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
651 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
653 if (linphone_gtk_use_in_call_view() && call)
654 linphone_gtk_in_call_view_terminate(call,error);
655 update_video_title();
658 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
659 LinphoneCore *lc=linphone_gtk_get_core();
660 GtkWidget *mw=linphone_gtk_get_main_window();
661 const MSList *calls=linphone_core_get_calls(lc);
663 bool_t start_active=TRUE;
664 bool_t stop_active=FALSE;
665 bool_t add_call=FALSE;
666 int call_list_size=ms_list_size(calls);
676 button=linphone_gtk_get_widget(mw,"start_call");
677 gtk_widget_set_sensitive(button,start_active);
678 gtk_widget_set_visible(button,!add_call);
680 button=linphone_gtk_get_widget(mw,"add_call");
681 gtk_widget_set_sensitive(button,start_active);
682 gtk_widget_set_visible(button,add_call);
684 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
686 linphone_gtk_enable_transfer_button(lc,call_list_size>1);
687 linphone_gtk_enable_conference_button(lc,call_list_size>1);
688 update_video_title();
691 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
692 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
693 if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
694 completion_add_text(GTK_ENTRY(uri_bar),entered);
696 linphone_gtk_call_terminated(NULL,NULL);
701 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
702 if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
703 linphone_core_accept_call (linphone_gtk_get_core(),call);
704 linphone_call_unref(call);
710 void linphone_gtk_start_call(GtkWidget *w){
711 LinphoneCore *lc=linphone_gtk_get_core();
713 /*change into in-call mode, then do the work later as it might block a bit */
714 GtkWidget *mw=gtk_widget_get_toplevel(w);
715 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
717 call=linphone_gtk_get_currently_displayed_call();
718 if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){
719 linphone_core_accept_call(lc,call);
721 /*immediately disable the button and delay a bit the execution the linphone_core_invite()
722 so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
723 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
724 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
729 void linphone_gtk_uri_bar_activate(GtkWidget *w){
730 linphone_gtk_start_call(w);
734 void linphone_gtk_terminate_call(GtkWidget *button){
735 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
737 linphone_core_terminate_call(linphone_gtk_get_core(),call);
740 void linphone_gtk_decline_clicked(GtkWidget *button){
741 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
743 linphone_core_terminate_call(linphone_gtk_get_core(),call);
746 void linphone_gtk_answer_clicked(GtkWidget *button){
747 LinphoneCall *call=linphone_gtk_get_currently_displayed_call ();
749 linphone_core_pause_all_calls(linphone_gtk_get_core());
750 linphone_core_accept_call(linphone_gtk_get_core(),call);
751 linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
755 void linphone_gtk_enable_video(GtkWidget *w){
756 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
757 GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
758 linphone_core_enable_video(linphone_gtk_get_core(),val,val);
759 gtk_widget_set_sensitive(selfview_item,val);
761 linphone_core_enable_video_preview(linphone_gtk_get_core(),
762 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT));
764 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
768 void linphone_gtk_enable_self_view(GtkWidget *w){
769 gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
770 LinphoneCore *lc=linphone_gtk_get_core();
771 linphone_core_enable_video_preview(lc,val);
772 linphone_core_enable_self_view(lc,val);
773 linphone_gtk_set_ui_config_int("videoselfview",val);
776 void linphone_gtk_used_identity_changed(GtkWidget *w){
777 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
778 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
779 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
780 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
781 linphone_gtk_show_directory_search();
783 if (sel) g_free(sel);
786 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
787 linphone_gtk_show_friends();
790 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
792 case GTK_RESPONSE_YES:
793 linphone_gtk_show_contact(lf);
796 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
798 gtk_widget_destroy(dialog);
801 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
804 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
805 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
809 gchar *message=g_strdup_printf(_("%s would like to add you to his contact list.\nWould you allow him to see your presence status or add him to your contact list ?\nIf you answer no, this person will be temporarily blacklisted."),url);
810 dialog = gtk_message_dialog_new (
811 GTK_WINDOW(linphone_gtk_get_main_window()),
812 GTK_DIALOG_DESTROY_WITH_PARENT,
813 GTK_MESSAGE_QUESTION,
818 g_signal_connect(G_OBJECT (dialog), "response",
819 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
820 /* actually show the box */
821 gtk_widget_show(dialog);
824 typedef struct _AuthTimeout{
829 static void auth_timeout_clean(AuthTimeout *tout){
833 static gboolean auth_timeout_destroy(AuthTimeout *tout){
835 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
836 gtk_widget_destroy(tout->w);
842 static AuthTimeout * auth_timeout_new(GtkWidget *w){
843 AuthTimeout *tout=g_new(AuthTimeout,1);
845 /*so that the timeout no more references the widget when it is destroyed:*/
846 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
847 /*so that the widget is automatically destroyed after some time */
848 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
852 void linphone_gtk_password_cancel(GtkWidget *w){
853 LinphoneAuthInfo *info;
854 GtkWidget *window=gtk_widget_get_toplevel(w);
855 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
856 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
857 gtk_widget_destroy(window);
860 void linphone_gtk_password_ok(GtkWidget *w){
862 GtkWidget *window=gtk_widget_get_toplevel(w);
863 LinphoneAuthInfo *info;
864 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
865 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
866 entry=linphone_gtk_get_widget(window,"password_entry");
867 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
868 linphone_auth_info_set_userid(info,
869 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
870 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
871 gtk_widget_destroy(window);
874 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
875 GtkWidget *w=linphone_gtk_create_window("password");
876 GtkWidget *label=linphone_gtk_get_widget(w,"message");
877 LinphoneAuthInfo *info;
879 GtkWidget *mw=linphone_gtk_get_main_window();
881 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
882 /*don't prompt for authentication when login frame is visible*/
883 linphone_core_abort_authentication(lc,NULL);
887 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
889 gtk_label_set_markup(GTK_LABEL(label),msg);
891 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
892 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
893 g_object_set_data(G_OBJECT(w),"auth_info",info);
894 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
899 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
900 GtkWidget *w=linphone_gtk_get_main_window();
901 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
902 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
903 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
907 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
908 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
911 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
912 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
915 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
917 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
918 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
921 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
922 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
923 if (w) linphone_gtk_call_log_update(w);
924 linphone_gtk_call_log_update(linphone_gtk_get_main_window());
928 static void make_notification(const char *title, const char *body){
929 NotifyNotification *n;
930 n = notify_notification_new(title,body,linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
931 if (n && !notify_notification_show(n,NULL))
932 ms_error("Failed to send notification.");
937 static void linphone_gtk_notify(LinphoneCall *call, const char *msg){
939 if (!notify_is_initted())
940 if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
944 if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL),NULL))
945 ms_error("Failed to send notification.");
947 linphone_gtk_show_main_window();
949 } else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
952 char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
953 switch(linphone_call_get_state(call)){
954 case LinphoneCallError:
955 make_notification(_("Call error"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>\n%s",msg,remote));
957 case LinphoneCallEnd:
958 make_notification(_("Call ended"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
960 case LinphoneCallIncomingReceived:
961 make_notification(_("Incoming call"),body=g_markup_printf_escaped("<span size=\"large\">%s</span>",remote));
963 case LinphoneCallPausedByRemote:
964 make_notification(_("Call paused"),body=g_markup_printf_escaped("<span size=\"large\">by %s</span>",remote));
969 if (body) g_free(body);
970 if (remote) g_free(remote);
975 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
977 case LinphoneCallOutgoingInit:
978 linphone_gtk_create_in_call_view (call);
980 case LinphoneCallOutgoingProgress:
981 linphone_gtk_in_call_view_set_calling (call);
983 case LinphoneCallStreamsRunning:
984 linphone_gtk_in_call_view_set_in_call(call);
986 case LinphoneCallError:
987 linphone_gtk_in_call_view_terminate (call,msg);
989 case LinphoneCallEnd:
990 linphone_gtk_in_call_view_terminate(call,NULL);
991 linphone_gtk_status_icon_set_blinking(FALSE);
993 case LinphoneCallIncomingReceived:
994 linphone_gtk_create_in_call_view (call);
995 linphone_gtk_in_call_view_set_incoming(call);
996 linphone_gtk_status_icon_set_blinking(TRUE);
998 linphone_call_ref(call);
999 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call);
1002 case LinphoneCallResuming:
1003 linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1004 linphone_gtk_in_call_view_set_in_call (call);
1006 case LinphoneCallPausing:
1007 linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1008 case LinphoneCallPausedByRemote:
1009 linphone_gtk_in_call_view_set_paused(call);
1011 case LinphoneCallConnected:
1012 linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1013 linphone_gtk_status_icon_set_blinking(FALSE);
1018 linphone_gtk_notify(call, msg);
1019 linphone_gtk_update_call_buttons (call);
1022 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1023 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1024 GtkTreeModel *model=gtk_combo_box_get_model(box);
1026 gboolean found=FALSE;
1027 const char *stock_id=NULL;
1029 if (gtk_tree_model_get_iter_first(model,&iter)){
1032 gtk_tree_model_get(model,&iter,2,&p,-1);
1037 }while(gtk_tree_model_iter_next(model,&iter));
1040 g_warning("Could not find proxy config in combo box of identities.");
1044 case LinphoneRegistrationOk:
1045 stock_id=GTK_STOCK_YES;
1047 case LinphoneRegistrationProgress:
1048 stock_id=GTK_STOCK_REFRESH;
1050 case LinphoneRegistrationCleared:
1053 case LinphoneRegistrationFailed:
1054 stock_id=GTK_STOCK_DIALOG_WARNING;
1059 gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,stock_id,-1);
1062 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1063 LinphoneRegistrationState rs, const char *msg){
1065 case LinphoneRegistrationOk:
1067 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1068 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1069 linphone_gtk_exit_login_frame();
1076 update_registration_status(cfg,rs);
1080 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
1081 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
1082 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
1085 void linphone_gtk_open_browser(const char *url){
1086 /*in gtk 2.16, gtk_show_uri does not work...*/
1088 #if GTK_CHECK_VERSION(2,18,3)
1089 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
1092 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
1093 g_spawn_command_line_async(cl,NULL);
1096 ShellExecute(0,"open",url,NULL,NULL,1);
1100 void linphone_gtk_link_to_website(GtkWidget *item){
1101 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1102 linphone_gtk_open_browser(home);
1105 static GtkWidget *create_icon_menu(){
1106 GtkWidget *menu=gtk_menu_new();
1107 GtkWidget *menu_item;
1110 const gchar *homesite;
1112 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1113 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1114 tmp=g_strdup(homesite);
1115 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1116 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1118 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1119 gtk_widget_show(image);
1120 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1121 //g_object_unref(G_OBJECT(image));
1122 gtk_widget_show(menu_item);
1123 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1124 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1126 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1127 gtk_widget_show(menu_item);
1128 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1129 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1130 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1131 gtk_widget_show(menu_item);
1132 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1133 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1134 gtk_widget_show(menu);
1138 static GtkStatusIcon *icon=NULL;
1140 static void linphone_gtk_init_status_icon(){
1141 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
1142 const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1143 GdkPixbuf *pbuf=create_pixbuf(icon_path);
1144 GtkWidget *menu=create_icon_menu();
1146 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
1147 icon=gtk_status_icon_new_from_pixbuf(pbuf);
1148 gtk_status_icon_set_name(icon,title);
1149 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)linphone_gtk_show_main_window,linphone_gtk_get_main_window());
1150 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
1151 gtk_status_icon_set_tooltip(icon,title);
1152 gtk_status_icon_set_visible(icon,TRUE);
1153 g_object_set_data(G_OBJECT(icon),"menu",menu);
1154 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
1155 g_object_set_data(G_OBJECT(icon),"icon",pbuf);
1156 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf);
1157 pbuf=create_pixbuf(call_icon_path);
1158 g_object_set_data(G_OBJECT(icon),"call_icon",pbuf);
1161 static gboolean do_icon_blink(GtkStatusIcon *gi){
1162 GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon");
1163 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon");
1164 GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi);
1165 if (cur_icon==call_icon){
1166 gtk_status_icon_set_from_pixbuf(gi,normal_icon);
1168 gtk_status_icon_set_from_pixbuf(gi,call_icon);
1173 static void linphone_gtk_status_icon_set_blinking(gboolean val){
1175 tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
1176 if (val && tout==0){
1177 tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon);
1178 g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
1179 }else if (!val && tout!=0){
1180 GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon");
1181 g_source_remove(tout);
1182 g_object_set_data(G_OBJECT(icon),"timeout",NULL);
1183 gtk_status_icon_set_from_pixbuf(icon,normal_icon);
1187 static void init_identity_combo(GtkComboBox *box){
1188 GtkListStore *store;
1189 GtkCellRenderer *r1,*r2;
1190 store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1191 gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1192 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1193 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1194 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1195 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"stock-id",1);
1196 g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1197 gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1200 void linphone_gtk_load_identities(void){
1202 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1204 LinphoneProxyConfig *def=NULL;
1206 GtkListStore *store;
1209 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1210 if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1211 /* model is empty, this is the first time we go here */
1212 init_identity_combo(box);
1213 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1215 gtk_list_store_clear(store);
1216 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
1217 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1218 gtk_list_store_append(store,&iter);
1219 gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1220 g_free(def_identity);
1221 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1223 elem=ms_list_next(elem),i++){
1224 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1225 gtk_list_store_append(store,&iter);
1226 gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1227 linphone_proxy_config_is_registered(cfg) ? GTK_STOCK_YES : NULL,
1233 gtk_combo_box_set_active(box,def_index);
1236 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1237 const char *label=gtk_button_get_label(button);
1238 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
1240 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1241 linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1242 if (linphone_core_in_call(linphone_gtk_get_core())){
1243 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
1247 static void linphone_gtk_dtmf_released(GtkButton *button){
1248 linphone_core_stop_dtmf (linphone_gtk_get_core());
1251 static void linphone_gtk_connect_digits(void){
1252 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1253 GList *children=gtk_container_get_children(cont);
1255 for(elem=children;elem!=NULL;elem=elem->next){
1256 GtkButton *button=GTK_BUTTON(elem->data);
1257 g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1258 g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1262 static void linphone_gtk_check_menu_items(void){
1263 bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core());
1264 bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1265 GtkWidget *selfview_item=linphone_gtk_get_widget(
1266 linphone_gtk_get_main_window(),"selfview_item");
1267 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1268 linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1269 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1270 gtk_widget_set_sensitive(selfview_item,video_enabled);
1273 static gboolean linphone_gtk_can_manage_accounts(){
1274 LinphoneCore *lc=linphone_gtk_get_core();
1276 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1277 SipSetup *ss=(SipSetup*)elem->data;
1278 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1285 static void linphone_gtk_configure_main_window(){
1286 static gboolean config_loaded=FALSE;
1287 static const char *title;
1288 static const char *home;
1289 static const char *start_call_icon;
1290 static const char *add_call_icon;
1291 static const char *stop_call_icon;
1292 static const char *search_icon;
1293 static gboolean update_check_menu;
1294 static gboolean buttons_have_borders;
1295 static gboolean show_abcd;
1296 GtkWidget *w=linphone_gtk_get_main_window();
1297 if (!config_loaded){
1298 title=linphone_gtk_get_ui_config("title","Linphone");
1299 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1300 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png");
1301 add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png");
1302 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png");
1303 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1304 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1305 buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1);
1306 show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1309 linphone_gtk_configure_window(w,"main_window");
1311 gtk_window_set_title(GTK_WINDOW(w),title);
1313 if (start_call_icon){
1314 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),
1315 create_pixmap (start_call_icon));
1316 if (!buttons_have_borders)
1317 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE);
1320 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),
1321 create_pixmap (add_call_icon));
1322 if (!buttons_have_borders)
1323 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE);
1325 if (stop_call_icon){
1326 gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),
1327 create_pixmap (stop_call_icon));
1328 if (!buttons_have_borders)
1329 gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE);
1332 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1333 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1334 g_object_unref(G_OBJECT(pbuf));
1338 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1340 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1344 GdkPixbuf *pbuf=create_pixbuf("contact-orange.png");
1346 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"contact_tab_icon")),pbuf);
1347 g_object_unref(G_OBJECT(pbuf));
1352 GdkPixbuf *pbuf=create_pixbuf("dialer-orange.png");
1354 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"keypad_tab_icon")),pbuf);
1355 g_object_unref(G_OBJECT(pbuf));
1358 if (linphone_gtk_can_manage_accounts())
1359 gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1360 if (update_check_menu){
1361 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1364 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A"));
1365 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B"));
1366 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C"));
1367 gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D"));
1368 gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3);
1372 void linphone_gtk_manage_login(void){
1373 LinphoneCore *lc=linphone_gtk_get_core();
1374 LinphoneProxyConfig *cfg=NULL;
1375 linphone_core_get_default_proxy(lc,&cfg);
1377 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1378 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1379 linphone_gtk_show_login_frame(cfg);
1385 gboolean linphone_gtk_close(GtkWidget *mw){
1386 /*shutdown calls if any*/
1387 LinphoneCore *lc=linphone_gtk_get_core();
1388 if (linphone_core_in_call(lc)){
1389 linphone_core_terminate_all_calls(lc);
1391 linphone_core_enable_video_preview(lc,FALSE);
1392 #ifdef __APPLE__ /*until with have a better option*/
1393 gtk_window_iconify(GTK_WINDOW(mw));
1395 gtk_widget_hide(mw);
1401 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1402 if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){
1403 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1405 linphone_core_enable_video_preview(linphone_gtk_get_core(),
1406 linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core()));
1413 static void linphone_gtk_init_main_window(){
1414 GtkWidget *main_window;
1416 linphone_gtk_configure_main_window();
1417 linphone_gtk_manage_login();
1419 linphone_gtk_load_identities();
1420 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1421 linphone_gtk_show_friends();
1422 linphone_gtk_connect_digits();
1423 main_window=linphone_gtk_get_main_window();
1424 linphone_gtk_call_log_update(main_window);
1426 linphone_gtk_update_call_buttons (NULL);
1427 /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1428 g_signal_connect (G_OBJECT (main_window), "delete-event",
1429 G_CALLBACK (linphone_gtk_close), main_window);
1432 GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1433 gtk_widget_destroy(linphone_gtk_get_widget(main_window,"imagemenuitem5"));
1434 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1435 gtk_osxapplication_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1436 gtk_widget_hide(menubar);
1437 gtk_osxapplication_ready(theMacApp);
1439 g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1441 linphone_gtk_check_menu_items();
1445 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1447 const char *lname="undef";
1449 #if defined(__linux) || defined(__APPLE__)
1450 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1469 g_error("Bad level !");
1471 #if defined(__linux) || defined(__APPLE__)
1473 msg=g_strdup_vprintf(fmt,cap);
1476 msg=g_strdup_vprintf(fmt,args);
1478 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1481 linphone_gtk_log_push(lev,fmt,args);
1485 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1486 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1487 linphone_gtk_get_main_window(), "uribar"));
1489 linphone_gtk_notify(NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
1491 gtk_entry_set_text(uri_bar, refer_to);
1492 linphone_gtk_start_call(linphone_gtk_get_main_window());
1495 static void linphone_gtk_check_soundcards(){
1496 const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
1497 if (devices==NULL || devices[0]==NULL){
1498 linphone_gtk_display_something(GTK_MESSAGE_WARNING,
1499 _("No sound cards have been detected on this computer.\n"
1500 "You won't be able to send or receive audio calls."));
1504 static void linphone_gtk_quit(void){
1505 linphone_gtk_uninit_instance();
1506 gdk_threads_leave();
1507 linphone_gtk_destroy_log_window();
1508 linphone_core_destroy(the_core);
1509 linphone_gtk_log_uninit();
1517 This is not the correct way to implement block termination.
1518 The good way would be to call gtk_main_quit(), and return TRUE.
1519 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
1520 As a result the program cannot exit at all.
1521 As a workaround we do all the cleanup (unregistration and config save) within the handler.
1523 static gboolean on_block_termination(void){
1525 linphone_gtk_quit();
1530 int main(int argc, char *argv[]){
1534 const char *config_file;
1535 const char *factory_config_file;
1537 GtkSettings *settings;
1539 const char *app_name="Linphone";
1541 g_thread_init(NULL);
1544 progpath = strdup(argv[0]);
1546 config_file=linphone_gtk_get_config_file();
1550 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1551 if ((lang=getenv("LANG"))!=NULL){
1554 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1560 g_setenv("PULSE_PROP_media.role", "phone", TRUE);
1563 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1566 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1569 setenv("LANG",lang,1);
1574 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1575 if (p==NULL) perror("bindtextdomain failed");
1576 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1577 textdomain (GETTEXT_PACKAGE);
1579 g_message("NLS disabled.\n");
1582 gtk_rc_add_default_file("./gtkrc");
1584 gdk_threads_enter();
1586 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1587 linphone_options,NULL,NULL)){
1588 gdk_threads_leave();
1592 settings=gtk_settings_get_default();
1593 g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
1594 g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
1595 g_object_set(settings, "gtk-menu-images", TRUE, NULL);
1596 g_object_set(settings, "gtk-button-images", TRUE, NULL);
1598 if (workingdir!=NULL){
1599 if (chdir(workingdir)==-1){
1600 g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
1604 /* Now, look for the factory configuration file, we do it this late
1605 since we want to have had time to change directory and to parse
1606 the options, in case we needed to access the working directory */
1607 factory_config_file = linphone_gtk_get_factory_config_file();
1609 if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
1610 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1611 g_warning("This instance is going to exit now.");
1612 gdk_threads_leave();
1616 add_pixmap_directory("pixmaps");
1617 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1620 GtkOSXApplication *theMacApp = (GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
1621 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
1622 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
1623 /*never block termination:*/
1624 g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
1627 the_ui=linphone_gtk_create_window("main");
1629 linphone_gtk_create_log_window();
1630 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1632 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1634 g_set_application_name(app_name);
1635 pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON));
1636 if (pbuf!=NULL) gtk_window_set_default_icon(pbuf);
1638 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1639 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1640 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1641 linphone_gtk_init_main_window();
1642 #ifndef HAVE_GTK_OSX
1643 linphone_gtk_init_status_icon();
1646 linphone_gtk_show_main_window();
1647 linphone_gtk_check_soundcards();
1649 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1650 linphone_gtk_check_for_new_version();
1653 linphone_gtk_quit();
1654 #ifndef HAVE_GTK_OSX
1655 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1656 gtk_status_icon_set_visible(icon,FALSE);