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