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.
20 #define USE_LIBGLADE 1
28 #include <glade/glade.h>
31 #include <sys/types.h>
35 #define LINPHONE_ICON "linphone2.png"
37 const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION;
39 static LinphoneCore *the_core=NULL;
40 static GtkWidget *the_ui=NULL;
42 static void linphone_gtk_show(LinphoneCore *lc);
43 static void linphone_gtk_inv_recv(LinphoneCore *lc, const char *from);
44 static void linphone_gtk_bye_recv(LinphoneCore *lc, const char *from);
45 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
46 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
47 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username);
48 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
49 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
50 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
51 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
52 static void linphone_gtk_display_question(LinphoneCore *lc, const char *question);
53 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
54 static void linphone_gtk_general_state(LinphoneCore *lc, LinphoneGeneralState *gstate);
55 static void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to);
56 static gboolean linphone_gtk_auto_answer(GtkWidget *incall_window);
58 static LinphoneCoreVTable vtable={
59 .show=linphone_gtk_show,
60 .inv_recv=linphone_gtk_inv_recv,
61 .bye_recv=linphone_gtk_bye_recv,
62 .notify_recv=linphone_gtk_notify_recv,
63 .new_unknown_subscriber=linphone_gtk_new_unknown_subscriber,
64 .auth_info_requested=linphone_gtk_auth_info_requested,
65 .display_status=linphone_gtk_display_status,
66 .display_message=linphone_gtk_display_message,
67 .display_warning=linphone_gtk_display_warning,
68 .display_url=linphone_gtk_display_url,
69 .display_question=linphone_gtk_display_question,
70 .call_log_updated=linphone_gtk_call_log_updated,
71 .text_received=linphone_gtk_text_received,
72 .general_state=linphone_gtk_general_state,
73 .refer_received=linphone_gtk_refer_received,
74 .buddy_info_updated=linphone_gtk_buddy_info_updated
77 static gboolean verbose=0;
78 static gboolean auto_answer = 0;
79 static gchar * addr_to_call = NULL;
80 static gboolean iconified=FALSE;
82 static gchar *workingdir=NULL;
84 static char *progpath=NULL;
86 static GOptionEntry linphone_options[]={
90 .arg=G_OPTION_ARG_NONE,
91 .arg_data= (gpointer)&verbose,
92 .description=N_("log to stdout some debug information while running.")
95 .long_name="iconified",
97 .arg=G_OPTION_ARG_NONE,
98 .arg_data= (gpointer)&iconified,
99 .description=N_("Start only in the system tray, do not show the main interface.")
104 .arg = G_OPTION_ARG_STRING,
105 .arg_data = &addr_to_call,
106 .description = N_("address to call right now")
109 .long_name = "auto-answer",
111 .arg = G_OPTION_ARG_NONE,
112 .arg_data = (gpointer) & auto_answer,
113 .description = N_("if set automatically answer incoming calls")
117 .long_name = "workdir",
119 .arg = G_OPTION_ARG_STRING,
120 .arg_data = (gpointer) & workingdir,
121 .description = N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")
127 #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
128 #define BUILD_TREE_XML_DIR "gtk-glade"
131 #define CONFIG_FILE ".linphonerc"
133 #define CONFIG_FILE "linphonerc"
138 static char _config_file[1024];
141 const char *linphone_gtk_get_config_file(){
142 /*try accessing a local file first if exists*/
143 if (access(CONFIG_FILE,F_OK)==0){
144 snprintf(_config_file,sizeof(_config_file),"%s",CONFIG_FILE);
147 const char *appdata=getenv("APPDATA");
149 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
150 CreateDirectory(_config_file,NULL);
151 snprintf(_config_file,sizeof(_config_file),"%s\\%s",appdata,LINPHONE_CONFIG_DIR "\\" CONFIG_FILE);
154 const char *home=getenv("HOME");
155 if (home==NULL) home=".";
156 snprintf(_config_file,sizeof(_config_file),"%s/%s",home,CONFIG_FILE);
163 #define FACTORY_CONFIG_FILE "linphonerc.factory"
164 static char _factory_config_file[1024];
165 static const char *linphone_gtk_get_factory_config_file(){
166 /*try accessing a local file first if exists*/
167 if (access(FACTORY_CONFIG_FILE,F_OK)==0){
168 snprintf(_factory_config_file,sizeof(_factory_config_file),
169 "%s",FACTORY_CONFIG_FILE);
173 if (progpath != NULL) {
175 progdir = strdup(progpath);
177 basename = strrchr(progdir, '\\');
178 if (basename != NULL) {
181 snprintf(_factory_config_file, sizeof(_factory_config_file),
182 "%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
184 if (workingdir!=NULL) {
185 snprintf(_factory_config_file, sizeof(_factory_config_file),
186 "%s\\%s", workingdir, FACTORY_CONFIG_FILE);
193 basename = strrchr(progdir, '/');
194 if (basename != NULL) {
197 snprintf(_factory_config_file, sizeof(_factory_config_file),
198 "%s/../share/Linphone/%s", progdir, FACTORY_CONFIG_FILE);
207 return _factory_config_file;
210 static void linphone_gtk_init_liblinphone(const char *config_file,
211 const char *factory_config_file) {
212 linphone_core_set_user_agent("Linphone", LINPHONE_VERSION);
213 the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
214 linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
219 LinphoneCore *linphone_gtk_get_core(void){
223 GtkWidget *linphone_gtk_get_main_window(){
227 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
228 static const char *icon_path=NULL;
229 static const char *hiddens=NULL;
230 static const char *shown=NULL;
231 static bool_t config_loaded=FALSE;
232 if (linphone_gtk_get_core()==NULL) return;
233 if (config_loaded==FALSE){
234 hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
235 shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
236 icon_path=linphone_gtk_get_ui_config("icon",NULL);
240 linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
242 linphone_gtk_visibility_set(shown,window_name,w,TRUE);
244 GdkPixbuf *pbuf=create_pixbuf(icon_path);
245 gtk_window_set_icon(GTK_WINDOW(w),pbuf);
246 g_object_unref(G_OBJECT(pbuf));
252 GtkWidget *linphone_gtk_create_window(const char *window_name){
256 snprintf(path,sizeof(path),"%s/%s.glade",BUILD_TREE_XML_DIR,window_name);
257 if (access(path,F_OK)!=0){
258 snprintf(path,sizeof(path),"%s/%s.glade",INSTALLED_XML_DIR,window_name);
259 if (access(path,F_OK)!=0){
260 g_error("Could not locate neither %s/%s.glade and %s/%s.glade .",BUILD_TREE_XML_DIR,window_name,
261 INSTALLED_XML_DIR,window_name);
265 gxml=glade_xml_new(path,NULL,NULL);
266 glade_xml_signal_autoconnect(gxml);
267 w=glade_xml_get_widget(gxml,window_name);
268 if (w==NULL) g_error("Could not retrieve '%s' window from xml file",window_name);
269 linphone_gtk_configure_window(w,window_name);
273 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
275 GladeXML *gxml=glade_get_widget_tree(window);
276 if (gxml==NULL) g_error("Could not retrieve XML tree of window %s",name);
277 w=glade_xml_get_widget(gxml,name);
278 if (w==NULL) g_error("Could not retrieve widget %s",name);
279 return GTK_WIDGET(w);
284 GtkWidget *linphone_gtk_create_window(const char *window_name){
288 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
289 GObject *w=gtk_builder_get_object(the_ui,name);
291 g_error("No widget named %s found in xml interface.",name);
293 return GTK_WIDGET(w);
298 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
300 GtkWidget *main_window=linphone_gtk_get_main_window();
302 gtk_widget_show(main_window);
303 if (type==GTK_MESSAGE_QUESTION)
305 /* draw a question box. link to dialog_click callback */
306 dialog = gtk_message_dialog_new (
307 GTK_WINDOW(main_window),
308 GTK_DIALOG_DESTROY_WITH_PARENT,
309 GTK_MESSAGE_QUESTION,
312 (const gchar*)message);
313 /* connect to some callback : REVISIT */
315 g_signal_connect_swapped (G_OBJECT (dialog), "response",
316 G_CALLBACK (dialog_click),
319 /* actually show the box */
320 gtk_widget_show(dialog);
324 dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
325 GTK_DIALOG_DESTROY_WITH_PARENT,
329 (const gchar*)message);
330 /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
331 g_signal_connect_swapped (G_OBJECT (dialog), "response",
332 G_CALLBACK (gtk_widget_destroy),
334 gtk_widget_show(dialog);
338 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
339 if (id==GTK_RESPONSE_CANCEL){
340 gtk_widget_destroy(GTK_WIDGET(dialog));
344 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
345 g_message("About url clicked");
346 linphone_gtk_open_browser(url);
349 void linphone_gtk_show_about(){
350 struct stat filestat;
351 const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
354 about=linphone_gtk_create_window("about");
355 gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
356 memset(&filestat,0,sizeof(filestat));
357 if (stat(license_file,&filestat)!=0){
358 license_file="COPYING";
359 stat(license_file,&filestat);
361 if (filestat.st_size>0){
362 char *license=g_malloc(filestat.st_size+1);
363 FILE *f=fopen(license_file,"r");
364 if (f && fread(license,filestat.st_size,1,f)==1){
365 license[filestat.st_size]='\0';
366 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
370 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LINPHONE_VERSION);
371 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
372 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
373 gtk_widget_show(about);
376 static void set_video_window_decorations(GdkWindow *w){
377 const char *title=linphone_gtk_get_ui_config("title","Linphone");
378 const char *icon_path=linphone_gtk_get_ui_config("icon","linphone2.png");
379 char video_title[256];
380 GdkPixbuf *pbuf=create_pixbuf(icon_path);
381 if (!linphone_core_in_call(linphone_gtk_get_core())){
382 snprintf(video_title,sizeof(video_title),"%s video",title);
383 /* When not in call, treat the video as a normal window */
384 gdk_window_set_keep_above(w, FALSE);
386 LinphoneAddress *uri =
387 linphone_address_clone(linphone_core_get_remote_uri(linphone_gtk_get_core()));
390 linphone_address_clean(uri);
391 if (linphone_address_get_display_name(uri)!=NULL){
392 display_name=ms_strdup(linphone_address_get_display_name(uri));
394 display_name=linphone_address_as_string(uri);
396 snprintf(video_title,sizeof(video_title),_("Call with %s"),display_name);
397 linphone_address_destroy(uri);
398 ms_free(display_name);
400 /* During calls, bring up the video window, arrange so that
401 it is above all the other windows */
402 gdk_window_deiconify(w);
403 gdk_window_set_keep_above(w,TRUE);
404 /* Maybe we should have the following, but then we want to
405 have a timer that turns it off after a little while. */
406 /* gdk_window_set_urgency_hint(w,TRUE); */
408 gdk_window_set_title(w,video_title);
409 /* Refrain the video window to be closed at all times. */
410 gdk_window_set_functions(w,
411 GDK_FUNC_RESIZE|GDK_FUNC_MOVE|
412 GDK_FUNC_MINIMIZE|GDK_FUNC_MAXIMIZE);
415 l=g_list_append(l,pbuf);
416 gdk_window_set_icon_list(w,l);
418 g_object_unref(G_OBJECT(pbuf));
422 static gboolean video_needs_update=FALSE;
424 static void update_video_title(){
425 video_needs_update=TRUE;
428 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
429 static gboolean first_time=TRUE;
431 static unsigned long previd=0;
432 static gboolean in_iterate=FALSE;
435 if (in_iterate) return TRUE;
437 linphone_core_iterate(lc);
439 /*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
440 linphone_gtk_show_directory_search();
444 id=linphone_core_get_native_video_window_id(lc);
445 if (id!=previd || video_needs_update){
449 ms_message("Updating window decorations");
451 w=gdk_window_foreign_new(id);
453 w=gdk_window_foreign_new((HANDLE)id);
456 set_video_window_decorations(w);
457 g_object_unref(G_OBJECT(w));
459 else ms_error("gdk_window_foreign_new() failed");
460 if (video_needs_update) video_needs_update=FALSE;
463 if (addr_to_call!=NULL){
464 /*make sure we are not showing the login screen*/
465 GtkWidget *mw=linphone_gtk_get_main_window();
466 GtkWidget *login_frame=linphone_gtk_get_widget(mw,"login_frame");
467 if (!GTK_WIDGET_VISIBLE(login_frame)){
468 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
469 gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
471 linphone_gtk_start_call(uri_bar);
478 static void load_uri_history(){
479 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
482 GtkEntryCompletion *gep=gtk_entry_completion_new();
483 GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
486 snprintf(key,sizeof(key),"uri%i",i);
487 uri=linphone_gtk_get_ui_config(key,NULL);
490 gtk_list_store_append(model,&iter);
491 gtk_list_store_set(model,&iter,0,uri,-1);
492 if (i==0) gtk_entry_set_text(uribar,uri);
496 gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
497 gtk_entry_completion_set_text_column(gep,0);
498 gtk_entry_set_completion(uribar,gep);
501 static void save_uri_history(){
502 LinphoneCore *lc=linphone_gtk_get_core();
503 LpConfig *cfg=linphone_core_get_config(lc);
504 GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
509 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
511 if (!gtk_tree_model_get_iter_first(model,&iter)) return;
513 gtk_tree_model_get(model,&iter,0,&uri,-1);
515 snprintf(key,sizeof(key),"uri%i",i);
516 lp_config_set_string(cfg,"GtkUi",key,uri);
521 }while(gtk_tree_model_iter_next(model,&iter));
525 static void completion_add_text(GtkEntry *entry, const char *text){
527 GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
529 if (gtk_tree_model_get_iter_first(model,&iter)){
532 gtk_tree_model_get(model,&iter,0,&uri,-1);
534 if (strcmp(uri,text)==0) {
536 gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
542 }while (gtk_tree_model_iter_next(model,&iter));
544 /* and prepend it on top of the list */
545 gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
546 gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
550 void linphone_gtk_call_terminated(const char *error){
551 GtkWidget *mw=linphone_gtk_get_main_window();
553 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE);
554 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
555 linphone_gtk_enable_mute_button(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(mw,"main_mute")),FALSE);
556 if (linphone_gtk_use_in_call_view())
557 linphone_gtk_in_call_view_terminate(error);
558 update_video_title();
559 icw=GTK_WIDGET(g_object_get_data(G_OBJECT(mw),"incoming_call"));
561 g_object_set_data(G_OBJECT(mw),"incoming_call",NULL);
562 gtk_widget_destroy(icw);
566 static gboolean in_call_timer(){
567 if (linphone_core_in_call(linphone_gtk_get_core())){
568 linphone_gtk_in_call_view_update_duration(
569 linphone_core_get_current_call_duration(linphone_gtk_get_core()));
575 static void linphone_gtk_call_started(GtkWidget *mw){
576 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
577 gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),TRUE);
578 update_video_title();
579 if (linphone_gtk_use_in_call_view())
580 g_timeout_add(250,(GSourceFunc)in_call_timer,NULL);
583 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
584 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
585 if (linphone_core_invite(linphone_gtk_get_core(),entered)==0) {
586 completion_add_text(GTK_ENTRY(uri_bar),entered);
588 linphone_gtk_call_terminated(NULL);
593 static void _linphone_gtk_accept_call(){
594 LinphoneCore *lc=linphone_gtk_get_core();
595 GtkWidget *mw=linphone_gtk_get_main_window();
596 GtkWidget *icw=GTK_WIDGET(g_object_get_data(G_OBJECT(mw),"incoming_call"));
598 g_object_set_data(G_OBJECT(mw),"incoming_call",NULL);
599 gtk_widget_destroy(icw);
602 linphone_core_accept_call(lc,NULL);
603 linphone_gtk_call_started(linphone_gtk_get_main_window());
604 if (linphone_gtk_use_in_call_view()){
605 linphone_gtk_in_call_view_set_in_call();
606 linphone_gtk_show_in_call_view();
608 linphone_gtk_enable_mute_button(
609 GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"main_mute"))
613 void linphone_gtk_start_call(GtkWidget *w){
614 LinphoneCore *lc=linphone_gtk_get_core();
615 if (linphone_core_inc_invite_pending(lc)){
617 _linphone_gtk_accept_call();
618 }else if (linphone_core_in_call(lc)) {
621 /*change into in-call mode, then do the work later as it might block a bit */
622 GtkWidget *mw=gtk_widget_get_toplevel(w);
623 GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
624 const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
625 linphone_gtk_call_started(mw);
626 if (linphone_gtk_use_in_call_view()){
627 linphone_gtk_in_call_view_set_calling(entered);
628 linphone_gtk_show_in_call_view();
630 g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
634 void linphone_gtk_uri_bar_activate(GtkWidget *w){
635 linphone_gtk_start_call(w);
639 void linphone_gtk_terminate_call(GtkWidget *button){
640 linphone_core_terminate_call(linphone_gtk_get_core(),NULL);
643 void linphone_gtk_decline_call(GtkWidget *button){
644 linphone_core_terminate_call(linphone_gtk_get_core(),NULL);
645 gtk_widget_destroy(gtk_widget_get_toplevel(button));
648 void linphone_gtk_accept_call(GtkWidget *button){
649 _linphone_gtk_accept_call();
652 static gboolean linphone_gtk_auto_answer(GtkWidget *incall_window){
653 linphone_gtk_accept_call(linphone_gtk_get_widget(incall_window,"accept_call"));
657 void linphone_gtk_set_audio_video(){
658 linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE);
659 linphone_core_enable_video_preview(linphone_gtk_get_core(),TRUE);
662 void linphone_gtk_set_audio_only(){
663 linphone_core_enable_video(linphone_gtk_get_core(),FALSE,FALSE);
664 linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
667 void linphone_gtk_enable_self_view(GtkWidget *w){
668 linphone_core_enable_self_view(linphone_gtk_get_core(),
669 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)));
672 void linphone_gtk_used_identity_changed(GtkWidget *w){
673 int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
674 char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
675 if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
676 linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
677 linphone_gtk_show_directory_search();
679 if (sel) g_free(sel);
682 static void linphone_gtk_show_main_window(){
683 GtkWidget *w=linphone_gtk_get_main_window();
684 LinphoneCore *lc=linphone_gtk_get_core();
685 linphone_core_enable_video_preview(lc,linphone_core_video_enabled(lc));
687 gtk_window_present(GTK_WINDOW(w));
690 static void linphone_gtk_show(LinphoneCore *lc){
691 linphone_gtk_show_main_window();
694 static void linphone_gtk_inv_recv(LinphoneCore *lc, const char *from){
695 GtkWidget *w=linphone_gtk_create_window("incoming_call");
700 g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer,w);
703 gtk_window_set_transient_for(GTK_WINDOW(w),GTK_WINDOW(linphone_gtk_get_main_window()));
704 gtk_window_set_position(GTK_WINDOW(w),GTK_WIN_POS_CENTER_ON_PARENT);
706 label=linphone_gtk_get_widget(w,"message");
707 msg=g_strdup_printf(_("Incoming call from %s"),from);
708 gtk_label_set_text(GTK_LABEL(label),msg);
709 gtk_window_set_title(GTK_WINDOW(w),msg);
711 gtk_window_present(GTK_WINDOW(w));
712 /*gtk_window_set_urgency_hint(GTK_WINDOW(w),TRUE);*/
714 g_object_set_data(G_OBJECT(linphone_gtk_get_main_window()),"incoming_call",w);
715 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar")),
719 static void linphone_gtk_bye_recv(LinphoneCore *lc, const char *from){
723 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
724 linphone_gtk_show_friends();
727 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
729 case GTK_RESPONSE_YES:
730 linphone_gtk_show_contact(lf);
733 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
735 gtk_widget_destroy(dialog);
738 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
741 if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
742 linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
746 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);
747 dialog = gtk_message_dialog_new (
748 GTK_WINDOW(linphone_gtk_get_main_window()),
749 GTK_DIALOG_DESTROY_WITH_PARENT,
750 GTK_MESSAGE_QUESTION,
755 g_signal_connect(G_OBJECT (dialog), "response",
756 G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
757 /* actually show the box */
758 gtk_widget_show(dialog);
761 typedef struct _AuthTimeout{
766 static void auth_timeout_clean(AuthTimeout *tout){
770 static gboolean auth_timeout_destroy(AuthTimeout *tout){
772 g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
773 gtk_widget_destroy(tout->w);
779 static AuthTimeout * auth_timeout_new(GtkWidget *w){
780 AuthTimeout *tout=g_new(AuthTimeout,1);
782 /*so that the timeout no more references the widget when it is destroyed:*/
783 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
784 /*so that the widget is automatically destroyed after some time */
785 g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
789 void linphone_gtk_password_cancel(GtkWidget *w){
790 LinphoneAuthInfo *info;
791 GtkWidget *window=gtk_widget_get_toplevel(w);
792 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
793 linphone_core_abort_authentication(linphone_gtk_get_core(),info);
794 gtk_widget_destroy(window);
797 void linphone_gtk_password_ok(GtkWidget *w){
799 GtkWidget *window=gtk_widget_get_toplevel(w);
800 LinphoneAuthInfo *info;
801 info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
802 g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
803 entry=linphone_gtk_get_widget(window,"password_entry");
804 linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
805 linphone_auth_info_set_userid(info,
806 gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
807 linphone_core_add_auth_info(linphone_gtk_get_core(),info);
808 gtk_widget_destroy(window);
811 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username){
812 GtkWidget *w=linphone_gtk_create_window("password");
813 GtkWidget *label=linphone_gtk_get_widget(w,"message");
814 LinphoneAuthInfo *info;
816 GtkWidget *mw=linphone_gtk_get_main_window();
818 if (mw && GTK_WIDGET_VISIBLE(linphone_gtk_get_widget(mw,"login_frame"))){
819 /*don't prompt for authentication when login frame is visible*/
820 linphone_core_abort_authentication(lc,NULL);
824 msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at domain <i>%s</i>:"),
826 gtk_label_set_markup(GTK_LABEL(label),msg);
828 gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
829 info=linphone_auth_info_new(username, NULL, NULL, NULL,realm);
830 g_object_set_data(G_OBJECT(w),"auth_info",info);
831 g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
836 static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
837 GtkWidget *w=linphone_gtk_get_main_window();
838 GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
839 gtk_statusbar_push(GTK_STATUSBAR(status_bar),
840 gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
844 static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
845 linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
848 static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
849 linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
852 static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
854 snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
855 linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
858 static void linphone_gtk_display_question(LinphoneCore *lc, const char *question){
859 linphone_gtk_display_something(GTK_MESSAGE_QUESTION,question);
862 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
863 GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
864 if (w) linphone_gtk_call_log_update(w);
867 static void linphone_gtk_general_state(LinphoneCore *lc, LinphoneGeneralState *gstate){
868 switch(gstate->new_state){
869 case GSTATE_CALL_OUT_CONNECTED:
870 case GSTATE_CALL_IN_CONNECTED:
871 if (linphone_gtk_use_in_call_view())
872 linphone_gtk_in_call_view_set_in_call();
873 linphone_gtk_enable_mute_button(
874 GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"main_mute")),
877 case GSTATE_CALL_ERROR:
878 linphone_gtk_call_terminated(gstate->message);
880 case GSTATE_CALL_END:
881 linphone_gtk_call_terminated(NULL);
888 static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){
889 GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu");
890 gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
893 void linphone_gtk_open_browser(const char *url){
894 /*in gtk 2.16, gtk_show_uri does not work...*/
896 #if GTK_CHECK_VERSION(2,18,3)
897 gtk_show_uri(NULL,url,GDK_CURRENT_TIME,NULL);
900 snprintf(cl,sizeof(cl),"/usr/bin/x-www-browser %s",url);
901 g_spawn_command_line_async(cl,NULL);
904 ShellExecute(0,"open",url,NULL,NULL,1);
908 void linphone_gtk_link_to_website(GtkWidget *item){
909 const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
910 linphone_gtk_open_browser(home);
913 static GtkWidget *create_icon_menu(){
914 GtkWidget *menu=gtk_menu_new();
915 GtkWidget *menu_item;
918 const gchar *homesite;
920 homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
921 menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
922 tmp=g_strdup(homesite);
923 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
924 g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
926 image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
927 gtk_widget_show(image);
928 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
929 //g_object_unref(G_OBJECT(image));
930 gtk_widget_show(menu_item);
931 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
932 g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
934 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
935 gtk_widget_show(menu_item);
936 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
937 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
938 menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
939 gtk_widget_show(menu_item);
940 gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
941 g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
942 gtk_widget_show(menu);
946 static GtkStatusIcon *icon=NULL;
948 static void linphone_gtk_init_status_icon(){
949 const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON);
950 GdkPixbuf *pbuf=create_pixbuf(icon_path);
951 GtkWidget *menu=create_icon_menu();
953 icon=gtk_status_icon_new_from_pixbuf(pbuf);
954 g_object_unref(G_OBJECT(pbuf));
955 g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)linphone_gtk_show_main_window,linphone_gtk_get_main_window());
956 g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL);
957 title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone"));
958 gtk_status_icon_set_tooltip(icon,title);
959 gtk_status_icon_set_visible(icon,TRUE);
960 g_object_set_data(G_OBJECT(icon),"menu",menu);
961 g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu);
964 void linphone_gtk_load_identities(void){
966 GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
968 LinphoneProxyConfig *def=NULL;
972 store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
973 gtk_list_store_clear(store);
975 linphone_core_get_default_proxy(linphone_gtk_get_core(),&def);
976 def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
977 gtk_combo_box_append_text(box,def_identity);
978 g_free(def_identity);
979 for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
981 elem=ms_list_next(elem),i++){
982 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
983 gtk_combo_box_append_text(box,linphone_proxy_config_get_identity(cfg));
988 gtk_combo_box_set_active(box,def_index);
991 static void linphone_gtk_dtmf_clicked(GtkButton *button){
992 const char *label=gtk_button_get_label(button);
993 if (linphone_core_in_call(linphone_gtk_get_core())){
994 linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]);
996 GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar");
998 gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1002 static void linphone_gtk_connect_digits(void){
1003 GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table"));
1004 GList *children=gtk_container_get_children(cont);
1006 for(elem=children;elem!=NULL;elem=elem->next){
1007 GtkButton *button=GTK_BUTTON(elem->data);
1008 g_signal_connect(G_OBJECT(button),"clicked",(GCallback)linphone_gtk_dtmf_clicked,NULL);
1012 static void linphone_gtk_check_menu_items(void){
1013 bool_t audio_only=!linphone_core_video_enabled(linphone_gtk_get_core());
1014 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1015 linphone_gtk_get_main_window(),
1016 audio_only ? "audio_only_item" : "video_item")), TRUE);
1019 static gboolean linphone_gtk_can_manage_accounts(){
1020 LinphoneCore *lc=linphone_gtk_get_core();
1022 for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1023 SipSetup *ss=(SipSetup*)elem->data;
1024 if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1031 static void linphone_gtk_configure_main_window(){
1032 static gboolean config_loaded=FALSE;
1033 static const char *title;
1034 static const char *home;
1035 static const char *start_call_icon;
1036 static const char *stop_call_icon;
1037 static const char *search_icon;
1038 static gboolean update_check_menu;
1039 GtkWidget *w=linphone_gtk_get_main_window();
1040 if (!config_loaded){
1041 title=linphone_gtk_get_ui_config("title","Linphone");
1042 home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1043 start_call_icon=linphone_gtk_get_ui_config("start_call_icon","green.png");
1044 stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","red.png");
1045 search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1046 update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1049 linphone_gtk_configure_window(w,"main_window");
1051 gtk_window_set_title(GTK_WINDOW(w),title);
1052 #if GTK_CHECK_VERSION(2,16,0)
1053 gtk_menu_item_set_label(GTK_MENU_ITEM(linphone_gtk_get_widget(w,"main_menu")),title);
1056 if (start_call_icon){
1057 GdkPixbuf *pbuf=create_pixbuf(start_call_icon);
1058 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"start_call_icon")),pbuf);
1059 g_object_unref(G_OBJECT(pbuf));
1061 if (stop_call_icon){
1062 GdkPixbuf *pbuf=create_pixbuf(stop_call_icon);
1063 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"terminate_call_icon")),pbuf);
1064 g_object_unref(G_OBJECT(pbuf));
1067 GdkPixbuf *pbuf=create_pixbuf(search_icon);
1068 gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1069 g_object_unref(G_OBJECT(pbuf));
1073 GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1075 g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1077 if (!linphone_gtk_can_manage_accounts())
1078 gtk_widget_hide(linphone_gtk_get_widget(w,"run_assistant"));
1079 if (update_check_menu){
1080 gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck"));
1084 void linphone_gtk_manage_login(void){
1085 LinphoneCore *lc=linphone_gtk_get_core();
1086 LinphoneProxyConfig *cfg=NULL;
1087 linphone_core_get_default_proxy(lc,&cfg);
1089 SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1090 if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1091 linphone_gtk_show_login_frame(cfg);
1096 static void linphone_gtk_init_main_window(){
1097 GtkWidget *main_window;
1099 linphone_gtk_configure_main_window();
1100 linphone_gtk_manage_login();
1102 linphone_gtk_load_identities();
1103 linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1104 linphone_gtk_show_friends();
1105 linphone_gtk_connect_digits();
1106 linphone_gtk_check_menu_items();
1107 main_window=linphone_gtk_get_main_window();
1108 linphone_gtk_enable_mute_button(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(main_window,
1109 "main_mute")),FALSE);
1110 linphone_gtk_enable_mute_button(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(main_window,
1111 "incall_mute")),FALSE);
1112 if (!linphone_gtk_use_in_call_view()) {
1113 gtk_widget_show(linphone_gtk_get_widget(main_window, "main_mute"));
1115 if (linphone_core_in_call(linphone_gtk_get_core())) linphone_gtk_call_started(
1116 linphone_gtk_get_main_window());/*hide the call button, show terminate button*/
1119 void linphone_gtk_close(){
1120 /* couldn't find a way to prevent closing to destroy the main window*/
1121 LinphoneCore *lc=linphone_gtk_get_core();
1123 the_ui=linphone_gtk_create_window("main");
1124 linphone_gtk_init_main_window();
1125 /*shutdown call if any*/
1126 if (linphone_core_in_call(lc)){
1127 linphone_core_terminate_call(lc,NULL);
1128 linphone_gtk_call_terminated(NULL);
1130 linphone_core_enable_video_preview(lc,FALSE);
1133 void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
1135 const char *lname="undef";
1138 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1157 g_error("Bad level !");
1161 msg=g_strdup_vprintf(fmt,cap);
1164 msg=g_strdup_vprintf(fmt,args);
1166 fprintf(stdout,"linphone-%s : %s\n",lname,msg);
1169 linphone_gtk_log_push(lev,fmt,args);
1173 static void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
1174 GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
1175 linphone_gtk_get_main_window(), "uribar"));
1176 linphone_gtk_show_main_window();
1177 gtk_entry_set_text(uri_bar, refer_to);
1178 linphone_gtk_start_call(linphone_gtk_get_main_window());
1181 int main(int argc, char *argv[]){
1185 const char *config_file;
1186 const char *factory_config_file;
1189 g_thread_init(NULL);
1191 progpath = strdup(argv[0]);
1193 config_file=linphone_gtk_get_config_file();
1196 /*workaround for windows: sometimes LANG is defined to an integer value, not understood by gtk */
1197 if ((lang=getenv("LANG"))!=NULL){
1200 snprintf(tmp,sizeof(tmp),"LANG=",lang);
1206 if ((lang=linphone_gtk_get_lang(config_file))!=NULL && lang[0]!='\0'){
1209 snprintf(tmp,sizeof(tmp),"LANG=%s",lang);
1212 setenv("LANG",lang,1);
1217 p=bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
1218 if (p==NULL) perror("bindtextdomain failed");
1219 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1220 textdomain (GETTEXT_PACKAGE);
1222 g_message("NLS disabled.\n");
1225 gtk_rc_add_default_file("./gtkrc");
1227 gdk_threads_enter();
1229 if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
1230 linphone_options,NULL,NULL)){
1231 gdk_threads_leave();
1235 if (workingdir!=NULL)
1238 /* Now, look for the factory configuration file, we do it this late
1239 since we want to have had time to change directory and to parse
1240 the options, in case we needed to access the working directory */
1241 factory_config_file = linphone_gtk_get_factory_config_file();
1243 if (linphone_core_wake_up_possible_already_running_instance(
1244 config_file, addr_to_call) == 0){
1245 g_message("addr_to_call=%s",addr_to_call);
1246 g_warning("Another running instance of linphone has been detected. It has been woken-up.");
1247 g_warning("This instance is going to exit now.");
1248 gdk_threads_leave();
1252 add_pixmap_directory("pixmaps");
1253 add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone");
1255 the_ui=linphone_gtk_create_window("main");
1257 linphone_gtk_create_log_window();
1258 linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
1260 linphone_gtk_init_liblinphone(config_file, factory_config_file);
1261 /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
1262 gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
1263 gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL);
1264 linphone_gtk_init_main_window();
1265 linphone_gtk_init_status_icon();
1267 linphone_gtk_show_main_window();
1268 if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
1269 linphone_gtk_check_for_new_version();
1272 gdk_threads_leave();
1273 linphone_gtk_destroy_log_window();
1274 linphone_core_destroy(the_core);
1275 /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/
1276 gtk_status_icon_set_visible(icon,FALSE);