1 /*
2 linphone, gtk-glade interface.
3 Copyright (C) 2008  Simon MORLAT (simon.morlat@linphone.org)
4 
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.
9 
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.
14 
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 */
19 
20 
21 #define VIDEOSELFVIEW_DEFAULT 0
22 
23 #include "linphone.h"
24 #include "linphone/lpconfig.h"
25 #include "liblinphone_gitversion.h"
26 #include <bctoolbox/vfs.h>
27 #include <bctoolbox/defs.h>
28 
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #ifndef _WIN32
32 #include <unistd.h>
33 #endif
34 
35 #ifdef HAVE_GTK_OSX
36 #include <gtkosxapplication.h>
37 #endif
38 
39 #ifdef _WIN32
40 #include "direct.h"
41 #define chdir _chdir
42 #ifndef F_OK
43 #define F_OK 00 /*visual studio does not define F_OK*/
44 #endif
45 #endif
46 
47 #if defined(HAVE_NOTIFY1) || defined(HAVE_NOTIFY4)
48 #define HAVE_NOTIFY
49 #endif
50 
51 #ifdef HAVE_NOTIFY
52 #include <libnotify/notify.h>
53 #endif
54 
55 #ifdef ENABLE_NLS
56 #include <locale.h>
57 #endif
58 
59 #include "status_icon.h"
60 
61 
62 const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION;
63 
64 static LinphoneCore *the_core=NULL;
65 static GtkWidget *the_ui=NULL;
66 static LinphoneLDAPContactProvider* ldap_provider = NULL;
67 
68 static void linphone_gtk_global_state_changed(LinphoneCore *lc, LinphoneGlobalState state, const char*str);
69 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg);
70 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid);
71 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url);
72 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username, const char *domain);
73 static void linphone_gtk_configuring_status(LinphoneCore *lc, LinphoneConfiguringState status, const char *message);
74 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl);
75 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg);
76 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token);
77 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate);
78 static void linphone_gtk_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf);
79 void linphone_gtk_save_main_window_position(GtkWindow* mw, GdkEvent *event, gpointer data);
80 static gboolean linphone_gtk_auto_answer(LinphoneCall *call);
81 void linphone_gtk_status_icon_set_blinking(gboolean val);
82 void _linphone_gtk_enable_video(gboolean val);
83 void linphone_gtk_on_uribar_changed(GtkEditable *uribar, gpointer user_data);
84 static void linphone_gtk_init_ui(void);
85 static void linphone_gtk_quit(void);
86 
87 #ifndef HAVE_GTK_OSX
88 static gint main_window_x=0;
89 static gint main_window_y=0;
90 #endif
91 static gboolean verbose=0;
92 static gboolean quit_done=FALSE;
93 static gchar * addr_to_call = NULL;
94 static int start_option = START_LINPHONE;
95 static gboolean no_video=FALSE;
96 static gboolean iconified=FALSE;
97 static gboolean run_audio_assistant=FALSE;
98 static gboolean version=FALSE;
99 static gboolean selftest=FALSE;
100 static gchar *workingdir=NULL;
101 static char *progpath=NULL;
102 gchar *linphone_logfile=NULL;
103 static gboolean workaround_gtk_entry_chinese_bug=FALSE;
104 static gchar *custom_config_file=NULL;
105 static gboolean restart=FALSE;
106 static GtkWidget *config_fetching_dialog=NULL;
107 
108 #if _MSC_VER
109 
110 #define LINPHONE_OPTION(optlname, optsname, optarg, optargdata, optdesc) \
111 { \
112 	optlname, \
113 	optsname, \
114 	0, \
115 	optarg, \
116 	optargdata, \
117 	optdesc, \
118 	NULL \
119 }
120 
121 #else
122 
123 #define LINPHONE_OPTION(optlname, optsname, optarg, optargdata, optdesc) \
124 { \
125 	.long_name = optlname, \
126 	.short_name = optsname, \
127 	.arg = optarg, \
128 	.arg_data = optargdata, \
129 	.description = optdesc, \
130 }
131 
132 #endif
133 
134 static GOptionEntry linphone_options[]={
135 	LINPHONE_OPTION("verbose",             '\0', G_OPTION_ARG_NONE,     (gpointer)&verbose,              N_("log to stdout some debug information while running.")),
136 	LINPHONE_OPTION("version",             '\0', G_OPTION_ARG_NONE,     (gpointer)&version,              N_("display version and exit.")),
137 	LINPHONE_OPTION("logfile",             'l',  G_OPTION_ARG_STRING,   &linphone_logfile,               N_("path to a file to write logs into.")),
138 	LINPHONE_OPTION("no-video",            '\0', G_OPTION_ARG_NONE,     (gpointer)&no_video,             N_("Start linphone with video disabled.")),
139 	LINPHONE_OPTION("iconified",           '\0', G_OPTION_ARG_NONE,     (gpointer)&iconified,            N_("Start only in the system tray, do not show the main interface.")),
140 	LINPHONE_OPTION("call",                'c',  G_OPTION_ARG_STRING,   &addr_to_call,                   N_("address to call right now")),
141 	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)")),
142 	LINPHONE_OPTION("config",              '\0', G_OPTION_ARG_FILENAME, (gpointer) &custom_config_file,  N_("Configuration file")),
143 	LINPHONE_OPTION("run-audio-assistant", '\0', G_OPTION_ARG_NONE,     (gpointer) &run_audio_assistant, N_("Run the audio assistant")),
144 	LINPHONE_OPTION("selftest",            '\0', G_OPTION_ARG_NONE,     (gpointer) &selftest,            N_("Run self test and exit 0 if succeed")),
145 	{0}
146 };
147 
148 #define RELATIVE_XML_DIR
149 #define BUILD_TREE_XML_DIR "gtk"
150 
151 #ifndef _WIN32
152 #define CONFIG_FILE ".linphonerc"
153 #define SECRETS_FILE ".linphone-zidcache"
154 #define CERTIFICATES_PATH ".linphone-usr-crt"
155 #else
156 #define CONFIG_FILE "linphonerc"
157 #define SECRETS_FILE "linphone-zidcache"
158 #define CERTIFICATES_PATH "linphone-usr-crt"
159 #endif
160 
linphone_gtk_get_config_file(const char * filename)161 char *linphone_gtk_get_config_file(const char *filename){
162 	const int path_max=1024;
163 	char *config_file=g_malloc0(path_max);
164 	if (filename==NULL) filename=CONFIG_FILE;
165 	if (g_path_is_absolute(filename)) {
166 		snprintf(config_file,path_max,"%s",filename);
167 	} else{
168 #ifdef _WIN32
169 		const char *appdata=getenv("APPDATA");
170 		if (appdata){
171 			snprintf(config_file,path_max,"%s\\%s",appdata,LINPHONE_CONFIG_DIR);
172 			CreateDirectory(config_file,NULL);
173 			snprintf(config_file,path_max,"%s\\%s\\%s",appdata,LINPHONE_CONFIG_DIR,filename);
174 		}
175 #else
176 		const char *home=getenv("HOME");
177 		if (home==NULL) home=".";
178 		snprintf(config_file,path_max,"%s/%s",home,filename);
179 #endif
180 	}
181 	return config_file;
182 }
183 
184 #define FACTORY_CONFIG_FILE "linphonerc.factory"
185 static char _factory_config_file[1024];
linphone_gtk_get_factory_config_file(void)186 static const char *linphone_gtk_get_factory_config_file(void){
187 	char* path = NULL;
188 	/*try accessing a local file first if exists*/
189 	if (bctbx_file_exist(FACTORY_CONFIG_FILE)==0){
190 		path = ms_strdup(FACTORY_CONFIG_FILE);
191 	} else {
192 		char *progdir;
193 
194 		if (progpath != NULL) {
195 			char *basename;
196 			progdir = strdup(progpath);
197 #ifdef _WIN32
198 			basename = strrchr(progdir, '\\');
199 			if (basename != NULL) {
200 				basename ++;
201 				*basename = '\0';
202 				path = ms_strdup_printf("%s\\..\\%s", progdir, FACTORY_CONFIG_FILE);
203 			} else if (workingdir!=NULL) {
204 				path = ms_strdup_printf("%s\\%s", workingdir, FACTORY_CONFIG_FILE);
205 			}
206 #else
207 			basename = strrchr(progdir, '/');
208 			if (basename != NULL) {
209 				basename ++;
210 				*basename = '\0';
211 				path = ms_strdup_printf("%s/../share/linphone/%s", progdir, FACTORY_CONFIG_FILE);
212 			}
213 #endif
214 			free(progdir);
215 		}
216 	}
217 	if (path) {
218 		ms_message("Factory config file expected at %s", path);
219 		//use factory file only if it exists
220 		if (bctbx_file_exist(path)==0){
221 			snprintf(_factory_config_file, sizeof(_factory_config_file), "%s", path);
222 			ms_free(path);
223 			return _factory_config_file;
224 		}
225 		ms_free(path);
226 	}
227 	return NULL;
228 }
229 
linphone_gtk_get_ldap(void)230 LinphoneLDAPContactProvider* linphone_gtk_get_ldap(void){
231 	return ldap_provider;
232 }
233 
linphone_gtk_is_ldap_supported(void)234 int linphone_gtk_is_ldap_supported(void){
235 	return linphone_ldap_contact_provider_available();
236 }
237 
linphone_gtk_set_ldap(LinphoneLDAPContactProvider * ldap)238 void linphone_gtk_set_ldap(LinphoneLDAPContactProvider* ldap)
239 {
240 	if( ldap_provider )
241 		linphone_contact_provider_unref(ldap_provider);
242 
243 	ldap_provider = ldap ? linphone_ldap_contact_provider_ref( ldap )
244 						 : NULL;
245 }
246 
linphone_gtk_schedule_restart(void)247 void linphone_gtk_schedule_restart(void){
248 	restart=TRUE;
249 }
250 
linphone_gtk_get_audio_assistant_option(void)251 gboolean linphone_gtk_get_audio_assistant_option(void){
252 	return run_audio_assistant;
253 }
254 
linphone_gtk_init_liblinphone(const char * config_file,const char * factory_config_file,const char * chat_messages_db_file,const char * call_logs_db_file,const char * friends_db_file)255 static void linphone_gtk_init_liblinphone(const char *config_file,
256 		const char *factory_config_file, const char *chat_messages_db_file,
257 		const char *call_logs_db_file, const char *friends_db_file) {
258 	LinphoneCoreVTable vtable={0};
259 	gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE);
260 	gchar *user_certificates_dir=linphone_gtk_get_config_file(CERTIFICATES_PATH);
261 	MSFactory *msfactory = NULL;
262 	MSFilterDesc *ogl_filter_desc;
263 
264 	vtable.global_state_changed=linphone_gtk_global_state_changed;
265 	vtable.call_state_changed=linphone_gtk_call_state_changed;
266 	vtable.registration_state_changed=linphone_gtk_registration_state_changed;
267 	vtable.notify_presence_received=linphone_gtk_notify_recv;
268 	vtable.new_subscription_requested=linphone_gtk_new_unknown_subscriber;
269 	vtable.auth_info_requested=linphone_gtk_auth_info_requested;
270 	vtable.call_log_updated=linphone_gtk_call_log_updated;
271 	vtable.message_received=linphone_gtk_text_received;
272 	vtable.is_composing_received=linphone_gtk_is_composing_received;
273 	vtable.refer_received=linphone_gtk_refer_received;
274 	vtable.buddy_info_updated=linphone_gtk_buddy_info_updated;
275 	vtable.call_encryption_changed=linphone_gtk_call_encryption_changed;
276 	vtable.transfer_state_changed=linphone_gtk_transfer_state_changed;
277 	vtable.dtmf_received=linphone_gtk_dtmf_received;
278 	vtable.configuring_status=linphone_gtk_configuring_status;
279 
280 	the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL);
281 	linphone_core_migrate_to_multi_transport(the_core);
282 	//lp_config_set_int(linphone_core_get_config(the_core), "sip", "store_auth_info", 0);
283 
284 
285 	if( lp_config_has_section(linphone_core_get_config(the_core),"ldap") ){
286 		LpConfig* cfg = linphone_core_get_config(the_core);
287 		LinphoneDictionary* ldap_cfg = lp_config_section_to_dict(cfg, "ldap");
288 		linphone_gtk_set_ldap( linphone_ldap_contact_provider_create(the_core, ldap_cfg) );
289 	}
290 
291 	linphone_core_set_user_agent(the_core,"Linphone", LINPHONE_VERSION);
292 	linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL);
293 	linphone_core_set_zrtp_secrets_file(the_core,secrets_file); /* XML cache is superseeded by the sqlite one, keep it for migration purpose but it shall be removed in future version */
294 	g_free(secrets_file);
295 	linphone_core_set_user_certificates_path(the_core,user_certificates_dir);
296 	g_free(user_certificates_dir);
297 	linphone_core_enable_video_capture(the_core, TRUE);
298 	linphone_core_enable_video_display(the_core, TRUE);
299 	linphone_core_set_native_video_window_id(the_core,LINPHONE_VIDEO_DISPLAY_NONE);/*don't create the window*/
300 	if (no_video) {
301 		_linphone_gtk_enable_video(FALSE);
302 		linphone_gtk_set_ui_config_int("videoselfview",0);
303 	}
304 	if (chat_messages_db_file) linphone_core_set_chat_database_path(the_core,chat_messages_db_file);
305 	if (call_logs_db_file) linphone_core_set_call_logs_database_path(the_core, call_logs_db_file);
306 	if (friends_db_file) linphone_core_set_friends_database_path(the_core, friends_db_file);
307 
308 	// Disable the generic OpenGL displaying filter
309 	msfactory = linphone_core_get_ms_factory(the_core);
310 	ogl_filter_desc = ms_factory_lookup_filter_by_id(msfactory, MS_OGL_ID);
311 	if (ogl_filter_desc != NULL) ogl_filter_desc->flags &= ~MS_FILTER_IS_ENABLED;
312 }
313 
linphone_gtk_get_core(void)314 LinphoneCore *linphone_gtk_get_core(void){
315 	return the_core;
316 }
317 
linphone_gtk_get_main_window(void)318 GtkWidget *linphone_gtk_get_main_window(void){
319 	return the_ui;
320 }
321 
linphone_gtk_destroy_main_window(void)322 void linphone_gtk_destroy_main_window(void) {
323 	linphone_gtk_destroy_window(the_ui);
324 	the_ui = NULL;
325 }
326 
linphone_gtk_configure_window(GtkWidget * w,const char * window_name)327 static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name){
328 	static const char *hiddens=NULL;
329 	static const char *shown=NULL;
330 	static bool_t config_loaded=FALSE;
331 	if (linphone_gtk_get_core()==NULL) return;
332 	if (config_loaded==FALSE){
333 		hiddens=linphone_gtk_get_ui_config("hidden_widgets",NULL);
334 		shown=linphone_gtk_get_ui_config("shown_widgets",NULL);
335 		config_loaded=TRUE;
336 	}
337 	if (hiddens) linphone_gtk_visibility_set(hiddens,window_name,w,FALSE);
338 	if (shown) linphone_gtk_visibility_set(shown,window_name,w,TRUE);
339 }
340 
get_ui_file(const char * name,char * path,int pathsize)341 static int get_ui_file(const char *name, char *path, int pathsize){
342 	snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
343 	if (bctbx_file_exist(path)!=0){
344 		LinphoneFactory *factory = linphone_factory_get();
345 		const char *data_dir = linphone_factory_get_data_resources_dir(factory);
346 		snprintf(path,pathsize,"%s/%s.ui",data_dir,name);
347 		if (bctbx_file_exist(path)!=0){
348 			g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,data_dir,name);
349 			return -1;
350 		}
351 	}
352 	return 0;
353 }
354 
linphone_gtk_destroy_window(GtkWidget * widget)355 void linphone_gtk_destroy_window(GtkWidget *widget) {
356 	GtkBuilder* builder = g_object_get_data(G_OBJECT(widget), "builder");
357 	gtk_widget_destroy(widget);
358 	g_object_unref (G_OBJECT (builder));
359 }
360 
linphone_gtk_create_widget(const char * widget_name)361 GtkWidget *linphone_gtk_create_widget(const char *widget_name) {
362 	char path[2048];
363 	GtkBuilder *builder = gtk_builder_new();
364 	GError *error = NULL;
365 	GObject *obj;
366 
367 	if(get_ui_file(widget_name, path, sizeof(path)) == -1) goto fail;
368 
369 	gtk_builder_set_translation_domain(builder, GETTEXT_PACKAGE);
370 
371 	if(gtk_builder_add_from_file(builder, path, &error) == 0) {
372 		g_error("Couldn't load builder file: %s", error->message);
373 		g_error_free(error);
374 		goto fail;
375 	}
376 
377 	obj = gtk_builder_get_object(builder, widget_name);
378 	if(obj == NULL) {
379 		g_error("'%s' widget not found", widget_name);
380 		goto fail;
381 	}
382 	g_object_set_data(G_OBJECT(obj), "builder", builder);
383 	g_signal_connect_data(G_OBJECT(obj),"destroy",(GCallback)g_object_unref,builder, NULL, G_CONNECT_AFTER|G_CONNECT_SWAPPED);
384 	gtk_builder_connect_signals(builder, obj);
385 
386 	return GTK_WIDGET(obj);
387 
388 fail:
389 	g_object_unref(builder);
390 	return NULL;
391 }
392 
linphone_gtk_create_window(const char * window_name,GtkWidget * parent)393 GtkWidget *linphone_gtk_create_window(const char *window_name, GtkWidget *parent){
394 	GtkWidget *w = linphone_gtk_create_widget(window_name);
395 	if(w) {
396 		linphone_gtk_configure_window(w,window_name);
397 		if(parent) {
398 			gtk_window_set_transient_for(GTK_WINDOW(w), GTK_WINDOW(parent));
399 			gtk_window_set_position(GTK_WINDOW(w), GTK_WIN_POS_CENTER_ON_PARENT);
400 		}
401 	}
402 	return w;
403 }
404 
entry_unmapped(GtkWidget * widget)405 static void entry_unmapped(GtkWidget *widget){
406 	ms_message("%s is unmapped, calling unrealize to workaround chinese bug.",G_OBJECT_TYPE_NAME(widget));
407 	gtk_widget_unrealize(widget);
408 }
409 
linphone_gtk_get_widget(GtkWidget * window,const char * name)410 GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){
411 	GtkBuilder *builder;
412 	GObject *w;
413 	if (window==NULL) return NULL;
414 	builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder");
415 	if (builder==NULL){
416 		g_error("Fail to retrieve builder from window !");
417 		return NULL;
418 	}
419 	w=gtk_builder_get_object(builder,name);
420 	if (w==NULL){
421 		g_error("No widget named %s found in xml interface.",name);
422 	}
423 	if (workaround_gtk_entry_chinese_bug){
424 		if (strcmp(G_OBJECT_TYPE_NAME(w),"GtkEntry")==0 || strcmp(G_OBJECT_TYPE_NAME(w),"GtkTextView")==0){
425 			if (g_object_get_data(G_OBJECT(w),"entry_bug_workaround")==NULL){
426 				g_object_set_data(G_OBJECT(w),"entry_bug_workaround",GINT_TO_POINTER(1));
427 				ms_message("%s is a %s",name,G_OBJECT_TYPE_NAME(w));
428 				g_signal_connect(G_OBJECT(w),"unmap",(GCallback)entry_unmapped,NULL);
429 			}
430 		}
431 	}
432 	return GTK_WIDGET(w);
433 }
434 
435 
linphone_gtk_display_something(GtkMessageType type,const gchar * message)436 void linphone_gtk_display_something(GtkMessageType type,const gchar *message){
437 	GtkWidget *dialog;
438 	GtkWidget *main_window=linphone_gtk_get_main_window();
439 
440 	gtk_widget_show(main_window);
441 	if (type==GTK_MESSAGE_QUESTION)
442 	{
443 		/* draw a question box. link to dialog_click callback */
444 		dialog = gtk_message_dialog_new (
445 				GTK_WINDOW(main_window),
446 								GTK_DIALOG_DESTROY_WITH_PARENT,
447 				GTK_MESSAGE_QUESTION,
448 								GTK_BUTTONS_YES_NO,
449 								"%s",
450 				(const gchar*)message);
451 		/* connect to some callback : REVISIT */
452 		/*
453 		g_signal_connect_swapped (G_OBJECT (dialog), "response",
454 						   G_CALLBACK (dialog_click),
455 						   G_OBJECT (dialog));
456 		*/
457 		/* actually show the box */
458 		gtk_widget_show(dialog);
459 	}
460 	else
461 	{
462 		dialog = gtk_message_dialog_new (GTK_WINDOW(main_window),
463 								  GTK_DIALOG_DESTROY_WITH_PARENT,
464 								  type,
465 								  GTK_BUTTONS_CLOSE,
466 								  "%s",
467 								  (const gchar*)message);
468 		/* Destroy the dialog when the user responds to it (e.g. clicks a button) */
469 		g_signal_connect_swapped (G_OBJECT (dialog), "response",
470 						   G_CALLBACK (gtk_widget_destroy),
471 						   G_OBJECT (dialog));
472 		gtk_widget_show(dialog);
473 	}
474 }
475 
linphone_gtk_about_response(GtkDialog * dialog,gint id)476 void linphone_gtk_about_response(GtkDialog *dialog, gint id){
477 	if (id==GTK_RESPONSE_CANCEL){
478 		gtk_widget_destroy(GTK_WIDGET(dialog));
479 	}
480 }
481 
about_url_clicked(GtkAboutDialog * dialog,const char * url,gpointer data)482 static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer data){
483 	g_message("About url clicked");
484 	linphone_gtk_open_browser(url);
485 }
486 
linphone_gtk_show_about(void)487 void linphone_gtk_show_about(void){
488 	struct stat filestat;
489 	const char *data_dir;
490 	char *license_file;
491 	GtkWidget *about;
492 	const char *tmp;
493 	GdkPixbuf *logo=create_pixbuf(
494 		linphone_gtk_get_ui_config("logo","linphone-banner.png"));
495 	static const char *defcfg="defcfg";
496 	LinphoneFactory *factory = linphone_factory_get();
497 
498 	about=linphone_gtk_create_window("about", the_ui);
499 
500 	gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL);
501 
502 	data_dir = linphone_factory_get_data_resources_dir(factory);
503 	license_file = bctbx_strdup_printf("%s/COPYING", data_dir);
504 	memset(&filestat,0,sizeof(filestat));
505 	if (stat(license_file,&filestat)!=0){
506 		license_file="COPYING";
507 		stat(license_file,&filestat);
508 	}
509 	if (filestat.st_size>0){
510 		char *license=g_malloc(filestat.st_size+1);
511 		FILE *f=fopen(license_file,"r");
512 		if (f && fread(license,1,filestat.st_size,f)>0){
513 			license[filestat.st_size]='\0';
514 			gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),license);
515 		}
516 		g_free(license);
517 	}
518 	bctbx_free(license_file);
519 	gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),linphone_core_get_version());
520 	gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone"));
521 	gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org"));
522 	if (logo) {
523 		gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about), logo);
524 		g_object_unref(logo);
525 	}
526 	tmp=linphone_gtk_get_ui_config("artists",defcfg);
527 	if (tmp!=defcfg){
528 		const char *tmp2[2];
529 		tmp2[0]=tmp;
530 		tmp2[1]=NULL;
531 		gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2);
532 	}
533 	tmp=linphone_gtk_get_ui_config("translators",defcfg);
534 	if (tmp!=defcfg)
535 		gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp);
536 	tmp=linphone_gtk_get_ui_config("comments",defcfg);
537 	if (tmp!=defcfg)
538 		gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp);
539 	gtk_widget_show(about);
540 }
541 
542 
linphone_gtk_iterate(LinphoneCore * lc)543 static gboolean linphone_gtk_iterate(LinphoneCore *lc){
544 	static gboolean first_time=TRUE;
545 	static gboolean in_iterate=FALSE;
546 
547 	/*avoid reentrancy*/
548 	if (in_iterate) return TRUE;
549 	in_iterate=TRUE;
550 	linphone_core_iterate(lc);
551 	if (first_time){
552 		/*after the first call to iterate, SipSetupContexts should be ready, so take actions:*/
553 		linphone_gtk_show_directory_search();
554 		first_time=FALSE;
555 	}
556 
557 	if (addr_to_call!=NULL){
558 		/*make sure we are not showing the login screen*/
559 		GtkWidget *mw=linphone_gtk_get_main_window();
560 		if (g_object_get_data(G_OBJECT(mw), "login_frame") == NULL){
561 			GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
562 			gtk_entry_set_text(GTK_ENTRY(uri_bar),addr_to_call);
563 			addr_to_call=NULL;
564 			linphone_gtk_start_call(uri_bar);
565 		}
566 	}
567 	in_iterate=FALSE;
568 	return TRUE;
569 }
570 
uribar_completion_matchfunc(GtkEntryCompletion * completion,const gchar * key,GtkTreeIter * iter,gpointer user_data)571 static gboolean uribar_completion_matchfunc(GtkEntryCompletion *completion, const gchar *key, GtkTreeIter *iter, gpointer user_data){
572 	char* address = NULL;
573 	gboolean ret  = FALSE;
574 	gtk_tree_model_get(gtk_entry_completion_get_model(completion),iter,0,&address,-1);
575 
576 	if(address) {
577 		gchar *tmp = g_utf8_casefold(address,-1);
578 		if (strstr(tmp,key)) ret=TRUE;
579 		g_free(tmp);
580 		g_free(address);
581 	}
582 
583 	return ret;
584 }
585 
load_uri_history(void)586 static void load_uri_history(void){
587 	GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
588 	char key[20];
589 	int i;
590 	GtkEntryCompletion *gep=gtk_entry_completion_new();
591 	GtkListStore *model=gtk_list_store_new(2,G_TYPE_STRING,G_TYPE_INT);
592 	for (i=0;;i++){
593 		const char *uri;
594 		snprintf(key,sizeof(key),"uri%i",i);
595 		uri=linphone_gtk_get_ui_config(key,NULL);
596 		if (uri!=NULL) {
597 			GtkTreeIter iter;
598 			gtk_list_store_append(model,&iter);
599 			gtk_list_store_set(model,&iter,0,uri,1,COMPLETION_HISTORY,-1);
600 			if (i==0) gtk_entry_set_text(uribar,uri);
601 		}
602 		else break;
603 	}
604 	gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
605 	gtk_entry_completion_set_text_column(gep,0);
606 	gtk_entry_completion_set_popup_completion(gep, TRUE);
607 	gtk_entry_completion_set_match_func(gep,uribar_completion_matchfunc, NULL, NULL);
608 	gtk_entry_completion_set_minimum_key_length(gep,3);
609 	gtk_entry_set_completion(uribar,gep);
610 	g_signal_connect (G_OBJECT (uribar), "changed", G_CALLBACK(linphone_gtk_on_uribar_changed), NULL);
611 }
612 
save_uri_history(void)613 static void save_uri_history(void){
614 	LinphoneCore *lc=linphone_gtk_get_core();
615 	LpConfig *cfg=linphone_core_get_config(lc);
616 	GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
617 	char key[20];
618 	int i=0;
619 	char *uri=NULL;
620 	GtkTreeIter iter;
621 	GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
622 
623 	if (!gtk_tree_model_get_iter_first(model,&iter)) return;
624 	do {
625 		gtk_tree_model_get(model,&iter,0,&uri,-1);
626 		if (uri) {
627 			snprintf(key,sizeof(key),"uri%i",i);
628 			lp_config_set_string(cfg,"GtkUi",key,uri);
629 			g_free(uri);
630 		}else break;
631 		i++;
632 		if (i>5) break;
633 	}while(gtk_tree_model_iter_next(model,&iter));
634 	lp_config_sync(cfg);
635 }
636 
completion_add_text(GtkEntry * entry,const char * text)637 static void completion_add_text(GtkEntry *entry, const char *text){
638 	GtkTreeIter iter;
639 	GtkTreeModel *model=gtk_entry_completion_get_model(gtk_entry_get_completion(entry));
640 
641 	if (gtk_tree_model_get_iter_first(model,&iter)){
642 		do {
643 			gchar *uri=NULL;
644 			gtk_tree_model_get(model,&iter,0,&uri,-1);
645 			if (uri!=NULL){
646 				if (strcmp(uri,text)==0) {
647 					/*remove text */
648 					gtk_list_store_remove(GTK_LIST_STORE(model),&iter);
649 					g_free(uri);
650 					break;
651 				}
652 				g_free(uri);
653 			}
654 		}while (gtk_tree_model_iter_next(model,&iter));
655 	}
656 	/* and prepend it on top of the list */
657 	gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
658 	gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,1,COMPLETION_HISTORY,-1);
659 	save_uri_history();
660 }
661 
on_contact_provider_search_results(LinphoneContactSearch * req,bctbx_list_t * friends,void * data)662 void on_contact_provider_search_results( LinphoneContactSearch* req, bctbx_list_t* friends, void* data )
663 {
664 	GtkTreeIter    iter;
665 	GtkEntry*    uribar = GTK_ENTRY(data);
666 	GtkEntryCompletion* compl = gtk_entry_get_completion(uribar);
667 	GtkTreeModel* model = gtk_entry_completion_get_model(compl);
668 	GtkListStore*  list = GTK_LIST_STORE(model);
669 	LinphoneLDAPContactSearch* search = linphone_ldap_contact_search_cast(req);
670 	gboolean valid;
671 
672 	// clear completion list from previous non-history entries
673 	valid = gtk_tree_model_get_iter_first(model,&iter);
674 	while(valid)
675 	{
676 		char* url;
677 		int type;
678 		gtk_tree_model_get(model,&iter, 0,&url, 1,&type, -1);
679 
680 		if (type != COMPLETION_HISTORY) {
681 			valid = gtk_list_store_remove(list, &iter);
682 		} else {
683 			valid = gtk_tree_model_iter_next(model,&iter);
684 		}
685 
686 		if( url ) g_free(url);
687 		if( !valid ) break;
688 	}
689 
690 	// add new non-history related matches
691 	while( friends ){
692 		LinphoneFriend* lf = friends->data;
693 		if( lf ) {
694 			const LinphoneAddress* la = linphone_friend_get_address(lf);
695 			if( la ){
696 				char *addr = linphone_address_as_string(la);
697 
698 				if( addr ){
699 					ms_message("[LDAP]Insert match: %s",  addr);
700 					gtk_list_store_insert_with_values(list, &iter, -1,
701 													  0, addr,
702 													  1, COMPLETION_LDAP, -1);
703 					ms_free(addr);
704 				}
705 			}
706 		}
707 		friends = friends->next;
708 	}
709 	gtk_entry_completion_complete(compl);
710 	// save the number of LDAP results to better decide if new results should be fetched when search predicate gets bigger
711 	gtk_object_set_data(GTK_OBJECT(uribar), "ldap_res_cout",
712 						GINT_TO_POINTER(
713 							linphone_ldap_contact_search_result_count(search)
714 							)
715 						);
716 
717 	// Gtk bug? we need to emit a "changed" signal so that the completion appears if
718 	// the list of results was previously empty
719 	g_signal_handlers_block_by_func(uribar, linphone_gtk_on_uribar_changed, NULL);
720 	g_signal_emit_by_name(uribar, "changed");
721 	g_signal_handlers_unblock_by_func(uribar, linphone_gtk_on_uribar_changed, NULL);
722 }
723 
724 struct CompletionTimeout {
725 	guint timeout_id;
726 };
727 
launch_contact_provider_search(void * userdata)728 static gboolean launch_contact_provider_search(void *userdata)
729 {
730 	LinphoneLDAPContactProvider* ldap = linphone_gtk_get_ldap();
731 	GtkWidget*      uribar = GTK_WIDGET(userdata);
732 	const gchar* predicate = gtk_entry_get_text(GTK_ENTRY(uribar));
733 	gchar* previous_search = gtk_object_get_data(GTK_OBJECT(uribar), "previous_search");
734 	unsigned int prev_res_count = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(uribar), "ldap_res_cout"));
735 
736 	if( ldap && strlen(predicate) >= 3 ){ // don't search too small predicates
737 		unsigned int max_res_count = linphone_ldap_contact_provider_get_max_result(ldap);
738 		LinphoneContactSearch* search;
739 		if( previous_search  &&
740 			(strstr(predicate, previous_search) == predicate) && // last search contained results from this one
741 			(prev_res_count != max_res_count) ){ // and we didn't reach the max result limit
742 
743 			ms_message("Don't launch search on already searched data (current: %s, old search: %s), (%d/%d results)",
744 					   predicate, previous_search, prev_res_count, max_res_count);
745 			return FALSE;
746 		}
747 
748 		// save current search
749 		if( previous_search ) ms_free(previous_search);
750 		gtk_object_set_data(GTK_OBJECT(uribar), "previous_search", ms_strdup(predicate));
751 
752 		ms_message("launch_contact_provider_search");
753 		search =linphone_contact_provider_begin_search(
754 					linphone_contact_provider_cast(ldap_provider),
755 					predicate, on_contact_provider_search_results, uribar
756 					);
757 
758 		if(search)
759 			linphone_contact_search_ref(search);
760 	}
761 	return FALSE;
762 }
763 
linphone_gtk_on_uribar_changed(GtkEditable * uribar,gpointer user_data)764 void linphone_gtk_on_uribar_changed(GtkEditable *uribar, gpointer user_data)
765 {
766 	if( linphone_gtk_get_ldap() ) {
767 		gchar* text = gtk_editable_get_chars(uribar, 0,-1);
768 		gint timeout = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(uribar), "complete_timeout"));
769 		if( text ) g_free(text);
770 
771 		if( timeout != 0 ) {
772 			g_source_remove(timeout);
773 		}
774 
775 		timeout = g_timeout_add_seconds(1,(GSourceFunc)launch_contact_provider_search, uribar);
776 
777 		gtk_object_set_data(GTK_OBJECT(uribar),"complete_timeout", GINT_TO_POINTER(timeout) );
778 	}
779 }
780 
linphone_gtk_video_enabled(void)781 bool_t linphone_gtk_video_enabled(void){
782 	const LinphoneVideoPolicy *vpol=linphone_core_get_video_policy(linphone_gtk_get_core());
783 	return vpol->automatically_accept && vpol->automatically_initiate;
784 }
785 
linphone_gtk_show_main_window()786 void linphone_gtk_show_main_window(){
787 	GtkWidget *w=linphone_gtk_get_main_window();
788 #ifdef HAVE_GTK_OSX
789 	GtkWidget *icon = linphone_gtk_get_widget(w, "history_tab_icon");
790 	GtkWidget *label = linphone_gtk_get_widget(w, "history_tab_label");
791 	gtk_misc_set_alignment(GTK_MISC(icon), 0.5f, 0.25f);
792 	gtk_misc_set_alignment(GTK_MISC(label), 0.5f, 0.f);
793 #endif
794 	gtk_widget_show(w);
795 	gtk_window_present(GTK_WINDOW(w));
796 }
797 
linphone_gtk_call_terminated(LinphoneCall * call,const char * error)798 void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){
799 	GtkWidget *mw=linphone_gtk_get_main_window();
800 	if (linphone_core_get_calls(linphone_gtk_get_core())==NULL){
801 		gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE);
802 	}
803 	if (linphone_gtk_use_in_call_view() && call)
804 		linphone_gtk_in_call_view_terminate(call,error);
805 }
806 
linphone_gtk_update_call_buttons(LinphoneCall * call)807 static void linphone_gtk_update_call_buttons(LinphoneCall *call){
808 	LinphoneCore *lc=linphone_gtk_get_core();
809 	GtkWidget *mw=linphone_gtk_get_main_window();
810 	const bctbx_list_t *calls=linphone_core_get_calls(lc);
811 	GtkWidget *button;
812 	bool_t add_call=(calls!=NULL);
813 	int call_list_size=bctbx_list_size(calls);
814 	GtkWidget *conf_frame;
815 
816 	button=linphone_gtk_get_widget(mw,"start_call");
817 	gtk_widget_set_sensitive(button,TRUE);
818 	gtk_widget_set_visible(button,!add_call);
819 
820 	button=linphone_gtk_get_widget(mw,"add_call");
821 
822 	if (linphone_core_sound_resources_locked(lc) || (call && linphone_call_get_state(call)==LinphoneCallIncomingReceived)) {
823 		gtk_widget_set_sensitive(button,FALSE);
824 	} else {
825 		gtk_widget_set_sensitive(button,TRUE);
826 	}
827 	gtk_widget_set_visible(button,add_call);
828 
829 	//gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active);
830 	conf_frame=(GtkWidget *)g_object_get_data(G_OBJECT(mw),"conf_frame");
831 	if(conf_frame==NULL){
832 		linphone_gtk_enable_transfer_button(lc,call_list_size>1);
833 	} else {
834 		linphone_gtk_enable_transfer_button(lc,FALSE);
835 	}
836 	if (call) {
837 		bool_t enable_conference = call_list_size>1 && linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(call)) == FALSE;
838 		linphone_gtk_enable_conference_button(lc,enable_conference);
839 		linphone_gtk_update_video_button(call);
840 	}
841 }
842 
linphone_gtk_get_record_path(const LinphoneAddress * address,gboolean is_conference)843 gchar *linphone_gtk_get_record_path(const LinphoneAddress *address, gboolean is_conference){
844 	const char *dir=g_get_user_special_dir(G_USER_DIRECTORY_MUSIC);
845 	const char *id="unknown";
846 	char filename[256]={0};
847 	char date[64]={0};
848 	time_t curtime=time(NULL);
849 	struct tm loctime;
850 	const char **fmts=linphone_core_get_supported_file_formats(linphone_gtk_get_core());
851 	int i;
852 	const char *ext="wav";
853 	char *record_path_utf8, *record_path;
854 
855 #ifdef _WIN32
856 	loctime=*localtime(&curtime);
857 #else
858 	localtime_r(&curtime,&loctime);
859 #endif
860 	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);
861 
862 	for (i=0;fmts[i]!=NULL;++i){
863 		if (strcmp(fmts[i],"mkv")==0){
864 			ext="mkv";
865 			break;
866 		}
867 	}
868 
869 	if (address){
870 		id=linphone_address_get_username(address);
871 		if (id==NULL) id=linphone_address_get_domain(address);
872 	}
873 	if (is_conference){
874 		snprintf(filename,sizeof(filename)-1,"%s-conference-%s.%s",
875 			linphone_gtk_get_ui_config("title","Linphone"),
876 			date,ext);
877 	}else{
878 		snprintf(filename,sizeof(filename)-1,"%s-call-%s-%s.%s",
879 			linphone_gtk_get_ui_config("title","Linphone"),
880 			date,
881 			id,ext);
882 	}
883 	if (!dir) {
884 		ms_message ("No directory for music, using [%s] instead",dir=getenv("HOME"));
885 	}
886 	record_path_utf8 = g_build_filename(dir,filename,NULL);
887 	record_path = g_locale_from_utf8(record_path_utf8, -1, NULL, NULL, NULL);
888 	g_free(record_path_utf8);
889 	return record_path;
890 }
891 
linphone_gtk_get_snapshot_path(void)892 gchar *linphone_gtk_get_snapshot_path(void) {
893 	const char *dir=g_get_user_special_dir(G_USER_DIRECTORY_PICTURES);
894 	char filename[256]={0};
895 	char date[64]={0};
896 	time_t curtime=time(NULL);
897 	struct tm loctime;
898 	const char *ext="jpg";
899 	char *snapshot_path_utf8, *snapshot_path;
900 
901 #ifdef _WIN32
902 	loctime=*localtime(&curtime);
903 #else
904 	localtime_r(&curtime,&loctime);
905 #endif
906 	snprintf(date,sizeof(date)-1,"%i%02i%02i-%02i%02i%02i",loctime.tm_year+1900,loctime.tm_mon+1,loctime.tm_mday, loctime.tm_hour, loctime.tm_min, loctime.tm_sec);
907 	snprintf(filename,sizeof(filename)-1,"%s-snapshot-%s.%s",
908 			linphone_gtk_get_ui_config("title","Linphone"),
909 			date, ext);
910 	if (!dir) {
911 		ms_message ("No directory for pictures, using [%s] instead",dir=getenv("HOME"));
912 	}
913 	snapshot_path_utf8 = g_build_filename(dir,filename,NULL);
914 	snapshot_path = g_locale_from_utf8(snapshot_path_utf8, -1, NULL, NULL, NULL);
915 	g_free(snapshot_path_utf8);
916 	return snapshot_path;
917 }
918 
linphone_gtk_start_call_do(GtkWidget * uri_bar)919 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
920 	const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
921 	LinphoneCore *lc=linphone_gtk_get_core();
922 	LinphoneAddress *addr=linphone_core_interpret_url(lc,entered);
923 
924 	if (addr!=NULL){
925 		LinphoneCallParams *params=linphone_core_create_call_params(lc, NULL);
926 		gchar *record_file=linphone_gtk_get_record_path(addr,FALSE);
927 		linphone_call_params_set_record_file(params,record_file);
928 		linphone_core_invite_address_with_params(lc,addr,params);
929 		completion_add_text(GTK_ENTRY(uri_bar),entered);
930 		linphone_address_unref(addr);
931 		linphone_call_params_unref(params);
932 		g_free(record_file);
933 	}else{
934 		linphone_gtk_call_terminated(NULL,NULL);
935 	}
936 	return FALSE;
937 }
938 
accept_incoming_call(LinphoneCall * call)939 static void accept_incoming_call(LinphoneCall *call){
940 	LinphoneCore *lc=linphone_gtk_get_core();
941 	LinphoneCallParams *params = linphone_core_create_call_params(lc, call);
942 	gchar *record_file=linphone_gtk_get_record_path(linphone_call_get_remote_address(call),FALSE);
943 	linphone_call_params_set_record_file(params,record_file);
944 	linphone_call_accept_with_params(call,params);
945 	linphone_call_params_unref(params);
946 }
947 
linphone_gtk_auto_answer(LinphoneCall * call)948 static gboolean linphone_gtk_auto_answer(LinphoneCall *call){
949 	LinphoneCallState state=linphone_call_get_state(call);
950 	if (state==LinphoneCallIncomingReceived || state==LinphoneCallIncomingEarlyMedia){
951 		accept_incoming_call(call);
952 	}
953 	return FALSE;
954 }
955 
linphone_gtk_start_call(GtkWidget * w)956 void linphone_gtk_start_call(GtkWidget *w){
957 	LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
958 	/*change into in-call mode, then do the work later as it might block a bit */
959 	GtkWidget *mw=gtk_widget_get_toplevel(w);
960 	GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
961 	LinphoneCallState state= call ? linphone_call_get_state(call) : LinphoneCallIdle;
962 
963 	if (state == LinphoneCallIncomingReceived || state == LinphoneCallIncomingEarlyMedia){
964 		accept_incoming_call(call);
965 	}else{
966 		/*immediately disable the button and delay a bit the execution the linphone_core_invite()
967 		so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/
968 		gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE);
969 		g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar);
970 	}
971 
972 }
973 
linphone_gtk_start_chat(GtkWidget * w)974 void linphone_gtk_start_chat(GtkWidget *w){
975 	GtkWidget *mw=gtk_widget_get_toplevel(w);
976 	GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar");
977 	const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
978 	LinphoneCore *lc=linphone_gtk_get_core();
979 	LinphoneAddress *addr=linphone_core_interpret_url(lc,entered);
980 	if (addr) {
981 		linphone_gtk_friend_list_set_chat_conversation(addr);
982 		linphone_address_unref(addr);
983 	}
984 }
985 
linphone_gtk_uri_bar_activate(GtkWidget * w)986 void linphone_gtk_uri_bar_activate(GtkWidget *w){
987 	linphone_gtk_start_call(w);
988 }
989 
linphone_gtk_terminate_call(GtkWidget * button)990 void linphone_gtk_terminate_call(GtkWidget *button){
991 	gboolean is_conf;
992 	LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf);
993 	if (call){
994 		linphone_call_terminate(call);
995 	}else if (is_conf){
996 		linphone_core_terminate_conference(linphone_gtk_get_core());
997 	}
998 }
999 
linphone_gtk_decline_clicked(GtkWidget * button)1000 void linphone_gtk_decline_clicked(GtkWidget *button){
1001 	LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
1002 	if (call)
1003 		linphone_call_terminate(call);
1004 }
1005 
linphone_gtk_answer_clicked(GtkWidget * button)1006 void linphone_gtk_answer_clicked(GtkWidget *button){
1007 	LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
1008 	if (call){
1009 		accept_incoming_call(call);
1010 		linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */
1011 	}
1012 }
1013 
_linphone_gtk_enable_video(gboolean val)1014 void _linphone_gtk_enable_video(gboolean val){
1015 	LinphoneVideoPolicy policy={0};
1016 	policy.automatically_initiate=policy.automatically_accept=val;
1017 	linphone_core_enable_video_capture(linphone_gtk_get_core(), TRUE);
1018 	linphone_core_enable_video_display(linphone_gtk_get_core(), TRUE);
1019 	linphone_core_set_video_policy(linphone_gtk_get_core(),&policy);
1020 }
1021 
linphone_gtk_enable_video(GtkWidget * w)1022 void linphone_gtk_enable_video(GtkWidget *w){
1023 	gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
1024 	//GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item");
1025 	_linphone_gtk_enable_video(val);
1026 }
1027 
linphone_gtk_enable_self_view(GtkWidget * w)1028 void linphone_gtk_enable_self_view(GtkWidget *w){
1029 	gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
1030 	LinphoneCore *lc=linphone_gtk_get_core();
1031 	linphone_core_enable_self_view(lc,val);
1032 	linphone_gtk_set_ui_config_int("videoselfview",val);
1033 }
1034 
linphone_gtk_used_identity_changed(GtkWidget * w)1035 void linphone_gtk_used_identity_changed(GtkWidget *w){
1036 	int active=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
1037 	char *sel=gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));
1038 	if (sel && strlen(sel)>0){ //avoid a dummy "changed" at gui startup
1039 		linphone_core_set_default_proxy_index(linphone_gtk_get_core(),(active==0) ? -1 : (active-1));
1040 		linphone_gtk_show_directory_search();
1041 	}
1042 	if (sel) g_free(sel);
1043 }
1044 
on_proxy_refresh_button_clicked(GtkWidget * w)1045 void on_proxy_refresh_button_clicked(GtkWidget *w){
1046 	LinphoneCore *lc=linphone_gtk_get_core();
1047 	linphone_core_refresh_registers(lc);
1048 }
1049 
grab_focus(GtkWidget * w)1050 static gboolean grab_focus(GtkWidget *w){
1051 	gtk_widget_grab_focus(w);
1052 	return FALSE;
1053 }
1054 
linphone_gtk_viewswitch_changed(GtkNotebook * notebook,GtkWidget * page,gint page_num,gpointer user_data)1055 void linphone_gtk_viewswitch_changed(GtkNotebook *notebook, GtkWidget *page, gint page_num, gpointer user_data){
1056 	GtkWidget *main_window = linphone_gtk_get_main_window();
1057 	GtkWidget *friendlist = linphone_gtk_get_widget(main_window,"contact_list");
1058 	GtkWidget *w = (GtkWidget*)g_object_get_data(G_OBJECT(friendlist),"chatview");
1059 
1060 	if (page_num == gtk_notebook_page_num(GTK_NOTEBOOK(notebook),w)) {
1061 		g_idle_add((GSourceFunc)grab_focus,linphone_gtk_get_widget(page,"text_entry"));
1062 	}
1063 }
1064 
linphone_gtk_notify_recv(LinphoneCore * lc,LinphoneFriend * fid)1065 static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){
1066 	linphone_gtk_show_friends();
1067 }
1068 
linphone_gtk_new_subscriber_response(GtkWidget * dialog,guint response_id,LinphoneFriend * lf)1069 static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){
1070 	switch(response_id){
1071 		case GTK_RESPONSE_YES:
1072 			linphone_gtk_show_contact(lf, the_ui);
1073 		break;
1074 		default:
1075 			linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
1076 	}
1077 	gtk_widget_destroy(dialog);
1078 }
1079 
linphone_gtk_new_unknown_subscriber(LinphoneCore * lc,LinphoneFriend * lf,const char * url)1080 static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url){
1081 	GtkWidget *dialog;
1082 	gchar *message;
1083 
1084 	if (linphone_gtk_get_ui_config_int("subscribe_deny_all",0)){
1085 		linphone_core_reject_subscriber(linphone_gtk_get_core(),lf);
1086 		return;
1087 	}
1088 
1089 	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);
1090 	dialog = gtk_message_dialog_new (
1091 				GTK_WINDOW(linphone_gtk_get_main_window()),
1092 								GTK_DIALOG_DESTROY_WITH_PARENT,
1093 				GTK_MESSAGE_QUESTION,
1094 								GTK_BUTTONS_YES_NO,
1095 								"%s",
1096 				message);
1097 	g_free(message);
1098 	g_signal_connect(G_OBJECT (dialog), "response",
1099 		G_CALLBACK (linphone_gtk_new_subscriber_response),lf);
1100 	/* actually show the box */
1101 	gtk_widget_show(dialog);
1102 }
1103 
1104 typedef struct _AuthTimeout{
1105 	GtkWidget *w;
1106 } AuthTimeout;
1107 
auth_timeout_clean(AuthTimeout * tout)1108 static void auth_timeout_clean(AuthTimeout *tout){
1109 	tout->w=NULL;
1110 }
1111 
auth_timeout_destroy(AuthTimeout * tout)1112 static gboolean auth_timeout_destroy(AuthTimeout *tout){
1113 	if (tout->w)  {
1114 		g_object_weak_unref(G_OBJECT(tout->w),(GWeakNotify)auth_timeout_clean,tout);
1115 		gtk_widget_destroy(tout->w);
1116 	}
1117 	g_free(tout);
1118 	return FALSE;
1119 }
1120 
auth_timeout_new(GtkWidget * w)1121 static AuthTimeout * auth_timeout_new(GtkWidget *w){
1122 	AuthTimeout *tout=g_new(AuthTimeout,1);
1123 	tout->w=w;
1124 	/*so that the timeout no more references the widget when it is destroyed:*/
1125 	g_object_weak_ref(G_OBJECT(w),(GWeakNotify)auth_timeout_clean,tout);
1126 	/*so that the widget is automatically destroyed after some time */
1127 	g_timeout_add(30000,(GtkFunction)auth_timeout_destroy,tout);
1128 	return tout;
1129 }
1130 
linphone_gtk_password_cancel(GtkWidget * w)1131 void linphone_gtk_password_cancel(GtkWidget *w){
1132 	LinphoneAuthInfo *info;
1133 	GtkWidget *window=gtk_widget_get_toplevel(w);
1134 	info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
1135 	linphone_core_abort_authentication(linphone_gtk_get_core(),info);
1136 	gtk_widget_destroy(window);
1137 }
1138 
linphone_gtk_password_ok(GtkWidget * w)1139 void linphone_gtk_password_ok(GtkWidget *w){
1140 	GtkWidget *entry;
1141 	GtkWidget *window=gtk_widget_get_toplevel(w);
1142 	LinphoneAuthInfo *info;
1143 	info=(LinphoneAuthInfo*)g_object_get_data(G_OBJECT(window),"auth_info");
1144 	g_object_weak_unref(G_OBJECT(window),(GWeakNotify)linphone_auth_info_destroy,info);
1145 	entry=linphone_gtk_get_widget(window,"password_entry");
1146 	linphone_auth_info_set_passwd(info,gtk_entry_get_text(GTK_ENTRY(entry)));
1147 	linphone_auth_info_set_userid(info,
1148 		gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(window,"userid_entry"))));
1149 	linphone_core_add_auth_info(linphone_gtk_get_core(),info);
1150 	gtk_widget_destroy(window);
1151 }
1152 
linphone_gtk_auth_info_requested(LinphoneCore * lc,const char * realm,const char * username,const char * domain)1153 static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username, const char *domain){
1154 	GtkWidget *w=linphone_gtk_create_window("password", the_ui);
1155 	GtkWidget *label=linphone_gtk_get_widget(w,"message");
1156 	LinphoneAuthInfo *info;
1157 	gchar *msg;
1158 	GtkWidget *mw=linphone_gtk_get_main_window();
1159 
1160 	if (mw && g_object_get_data(G_OBJECT(mw), "login_frame") != NULL){
1161 		/*don't prompt for authentication when login frame is visible*/
1162 		linphone_core_abort_authentication(lc,NULL);
1163 		return;
1164 	}
1165 
1166 	msg=g_strdup_printf(_("Please enter your password for username <i>%s</i>\n at realm <i>%s</i>:"),
1167 		username,realm);
1168 	gtk_label_set_markup(GTK_LABEL(label),msg);
1169 	g_free(msg);
1170 	gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"userid_entry")),username);
1171 	info=linphone_auth_info_new(username, NULL, NULL, NULL,realm,domain);
1172 	g_object_set_data(G_OBJECT(w),"auth_info",info);
1173 	g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_auth_info_destroy,info);
1174 	gtk_widget_show(w);
1175 	auth_timeout_new(w);
1176 }
1177 
linphone_gtk_dtmf_received(LinphoneCore * lc,LinphoneCall * call,int dtmf)1178 static void linphone_gtk_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf){
1179 	ms_message("Dtmf %c received.",dtmf);
1180 }
1181 
1182 
linphone_gtk_configuring_status(LinphoneCore * lc,LinphoneConfiguringState status,const char * message)1183 static void linphone_gtk_configuring_status(LinphoneCore *lc, LinphoneConfiguringState status, const char *message) {
1184 	if (config_fetching_dialog) linphone_gtk_close_config_fetching(config_fetching_dialog, status);
1185 	config_fetching_dialog=NULL;
1186 }
1187 
linphone_gtk_call_log_updated(LinphoneCore * lc,LinphoneCallLog * cl)1188 static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){
1189 	GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs");
1190 	if (w) linphone_gtk_call_log_update(w);
1191 	linphone_gtk_call_log_update(linphone_gtk_get_main_window());
1192 }
1193 
1194 #ifdef HAVE_NOTIFY
notify_actions_supported(void)1195 static bool_t notify_actions_supported(void) {
1196 	bool_t accepts_actions = FALSE;
1197 	GList *capabilities = notify_get_server_caps();
1198 	GList *c;
1199 	if(capabilities != NULL) {
1200 		for(c = capabilities; c != NULL; c = c->next) {
1201 			if(strcmp((char*)c->data, "actions") == 0 ) {
1202 				accepts_actions = TRUE;
1203 				break;
1204 			}
1205 		}
1206 		g_list_foreach(capabilities, (GFunc)g_free, NULL);
1207 		g_list_free(capabilities);
1208 	}
1209 	return accepts_actions;
1210 }
1211 
build_notification(const char * title,const char * body)1212 static NotifyNotification* build_notification(const char *title, const char *body) {
1213 	NotifyNotification *n = notify_notification_new(title, body, NULL
1214 #ifdef HAVE_NOTIFY1
1215 		,NULL
1216 #endif
1217 	);
1218 #ifndef HAVE_NOTIFY1
1219 	{
1220 		GError *error = NULL;
1221 		const char *icon_name = linphone_gtk_get_ui_config("icon_name", LINPHONE_ICON_NAME);
1222 		GdkPixbuf *pbuf = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), icon_name, 48, 0, &error);
1223 		if(error) {
1224 			g_warning("Could not load '%s' icon: %s", icon_name, error->message);
1225 			g_error_free(error);
1226 		}
1227 		notify_notification_set_image_from_pixbuf(n, pbuf);
1228 	}
1229 #endif
1230 	return n;
1231 }
1232 
show_notification(NotifyNotification * n)1233 static void show_notification(NotifyNotification* n){
1234 	if (n && !notify_notification_show(n,NULL))
1235 		ms_error("Failed to send notification.");
1236 }
1237 
make_notification(const char * title,const char * body)1238 static void make_notification(const char *title, const char *body){
1239 	show_notification(build_notification(title,body));
1240 }
1241 
1242 #endif
1243 
linphone_gtk_notify(LinphoneCall * call,LinphoneChatMessage * chat_message,const char * msg)1244 void linphone_gtk_notify(LinphoneCall *call, LinphoneChatMessage *chat_message, const char *msg){
1245 #ifdef HAVE_NOTIFY
1246 	if (!notify_is_initted())
1247 		if (!notify_init ("Linphone")) ms_error("Libnotify failed to init.");
1248 #endif
1249 	if (!call) {
1250 #ifdef HAVE_NOTIFY
1251 		if (chat_message) {
1252 			const LinphoneAddress *address = linphone_chat_message_get_peer_address(chat_message);
1253 			char *remote = linphone_address_as_string(address);
1254 			make_notification(remote, linphone_chat_message_get_text(chat_message));
1255 		} else {
1256 			if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL
1257 #ifdef HAVE_NOTIFY1
1258 				,NULL
1259 #endif
1260 				),NULL)) {
1261 				ms_error("Failed to send notification.");
1262 			}
1263 		}
1264 #else
1265 		linphone_gtk_show_main_window();
1266 #endif
1267 	} else if (!gtk_window_is_active((GtkWindow*)linphone_gtk_get_main_window())) {
1268 		gboolean show_main_window = FALSE;
1269 #ifdef HAVE_NOTIFY
1270 		char *body=NULL;
1271 		char *remote=call!=NULL ? linphone_call_get_remote_address_as_string(call) : NULL;
1272 		NotifyNotification *n;
1273 		switch(linphone_call_get_state(call)){
1274 			case LinphoneCallError:
1275 				make_notification(_("Call error"),body=g_markup_printf_escaped("<b>%s</b>\n%s",msg,remote));
1276 			break;
1277 			case LinphoneCallEnd:
1278 				make_notification(_("Call ended"),body=g_markup_printf_escaped("<b>%s</b>",remote));
1279 			break;
1280 			case LinphoneCallIncomingReceived:
1281 				n=build_notification(_("Incoming call"),body=g_markup_printf_escaped("<b>%s</b>",remote));
1282 				if (n){
1283 					if (notify_actions_supported()) {
1284 						notify_notification_add_action (n,"answer", _("Answer"),
1285 							NOTIFY_ACTION_CALLBACK(linphone_gtk_answer_clicked),NULL,NULL);
1286 						notify_notification_add_action (n,"decline",_("Decline"),
1287 							NOTIFY_ACTION_CALLBACK(linphone_gtk_decline_clicked),NULL,NULL);
1288 					}
1289 					show_notification(n);
1290 				}else show_main_window = TRUE;
1291 			break;
1292 			case LinphoneCallPausedByRemote:
1293 				make_notification(_("Call paused"),body=g_markup_printf_escaped(_("<b>by %s</b>"),remote));
1294 			break;
1295 			default:
1296 			break;
1297 		}
1298 		if (body) g_free(body);
1299 		if (remote) g_free(remote);
1300 #else
1301 		if (linphone_call_get_state(call) == LinphoneCallIncomingReceived)
1302 			show_main_window = TRUE;
1303 #endif
1304 		if (show_main_window) linphone_gtk_show_main_window();
1305 	}
1306 }
1307 
linphone_gtk_global_state_changed(LinphoneCore * lc,LinphoneGlobalState state,const char * str)1308 static void linphone_gtk_global_state_changed(LinphoneCore *lc, LinphoneGlobalState state, const char*str){
1309 	switch(state){
1310 		case LinphoneGlobalStartup:
1311 			the_core=lc;
1312 		break;
1313 		case LinphoneGlobalConfiguring:
1314 			if (linphone_core_get_provisioning_uri(lc)){
1315 				config_fetching_dialog=linphone_gtk_show_config_fetching();
1316 			}
1317 		break;
1318 		case LinphoneGlobalOn:
1319 			linphone_gtk_init_ui();
1320 			if (selftest) {
1321 				gtk_timeout_add(300,(GtkFunction)gtk_main_quit,NULL);
1322 			}
1323 		break;
1324 		default:
1325 		break;
1326 	}
1327 }
1328 
on_call_updated_response(GtkWidget * dialog,gint responseid,gpointer user_data)1329 static void on_call_updated_response(GtkWidget *dialog, gint responseid, gpointer user_data){
1330 	LinphoneCall *call = (LinphoneCall *)g_object_get_data(G_OBJECT(dialog), "call");
1331 	if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){
1332 		LinphoneCore *lc=linphone_call_get_core(call);
1333 		LinphoneCallParams *params = linphone_core_create_call_params(lc, call);
1334 		linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES);
1335 		linphone_call_accept_update(call,params);
1336 		linphone_call_params_unref(params);
1337 	}
1338 	g_source_remove_by_user_data(dialog);
1339 	gtk_widget_destroy(dialog);
1340 }
1341 
on_call_updated_timeout(GtkWidget * dialog)1342 static void on_call_updated_timeout(GtkWidget *dialog){
1343 	on_call_updated_response(dialog, GTK_RESPONSE_NO, NULL);
1344 }
1345 
linphone_gtk_call_updated_by_remote(LinphoneCall * call)1346 static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){
1347 	LinphoneCore *lc=linphone_call_get_core(call);
1348 	const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc);
1349 	const LinphoneCallParams *rparams=linphone_call_get_remote_params(call);
1350 	const LinphoneCallParams *current_params=linphone_call_get_current_params(call);
1351 	gboolean video_requested=linphone_call_params_video_enabled(rparams);
1352 	gboolean video_used=linphone_call_params_video_enabled(current_params);
1353 	g_message("Video used=%i, video requested=%i, automatically_accept=%i",
1354 			  video_used,video_requested,pol->automatically_accept);
1355 	if (!video_used && video_requested && !pol->automatically_accept){
1356 		linphone_call_defer_update(call);
1357 		{
1358 			const LinphoneAddress *addr=linphone_call_get_remote_address(call);
1359 			GtkWidget *dialog;
1360 			const char *dname=linphone_address_get_display_name(addr);
1361 			if (dname==NULL) dname=linphone_address_get_username(addr);
1362 			if (dname==NULL) dname=linphone_address_get_domain(addr);
1363 			dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()),
1364 													 GTK_DIALOG_DESTROY_WITH_PARENT,
1365 													 GTK_MESSAGE_WARNING,
1366 													 GTK_BUTTONS_YES_NO,
1367 													 _("%s proposed to start video. Do you accept ?"),dname);
1368 			g_object_set_data_full(G_OBJECT(dialog), "call", linphone_call_ref(call), (GDestroyNotify)linphone_call_unref);
1369 			g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(on_call_updated_response), NULL);
1370 			g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog);
1371 			gtk_widget_show(dialog);
1372 		}
1373 	}
1374 }
1375 
linphone_gtk_call_state_changed(LinphoneCore * lc,LinphoneCall * call,LinphoneCallState cs,const char * msg)1376 static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){
1377 	const LinphoneErrorInfo *ei;
1378 
1379 	switch(cs){
1380 		case LinphoneCallOutgoingInit:
1381 			linphone_gtk_create_in_call_view (call);
1382 		break;
1383 		case LinphoneCallOutgoingProgress:
1384 			linphone_gtk_in_call_view_set_calling (call);
1385 		break;
1386 		case LinphoneCallStreamsRunning:
1387 			linphone_gtk_in_call_view_set_in_call(call);
1388 		break;
1389 		case LinphoneCallUpdatedByRemote:
1390 			linphone_gtk_call_updated_by_remote(call);
1391 		break;
1392 		case LinphoneCallError:
1393 			linphone_gtk_in_call_view_terminate (call,msg);
1394 		break;
1395 		case LinphoneCallEnd:
1396 			ei = linphone_call_get_error_info(call);
1397 			if (ei && linphone_error_info_get_reason(ei) != LinphoneReasonNone) {
1398 				linphone_gtk_in_call_view_terminate(call, linphone_error_info_get_phrase(ei));
1399 			} else {
1400 				linphone_gtk_in_call_view_terminate(call, NULL);
1401 			}
1402 			linphone_gtk_status_icon_set_blinking(FALSE);
1403 		break;
1404 		case LinphoneCallIncomingReceived:
1405 			linphone_gtk_create_in_call_view(call);
1406 			linphone_gtk_in_call_view_set_incoming(call);
1407 			linphone_gtk_status_icon_set_blinking(TRUE);
1408 			if (linphone_gtk_auto_answer_enabled())  {
1409 				int delay = linphone_gtk_get_ui_config_int("auto_answer_delay", 2000);
1410 				linphone_call_ref(call);
1411 				g_timeout_add(delay, (GSourceFunc)linphone_gtk_auto_answer, call);
1412 			}
1413 		break;
1414 		case LinphoneCallResuming:
1415 			linphone_gtk_enable_hold_button(call,TRUE,TRUE);
1416 			linphone_gtk_in_call_view_set_in_call (call);
1417 		break;
1418 		case LinphoneCallPausing:
1419 			linphone_gtk_enable_hold_button(call,TRUE,FALSE);
1420 			linphone_gtk_call_update_tab_header(call,FALSE);
1421 			BCTBX_NO_BREAK;
1422 		case LinphoneCallPausedByRemote:
1423 			linphone_gtk_in_call_view_set_paused(call);
1424 			linphone_gtk_call_update_tab_header(call,TRUE);
1425 		break;
1426 		case LinphoneCallConnected:
1427 			linphone_gtk_enable_hold_button (call,TRUE,TRUE);
1428 			linphone_gtk_status_icon_set_blinking(FALSE);
1429 		break;
1430 		default:
1431 		break;
1432 	}
1433 	linphone_gtk_notify(call, NULL, msg);
1434 	linphone_gtk_update_call_buttons (call);
1435 }
1436 
linphone_gtk_call_encryption_changed(LinphoneCore * lc,LinphoneCall * call,bool_t enabled,const char * token)1437 static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token){
1438 	linphone_gtk_in_call_view_show_encryption(call);
1439 }
1440 
linphone_gtk_transfer_state_changed(LinphoneCore * lc,LinphoneCall * call,LinphoneCallState cstate)1441 static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){
1442 	linphone_gtk_in_call_view_set_transfer_status(call,cstate);
1443 }
1444 
update_registration_status(LinphoneProxyConfig * cfg,LinphoneRegistrationState rs)1445 static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){
1446 	GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1447 	GtkTreeModel *model=gtk_combo_box_get_model(box);
1448 	GtkTreeIter iter;
1449 	gboolean found=FALSE;
1450 	const char *icon_name=NULL;
1451 
1452 	if (gtk_tree_model_get_iter_first(model,&iter)){
1453 		gpointer p;
1454 		do{
1455 			gtk_tree_model_get(model,&iter,2,&p,-1);
1456 			if (p==cfg) {
1457 				found=TRUE;
1458 				break;
1459 			}
1460 		}while(gtk_tree_model_iter_next(model,&iter));
1461 	}
1462 	if (!found) {
1463 		/*ignored, this is a notification for a removed proxy config.*/
1464 		return;
1465 	}
1466 	switch (rs){
1467 		case LinphoneRegistrationOk:
1468 			icon_name="linphone-ok";
1469 		break;
1470 		case LinphoneRegistrationProgress:
1471 			icon_name="linphone-inprogress";
1472 		break;
1473 		case LinphoneRegistrationCleared:
1474 			icon_name=NULL;
1475 		break;
1476 		case LinphoneRegistrationFailed:
1477 			icon_name="linphone-failed";
1478 		break;
1479 		default:
1480 		break;
1481 	}
1482 	gtk_list_store_set(GTK_LIST_STORE(model),&iter,1,icon_name,-1);
1483 }
1484 
linphone_gtk_registration_state_changed(LinphoneCore * lc,LinphoneProxyConfig * cfg,LinphoneRegistrationState rs,const char * msg)1485 static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg,
1486 													LinphoneRegistrationState rs, const char *msg){
1487 	switch (rs){
1488 		case LinphoneRegistrationOk:
1489 			if (cfg){
1490 				SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1491 				if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1492 					linphone_gtk_exit_login_frame();
1493 				}
1494 			}
1495 		break;
1496 		default:
1497 		break;
1498 	}
1499 	update_registration_status(cfg,rs);
1500 }
1501 
linphone_gtk_open_browser(const char * uri)1502 void linphone_gtk_open_browser(const char *uri) {
1503 #ifdef __APPLE__
1504 	GError *error = NULL;
1505 	char cmd_line[256];
1506 
1507 	g_snprintf(cmd_line, sizeof(cmd_line), "%s %s", "/usr/bin/open", uri);
1508 	g_spawn_command_line_async(cmd_line, &error);
1509 	if (error) {
1510 		g_warning("Could not open %s: %s", uri, error->message);
1511 		g_error_free(error);
1512 	}
1513 #elif defined(_WIN32)
1514 	HINSTANCE instance = ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOWNORMAL);
1515 	if ((int)instance <= 32) {
1516 		g_warning("Could not open %s (error #%i)", uri, (int)instance);
1517 	}
1518 #else
1519 	GError *error = NULL;
1520 	gtk_show_uri(NULL, uri, GDK_CURRENT_TIME, &error);
1521 	if (error) {
1522 		g_warning("Could not open %s: %s", uri, error->message);
1523 		g_error_free(error);
1524 	}
1525 #endif
1526 }
1527 
linphone_gtk_link_to_website(GtkWidget * item)1528 void linphone_gtk_link_to_website(GtkWidget *item){
1529 	const gchar *home=(const gchar*)g_object_get_data(G_OBJECT(item),"home");
1530 	linphone_gtk_open_browser(home);
1531 }
1532 
create_icon_menu(void)1533 static GtkWidget *create_icon_menu(void){
1534 	GtkWidget *menu=gtk_menu_new();
1535 	GtkWidget *menu_item;
1536 	GtkWidget *image;
1537 	gchar *tmp;
1538 	const gchar *homesite;
1539 
1540 	homesite=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1541 	menu_item=gtk_image_menu_item_new_with_label(_("Website link"));
1542 	tmp=g_strdup(homesite);
1543 	g_object_set_data(G_OBJECT(menu_item),"home",tmp);
1544 	g_object_weak_ref(G_OBJECT(menu_item),(GWeakNotify)g_free,tmp);
1545 
1546 	image=gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU);
1547 	gtk_widget_show(image);
1548 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),image);
1549 	//g_object_unref(G_OBJECT(image));
1550 	gtk_widget_show(menu_item);
1551 	gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1552 	g_signal_connect(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_link_to_website,NULL);
1553 
1554 	menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT,NULL);
1555 	gtk_widget_show(menu_item);
1556 	gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1557 	g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_show_about,NULL);
1558 	menu_item=gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,NULL);
1559 	gtk_widget_show(menu_item);
1560 	gtk_menu_shell_append(GTK_MENU_SHELL(menu),menu_item);
1561 	g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)gtk_main_quit,NULL);
1562 	gtk_widget_show(menu);
1563 	return menu;
1564 }
1565 
1566 #ifndef HAVE_GTK_OSX
linphone_gtk_save_main_window_position(GtkWindow * mw,GdkEvent * event,gpointer data)1567 void linphone_gtk_save_main_window_position(GtkWindow* mw, GdkEvent *event, gpointer data){
1568 	   gtk_window_get_position(GTK_WINDOW(mw), &main_window_x, &main_window_y);
1569 }
1570 #endif
1571 
handle_icon_click(LinphoneStatusIcon * si,void * user_data)1572 static void handle_icon_click(LinphoneStatusIcon *si, void *user_data) {
1573 #ifndef HAVE_GTK_OSX
1574 	GtkWidget *mw=linphone_gtk_get_main_window();
1575 	if (!gtk_window_is_active((GtkWindow*)mw)) {
1576 		if(!gtk_widget_is_drawable(mw)){
1577 			//we only move if window was hidden. If it was simply behind the window stack, ie, drawable, we keep it as it was
1578 			gtk_window_move (GTK_WINDOW(mw), main_window_x, main_window_y);
1579 		}
1580 		linphone_gtk_show_main_window();
1581 	} else {
1582 		linphone_gtk_save_main_window_position((GtkWindow*)mw, NULL, NULL);
1583 		gtk_widget_hide(mw);
1584 	}
1585 #endif
1586 }
1587 
linphone_gtk_status_icon_initialised_cb(LinphoneStatusIconParams * params)1588 static void linphone_gtk_status_icon_initialised_cb(LinphoneStatusIconParams *params) {
1589 	LinphoneStatusIcon *icon = linphone_status_icon_get();
1590 	if(icon) {
1591 		linphone_status_icon_start(icon, params);
1592 	}
1593 	linphone_status_icon_params_unref(params);
1594 }
1595 
linphone_gtk_init_status_icon(void)1596 static void linphone_gtk_init_status_icon(void) {
1597 	GtkWidget *menu = create_icon_menu();
1598 	LinphoneStatusIconParams *params = linphone_status_icon_params_new();
1599 	linphone_status_icon_params_set_menu(params, menu);
1600 	linphone_status_icon_params_set_title(params, _("Linphone"));
1601 	linphone_status_icon_params_set_description(params, _("A video internet phone"));
1602 	linphone_status_icon_params_set_on_click_cb(params, handle_icon_click, NULL);
1603 
1604 	if(linphone_status_icon_init(
1605 		(LinphoneStatusIconReadyCb)linphone_gtk_status_icon_initialised_cb,
1606 		params)) {
1607 
1608 		LinphoneStatusIcon *icon = linphone_status_icon_get();
1609 		if(icon) {
1610 			linphone_status_icon_start(icon, params);
1611 		}
1612 		linphone_status_icon_params_unref(params);
1613 	}
1614 }
1615 
linphone_gtk_status_icon_set_blinking(gboolean val)1616 void linphone_gtk_status_icon_set_blinking(gboolean val) {
1617 	LinphoneStatusIcon *icon = linphone_status_icon_get();
1618 	if(icon) {
1619 		linphone_status_icon_enable_blinking(icon, val);
1620 	}
1621 #ifdef __APPLE__
1622 	linphone_gtk_update_badge_count();
1623 #endif
1624 }
1625 
linphone_gtk_options_activate(GtkWidget * item)1626 void linphone_gtk_options_activate(GtkWidget *item){
1627 #ifndef HAVE_GTK_OSX
1628 	gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"),
1629 		TRUE);
1630 #endif
1631 }
1632 
init_identity_combo(GtkComboBox * box)1633 static void init_identity_combo(GtkComboBox *box){
1634 	GtkListStore *store;
1635 	GtkCellRenderer *r1,*r2;
1636 	store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_POINTER);
1637 	gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
1638 	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box),(r1=gtk_cell_renderer_text_new()),TRUE);
1639 	gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(box),(r2=gtk_cell_renderer_pixbuf_new()),FALSE);
1640 	gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r1,"text",0);
1641 	gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(box),r2,"icon-name",1);
1642 	g_object_set(G_OBJECT(r1),"ellipsize",PANGO_ELLIPSIZE_END,NULL);
1643 	gtk_combo_box_set_model(box,GTK_TREE_MODEL(store));
1644 }
1645 
linphone_gtk_load_identities(void)1646 void linphone_gtk_load_identities(void){
1647 	const bctbx_list_t *elem;
1648 	GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities"));
1649 	char *def_identity;
1650 	LinphoneProxyConfig *def=NULL;
1651 	int def_index=0,i;
1652 	GtkListStore *store;
1653 	GtkTreeIter iter;
1654 
1655 	store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1656 	if (gtk_tree_model_get_n_columns(GTK_TREE_MODEL(store))==1){
1657 		/* model is empty, this is the first time we go here */
1658 		init_identity_combo(box);
1659 		store=GTK_LIST_STORE(gtk_combo_box_get_model(box));
1660 	}
1661 	gtk_list_store_clear(store);
1662 	def = linphone_core_get_default_proxy_config(linphone_gtk_get_core());
1663 	def_identity=g_strdup_printf(_("%s (Default)"),linphone_core_get_primary_contact(linphone_gtk_get_core()));
1664 	gtk_list_store_append(store,&iter);
1665 	gtk_list_store_set(store,&iter,0,def_identity,1,NULL,2,NULL,-1);
1666 	g_free(def_identity);
1667 	for(i=1,elem=linphone_core_get_proxy_config_list(linphone_gtk_get_core());
1668 			elem!=NULL;
1669 			elem=bctbx_list_next(elem),i++){
1670 		LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1671 		gtk_list_store_append(store,&iter);
1672 		gtk_list_store_set(store,&iter,0,linphone_proxy_config_get_identity(cfg),1,
1673 						   linphone_proxy_config_is_registered(cfg) ? "linphone-ok" : NULL,
1674 						   2,cfg,-1);
1675 		if (cfg==def) {
1676 			def_index=i;
1677 		}
1678 	}
1679 	gtk_combo_box_set_active(box,def_index);
1680 }
1681 
linphone_gtk_dtmf_pressed(GtkButton * button)1682 static void linphone_gtk_dtmf_pressed(GtkButton *button){
1683 	const char *label=(char *)g_object_get_data(G_OBJECT(button),"label");
1684 	GtkWidget *uri_bar=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar");
1685 	int pos=-1;
1686 	gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos);
1687 	linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1);
1688 	if (linphone_core_in_call(linphone_gtk_get_core())){
1689 		linphone_call_send_dtmf(linphone_core_get_current_call(linphone_gtk_get_core()),label[0]);
1690 	}
1691 }
1692 
linphone_gtk_dtmf_released(GtkButton * button)1693 static void linphone_gtk_dtmf_released(GtkButton *button){
1694 	linphone_core_stop_dtmf (linphone_gtk_get_core());
1695 }
1696 
1697 
linphone_gtk_connect_digits(GtkWidget * w)1698 static void linphone_gtk_connect_digits(GtkWidget *w){
1699 	GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(w,"dtmf_table"));
1700 	GList *children=gtk_container_get_children(cont);
1701 	GList *elem;
1702 	for(elem=children;elem!=NULL;elem=elem->next){
1703 		GtkButton *button=GTK_BUTTON(elem->data);
1704 		g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL);
1705 		g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL);
1706 	}
1707 }
1708 
linphone_gtk_check_menu_items(void)1709 static void linphone_gtk_check_menu_items(void){
1710 	bool_t video_enabled=linphone_gtk_video_enabled();
1711 	bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT);
1712 	GtkWidget *selfview_item=linphone_gtk_get_widget(
1713 					linphone_gtk_get_main_window(),"selfview_item");
1714 	gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget(
1715 					linphone_gtk_get_main_window(),"enable_video_item")), video_enabled);
1716 	gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview);
1717 }
1718 
linphone_gtk_can_manage_accounts(void)1719 static gboolean linphone_gtk_can_manage_accounts(void){
1720 	LinphoneCore *lc=linphone_gtk_get_core();
1721 	const bctbx_list_t *elem;
1722 	for(elem=linphone_core_get_sip_setups(lc);elem!=NULL;elem=elem->next){
1723 		SipSetup *ss=(SipSetup*)elem->data;
1724 		if (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_ACCOUNT_MANAGER){
1725 			return TRUE;
1726 		}
1727 	}
1728 	return FALSE;
1729 }
1730 
linphone_gtk_configure_main_window(void)1731 static void linphone_gtk_configure_main_window(void){
1732 	static gboolean config_loaded=FALSE;
1733 	static const char *title;
1734 	static const char *home;
1735 	static const char *search_icon;
1736 	static gboolean update_check_menu;
1737 	static gboolean show_abcd;
1738 	GtkWidget *w=linphone_gtk_get_main_window();
1739 
1740 	if (!config_loaded){
1741 		title=linphone_gtk_get_ui_config("title","Linphone");
1742 		home=linphone_gtk_get_ui_config("home","http://www.linphone.org");
1743 		search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL);
1744 		update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0);
1745 		show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1);
1746 		config_loaded=TRUE;
1747 	}
1748 	linphone_gtk_configure_window(w,"main_window");
1749 	if (title) {
1750 		gtk_window_set_title(GTK_WINDOW(w),title);
1751 	}
1752 	if (search_icon){
1753 		GdkPixbuf *pbuf=create_pixbuf(search_icon);
1754 		if(pbuf) {
1755 			gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf);
1756 			g_object_unref(G_OBJECT(pbuf));
1757 		}
1758 	}
1759 	if (home){
1760 		gchar *tmp;
1761 		GtkWidget *menu_item=linphone_gtk_get_widget(w,"home_item");
1762 		tmp=g_strdup(home);
1763 		g_object_set_data_full(G_OBJECT(menu_item),"home",tmp, (GDestroyNotify)g_free);
1764 	}
1765 	if (linphone_gtk_can_manage_accounts()) {
1766 		gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item"));
1767 	}
1768 	if (update_check_menu){
1769 		gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item"));
1770 	}
1771 	g_object_set_data(G_OBJECT(w),"show_abcd",GINT_TO_POINTER(show_abcd));
1772 }
1773 
linphone_gtk_manage_login(void)1774 void linphone_gtk_manage_login(void){
1775 	LinphoneCore *lc=linphone_gtk_get_core();
1776 	LinphoneProxyConfig *cfg=linphone_core_get_default_proxy_config(lc);
1777 	if (cfg){
1778 		SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg);
1779 		if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){
1780 			linphone_gtk_show_login_frame(cfg,FALSE);
1781 		}
1782 	}
1783 }
1784 
linphone_gtk_close(GtkWidget * mw)1785 gboolean linphone_gtk_close(GtkWidget *mw){
1786 	/*shutdown calls if any*/
1787 	LinphoneCore *lc=linphone_gtk_get_core();
1788 	GtkWidget *camera_preview=linphone_gtk_get_camera_preview_window();
1789 	if (linphone_core_in_call(lc)){
1790 		linphone_core_terminate_all_calls(lc);
1791 	}
1792 	if (camera_preview) gtk_widget_destroy(camera_preview);
1793 #ifdef __APPLE__ /*until with have a better option*/
1794 	gtk_window_iconify(GTK_WINDOW(mw));
1795 #else
1796 	gtk_widget_hide(mw);
1797 #endif
1798 	return TRUE;
1799 }
1800 
1801 #ifdef HAVE_GTK_OSX
on_window_state_event(GtkWidget * w,GdkEventWindowState * event)1802 static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){
1803 	return FALSE;
1804 }
1805 #endif
1806 
linphone_gtk_init_dtmf_table(GtkWidget * mw)1807 void linphone_gtk_init_dtmf_table(GtkWidget *mw){
1808 	GtkWidget *dtmf_table=linphone_gtk_get_widget(mw,"dtmf_table");
1809 	gtk_widget_set_direction(dtmf_table, GTK_TEXT_DIR_LTR);
1810 
1811 	g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_A")),"label","A");
1812 	g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_B")),"label","B");
1813 	g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_C")),"label","C");
1814 	g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_D")),"label","D");
1815 	g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_1")),"label","1");
1816 	g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_2")),"label","2");
1817 	g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_3")),"label","3");
1818 	g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_4")),"label","4");
1819 	g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_5")),"label","5");
1820 	g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_6")),"label","6");
1821 	g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_7")),"label","7");
1822 	g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_8")),"label","8");
1823 	g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_9")),"label","9");
1824 	g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_0")),"label","0");
1825 	g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_#")),"label","#");
1826 	g_object_set_data(G_OBJECT(linphone_gtk_get_widget(mw,"dtmf_*")),"label","*");
1827 }
1828 
key_allowed(guint32 code)1829 static gboolean key_allowed(guint32 code){
1830 	static const char *allowed="1234567890#*ABCD";
1831 	return code!=0 && strchr(allowed,(char)code)!=NULL;
1832 }
1833 
get_button_from_key(GtkWidget * w,GdkEvent * event)1834 static GtkButton *get_button_from_key(GtkWidget *w, GdkEvent *event){
1835 	guint keyval=event->key.keyval;
1836 	guint32 code=gdk_keyval_to_unicode(keyval);
1837 	code=g_unichar_toupper(code);
1838 	if (key_allowed(code)){
1839 		char widgetname[16];
1840 		w=gtk_widget_get_toplevel(w);
1841 		snprintf(widgetname,sizeof(widgetname),"dtmf_%c",code);
1842 		return GTK_BUTTON(linphone_gtk_get_widget(w,widgetname));
1843 	}
1844 	return NULL;
1845 }
1846 
linphone_gtk_keypad_key_pressed(GtkWidget * w,GdkEvent * event,gpointer userdata)1847 void linphone_gtk_keypad_key_pressed(GtkWidget *w, GdkEvent *event, gpointer userdata){
1848 	GtkButton *button=get_button_from_key(w,event);
1849 	if (button) {
1850 		linphone_gtk_dtmf_pressed(button);
1851 		/*g_signal_emit_by_name(button, "button-press-event");*/
1852 	}
1853 }
1854 
linphone_gtk_keypad_key_released(GtkWidget * w,GdkEvent * event,gpointer userdata)1855 void linphone_gtk_keypad_key_released(GtkWidget *w, GdkEvent *event, gpointer userdata){
1856 	GtkButton *button=get_button_from_key(w,event);
1857 	if (button) {
1858 		linphone_gtk_dtmf_released(button);
1859 		/*g_signal_emit_by_name(button, "button-release-event");*/
1860 	}
1861 }
1862 
linphone_gtk_show_keypad(void)1863 static void linphone_gtk_show_keypad(void){
1864 	GtkWidget *mw=linphone_gtk_get_main_window();
1865 	GtkWidget *k=(GtkWidget *)g_object_get_data(G_OBJECT(mw),"keypad");
1866 	GtkWidget *keypad;
1867 	if(k!=NULL){
1868 		gtk_widget_destroy(k);
1869 	}
1870 	keypad=linphone_gtk_create_window("keypad", NULL);
1871 	linphone_gtk_connect_digits(keypad);
1872 	linphone_gtk_init_dtmf_table(keypad);
1873 	g_object_set_data(G_OBJECT(mw),"keypad", keypad);
1874 	if(!GPOINTER_TO_INT(g_object_get_data(G_OBJECT(mw),"show_abcd"))){
1875 		gtk_widget_hide(linphone_gtk_get_widget(keypad,"dtmf_A"));
1876 		gtk_widget_hide(linphone_gtk_get_widget(keypad,"dtmf_B"));
1877 		gtk_widget_hide(linphone_gtk_get_widget(keypad,"dtmf_C"));
1878 		gtk_widget_hide(linphone_gtk_get_widget(keypad,"dtmf_D"));
1879 		gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(keypad,"dtmf_table")),4,3);
1880 	}
1881 	gtk_widget_show(keypad);
1882 }
1883 
linphone_gtk_destroy_keypad(void)1884 static void linphone_gtk_destroy_keypad(void) {
1885 	GtkWidget *mw = linphone_gtk_get_main_window();
1886 	GtkWidget *keypad = GTK_WIDGET(g_object_get_data(G_OBJECT(mw), "keypad"));
1887 	if(keypad) {
1888 		gtk_widget_destroy(keypad);
1889 		g_object_set_data(G_OBJECT(mw), "keypad", NULL);
1890 	}
1891 }
1892 
linphone_gtk_show_keypad_checked(GtkCheckMenuItem * check_menu_item)1893 void linphone_gtk_show_keypad_checked(GtkCheckMenuItem *check_menu_item) {
1894 	if(gtk_check_menu_item_get_active(check_menu_item)) {
1895 		linphone_gtk_show_keypad();
1896 	} else {
1897 		linphone_gtk_destroy_keypad();
1898 	}
1899 }
1900 
linphone_gtk_import_contacts(void)1901 void linphone_gtk_import_contacts(void) {
1902 	GtkWidget *mw = linphone_gtk_get_main_window();
1903 	GtkWidget *dialog = gtk_file_chooser_dialog_new("Open vCard file", (GtkWindow *)mw, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
1904 
1905 	if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
1906 		LinphoneCore *lc = linphone_gtk_get_core();
1907 		char *filename_utf8 = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1908 		char *filename = g_locale_from_utf8(filename_utf8, -1, NULL, NULL, NULL);
1909 		LinphoneFriendList *list = linphone_core_get_default_friend_list(lc);
1910 		linphone_friend_list_import_friends_from_vcard4_file(list, filename);
1911 		linphone_gtk_show_friends();
1912 		g_free(filename_utf8);
1913 		g_free(filename);
1914 	}
1915 	gtk_widget_destroy(dialog);
1916 }
1917 
linphone_gtk_export_contacts(void)1918 void linphone_gtk_export_contacts(void) {
1919 	GtkWidget *mw = linphone_gtk_get_main_window();
1920 	GtkWidget *dialog = gtk_file_chooser_dialog_new("Save vCards as", (GtkWindow *)mw, GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
1921 	gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1922 
1923 	if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
1924 		LinphoneCore *lc = linphone_gtk_get_core();
1925 		char *filename_utf8 = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1926 		char *filename = g_locale_from_utf8(filename_utf8, -1, NULL, NULL, NULL);
1927 		LinphoneFriendList *list = linphone_core_get_default_friend_list(lc);
1928 		linphone_friend_list_export_friends_as_vcard4_file(list, filename);
1929 		g_free(filename_utf8);
1930 		g_free(filename);
1931 	}
1932 	gtk_widget_destroy(dialog);
1933 }
1934 
linphone_gtk_keypad_destroyed_handler(void)1935 gboolean linphone_gtk_keypad_destroyed_handler(void) {
1936 	GtkWidget *mw = linphone_gtk_get_main_window();
1937 	GtkWidget *show_keypad_item = linphone_gtk_get_widget(mw, "show_keypad_menu_item");
1938 	gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(show_keypad_item), FALSE);
1939 	return FALSE;
1940 }
1941 
linphone_gtk_update_status_bar_icons(void)1942 void linphone_gtk_update_status_bar_icons(void) {
1943 	GtkWidget *mw = linphone_gtk_get_main_window();
1944 	GtkWidget *icon = linphone_gtk_get_widget(mw, "autoanswer_icon");
1945 	gtk_widget_set_visible(icon, linphone_gtk_auto_answer_enabled());
1946 }
1947 
linphone_gtk_init_main_window(void)1948 static void linphone_gtk_init_main_window(void){
1949 	GtkWidget *main_window;
1950 	linphone_gtk_configure_main_window();
1951 	linphone_gtk_manage_login();
1952 	linphone_gtk_load_identities();
1953 	linphone_gtk_set_my_presence(linphone_core_get_presence_info(linphone_gtk_get_core()));
1954 	linphone_gtk_show_friends();
1955 	linphone_gtk_update_status_bar_icons();
1956 	load_uri_history();
1957 	linphone_core_reset_missed_calls_count(linphone_gtk_get_core());
1958 	main_window=linphone_gtk_get_main_window();
1959 	linphone_gtk_call_log_update(main_window);
1960 
1961 	linphone_gtk_update_call_buttons (NULL);
1962 	g_object_set_data(G_OBJECT(main_window),"keypad",NULL);
1963 	g_object_set_data(G_OBJECT(main_window),"is_conf",GINT_TO_POINTER(FALSE));
1964 	/*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/
1965 	g_signal_connect (G_OBJECT (main_window), "delete-event",
1966 		G_CALLBACK (linphone_gtk_close), main_window);
1967 #ifdef HAVE_GTK_OSX
1968 	{
1969 		gtk_widget_show(main_window);
1970 		GtkWidget *menubar=linphone_gtk_get_widget(main_window,"menubar1");
1971 		GtkosxApplication *theMacApp = gtkosx_application_get();
1972 		gtkosx_application_set_menu_bar(theMacApp,GTK_MENU_SHELL(menubar));
1973 		gtk_widget_hide(menubar);
1974 		gtkosx_application_ready(theMacApp);
1975 	}
1976 	g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL);
1977 #endif
1978 	linphone_gtk_check_menu_items();
1979 	linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE);
1980 #ifdef BUILD_WIZARD
1981 	gtk_widget_set_visible(linphone_gtk_get_widget(main_window, "assistant_item"), TRUE);
1982 #else
1983 	gtk_widget_set_visible(linphone_gtk_get_widget(main_window, "assistant_item"), FALSE);
1984 #endif
1985 }
1986 
linphone_gtk_log_handler(const char * domain,OrtpLogLevel lev,const char * fmt,va_list args)1987 void linphone_gtk_log_handler(const char*domain, OrtpLogLevel lev, const char *fmt, va_list args){
1988 	if (verbose){
1989 		const char *lname="undef";
1990 		char *msg;
1991 #if defined(__linux) || defined(__APPLE__)
1992 		va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
1993 #endif
1994 		switch(lev){
1995 			case ORTP_DEBUG:
1996 				lname="debug";
1997 				break;
1998 			case ORTP_MESSAGE:
1999 				lname="message";
2000 				break;
2001 			case ORTP_WARNING:
2002 				lname="warning";
2003 				break;
2004 			case ORTP_ERROR:
2005 				lname="error";
2006 				break;
2007 			case ORTP_FATAL:
2008 				lname="fatal";
2009 				break;
2010 			default:
2011 				g_error("Bad level !");
2012 		}
2013 #if defined(__linux) || defined(__APPLE__)
2014 		va_copy(cap,args);
2015 		msg=g_strdup_vprintf(fmt,cap);
2016 		va_end(cap);
2017 #else
2018 		msg=g_strdup_vprintf(fmt,args);
2019 #endif
2020 		fprintf(stdout,"linphone-%s : %s\n",lname,msg);
2021 		ortp_free(msg);
2022 	}
2023 	linphone_gtk_log_push(lev,fmt,args);
2024 }
2025 
2026 
linphone_gtk_refer_received(LinphoneCore * lc,const char * refer_to)2027 void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
2028 	char method[20] = "";
2029 	LinphoneAddress *addr = linphone_address_new(refer_to);
2030 	if(addr) {
2031 		const char *tmp = linphone_address_get_method_param(addr);
2032 		strncpy(method, tmp, sizeof(20));
2033 		linphone_address_unref(addr);
2034 	}
2035 	if(strlen(method) == 0 || strcmp(method, "INVITE") == 0) {
2036 		GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
2037 			linphone_gtk_get_main_window(), "uribar"));
2038 		char *text;
2039 		linphone_gtk_notify(NULL,NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
2040 		g_free(text);
2041 		gtk_entry_set_text(uri_bar, refer_to);
2042 		linphone_gtk_start_call(linphone_gtk_get_main_window());
2043 	}
2044 }
2045 
linphone_gtk_check_soundcards(void)2046 static void linphone_gtk_check_soundcards(void){
2047 	const char **devices=linphone_core_get_sound_devices(linphone_gtk_get_core());
2048 	if (devices==NULL || devices[0]==NULL){
2049 		linphone_gtk_display_something(GTK_MESSAGE_WARNING,
2050 			_("No sound cards have been detected on this computer.\n"
2051 				"You won't be able to send or receive audio calls."));
2052 	}
2053 }
2054 
linphone_gtk_quit_core(void)2055 static void linphone_gtk_quit_core(void){
2056 #ifdef HAVE_GTK_OSX
2057 	{
2058 		GtkosxApplication *theMacApp = gtkosx_application_get();
2059 		gtkosx_application_set_menu_bar(theMacApp,NULL);
2060 	}
2061 #endif
2062 	linphone_gtk_unmonitor_usb();
2063 	g_source_remove_by_user_data(linphone_gtk_get_core());
2064 #ifdef BUILD_WIZARD
2065 	linphone_gtk_close_assistant();
2066 #endif
2067 	linphone_gtk_set_ldap(NULL);
2068 	linphone_gtk_destroy_log_window();
2069 	linphone_core_destroy(the_core);
2070 	linphone_gtk_log_uninit();
2071 }
2072 
linphone_gtk_quit(void)2073 static void linphone_gtk_quit(void){
2074 	if (!quit_done){
2075 		quit_done=TRUE;
2076 		linphone_gtk_quit_core();
2077 		linphone_gtk_uninit_instance();
2078 #ifdef HAVE_NOTIFY
2079 		notify_uninit();
2080 #endif
2081 		linphone_status_icon_uninit();
2082 		gtk_widget_destroy(the_ui);
2083 		the_ui=NULL;
2084 		gdk_threads_leave();
2085 	}
2086 }
2087 
2088 #ifdef HAVE_GTK_OSX
2089 /*
2090 This is not the correct way to implement block termination.
2091 The good way would be to call gtk_main_quit(), and return TRUE.
2092 Unfortunately this does not work, because if we return TRUE the NSApplication sometimes calls the CFRunLoop recursively, which prevents gtk_main() to exit.
2093 As a result the program cannot exit at all.
2094 As a workaround we do all the cleanup (unregistration and config save) within the handler.
2095 */
on_block_termination(void)2096 static gboolean on_block_termination(void){
2097 	gtk_main_quit();
2098 	linphone_gtk_quit();
2099 	return FALSE;
2100 }
2101 #endif
2102 
linphone_gtk_init_ui(void)2103 static void linphone_gtk_init_ui(void){
2104 	linphone_gtk_init_main_window();
2105 
2106 #ifdef BUILD_WIZARD
2107 	// Veryfing if at least one sip account is configured. If not, show wizard
2108 	if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) {
2109 		linphone_gtk_show_assistant();
2110 	}
2111 #endif
2112 
2113 	if(run_audio_assistant){
2114 		linphone_gtk_show_audio_assistant();
2115 		start_option=START_AUDIO_ASSISTANT;
2116 		iconified = TRUE;
2117 	}
2118 
2119 	linphone_gtk_init_status_icon();
2120 
2121 	if (!iconified){
2122 		linphone_gtk_show_main_window();
2123 		linphone_gtk_check_soundcards();
2124 	}
2125 	if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
2126 		linphone_gtk_check_for_new_version();
2127 	linphone_gtk_monitor_usb();
2128 }
2129 
sigint_handler(int signum)2130 static void sigint_handler(int signum){
2131 	gtk_main_quit();
2132 }
2133 
populate_xdg_data_dirs_envvar(void)2134 static void populate_xdg_data_dirs_envvar(void) {
2135 #ifndef _WIN32
2136 	int i;
2137 	gchar *value;
2138 	gchar **paths;
2139 	const char *data_dir;
2140 	LinphoneFactory *factory = linphone_factory_get();
2141 
2142 	if(g_getenv("XDG_DATA_DIRS") == NULL) {
2143 		value = g_strdup("/usr/share:/usr/local/share:/opt/local/share");
2144 	} else {
2145 		value = g_strdup(g_getenv("XDG_DATA_DIRS"));
2146 	}
2147 	paths = g_strsplit(value, ":", -1);
2148 	data_dir = linphone_factory_get_top_resources_dir(factory);
2149 	for(i=0; paths[i] && strcmp(paths[i], data_dir) != 0; i++);
2150 	if(paths[i] == NULL) {
2151 		gchar *new_value = g_strdup_printf("%s:%s", data_dir, value);
2152 		g_setenv("XDG_DATA_DIRS", new_value, TRUE);
2153 		g_free(new_value);
2154 	}
2155 	g_strfreev(paths);
2156 #endif
2157 }
2158 #define ZRTP_CACHE_CONFIG_FILE ".linphone-zidcache.db"
2159 
2160 
main(int argc,char * argv[])2161 int main(int argc, char *argv[]){
2162 	char *config_file;
2163 	const char *factory_config_file;
2164 	const char *lang;
2165 	GtkSettings *settings;
2166 	const char *icon_name=LINPHONE_ICON_NAME;
2167 	const char *app_name="Linphone";
2168 	LpConfig *factory_config;
2169 	char *chat_messages_db_file, *call_logs_db_file, *friends_db_file;
2170 	GError *error=NULL;
2171 	const char *tmp;
2172 	const char *resources_dir;
2173 	char *pixmaps_dir;
2174 	LinphoneFactory *factory;
2175 
2176 #if !GLIB_CHECK_VERSION(2, 31, 0)
2177 	g_thread_init(NULL);
2178 #endif
2179 	gdk_threads_init();
2180 
2181 	progpath = strdup(argv[0]);
2182 
2183 	config_file=linphone_gtk_get_config_file(NULL);
2184 
2185 	workingdir= (tmp=g_getenv("LINPHONE_WORKDIR")) ? g_strdup(tmp) : NULL;
2186 
2187 #ifdef __linux
2188 	/*for pulseaudio:*/
2189 	g_setenv("PULSE_PROP_media.role", "phone", TRUE);
2190 #endif
2191 
2192 	populate_xdg_data_dirs_envvar();
2193 
2194 	lang=linphone_gtk_get_lang(config_file);
2195 	if (lang == NULL || lang[0]=='\0'){
2196 		lang = g_getenv("LANGUAGE");
2197 		if (!lang) lang = g_getenv("LANG");
2198 	}
2199 	if (lang && lang[0]!='\0'){
2200 #ifdef _WIN32
2201 		if (strncmp(lang,"zh",2)==0){
2202 			workaround_gtk_entry_chinese_bug=TRUE;
2203 		}
2204 #endif
2205 		g_setenv("LANGUAGE",lang,1);
2206 	}
2207 
2208 #ifdef ENABLE_NLS
2209 	setlocale(LC_ALL, "");
2210 	bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
2211 	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2212 
2213 	/*do not use textdomain(): this sets a global default domain. On Mac OS bundle, it breaks gtk translations (obscure bug somewhere)*/
2214 	/*textdomain (GETTEXT_PACKAGE);*/
2215 #else
2216 	g_message("NLS disabled.\n");
2217 #endif
2218 #ifdef _WIN32
2219 	gtk_rc_add_default_file("./gtkrc");
2220 #endif
2221 	gdk_threads_enter();
2222 
2223 	if (!gtk_init_with_args(&argc,&argv,_("A free SIP video-phone"),
2224 				linphone_options,NULL,&error)){
2225 		gdk_threads_leave();
2226 		g_critical("%s", error->message);
2227 		return -1;
2228 	}
2229 	if(version) {
2230 		g_message("Linphone version %s.", linphone_core_get_version());
2231 		return 0;
2232 	}
2233 
2234 	if (config_file) g_free(config_file);
2235 	if (custom_config_file && !g_path_is_absolute(custom_config_file)) {
2236 		gchar *res = g_get_current_dir();
2237 		res = g_strjoin(G_DIR_SEPARATOR_S, res, custom_config_file, NULL);
2238 		free(custom_config_file);
2239 		custom_config_file = res;
2240 	}
2241 	config_file=linphone_gtk_get_config_file(custom_config_file);
2242 
2243 	if(run_audio_assistant) start_option=START_AUDIO_ASSISTANT;
2244 	if(addr_to_call != NULL) start_option=START_LINPHONE_WITH_CALL;
2245 
2246 	settings=gtk_settings_get_default();
2247 	g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM));
2248 	g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON));
2249 	g_object_set(settings, "gtk-menu-images", TRUE, NULL);
2250 	g_object_set(settings, "gtk-button-images", TRUE, NULL);
2251 
2252 	if (workingdir!=NULL){
2253 		if (chdir(workingdir)==-1){
2254 			g_error("Could not change directory to %s : %s",workingdir,strerror(errno));
2255 		}
2256 	}
2257 
2258 #if defined(__APPLE__) && defined(ENABLE_NLS)
2259 	/*workaround for bundles. GTK is unable to find translations in the bundle (obscure bug again).
2260 	So we help it:*/
2261 	{
2262 		if (g_file_test(PACKAGE_LOCALE_DIR, G_FILE_TEST_IS_DIR)){
2263 			bindtextdomain("gtk20",PACKAGE_LOCALE_DIR);
2264 			bindtextdomain("gdk-pixbuf",PACKAGE_LOCALE_DIR);
2265 			bindtextdomain("glib20",PACKAGE_LOCALE_DIR);
2266 		}
2267 	}
2268 #endif
2269 	add_pixmap_directory("pixmaps");
2270 	factory = linphone_factory_get();
2271 	resources_dir = linphone_factory_get_top_resources_dir(factory);
2272 	pixmaps_dir = bctbx_strdup_printf("%s/pixmaps/linphone", resources_dir);
2273 	add_pixmap_directory(pixmaps_dir);
2274 	bctbx_free(pixmaps_dir);
2275 
2276 	/* Now, look for the factory configuration file, we do it this late
2277 		 since we want to have had time to change directory and to parse
2278 		 the options, in case we needed to access the working directory */
2279 	factory_config_file = linphone_gtk_get_factory_config_file();
2280 	if (factory_config_file){
2281 		factory_config=lp_config_new(NULL);
2282 		lp_config_read_file(factory_config,factory_config_file);
2283 		app_name=lp_config_get_string(factory_config,"GtkUi","title","Linphone");
2284 		icon_name=lp_config_get_string(factory_config,"GtkUi","icon_name",LINPHONE_ICON_NAME);
2285 	}
2286 	g_set_application_name(app_name);
2287 	gtk_window_set_default_icon_name(icon_name);
2288 
2289 #ifdef HAVE_GTK_OSX
2290 	GtkosxApplication *theMacApp = gtkosx_application_get();
2291 	g_signal_connect(G_OBJECT(theMacApp),"NSApplicationDidBecomeActive",(GCallback)linphone_gtk_show_main_window,NULL);
2292 	g_signal_connect(G_OBJECT(theMacApp),"NSApplicationWillTerminate",(GCallback)gtk_main_quit,NULL);
2293 	/*never block termination:*/
2294 	g_signal_connect(G_OBJECT(theMacApp),"NSApplicationBlockTermination",(GCallback)on_block_termination,NULL);
2295 #endif
2296 
2297 core_start:
2298 	if (linphone_gtk_init_instance(app_name, start_option, addr_to_call) == FALSE){
2299 		g_warning("Another running instance of Linphone has been detected. It has been woken-up.");
2300 		g_warning("This instance is going to exit now.");
2301 		gdk_threads_leave();
2302 		return 0;
2303 	}
2304 	the_ui=linphone_gtk_create_window("main", NULL);
2305 
2306 	g_object_set_data(G_OBJECT(the_ui),"is_created",GINT_TO_POINTER(FALSE));
2307 	g_signal_connect(G_OBJECT (the_ui), "key_press_event", G_CALLBACK (linphone_gtk_on_key_press), NULL);
2308 
2309 	linphone_gtk_create_log_window();
2310 	linphone_core_enable_logs_with_cb(linphone_gtk_log_handler);
2311 	/*it is possible to filter in or out some logs by configuring per log domain:*/
2312 	/*ortp_set_log_level_mask("belle-sip", ORTP_ERROR);*/
2313 
2314 	chat_messages_db_file=linphone_gtk_message_storage_get_db_file(NULL);
2315 	call_logs_db_file = linphone_gtk_call_logs_storage_get_db_file(NULL);
2316 	friends_db_file = linphone_gtk_friends_storage_get_db_file(NULL);
2317 	linphone_gtk_init_liblinphone(config_file, factory_config_file, chat_messages_db_file, call_logs_db_file, friends_db_file);
2318 	g_free(chat_messages_db_file);
2319 	g_free(call_logs_db_file);
2320 	g_free(friends_db_file);
2321 
2322 	linphone_gtk_call_log_update(the_ui);
2323 	linphone_gtk_show_friends();
2324 
2325 	/* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/
2326 	gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core());
2327 	gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)linphone_gtk_get_core());
2328 
2329 	signal(SIGINT, sigint_handler);
2330 
2331 	gtk_main();
2332 	linphone_gtk_quit();
2333 
2334 	if (restart){
2335 		quit_done=FALSE;
2336 		restart=FALSE;
2337 		goto core_start;
2338 	}
2339 	if (config_file) g_free(config_file);
2340 	free(progpath);
2341 	/*output a translated "hello" string to the terminal, which allows the builder to check that translations are working.*/
2342 	if (selftest){
2343 		printf(_("Hello\n"));
2344 	}
2345 	return 0;
2346 }
2347 
2348 #ifdef _MSC_VER
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)2349 int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
2350 	return main(__argc, __argv);
2351 }
2352 #endif
2353 
linphone_gtk_make_tab_header(const gchar * label,const gchar * icon_name,gboolean show_quit_button,GCallback cb,gpointer user_data)2354 GtkWidget *linphone_gtk_make_tab_header(const gchar *label, const gchar *icon_name, gboolean show_quit_button, GCallback cb, gpointer user_data) {
2355 	GtkWidget *tab_header=gtk_hbox_new (FALSE,0);
2356 	GtkWidget *label_widget = gtk_label_new (label);
2357 
2358 	if(icon_name) {
2359 		GtkWidget *icon=gtk_image_new_from_icon_name(icon_name, GTK_ICON_SIZE_MENU);
2360 #ifdef HAVE_GTK_OSX
2361 		gtk_misc_set_alignment(GTK_MISC(icon), 0.5f, 0.25f);
2362 #endif
2363 		gtk_box_pack_start (GTK_BOX(tab_header),icon,FALSE,FALSE,4);
2364 	}
2365 #ifdef HAVE_GTK_OSX
2366 	gtk_misc_set_alignment(GTK_MISC(label_widget), 0.5f, 0.f);
2367 #endif
2368 	gtk_box_pack_start (GTK_BOX(tab_header),label_widget,FALSE,FALSE,0);
2369 	if(show_quit_button) {
2370 		GtkWidget *button = gtk_button_new();
2371 		GtkWidget *button_image=gtk_image_new_from_stock(GTK_STOCK_CLOSE,GTK_ICON_SIZE_MENU);
2372 		gtk_button_set_image(GTK_BUTTON(button),button_image);
2373 		gtk_button_set_relief(GTK_BUTTON(button),GTK_RELIEF_NONE);
2374 #ifdef HAVE_GTK_OSX
2375 		gtk_misc_set_alignment(GTK_MISC(button_image), 0.5f, 0.f);
2376 #endif
2377 		g_signal_connect_swapped(G_OBJECT(button),"clicked",cb,user_data);
2378 		gtk_box_pack_end(GTK_BOX(tab_header),button,FALSE,FALSE,4);
2379 		g_object_set_data(G_OBJECT(tab_header), "button", button);
2380 	}
2381 	g_object_set_data(G_OBJECT(tab_header), "label", label_widget);
2382 	return tab_header;
2383 }
2384