main.c 76.9 KB
Newer Older
aymeric's avatar
aymeric committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
linphone, gtk-glade interface.
Copyright (C) 2008  Simon MORLAT (simon.morlat@linphone.org)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/


21
#define VIDEOSELFVIEW_DEFAULT 0
22

23
#include "linphone.h"
aymeric's avatar
aymeric committed
24
#include "lpconfig.h"
25
#include "liblinphone_gitversion.h"
aymeric's avatar
aymeric committed
26

27

aymeric's avatar
aymeric committed
28 29
#include <sys/types.h>
#include <sys/stat.h>
30
#ifndef WIN32
aymeric's avatar
aymeric committed
31
#include <unistd.h>
32
#endif
aymeric's avatar
aymeric committed
33

jehan's avatar
jehan committed
34
#ifdef HAVE_GTK_OSX
35 36 37
#include <gtkosxapplication.h>
#endif

38 39
#ifdef WIN32
#define chdir _chdir
40
#include "direct.h"
Simon Morlat's avatar
Simon Morlat committed
41 42 43
#ifndef F_OK
#define F_OK 00 /*visual studio does not define F_OK*/
#endif
44 45
#endif

46
#if defined(HAVE_NOTIFY1) || defined(HAVE_NOTIFY4)
47 48 49
#define HAVE_NOTIFY
#endif

50 51 52 53
#ifdef HAVE_NOTIFY
#include <libnotify/notify.h>
#endif

54
#ifdef ENABLE_NLS
55 56
#include <locale.h>
#endif
57

58
#include "status_icon.h"
59

aymeric's avatar
aymeric committed
60

smorlat's avatar
smorlat committed
61 62
const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION;

aymeric's avatar
aymeric committed
63 64
static LinphoneCore *the_core=NULL;
static GtkWidget *the_ui=NULL;
65
static LinphoneLDAPContactProvider* ldap_provider = NULL;
aymeric's avatar
aymeric committed
66

67
static void linphone_gtk_global_state_changed(LinphoneCore *lc, LinphoneGlobalState state, const char*str);
Simon Morlat's avatar
Simon Morlat committed
68
static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg);
69
static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
aymeric's avatar
aymeric committed
70
static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
71
static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username, const char *domain);
aymeric's avatar
aymeric committed
72
static void linphone_gtk_display_status(LinphoneCore *lc, const char *status);
73
static void linphone_gtk_configuring_status(LinphoneCore *lc, LinphoneConfiguringState status, const char *message);
aymeric's avatar
aymeric committed
74 75 76 77
static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg);
static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning);
static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url);
static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
Simon Morlat's avatar
Simon Morlat committed
78
static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
79
static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token);
80
static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate);
81
static void linphone_gtk_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf);
Margaux Clerc's avatar
Margaux Clerc committed
82
void linphone_gtk_save_main_window_position(GtkWindow* mw, GdkEvent *event, gpointer data);
83
static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
84
void linphone_gtk_status_icon_set_blinking(gboolean val);
85
void _linphone_gtk_enable_video(gboolean val);
86
void linphone_gtk_on_uribar_changed(GtkEditable *uribar, gpointer user_data);
87
static void linphone_gtk_init_ui(void);
88
static void linphone_gtk_quit(void);
aymeric's avatar
aymeric committed
89

jehan's avatar
jehan committed
90
#ifndef HAVE_GTK_OSX
Margaux Clerc's avatar
Margaux Clerc committed
91 92
static gint main_window_x=0;
static gint main_window_y=0;
jehan's avatar
jehan committed
93
#endif
aymeric's avatar
aymeric committed
94
static gboolean verbose=0;
95
static gboolean quit_done=FALSE;
96
static gchar * addr_to_call = NULL;
Margaux Clerc's avatar
Margaux Clerc committed
97
static int start_option = START_LINPHONE;
98
static gboolean no_video=FALSE;
smorlat's avatar
smorlat committed
99
static gboolean iconified=FALSE;
Margaux Clerc's avatar
Margaux Clerc committed
100
static gboolean run_audio_assistant=FALSE;
101
static gboolean version=FALSE;
102
static gboolean selftest=FALSE;
103
static gchar *workingdir=NULL;
104
static char *progpath=NULL;
105
gchar *linphone_logfile=NULL;
106
static gboolean workaround_gtk_entry_chinese_bug=FALSE;
107
static gchar *custom_config_file=NULL;
108
static gboolean restart=FALSE;
109
static GtkWidget *config_fetching_dialog=NULL;
smorlat's avatar
smorlat committed
110

111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
#if _MSC_VER

#define LINPHONE_OPTION(optlname, optsname, optarg, optargdata, optdesc) \
{ \
	optlname, \
	optsname, \
	0, \
	optarg, \
	optargdata, \
	optdesc, \
	NULL \
}

#else

#define LINPHONE_OPTION(optlname, optsname, optarg, optargdata, optdesc) \
{ \
	.long_name = optlname, \
	.short_name = optsname, \
	.arg = optarg, \
	.arg_data = optargdata, \
	.description = optdesc, \
}

#endif

137
static GOptionEntry linphone_options[]={
138
	LINPHONE_OPTION("verbose",             '\0', G_OPTION_ARG_NONE,     (gpointer)&verbose,              N_("log to stdout some debug information while running.")),
139
	LINPHONE_OPTION("version",             '\0', G_OPTION_ARG_NONE,     (gpointer)&version,              N_("display version and exit.")),
140 141 142 143 144 145 146 147
	LINPHONE_OPTION("logfile",             'l',  G_OPTION_ARG_STRING,   &linphone_logfile,               N_("path to a file to write logs into.")),
	LINPHONE_OPTION("no-video",            '\0', G_OPTION_ARG_NONE,     (gpointer)&no_video,             N_("Start linphone with video disabled.")),
	LINPHONE_OPTION("iconified",           '\0', G_OPTION_ARG_NONE,     (gpointer)&iconified,            N_("Start only in the system tray, do not show the main interface.")),
	LINPHONE_OPTION("call",                'c',  G_OPTION_ARG_STRING,   &addr_to_call,                   N_("address to call right now")),
	LINPHONE_OPTION("workdir",             '\0', G_OPTION_ARG_STRING,   (gpointer) & workingdir,         N_("Specifiy a working directory (should be the base of the installation, eg: c:\\Program Files\\Linphone)")),
	LINPHONE_OPTION("config",              '\0', G_OPTION_ARG_FILENAME, (gpointer) &custom_config_file,  N_("Configuration file")),
	LINPHONE_OPTION("run-audio-assistant", '\0', G_OPTION_ARG_NONE,     (gpointer) &run_audio_assistant, N_("Run the audio assistant")),
	LINPHONE_OPTION("selftest",            '\0', G_OPTION_ARG_NONE,     (gpointer) &selftest,            N_("Run self test and exit 0 if succeed")),
148
	{0}
aymeric's avatar
aymeric committed
149 150 151
};

#define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone"
152
#define RELATIVE_XML_DIR
153
#define BUILD_TREE_XML_DIR "gtk"
154 155

#ifndef WIN32
aymeric's avatar
aymeric committed
156
#define CONFIG_FILE ".linphonerc"
157
#define SECRETS_FILE ".linphone-zidcache"
johan's avatar
johan committed
158
#define CERTIFICATES_PATH ".linphone-usr-crt"
159 160
#else
#define CONFIG_FILE "linphonerc"
161
#define SECRETS_FILE "linphone-zidcache"
johan's avatar
johan committed
162
#define CERTIFICATES_PATH "linphone-usr-crt"
163 164
#endif

165 166 167 168
char *linphone_gtk_get_config_file(const char *filename){
	const int path_max=1024;
	char *config_file=g_malloc0(path_max);
	if (filename==NULL) filename=CONFIG_FILE;
169
	if (g_path_is_absolute(filename)) {
170 171
		snprintf(config_file,path_max,"%s",filename);
	} else{
aymeric's avatar
aymeric committed
172 173 174
#ifdef WIN32
		const char *appdata=getenv("APPDATA");
		if (appdata){
175 176 177
			snprintf(config_file,path_max,"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
			CreateDirectory(config_file,NULL);
			snprintf(config_file,path_max,"%s\\%s\\%s",appdata,LINPHONE_CONFIG_DIR,filename);
aymeric's avatar
aymeric committed
178 179
		}
#else
180 181
		const char *home=getenv("HOME");
		if (home==NULL) home=".";
182
		snprintf(config_file,path_max,"%s/%s",home,filename);
aymeric's avatar
aymeric committed
183 184
#endif
	}
185
	return config_file;
smorlat's avatar
smorlat committed
186 187
}

188 189
#define FACTORY_CONFIG_FILE "linphonerc.factory"
static char _factory_config_file[1024];
190
static const char *linphone_gtk_get_factory_config_file(void){
191
	char* path = NULL;
192 193
	/*try accessing a local file first if exists*/
	if (access(FACTORY_CONFIG_FILE,F_OK)==0){
194
		path = ms_strdup(FACTORY_CONFIG_FILE);
195 196
	} else {
		char *progdir;
197

198 199 200 201 202 203 204 205
		if (progpath != NULL) {
			char *basename;
			progdir = strdup(progpath);
#ifdef WIN32
			basename = strrchr(progdir, '\\');
			if (basename != NULL) {
				basename ++;
				*basename = '\0';
206 207 208
				path = ms_strdup_printf("%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
			} else if (workingdir!=NULL) {
				path = ms_strdup_printf("%s\\%s", workingdir, FACTORY_CONFIG_FILE);
209 210 211 212 213 214
			}
#else
			basename = strrchr(progdir, '/');
			if (basename != NULL) {
				basename ++;
				*basename = '\0';
215
				path = ms_strdup_printf("%s/../share/linphone/%s", progdir, FACTORY_CONFIG_FILE);
216 217 218 219 220
			}
#endif
			free(progdir);
		}
	}
221 222 223 224 225 226 227 228 229 230
	if (path) {
		//use factory file only if it exists
		if (access(path,F_OK)==0){
			snprintf(_factory_config_file, sizeof(_factory_config_file), "%s", path);
			ms_free(path);
			return _factory_config_file;
		}
		ms_free(path);
	}
	return NULL;
231 232
}

233
LinphoneLDAPContactProvider* linphone_gtk_get_ldap(void){
234 235 236
	return ldap_provider;
}

237 238 239 240
int linphone_gtk_is_ldap_supported(void){
	return linphone_ldap_contact_provider_available();
}

241 242 243
void linphone_gtk_set_ldap(LinphoneLDAPContactProvider* ldap)
{
	if( ldap_provider )
244
		linphone_contact_provider_unref(ldap_provider);
245

246
	ldap_provider = ldap ? linphone_ldap_contact_provider_ref( ldap )
247
						 : NULL;
248 249
}

250 251 252 253
void linphone_gtk_schedule_restart(void){
	restart=TRUE;
}

254 255 256 257
gboolean linphone_gtk_get_audio_assistant_option(void){
	return run_audio_assistant;
}

258
static void linphone_gtk_init_liblinphone(const char *config_file,
259
		const char *factory_config_file, const char *chat_messages_db_file, const char *call_logs_db_file) {
Simon Morlat's avatar
Simon Morlat committed
260
	LinphoneCoreVTable vtable={0};
261
	gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
johan's avatar
johan committed
262
	gchar *user_certificates_dir=linphone_gtk_get_config_file(CERTIFICATES_PATH);
Simon Morlat's avatar
Simon Morlat committed
263

264
	vtable.global_state_changed=linphone_gtk_global_state_changed;
Simon Morlat's avatar
Simon Morlat committed
265
	vtable.call_state_changed=linphone_gtk_call_state_changed;
Simon Morlat's avatar
Simon Morlat committed
266
	vtable.registration_state_changed=linphone_gtk_registration_state_changed;
267 268
	vtable.notify_presence_received=linphone_gtk_notify_recv;
	vtable.new_subscription_requested=linphone_gtk_new_unknown_subscriber;
Simon Morlat's avatar
Simon Morlat committed
269 270 271 272 273 274
	vtable.auth_info_requested=linphone_gtk_auth_info_requested;
	vtable.display_status=linphone_gtk_display_status;
	vtable.display_message=linphone_gtk_display_message;
	vtable.display_warning=linphone_gtk_display_warning;
	vtable.display_url=linphone_gtk_display_url;
	vtable.call_log_updated=linphone_gtk_call_log_updated;
275 276
	//vtable.text_received=linphone_gtk_text_received;
	vtable.message_received=linphone_gtk_text_received;
277
	vtable.is_composing_received=linphone_gtk_is_composing_received;
Simon Morlat's avatar
Simon Morlat committed
278 279
	vtable.refer_received=linphone_gtk_refer_received;
	vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
280
	vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
281
	vtable.transfer_state_changed=linphone_gtk_transfer_state_changed;
282
	vtable.dtmf_received=linphone_gtk_dtmf_received;
283
	vtable.configuring_status=linphone_gtk_configuring_status;
Simon Morlat's avatar
Simon Morlat committed
284

285
	the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
286
	linphone_core_migrate_to_multi_transport(the_core);
287
	//lp_config_set_int(linphone_core_get_config(the_core), "sip", "store_auth_info", 0);
288 289 290 291 292


	if( lp_config_has_section(linphone_core_get_config(the_core),"ldap") ){
		LpConfig* cfg = linphone_core_get_config(the_core);
		LinphoneDictionary* ldap_cfg = lp_config_section_to_dict(cfg, "ldap");
293
		linphone_gtk_set_ldap( linphone_ldap_contact_provider_create(the_core, ldap_cfg) );
294 295
	}

296
	linphone_core_set_user_agent(the_core,"Linphone", LINPHONE_VERSION);
297
	linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
298 299
	linphone_core_set_zrtp_secrets_file(the_core,secrets_file);
	g_free(secrets_file);
johan's avatar
johan committed
300 301
	linphone_core_set_user_certificates_path(the_core,user_certificates_dir);
	g_free(user_certificates_dir);
302 303
	linphone_core_enable_video_capture(the_core, TRUE);
	linphone_core_enable_video_display(the_core, TRUE);
304
	linphone_core_set_native_video_window_id(the_core,LINPHONE_VIDEO_DISPLAY_NONE);/*don't create the window*/
305 306 307 308
	if (no_video) {
		_linphone_gtk_enable_video(FALSE);
		linphone_gtk_set_ui_config_int("videoselfview",0);
	}
309 310
	if (chat_messages_db_file) linphone_core_set_chat_database_path(the_core,chat_messages_db_file);
	if (call_logs_db_file) linphone_core_set_call_logs_database_path(the_core, call_logs_db_file);
aymeric's avatar
aymeric committed
311 312 313 314 315 316
}

LinphoneCore *linphone_gtk_get_core(void){
	return the_core;
}

317
GtkWidget *linphone_gtk_get_main_window(void){
aymeric's avatar
aymeric committed
318 319 320
	return the_ui;
}

321
void linphone_gtk_destroy_main_window(void) {
322
	linphone_gtk_destroy_window(the_ui);
323 324 325
	the_ui = NULL;
}

smorlat's avatar
smorlat committed
326
static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
smorlat's avatar
smorlat committed
327 328
	static const char *hiddens=NULL;
	static const char *shown=NULL;
329
	static bool_t config_loaded=FALSE;
smorlat's avatar
smorlat committed
330
	if (linphone_gtk_get_core()==NULL) return;
331 332
	if (config_loaded==FALSE){
		hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
smorlat's avatar
smorlat committed
333
		shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
334 335
		config_loaded=TRUE;
	}
336 337
	if (hiddens) linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
	if (shown) linphone_gtk_visibility_set(shown,window_name,w,TRUE);
smorlat's avatar
smorlat committed
338 339
}

340 341
static int get_ui_file(const char *name, char *path, int pathsize){
	snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
342
	if (access(path,F_OK)!=0){
343
		snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name);
344
		if (access(path,F_OK)!=0){
Simon Morlat's avatar
Simon Morlat committed
345
			g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,
346 347
				INSTALLED_XML_DIR,name);
			return -1;
348 349
		}
	}
350 351 352
	return 0;
}

353 354 355 356 357 358
void linphone_gtk_destroy_window(GtkWidget *widget) {
	GtkBuilder* builder = g_object_get_data(G_OBJECT(widget), "builder");
	gtk_widget_destroy(widget);
	g_object_unref (G_OBJECT (builder));
}

359
GtkWidget *linphone_gtk_create_widget(const char *widget_name) {
360 361 362 363
	char path[2048];
	GtkBuilder *builder = gtk_builder_new();
	GError *error = NULL;
	GObject *obj;
364

365
	if(get_ui_file(widget_name, path, sizeof(path)) == -1) goto fail;
366

367
	gtk_builder_set_translation_domain(builder, GETTEXT_PACKAGE);
368

369 370 371
	if(gtk_builder_add_from_file(builder, path, &error) == 0) {
		g_error("Couldn't load builder file: %s", error->message);
		g_error_free(error);
372
		goto fail;
373
	}
374

375 376 377
	obj = gtk_builder_get_object(builder, widget_name);
	if(obj == NULL) {
		g_error("'%s' widget not found", widget_name);
378
		goto fail;
379
	}
380 381
	g_object_set_data(G_OBJECT(obj), "builder", builder);
	g_signal_connect_data(G_OBJECT(obj),"destroy",(GCallback)g_object_unref,builder, NULL, G_CONNECT_AFTER|G_CONNECT_SWAPPED);
382
	gtk_builder_connect_signals(builder, obj);
383

384
	return GTK_WIDGET(obj);
385

386 387 388 389 390 391 392 393 394 395 396 397 398 399
fail:
	g_object_unref(builder);
	return NULL;
}

GtkWidget *linphone_gtk_create_window(const char *window_name, GtkWidget *parent){
	GtkWidget *w = linphone_gtk_create_widget(window_name);
	if(w) {
		linphone_gtk_configure_window(w,window_name);
		if(parent) {
			gtk_window_set_transient_for(GTK_WINDOW(w), GTK_WINDOW(parent));
			gtk_window_set_position(GTK_WINDOW(w), GTK_WIN_POS_CENTER_ON_PARENT);
		}
	}
400 401 402
	return w;
}

403 404 405
static void entry_unmapped(GtkWidget *widget){
	ms_message("%s is unmapped, calling unrealize to workaround chinese bug.",G_OBJECT_TYPE_NAME(widget));
	gtk_widget_unrealize(widget);
406 407
}

aymeric's avatar
aymeric committed
408
GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
409
	GtkBuilder *builder;
410
	GObject *w;
411 412
	if (window==NULL) return NULL;
	builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
413 414 415 416 417
	if (builder==NULL){
		g_error("Fail to retrieve builder from window !");
		return NULL;
	}
	w=gtk_builder_get_object(builder,name);
aymeric's avatar
aymeric committed
418 419 420
	if (w==NULL){
		g_error("No widget named %s found in xml interface.",name);
	}
421
	if (workaround_gtk_entry_chinese_bug){
422
		if (strcmp(G_OBJECT_TYPE_NAME(w),"GtkEntry")==0 || strcmp(G_OBJECT_TYPE_NAME(w),"GtkTextView")==0){
423 424
			if (g_object_get_data(G_OBJECT(w),"entry_bug_workaround")==NULL){
				g_object_set_data(G_OBJECT(w),"entry_bug_workaround",GINT_TO_POINTER(1));
425
				ms_message("%s is a %s",name,G_OBJECT_TYPE_NAME(w));
426 427 428 429
				g_signal_connect(G_OBJECT(w),"unmap",(GCallback)entry_unmapped,NULL);
			}
		}
	}
aymeric's avatar
aymeric committed
430 431 432 433 434 435 436
	return GTK_WIDGET(w);
}


void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
	GtkWidget *dialog;
	GtkWidget *main_window=linphone_gtk_get_main_window();
437

aymeric's avatar
aymeric committed
438 439 440 441 442 443
	gtk_widget_show(main_window);
	if (type==GTK_MESSAGE_QUESTION)
	{
		/* draw a question box. link to dialog_click callback */
		dialog = gtk_message_dialog_new (
				GTK_WINDOW(main_window),
444
								GTK_DIALOG_DESTROY_WITH_PARENT,
aymeric's avatar
aymeric committed
445
				GTK_MESSAGE_QUESTION,
446 447
								GTK_BUTTONS_YES_NO,
								"%s",
aymeric's avatar
aymeric committed
448 449 450 451
				(const gchar*)message);
		/* connect to some callback : REVISIT */
		/*
		g_signal_connect_swapped (G_OBJECT (dialog), "response",
452 453
						   G_CALLBACK (dialog_click),
						   G_OBJECT (dialog));
aymeric's avatar
aymeric committed
454 455 456 457 458 459 460
		*/
		/* actually show the box */
		gtk_widget_show(dialog);
	}
	else
	{
		dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
461 462 463 464 465
								  GTK_DIALOG_DESTROY_WITH_PARENT,
								  type,
								  GTK_BUTTONS_CLOSE,
								  "%s",
								  (const gchar*)message);
aymeric's avatar
aymeric committed
466 467
		/* Destroy the dialog when the user responds to it (e.g. clicks a button) */
		g_signal_connect_swapped (G_OBJECT (dialog), "response",
468 469
						   G_CALLBACK (gtk_widget_destroy),
						   G_OBJECT (dialog));
aymeric's avatar
aymeric committed
470 471 472 473 474 475 476 477 478 479
		gtk_widget_show(dialog);
	}
}

void linphone_gtk_about_response(GtkDialog *dialog, gint id){
	if (id==GTK_RESPONSE_CANCEL){
		gtk_widget_destroy(GTK_WIDGET(dialog));
	}
}

480 481 482 483 484
static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
	g_message("About url clicked");
	linphone_gtk_open_browser(url);
}

485
void linphone_gtk_show_about(void){
aymeric's avatar
aymeric committed
486
	struct stat filestat;
487
	const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING";
aymeric's avatar
aymeric committed
488
	GtkWidget *about;
489
	const char *tmp;
490
	GdkPixbuf *logo=create_pixbuf(
491
		linphone_gtk_get_ui_config("logo","linphone-banner.png"));
492
	static const char *defcfg="defcfg";
493

494
	about=linphone_gtk_create_window("about", the_ui);
495

496
	gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
497

aymeric's avatar
aymeric committed
498 499 500 501 502 503 504 505
	memset(&filestat,0,sizeof(filestat));
	if (stat(license_file,&filestat)!=0){
		license_file="COPYING";
		stat(license_file,&filestat);
	}
	if (filestat.st_size>0){
		char *license=g_malloc(filestat.st_size+1);
		FILE *f=fopen(license_file,"r");
506
		if (f && fread(license,1,filestat.st_size,f)>0){
aymeric's avatar
aymeric committed
507 508 509 510 511
			license[filestat.st_size]='\0';
			gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
		}
		g_free(license);
	}
512
	gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),LIBLINPHONE_GIT_VERSION);
513 514
	gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
	gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
515 516 517 518
	if (logo) {
		gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about), logo);
		g_object_unref(logo);
	}
519 520 521 522 523 524 525 526 527 528 529 530 531
	tmp=linphone_gtk_get_ui_config("artists",defcfg);
	if (tmp!=defcfg){
		const char *tmp2[2];
		tmp2[0]=tmp;
		tmp2[1]=NULL;
		gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
	}
	tmp=linphone_gtk_get_ui_config("translators",defcfg);
	if (tmp!=defcfg)
		gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
	tmp=linphone_gtk_get_ui_config("comments",defcfg);
	if (tmp!=defcfg)
		gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
aymeric's avatar
aymeric committed
532 533 534
	gtk_widget_show(about);
}

535 536 537 538 539 540 541 542 543 544 545 546 547 548 549

static gboolean linphone_gtk_iterate(LinphoneCore *lc){
	static gboolean first_time=TRUE;
	static gboolean in_iterate=FALSE;

	/*avoid reentrancy*/
	if (in_iterate) return TRUE;
	in_iterate=TRUE;
	linphone_core_iterate(lc);
	if (first_time){
		/*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
		linphone_gtk_show_directory_search();
		first_time=FALSE;
	}

550
	if (addr_to_call!=NULL){
smorlat's avatar
smorlat committed
551 552
		/*make sure we are not showing the login screen*/
		GtkWidget *mw=linphone_gtk_get_main_window();
553
		if (g_object_get_data(G_OBJECT(mw), "login_frame") == NULL){
smorlat's avatar
smorlat committed
554 555 556 557 558
			GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
			gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
			addr_to_call=NULL;
			linphone_gtk_start_call(uri_bar);
		}
559
	}
smorlat's avatar
smorlat committed
560
	in_iterate=FALSE;
aymeric's avatar
aymeric committed
561 562 563
	return TRUE;
}

564 565 566 567 568
static gboolean uribar_completion_matchfunc(GtkEntryCompletion *completion, const gchar *key, GtkTreeIter *iter, gpointer user_data){
	char* address = NULL;
	gboolean ret  = FALSE;
	gtk_tree_model_get(gtk_entry_completion_get_model(completion),iter,0,&address,-1);

François Grisez's avatar
François Grisez committed
569 570 571
	if(address) {
		gchar *tmp = g_utf8_casefold(address,-1);
		if (strstr(tmp,key)) ret=TRUE;
572
		g_free(tmp);
573
		g_free(address);
François Grisez's avatar
François Grisez committed
574
	}
575

576 577 578
	return ret;
}

579
static void load_uri_history(void){
aymeric's avatar
aymeric committed
580 581 582 583
	GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
	char key[20];
	int i;
	GtkEntryCompletion *gep=gtk_entry_completion_new();
584
	GtkListStore *model=gtk_list_store_new(2,G_TYPE_STRING,G_TYPE_INT);
aymeric's avatar
aymeric committed
585 586 587
	for (i=0;;i++){
		const char *uri;
		snprintf(key,sizeof(key),"uri%i",i);
588
		uri=linphone_gtk_get_ui_config(key,NULL);
aymeric's avatar
aymeric committed
589 590 591
		if (uri!=NULL) {
			GtkTreeIter iter;
			gtk_list_store_append(model,&iter);
592
			gtk_list_store_set(model,&iter,0,uri,1,COMPLETION_HISTORY,-1);
aymeric's avatar
aymeric committed
593 594 595 596 597 598
			if (i==0) gtk_entry_set_text(uribar,uri);
		}
		else break;
	}
	gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
	gtk_entry_completion_set_text_column(gep,0);
599
	gtk_entry_completion_set_popup_completion(gep, TRUE);
600
	gtk_entry_completion_set_match_func(gep,uribar_completion_matchfunc, NULL, NULL);
601
	gtk_entry_completion_set_minimum_key_length(gep,3);
aymeric's avatar
aymeric committed
602
	gtk_entry_set_completion(uribar,gep);
603
	g_signal_connect (G_OBJECT (uribar), "changed", G_CALLBACK(linphone_gtk_on_uribar_changed), NULL);
aymeric's avatar
aymeric committed
604 605
}

606
static void save_uri_history(void){
aymeric's avatar
aymeric committed
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
	LinphoneCore *lc=linphone_gtk_get_core();
	LpConfig *cfg=linphone_core_get_config(lc);
	GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
	char key[20];
	int i=0;
	char *uri=NULL;
	GtkTreeIter iter;
	GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));

	if (!gtk_tree_model_get_iter_first(model,&iter)) return;
	do {
		gtk_tree_model_get(model,&iter,0,&uri,-1);
		if (uri) {
			snprintf(key,sizeof(key),"uri%i",i);
			lp_config_set_string(cfg,"GtkUi",key,uri);
			g_free(uri);
		}else break;
		i++;
		if (i>5) break;
	}while(gtk_tree_model_iter_next(model,&iter));
	lp_config_sync(cfg);
}

static void completion_add_text(GtkEntry *entry, const char *text){
	GtkTreeIter iter;
	GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
633 634

	if (gtk_tree_model_get_iter_first(model,&iter)){
aymeric's avatar
aymeric committed
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650
		do {
			gchar *uri=NULL;
			gtk_tree_model_get(model,&iter,0,&uri,-1);
			if (uri!=NULL){
				if (strcmp(uri,text)==0) {
					/*remove text */
					gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
					g_free(uri);
					break;
				}
				g_free(uri);
			}
		}while (gtk_tree_model_iter_next(model,&iter));
	}
	/* and prepend it on top of the list */
	gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
651
	gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,1,COMPLETION_HISTORY,-1);
aymeric's avatar
aymeric committed
652 653 654
	save_uri_history();
}

655 656 657 658 659 660 661
void on_contact_provider_search_results( LinphoneContactSearch* req, MSList* friends, void* data )
{
	GtkTreeIter    iter;
	GtkEntry*    uribar = GTK_ENTRY(data);
	GtkEntryCompletion* compl = gtk_entry_get_completion(uribar);
	GtkTreeModel* model = gtk_entry_completion_get_model(compl);
	GtkListStore*  list = GTK_LIST_STORE(model);
662
	LinphoneLDAPContactSearch* search = linphone_ldap_contact_search_cast(req);
663 664
	gboolean valid;

665
	// clear completion list from previous non-history entries
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
	valid = gtk_tree_model_get_iter_first(model,&iter);
	while(valid)
	{
		char* url;
		int type;
		gtk_tree_model_get(model,&iter, 0,&url, 1,&type, -1);

		if (type != COMPLETION_HISTORY) {
			valid = gtk_list_store_remove(list, &iter);
		} else {
			valid = gtk_tree_model_iter_next(model,&iter);
		}

		if( url ) g_free(url);
		if( !valid ) break;
	}

	// add new non-history related matches
	while( friends ){
		LinphoneFriend* lf = friends->data;
		if( lf ) {
			const LinphoneAddress* la = linphone_friend_get_address(lf);
			if( la ){
				char *addr = linphone_address_as_string(la);

				if( addr ){
					ms_message("[LDAP]Insert match: %s",  addr);
					gtk_list_store_insert_with_values(list, &iter, -1,
													  0, addr,
													  1, COMPLETION_LDAP, -1);
					ms_free(addr);
				}
			}
		}
		friends = friends->next;
	}
	gtk_entry_completion_complete(compl);
703 704 705
	// save the number of LDAP results to better decide if new results should be fetched when search predicate gets bigger
	gtk_object_set_data(GTK_OBJECT(uribar), "ldap_res_cout",
						GINT_TO_POINTER(
706
							linphone_ldap_contact_search_result_count(search)
707 708
							)
						);
709 710 711 712 713 714 715 716 717 718 719 720 721 722

	// Gtk bug? we need to emit a "changed" signal so that the completion appears if
	// the list of results was previously empty
	g_signal_handlers_block_by_func(uribar, linphone_gtk_on_uribar_changed, NULL);
	g_signal_emit_by_name(uribar, "changed");
	g_signal_handlers_unblock_by_func(uribar, linphone_gtk_on_uribar_changed, NULL);
}

struct CompletionTimeout {
	guint timeout_id;
};

static gboolean launch_contact_provider_search(void *userdata)
{
723
	LinphoneLDAPContactProvider* ldap = linphone_gtk_get_ldap();
724
	GtkWidget*      uribar = GTK_WIDGET(userdata);
725 726
	const gchar* predicate = gtk_entry_get_text(GTK_ENTRY(uribar));
	gchar* previous_search = gtk_object_get_data(GTK_OBJECT(uribar), "previous_search");
727
	unsigned int prev_res_count = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(uribar), "ldap_res_cout"));
728

729
	if( ldap && strlen(predicate) >= 3 ){ // don't search too small predicates
730
		unsigned int max_res_count = linphone_ldap_contact_provider_get_max_result(ldap);
731
		LinphoneContactSearch* search;
732 733 734 735 736 737
		if( previous_search  &&
			(strstr(predicate, previous_search) == predicate) && // last search contained results from this one
			(prev_res_count != max_res_count) ){ // and we didn't reach the max result limit

			ms_message("Don't launch search on already searched data (current: %s, old search: %s), (%d/%d results)",
					   predicate, previous_search, prev_res_count, max_res_count);
738 739 740 741 742 743
			return FALSE;
		}

		// save current search
		if( previous_search ) ms_free(previous_search);
		gtk_object_set_data(GTK_OBJECT(uribar), "previous_search", ms_strdup(predicate));
744

745
		ms_message("launch_contact_provider_search");
746
		search =linphone_contact_provider_begin_search(
747 748 749
					linphone_contact_provider_cast(ldap_provider),
					predicate, on_contact_provider_search_results, uribar
					);
750 751

		if(search)
752
			linphone_contact_search_ref(search);
753 754 755 756 757 758
	}
	return FALSE;
}

void linphone_gtk_on_uribar_changed(GtkEditable *uribar, gpointer user_data)
{
759 760 761 762
	if( linphone_gtk_get_ldap() ) {
		gchar* text = gtk_editable_get_chars(uribar, 0,-1);
		gint timeout = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(uribar), "complete_timeout"));
		if( text ) g_free(text);
763

764 765 766
		if( timeout != 0 ) {
			g_source_remove(timeout);
		}
767

768
		timeout = g_timeout_add_seconds(1,(GSourceFunc)launch_contact_provider_search, uribar);
769

770 771
		gtk_object_set_data(GTK_OBJECT(uribar),"complete_timeout", GINT_TO_POINTER(timeout) );
	}
772 773
}

774 775 776 777 778
bool_t linphone_gtk_video_enabled(void){
	const LinphoneVideoPolicy *vpol=linphone_core_get_video_policy(linphone_gtk_get_core());
	return vpol->automatically_accept && vpol->automatically_initiate;
}

779
void linphone_gtk_show_main_window(){
780
	GtkWidget *w=linphone_gtk_get_main_window();
781 782 783 784 785 786
#ifdef HAVE_GTK_OSX
	GtkWidget *icon = linphone_gtk_get_widget(w, "history_tab_icon");
	GtkWidget *label = linphone_gtk_get_widget(w, "history_tab_label");
	gtk_misc_set_alignment(GTK_MISC(icon), 0.5f, 0.25f);
	gtk_misc_set_alignment(GTK_MISC(label), 0.5f, 0.f);
#endif
787 788 789 790
	gtk_widget_show(w);
	gtk_window_present(GTK_WINDOW(w));
}

791
void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
smorlat's avatar
smorlat committed
792
	GtkWidget *mw=linphone_gtk_get_main_window();
793
	if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
794
		gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
795
	}
796 797
	if (linphone_gtk_use_in_call_view() && call)
		linphone_gtk_in_call_view_terminate(call,error);
smorlat's avatar
smorlat committed
798 799
}

800 801 802 803 804
static void linphone_gtk_update_call_buttons(LinphoneCall *call){
	LinphoneCore *lc=linphone_gtk_get_core();
	GtkWidget *mw=linphone_gtk_get_main_window();
	const MSList *calls=linphone_core_get_calls(lc);
	GtkWidget *button;
805
	bool_t add_call=(calls!=NULL);
806
	int call_list_size=ms_list_size(calls);
807
	GtkWidget *conf_frame;
808

809
	button=linphone_gtk_get_widget(mw,"start_call");
810
	gtk_widget_set_sensitive(button,TRUE);
811
	gtk_widget_set_visible(button,!add_call);
812

813
	button=linphone_gtk_get_widget(mw,"add_call");
814

815 816 817
	if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
		gtk_widget_set_sensitive(button,FALSE);
	} else {
818
		gtk_widget_set_sensitive(button,TRUE);
819
	}
820
	gtk_widget_set_visible(button,add_call);
821

822
	//gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
823
	conf_frame=(GtkWidget *)g_object_get_data(G_OBJECT(mw),"conf_frame");
824 825 826 827 828 829 830
	if(conf_frame==NULL){
		linphone_gtk_enable_transfer_button(lc,call_list_size>1);
		linphone_gtk_enable_conference_button(lc,call_list_size>1);
	} else {
		linphone_gtk_enable_transfer_button(lc,FALSE);
		linphone_gtk_enable_conference_button(lc,FALSE);
	}
Margaux Clerc's avatar
Margaux Clerc committed
831 832 833
	if (call) {
		linphone_gtk_update_video_button(call);
	}
smorlat's avatar
smorlat committed
834 835
}

836
gchar *linphone_gtk_get_record_path(const LinphoneAddress *address, gboolean is_conference){
Simon Morlat's avatar
Simon Morlat committed
837
	const char *dir=g_get_user_special_dir(G_USER_DIRECTORY_MUSIC);
838
	const char *id="unknown";
Simon Morlat's avatar
Simon Morlat committed
839
	char filename[256]={0};
840 841 842
	char date[64]={0};
	time_t curtime=time(NULL);
	struct tm loctime;
Simon Morlat's avatar
Simon Morlat committed
843 844 845
	const char **fmts=linphone_core_get_supported_file_formats(linphone_gtk_get_core());
	int i;
	const char *ext="wav";
846

847 848 849 850 851 852
#ifdef WIN32
	loctime=*localtime(&curtime);
#else
	localtime_r(&curtime,&loctime);
#endif
	snprintf(date,sizeof(date)-1,"%i%02i%02i-%02i%02i",loctime.tm_year+1900,loctime.tm_mon+1,loctime.tm_mday, loctime.tm_hour, loctime.tm_min);
853

Simon Morlat's avatar
Simon Morlat committed
854 855 856 857 858 859
	for (i=0;fmts[i]!=NULL;++i){
		if (strcmp(fmts[i],"mkv")==0){
			ext="mkv";
			break;
		}
	}
860

861 862 863 864 865
	if (address){
		id=linphone_address_get_username(address);
		if (id==NULL) id=linphone_address_get_domain(address);
	}
	if (is_conference){
Simon Morlat's avatar
Simon Morlat committed
866
		snprintf(filename,sizeof(filename)-1,"%s-conference-%s.%s",
867
			linphone_gtk_get_ui_config("title","Linphone"),
Simon Morlat's avatar
Simon Morlat committed
868
			date,ext);
869
	}else{
Simon Morlat's avatar
Simon Morlat committed
870
		snprintf(filename,sizeof(filename)-1,"%s-call-%s-%s.%s",
871 872
			linphone_gtk_get_ui_config("title","Linphone"),
			date,
Simon Morlat's avatar
Simon Morlat committed
873
			id,ext);
874
	}
875 876 877
	if (!dir) {
		ms_message ("No directory for music, using [%s] instead",dir=getenv("HOME"));
	}
Simon Morlat's avatar
Simon Morlat committed
878 879 880
	return g_build_filename(dir,filename,NULL);
}

smorlat's avatar
smorlat committed
881 882
static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
	const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
Simon Morlat's avatar
Simon Morlat committed
883 884
	LinphoneCore *lc=linphone_gtk_get_core();
	LinphoneAddress *addr=linphone_core_interpret_url(lc,entered);
885

Simon Morlat's avatar
Simon Morlat committed
886
	if (addr!=NULL){
887
		LinphoneCallParams *params=linphone_core_create_call_params(lc, NULL);
888
		gchar *record_file=linphone_gtk_get_record_path(addr,FALSE);
Simon Morlat's avatar
Simon Morlat committed
889 890
		linphone_call_params_set_record_file(params,record_file);
		linphone_core_invite_address_with_params(lc,addr,params);
smorlat's avatar
smorlat committed
891
		completion_add_text(GTK_ENTRY(uri_bar),entered);
Simon Morlat's avatar
Simon Morlat committed
892 893 894
		linphone_address_destroy(addr);
		linphone_call_params_destroy(params);
		g_free(record_file);
smorlat's avatar
smorlat committed
895
	}else{
896
		linphone_gtk_call_terminated(NULL,NULL);
smorlat's avatar
smorlat committed
897 898
	}
	return FALSE;
aymeric's avatar
aymeric committed
899 900
}

901 902 903

static void accept_incoming_call(LinphoneCall *call){
	LinphoneCore *lc=linphone_gtk_get_core();
904
	LinphoneCallParams *params = linphone_core_create_call_params(lc, call);
905 906 907 908 909 910
	gchar *record_file=linphone_gtk_get_record_path(linphone_call_get_remote_address(call),FALSE);
	linphone_call_params_set_record_file(params,record_file);
	linphone_core_accept_call_with_params(lc,call,params);
	linphone_call_params_destroy(params);
}

911
static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
912 913 914
	LinphoneCallState state=linphone_call_get_state(call);
	if (state==LinphoneCallIncomingReceived || state==LinphoneCallIncomingEarlyMedia){
		accept_incoming_call(call);
smorlat's avatar
smorlat committed
915
	}
916
	return FALSE;
smorlat's avatar
smorlat committed
917 918
}

919
void linphone_gtk_start_call(GtkWidget *w){
920
	LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
921 922 923
	/*change into in-call mode, then do the work later as it might block a bit */
	GtkWidget *mw=gtk_widget_get_toplevel(w);
	GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
924
	LinphoneCallState state= call ? linphone_call_get_state(call) : LinphoneCallIdle;
925

926 927
	if (state == LinphoneCallIncomingReceived || state == LinphoneCallIncomingEarlyMedia){
		accept_incoming_call(call);
aymeric's avatar
aymeric committed
928
	}else{
929 930 931
		/*immediately disable the button and delay a bit the execution the linphone_core_invite()
		so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
		gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
smorlat's avatar
smorlat committed
932
		g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
aymeric's avatar
aymeric committed
933
	}
934

aymeric's avatar
aymeric committed
935 936
}

937 938 939 940 941 942 943 944 945 946 947 948
void linphone_gtk_start_chat(GtkWidget *w){
	GtkWidget *mw=gtk_widget_get_toplevel(w);
	GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
	const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
	LinphoneCore *lc=linphone_gtk_get_core();
	LinphoneAddress *addr=linphone_core_interpret_url(lc,entered);
	if (addr) {
		linphone_gtk_friend_list_set_chat_conversation(addr);
		linphone_address_destroy(addr);
	}
}

949 950 951 952
void linphone_gtk_uri_bar_activate(GtkWidget *w){
	linphone_gtk_start_call(w);
}

aymeric's avatar
aymeric committed
953
void linphone_gtk_terminate_call(GtkWidget *button){
954 955 956
	gboolean is_conf;
	LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
	if (call){
957
		linphone_core_terminate_call(linphone_gtk_get_core(),call);
958 959 960
	}else if (is_conf){
		linphone_core_terminate_conference(linphone_gtk_get_core());
	}
aymeric's avatar
aymeric committed
961 962
}

963
void linphone_gtk_decline_clicked(GtkWidget *button){
964
	LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
965 966
	if (call)
		linphone_core_terminate_call(linphone_gtk_get_core(),call);
aymeric's avatar
aymeric committed
967 968
}

969
void linphone_gtk_answer_clicked(GtkWidget *button){
970
	LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
971
	if (call){
972
		accept_incoming_call(call);
973
		linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
974
	}
975 976
}

977
void _linphone_gtk_enable_video(gboolean val){
978 979
	LinphoneVideoPolicy policy={0};
	policy.automatically_initiate=policy.automatically_accept=val;
980 981
	linphone_core_enable_video_capture(linphone_gtk_get_core(), TRUE);
	linphone_core_enable_video_display(linphone_gtk_get_core(), TRUE);
982
	linphone_core_set_video_policy(linphone_gtk_get_core(),&policy);
aymeric's avatar
aymeric committed
983 984
}

985 986 987 988 989 990
void linphone_gtk_enable_video(GtkWidget *w){
	gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
	//GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
	_linphone_gtk_enable_video(val);
}

smorlat's avatar
smorlat committed
991
void linphone_gtk_enable_self_view(GtkWidget *w){
992 993 994 995
	gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
	LinphoneCore *lc=linphone_gtk_get_core();
	linphone_core_enable_self_view(lc,val);
	linphone_gtk_set_ui_config_int("videoselfview",val);
smorlat's avatar
smorlat committed
996 997
}

aymeric's avatar
aymeric committed
998 999 1000
void linphone_gtk_used_identity_changed(GtkWidget *w){
	int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
	char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
1001
	if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
aymeric's avatar
aymeric committed
1002
		linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
1003 1004
		linphone_gtk_show_directory_search();
	}
smorlat's avatar
smorlat committed
1005
	if (sel) g_free(sel);
aymeric's avatar
aymeric committed
1006 1007
}

1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
void on_proxy_refresh_button_clicked(GtkWidget *w){
	LinphoneCore *lc=linphone_gtk_get_core();
	MSList const *item=linphone_core_get_proxy_config_list(lc);
	while (item != NULL) {
		LinphoneProxyConfig *lpc=(LinphoneProxyConfig*)item->data;
		linphone_proxy_config_edit(lpc);
		linphone_proxy_config_done(lpc);
		item = item->next;
	}
}

1019
static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
1020
	linphone_gtk_show_friends();
aymeric's avatar
aymeric committed
1021 1022 1023 1024 1025
}

static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
	switch(response_id){
		case GTK_RESPONSE_YES:
1026
			linphone_gtk_show_contact(lf, the_ui);
aymeric's avatar
aymeric committed
1027 1028 1029 1030 1031 1032 1033 1034 1035
		break;
		default:
			linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
	}
	gtk_widget_destroy(dialog);
}

static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
	GtkWidget *dialog;
1036
	gchar *message;
1037 1038 1039 1040 1041 1042

	if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
		linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
		return;
	}

1043
	message=g_strdup_printf(_("%s would like to add you to his/her contact list.\nWould you add him/her to your contact list and allow him/her to see your presence status?\nIf you answer no, this person will be temporarily blacklisted."),url);
aymeric's avatar
aymeric committed
1044 1045
	dialog = gtk_message_dialog_new (
				GTK_WINDOW(linphone_gtk_get_main_window()),
1046
								GTK_DIALOG_DESTROY_WITH_PARENT,
aymeric's avatar
aymeric committed
1047
				GTK_MESSAGE_QUESTION,
1048 1049
								GTK_BUTTONS_YES_NO,
								"%s",
aymeric's avatar
aymeric committed
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100
				message);
	g_free(message);
	g_signal_connect(G_OBJECT (dialog), "response",
		G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
	/* actually show the box */
	gtk_widget_show(dialog);
}

typedef struct _AuthTimeout{
	GtkWidget *w;
} AuthTimeout;

static void auth_timeout_clean(AuthTimeout *tout){
	tout->w=NULL;
}

static gboolean auth_timeout_destroy(AuthTimeout *tout){
	if (tout->w)  {
		g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
		gtk_widget_destroy(tout->w);
	}
	g_free(tout);
	return FALSE;
}

static AuthTimeout * auth_timeout_new(GtkWidget *w){
	AuthTimeout *tout=g_new(AuthTimeout,1);
	tout->w=w;
	/*so that the timeout no more references the widget when it is destroyed:*/
	g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
	/*so that the widget is automatically destroyed after some time */
	g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
	return tout;
}

void linphone_gtk_password_cancel(GtkWidget *w){
	LinphoneAuthInfo *info;
	GtkWidget *window=gtk_widget_get_toplevel(w);
	info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
	linphone_core_abort_authentication(linphone_gtk_get_core(),info);
	gtk_widget_destroy(window);
}

void linphone_gtk_password_ok(GtkWidget *w){
	GtkWidget *entry;
	GtkWidget *window=gtk_widget_get_toplevel(w);
	LinphoneAuthInfo *info;
	info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
	g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
	entry=linphone_gtk_get_widget(window,"password_entry");
	linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
1101 1102
	linphone_auth_info_set_userid(info,
		gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
aymeric's avatar
aymeric committed
1103 1104 1105 1106
	linphone_core_add_auth_info(linphone_gtk_get_core(),info);
	gtk_widget_destroy(window);
}

1107
static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username, const char *domain){
1108
	GtkWidget *w=linphone_gtk_create_window("password", the_ui);
aymeric's avatar
aymeric committed
1109 1110 1111
	GtkWidget *label=linphone_gtk_get_widget(w,"message");
	LinphoneAuthInfo *info;
	gchar *msg;
1112
	GtkWidget *mw=linphone_gtk_get_main_window();
1113

1114
	if (mw && g_object_get_data(G_OBJECT(mw), "login_frame") != NULL){
1115 1116 1117 1118 1119
		/*don't prompt for authentication when login frame is visible*/
		linphone_core_abort_authentication(lc,NULL);
		return;
	}

1120
	msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at realm <i>%s</i>:"),
1121 1122
		username,realm);
	gtk_label_set_markup(GTK_LABEL(label),msg);
aymeric's avatar
aymeric committed
1123
	g_free(msg);
1124
	gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
1125
	info=linphone_auth_info_new(username, NULL, NULL, NULL,realm,domain);
aymeric's avatar
aymeric committed
1126 1127 1128 1129 1130 1131
	g_object_set_data(G_OBJECT(w),"auth_info",info);
	g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
	gtk_widget_show(w);
	auth_timeout_new(w);
}

1132 1133 1134 1135
static void linphone_gtk_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf){
	ms_message("Dtmf %c received.",dtmf);
}

aymeric's avatar
aymeric committed
1136 1137 1138
static void linphone_gtk_display_status(LinphoneCore *lc, const char *status){
	GtkWidget *w=linphone_gtk_get_main_window();
	GtkWidget *status_bar=linphone_gtk_get_widget(w,"status_bar");
1139

aymeric's avatar
aymeric committed
1140 1141 1142 1143 1144
	gtk_statusbar_push(GTK_STATUSBAR(status_bar),
			gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),""),
			status);
}

1145
static void linphone_gtk_configuring_status(LinphoneCore *lc, LinphoneConfiguringState status, const char *message) {
1146 1147
	if (config_fetching_dialog) linphone_gtk_close_config_fetching(config_fetching_dialog, status);
	config_fetching_dialog=NULL;
1148 1149
}

aymeric's avatar
aymeric committed
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg){
	linphone_gtk_display_something(GTK_MESSAGE_INFO,msg);
}

static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning){
	linphone_gtk_display_something(GTK_MESSAGE_WARNING,warning);
}

static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url){
	char richtext[4096];
	snprintf(richtext,sizeof(richtext),"%s %s",msg,url);
	linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext);
}

static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
	GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
	if (w) linphone_gtk_call_log_update(w);
1167
	linphone_gtk_call_log_update(linphone_gtk_get_main_window());
aymeric's avatar
aymeric committed
1168 1169
}

1170
#ifdef HAVE_NOTIFY
1171
static bool_t notify_actions_supported(void) {
1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187
	bool_t accepts_actions = FALSE;
	GList *capabilities = notify_get_server_caps();
	GList *c;
	if(capabilities != NULL) {
		for(c = capabilities; c != NULL; c = c->next) {
			if(strcmp((char*)c->data, "actions") == 0 ) {
				accepts_actions = TRUE;
				break;
			}
		}
		g_list_foreach(capabilities, (GFunc)g_free, NULL);
		g_list_free(capabilities);
	}
	return accepts_actions;
}

1188 1189
static NotifyNotification* build_notification(const char *title, const char *body) {
	NotifyNotification *n = notify_notification_new(title, body, NULL
1190
#ifdef HAVE_NOTIFY1
1191
		,NULL
1192 1193
#endif
	);
1194 1195
#ifndef HAVE_NOTIFY1
	{
1196 1197 1198 1199 1200 1201 1202 1203
		GError *error = NULL;
		const char *icon_name = linphone_gtk_get_ui_config("icon_name", LINPHONE_ICON_NAME);
		GdkPixbuf *pbuf = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), icon_name, 48, 0, &error);
		if(error) {
			g_warning("Could not load '%s' icon: %s", icon_name, error->message);
			g_error_free(error);
		}
		notify_notification_set_image_from_pixbuf(n, pbuf);
1204 1205
	}
#endif
1206
	return n;
1207 1208 1209
}

static void show_notification(NotifyNotification* n){
1210
	if (n && !notify_notification_show(n,NULL))
1211 1212 1213 1214 1215
		ms_error("Failed to send notification.");
}

static void make_notification(const char *title, const char *body){
	show_notification(build_notification(title,body));
1216 1217 1218 1219
}

#endif

1220
void linphone_gtk_notify(LinphoneCall *call, LinphoneChatMessage *chat_message, const char *msg){
1221 1222 1223 1224 1225 1226
#ifdef HAVE_NOTIFY
	if (!notify_is_initted())
		if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
#endif
	if (!call) {
#ifdef HAVE_NOTIFY
1227 1228 1229 1230 1231 1232
		if (chat_message) {
			const LinphoneAddress *address = linphone_chat_message_get_peer_address(chat_message);
			char *remote = linphone_address_as_string(address);
			make_notification(remote, linphone_chat_message_get_text(chat_message));
		} else {
			if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
1233
#ifdef HAVE_NOTIFY1