pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
G_CONST_RETURN has been deprecated for awhile and honestly it's not necessary for us
release-2.x.y
2019-10-07, Gary Kramlich
e3c4fde8a94a
G_CONST_RETURN has been deprecated for awhile and honestly it's not necessary for us
/**
* @file gtkprefs.c GTK+ Preferences
* @ingroup pidgin
*/
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
#include
"internal.h"
#include
"pidgin.h"
#include
"debug.h"
#include
"nat-pmp.h"
#include
"notify.h"
#include
"prefs.h"
#include
"proxy.h"
#include
"prpl.h"
#include
"request.h"
#include
"savedstatuses.h"
#include
"sound.h"
#include
"sound-theme.h"
#include
"stun.h"
#include
"theme-manager.h"
#include
"upnp.h"
#include
"util.h"
#include
"network.h"
#include
"gtkblist.h"
#include
"gtkconv.h"
#include
"gtkdebug.h"
#include
"gtkdialogs.h"
#include
"gtkimhtml.h"
#include
"gtkimhtmltoolbar.h"
#include
"gtkprefs.h"
#include
"gtksavedstatuses.h"
#include
"gtksound.h"
#include
"gtkstatus-icon-theme.h"
#include
"gtkthemes.h"
#include
"gtkutils.h"
#include
"pidginstock.h"
#ifdef USE_VV
#include
"media-gst.h"
#if GST_CHECK_VERSION(1,0,0)
#include
<gst/video/videooverlay.h>
#elif GST_CHECK_VERSION(0,10,31)
#include
<gst/interfaces/xoverlay.h>
#endif
#ifdef GDK_WINDOWING_WIN32
#include
<gdk/gdkwin32.h>
#endif
#ifdef GDK_WINDOWING_X11
#include
<gdk/gdkx.h>
#endif
#ifdef GDK_WINDOWING_QUARTZ
#include
<gdk/gdkquartz.h>
#endif
#endif
#define PROXYHOST 0
#define PROXYPORT 1
#define PROXYUSER 2
#define PROXYPASS 3
#define PREFS_OPTIMAL_ICON_SIZE 32
struct
theme_info
{
gchar
*
type
;
gchar
*
extension
;
gchar
*
original_name
;
};
/* Main dialog */
static
GtkWidget
*
prefs
=
NULL
;
/* Notebook */
static
GtkWidget
*
prefsnotebook
=
NULL
;
static
int
notebook_page
=
0
;
/* Conversations page */
static
GtkWidget
*
sample_imhtml
=
NULL
;
/* Themes page */
static
GtkWidget
*
prefs_sound_themes_combo_box
;
static
GtkWidget
*
prefs_blist_themes_combo_box
;
static
GtkWidget
*
prefs_status_themes_combo_box
;
static
GtkWidget
*
prefs_smiley_themes_combo_box
;
/* Sound theme specific */
static
GtkWidget
*
sound_entry
=
NULL
;
static
int
sound_row_sel
=
0
;
static
gboolean
prefs_sound_themes_loading
;
/* These exist outside the lifetime of the prefs dialog */
static
GtkListStore
*
prefs_sound_themes
;
static
GtkListStore
*
prefs_blist_themes
;
static
GtkListStore
*
prefs_status_icon_themes
;
static
GtkListStore
*
prefs_smiley_themes
;
#ifdef USE_VV
static
GtkWidget
*
voice_level
;
static
GtkWidget
*
voice_threshold
;
static
GtkWidget
*
voice_volume
;
static
GstElement
*
voice_pipeline
;
static
GtkWidget
*
video_drawing_area
;
static
GstElement
*
video_pipeline
;
#endif
/*
* PROTOTYPES
*/
static
void
delete_prefs
(
GtkWidget
*
,
void
*
);
static
void
update_spin_value
(
GtkWidget
*
w
,
GtkWidget
*
spin
)
{
const
char
*
key
=
g_object_get_data
(
G_OBJECT
(
spin
),
"val"
);
int
value
;
value
=
gtk_spin_button_get_value_as_int
(
GTK_SPIN_BUTTON
(
spin
));
purple_prefs_set_int
(
key
,
value
);
}
GtkWidget
*
pidgin_prefs_labeled_spin_button
(
GtkWidget
*
box
,
const
gchar
*
title
,
const
char
*
key
,
int
min
,
int
max
,
GtkSizeGroup
*
sg
)
{
GtkWidget
*
spin
;
GtkObject
*
adjust
;
int
val
;
val
=
purple_prefs_get_int
(
key
);
adjust
=
gtk_adjustment_new
(
val
,
min
,
max
,
1
,
1
,
0
);
spin
=
gtk_spin_button_new
(
GTK_ADJUSTMENT
(
adjust
),
1
,
0
);
g_object_set_data
(
G_OBJECT
(
spin
),
"val"
,
(
char
*
)
key
);
if
(
max
<
10000
)
gtk_widget_set_size_request
(
spin
,
50
,
-1
);
else
gtk_widget_set_size_request
(
spin
,
60
,
-1
);
g_signal_connect
(
G_OBJECT
(
adjust
),
"value-changed"
,
G_CALLBACK
(
update_spin_value
),
GTK_WIDGET
(
spin
));
gtk_widget_show
(
spin
);
return
pidgin_add_widget_to_vbox
(
GTK_BOX
(
box
),
title
,
sg
,
spin
,
FALSE
,
NULL
);
}
static
void
entry_set
(
GtkEntry
*
entry
,
gpointer
data
)
{
const
char
*
key
=
(
const
char
*
)
data
;
purple_prefs_set_string
(
key
,
gtk_entry_get_text
(
entry
));
}
GtkWidget
*
pidgin_prefs_labeled_entry
(
GtkWidget
*
page
,
const
gchar
*
title
,
const
char
*
key
,
GtkSizeGroup
*
sg
)
{
GtkWidget
*
entry
;
const
gchar
*
value
;
value
=
purple_prefs_get_string
(
key
);
entry
=
gtk_entry_new
();
gtk_entry_set_text
(
GTK_ENTRY
(
entry
),
value
);
g_signal_connect
(
G_OBJECT
(
entry
),
"changed"
,
G_CALLBACK
(
entry_set
),
(
char
*
)
key
);
gtk_widget_show
(
entry
);
return
pidgin_add_widget_to_vbox
(
GTK_BOX
(
page
),
title
,
sg
,
entry
,
TRUE
,
NULL
);
}
GtkWidget
*
pidgin_prefs_labeled_password
(
GtkWidget
*
page
,
const
gchar
*
title
,
const
char
*
key
,
GtkSizeGroup
*
sg
)
{
GtkWidget
*
entry
;
const
gchar
*
value
;
value
=
purple_prefs_get_string
(
key
);
entry
=
gtk_entry_new
();
gtk_entry_set_visibility
(
GTK_ENTRY
(
entry
),
FALSE
);
gtk_entry_set_text
(
GTK_ENTRY
(
entry
),
value
);
g_signal_connect
(
G_OBJECT
(
entry
),
"changed"
,
G_CALLBACK
(
entry_set
),
(
char
*
)
key
);
gtk_widget_show
(
entry
);
return
pidgin_add_widget_to_vbox
(
GTK_BOX
(
page
),
title
,
sg
,
entry
,
TRUE
,
NULL
);
}
static
void
dropdown_set
(
GObject
*
w
,
const
char
*
key
)
{
const
char
*
str_value
;
int
int_value
;
PurplePrefType
type
;
type
=
GPOINTER_TO_INT
(
g_object_get_data
(
w
,
"type"
));
if
(
type
==
PURPLE_PREF_INT
)
{
int_value
=
GPOINTER_TO_INT
(
g_object_get_data
(
w
,
"value"
));
purple_prefs_set_int
(
key
,
int_value
);
}
else
if
(
type
==
PURPLE_PREF_STRING
)
{
str_value
=
(
const
char
*
)
g_object_get_data
(
w
,
"value"
);
purple_prefs_set_string
(
key
,
str_value
);
}
else
if
(
type
==
PURPLE_PREF_BOOLEAN
)
{
purple_prefs_set_bool
(
key
,
GPOINTER_TO_INT
(
g_object_get_data
(
w
,
"value"
)));
}
}
GtkWidget
*
pidgin_prefs_dropdown_from_list
(
GtkWidget
*
box
,
const
gchar
*
title
,
PurplePrefType
type
,
const
char
*
key
,
GList
*
menuitems
)
{
GtkWidget
*
dropdown
,
*
opt
,
*
menu
;
GtkWidget
*
label
=
NULL
;
gchar
*
text
;
const
char
*
stored_str
=
NULL
;
int
stored_int
=
0
;
int
int_value
=
0
;
const
char
*
str_value
=
NULL
;
int
o
=
0
;
g_return_val_if_fail
(
menuitems
!=
NULL
,
NULL
);
dropdown
=
gtk_option_menu_new
();
menu
=
gtk_menu_new
();
if
(
type
==
PURPLE_PREF_INT
)
stored_int
=
purple_prefs_get_int
(
key
);
else
if
(
type
==
PURPLE_PREF_STRING
)
stored_str
=
purple_prefs_get_string
(
key
);
while
(
menuitems
!=
NULL
&&
(
text
=
(
char
*
)
menuitems
->
data
)
!=
NULL
)
{
menuitems
=
g_list_next
(
menuitems
);
g_return_val_if_fail
(
menuitems
!=
NULL
,
NULL
);
opt
=
gtk_menu_item_new_with_label
(
text
);
g_object_set_data
(
G_OBJECT
(
opt
),
"type"
,
GINT_TO_POINTER
(
type
));
if
(
type
==
PURPLE_PREF_INT
)
{
int_value
=
GPOINTER_TO_INT
(
menuitems
->
data
);
g_object_set_data
(
G_OBJECT
(
opt
),
"value"
,
GINT_TO_POINTER
(
int_value
));
}
else
if
(
type
==
PURPLE_PREF_STRING
)
{
str_value
=
(
const
char
*
)
menuitems
->
data
;
g_object_set_data
(
G_OBJECT
(
opt
),
"value"
,
(
char
*
)
str_value
);
}
else
if
(
type
==
PURPLE_PREF_BOOLEAN
)
{
g_object_set_data
(
G_OBJECT
(
opt
),
"value"
,
menuitems
->
data
);
}
g_signal_connect
(
G_OBJECT
(
opt
),
"activate"
,
G_CALLBACK
(
dropdown_set
),
(
char
*
)
key
);
gtk_widget_show
(
opt
);
gtk_menu_shell_append
(
GTK_MENU_SHELL
(
menu
),
opt
);
if
((
type
==
PURPLE_PREF_INT
&&
stored_int
==
int_value
)
||
(
type
==
PURPLE_PREF_STRING
&&
stored_str
!=
NULL
&&
purple_strequal
(
stored_str
,
str_value
))
||
(
type
==
PURPLE_PREF_BOOLEAN
&&
(
purple_prefs_get_bool
(
key
)
==
GPOINTER_TO_INT
(
menuitems
->
data
))))
{
gtk_menu_set_active
(
GTK_MENU
(
menu
),
o
);
}
menuitems
=
g_list_next
(
menuitems
);
o
++
;
}
gtk_option_menu_set_menu
(
GTK_OPTION_MENU
(
dropdown
),
menu
);
pidgin_add_widget_to_vbox
(
GTK_BOX
(
box
),
title
,
NULL
,
dropdown
,
FALSE
,
&
label
);
return
label
;
}
GtkWidget
*
pidgin_prefs_dropdown
(
GtkWidget
*
box
,
const
gchar
*
title
,
PurplePrefType
type
,
const
char
*
key
,
...)
{
va_list
ap
;
GList
*
menuitems
=
NULL
;
GtkWidget
*
dropdown
=
NULL
;
char
*
name
;
int
int_value
;
const
char
*
str_value
;
g_return_val_if_fail
(
type
==
PURPLE_PREF_BOOLEAN
||
type
==
PURPLE_PREF_INT
||
type
==
PURPLE_PREF_STRING
,
NULL
);
va_start
(
ap
,
key
);
while
((
name
=
va_arg
(
ap
,
char
*
))
!=
NULL
)
{
menuitems
=
g_list_prepend
(
menuitems
,
name
);
if
(
type
==
PURPLE_PREF_INT
||
type
==
PURPLE_PREF_BOOLEAN
)
{
int_value
=
va_arg
(
ap
,
int
);
menuitems
=
g_list_prepend
(
menuitems
,
GINT_TO_POINTER
(
int_value
));
}
else
{
str_value
=
va_arg
(
ap
,
const
char
*
);
menuitems
=
g_list_prepend
(
menuitems
,
(
char
*
)
str_value
);
}
}
va_end
(
ap
);
g_return_val_if_fail
(
menuitems
!=
NULL
,
NULL
);
menuitems
=
g_list_reverse
(
menuitems
);
dropdown
=
pidgin_prefs_dropdown_from_list
(
box
,
title
,
type
,
key
,
menuitems
);
g_list_free
(
menuitems
);
return
dropdown
;
}
static
void
delete_prefs
(
GtkWidget
*
asdf
,
void
*
gdsa
)
{
/* Close any "select sound" request dialogs */
purple_request_close_with_handle
(
prefs
);
/* Unregister callbacks. */
purple_prefs_disconnect_by_handle
(
prefs
);
/* NULL-ify globals */
sound_entry
=
NULL
;
sound_row_sel
=
0
;
prefs_sound_themes_loading
=
FALSE
;
prefs_sound_themes_combo_box
=
NULL
;
prefs_blist_themes_combo_box
=
NULL
;
prefs_status_themes_combo_box
=
NULL
;
prefs_smiley_themes_combo_box
=
NULL
;
sample_imhtml
=
NULL
;
notebook_page
=
0
;
prefsnotebook
=
NULL
;
prefs
=
NULL
;
}
static
gchar
*
get_theme_markup
(
const
char
*
name
,
gboolean
custom
,
const
char
*
author
,
const
char
*
description
)
{
return
g_strdup_printf
(
"<b>%s</b>%s%s%s%s
\n
<span foreground='dim grey'>%s</span>"
,
name
,
custom
?
" "
:
""
,
custom
?
_
(
"(Custom)"
)
:
""
,
author
!=
NULL
?
" - "
:
""
,
author
!=
NULL
?
author
:
""
,
description
!=
NULL
?
description
:
""
);
}
static
void
smileys_refresh_theme_list
(
void
)
{
GdkPixbuf
*
pixbuf
;
GSList
*
themes
;
GtkTreeIter
iter
;
pidgin_themes_smiley_theme_probe
();
if
(
!
(
themes
=
smiley_themes
))
return
;
while
(
themes
)
{
struct
smiley_theme
*
theme
=
themes
->
data
;
char
*
description
=
get_theme_markup
(
_
(
theme
->
name
),
FALSE
,
_
(
theme
->
author
),
_
(
theme
->
desc
));
gtk_list_store_append
(
prefs_smiley_themes
,
&
iter
);
/*
* LEAK - Gentoo memprof thinks pixbuf is leaking here... but it
* looks like it should be ok to me. Anyone know what's up? --Mark
*/
pixbuf
=
(
theme
->
icon
?
pidgin_pixbuf_new_from_file
(
theme
->
icon
)
:
NULL
);
gtk_list_store_set
(
prefs_smiley_themes
,
&
iter
,
0
,
pixbuf
,
1
,
description
,
2
,
theme
->
name
,
-1
);
if
(
pixbuf
!=
NULL
)
g_object_unref
(
G_OBJECT
(
pixbuf
));
g_free
(
description
);
themes
=
themes
->
next
;
}
}
/* Rebuild the markup for the sound theme selection for "(Custom)" themes */
static
void
pref_sound_generate_markup
(
void
)
{
gboolean
print_custom
,
customized
;
const
gchar
*
author
,
*
description
,
*
current_theme
;
gchar
*
name
,
*
markup
;
PurpleSoundTheme
*
theme
;
GtkTreeIter
iter
;
customized
=
pidgin_sound_is_customized
();
current_theme
=
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/sound/theme"
);
if
(
gtk_tree_model_get_iter_first
(
GTK_TREE_MODEL
(
prefs_sound_themes
),
&
iter
))
{
do
{
gtk_tree_model_get
(
GTK_TREE_MODEL
(
prefs_sound_themes
),
&
iter
,
2
,
&
name
,
-1
);
print_custom
=
customized
&&
name
&&
purple_strequal
(
current_theme
,
name
);
if
(
!
name
||
*
name
==
'\0'
)
{
g_free
(
name
);
name
=
g_strdup
(
_
(
"Default"
));
author
=
_
(
"Penguin Pimps"
);
description
=
_
(
"The default Pidgin sound theme"
);
}
else
{
theme
=
PURPLE_SOUND_THEME
(
purple_theme_manager_find_theme
(
name
,
"sound"
));
author
=
purple_theme_get_author
(
PURPLE_THEME
(
theme
));
description
=
purple_theme_get_description
(
PURPLE_THEME
(
theme
));
}
markup
=
get_theme_markup
(
name
,
print_custom
,
author
,
description
);
gtk_list_store_set
(
prefs_sound_themes
,
&
iter
,
1
,
markup
,
-1
);
g_free
(
name
);
g_free
(
markup
);
}
while
(
gtk_tree_model_iter_next
(
GTK_TREE_MODEL
(
prefs_sound_themes
),
&
iter
));
}
}
/* adds the themes to the theme list from the manager so they can be displayed in prefs */
static
void
prefs_themes_sort
(
PurpleTheme
*
theme
)
{
GdkPixbuf
*
pixbuf
=
NULL
;
GtkTreeIter
iter
;
gchar
*
image_full
=
NULL
,
*
markup
;
const
gchar
*
name
,
*
author
,
*
description
;
if
(
PURPLE_IS_SOUND_THEME
(
theme
)){
image_full
=
purple_theme_get_image_full
(
theme
);
if
(
image_full
!=
NULL
){
pixbuf
=
pidgin_pixbuf_new_from_file_at_scale
(
image_full
,
PREFS_OPTIMAL_ICON_SIZE
,
PREFS_OPTIMAL_ICON_SIZE
,
TRUE
);
g_free
(
image_full
);
}
else
pixbuf
=
NULL
;
gtk_list_store_append
(
prefs_sound_themes
,
&
iter
);
gtk_list_store_set
(
prefs_sound_themes
,
&
iter
,
0
,
pixbuf
,
2
,
purple_theme_get_name
(
theme
),
-1
);
if
(
pixbuf
!=
NULL
)
g_object_unref
(
G_OBJECT
(
pixbuf
));
}
else
if
(
PIDGIN_IS_BLIST_THEME
(
theme
)
||
PIDGIN_IS_STATUS_ICON_THEME
(
theme
)){
GtkListStore
*
store
;
if
(
PIDGIN_IS_BLIST_THEME
(
theme
))
store
=
prefs_blist_themes
;
else
store
=
prefs_status_icon_themes
;
image_full
=
purple_theme_get_image_full
(
theme
);
if
(
image_full
!=
NULL
){
pixbuf
=
pidgin_pixbuf_new_from_file_at_scale
(
image_full
,
PREFS_OPTIMAL_ICON_SIZE
,
PREFS_OPTIMAL_ICON_SIZE
,
TRUE
);
g_free
(
image_full
);
}
else
pixbuf
=
NULL
;
name
=
purple_theme_get_name
(
theme
);
author
=
purple_theme_get_author
(
theme
);
description
=
purple_theme_get_description
(
theme
);
markup
=
get_theme_markup
(
name
,
FALSE
,
author
,
description
);
gtk_list_store_append
(
store
,
&
iter
);
gtk_list_store_set
(
store
,
&
iter
,
0
,
pixbuf
,
1
,
markup
,
2
,
name
,
-1
);
g_free
(
markup
);
if
(
pixbuf
!=
NULL
)
g_object_unref
(
G_OBJECT
(
pixbuf
));
}
}
static
void
prefs_set_active_theme_combo
(
GtkWidget
*
combo_box
,
GtkListStore
*
store
,
const
gchar
*
current_theme
)
{
GtkTreeIter
iter
;
gchar
*
theme
=
NULL
;
gboolean
unset
=
TRUE
;
if
(
current_theme
&&
*
current_theme
&&
gtk_tree_model_get_iter_first
(
GTK_TREE_MODEL
(
store
),
&
iter
))
{
do
{
gtk_tree_model_get
(
GTK_TREE_MODEL
(
store
),
&
iter
,
2
,
&
theme
,
-1
);
if
(
purple_strequal
(
current_theme
,
theme
))
{
gtk_combo_box_set_active_iter
(
GTK_COMBO_BOX
(
combo_box
),
&
iter
);
unset
=
FALSE
;
}
g_free
(
theme
);
}
while
(
gtk_tree_model_iter_next
(
GTK_TREE_MODEL
(
store
),
&
iter
));
}
if
(
unset
)
gtk_combo_box_set_active
(
GTK_COMBO_BOX
(
combo_box
),
0
);
}
static
void
prefs_themes_refresh
(
void
)
{
GdkPixbuf
*
pixbuf
=
NULL
;
gchar
*
tmp
;
GtkTreeIter
iter
;
prefs_sound_themes_loading
=
TRUE
;
/* refresh the list of themes in the manager */
purple_theme_manager_refresh
();
tmp
=
g_build_filename
(
DATADIR
,
"icons"
,
"hicolor"
,
"32x32"
,
"apps"
,
"pidgin.png"
,
NULL
);
pixbuf
=
pidgin_pixbuf_new_from_file_at_scale
(
tmp
,
PREFS_OPTIMAL_ICON_SIZE
,
PREFS_OPTIMAL_ICON_SIZE
,
TRUE
);
g_free
(
tmp
);
/* sound themes */
gtk_list_store_clear
(
prefs_sound_themes
);
gtk_list_store_append
(
prefs_sound_themes
,
&
iter
);
gtk_list_store_set
(
prefs_sound_themes
,
&
iter
,
0
,
pixbuf
,
2
,
""
,
-1
);
/* blist themes */
gtk_list_store_clear
(
prefs_blist_themes
);
gtk_list_store_append
(
prefs_blist_themes
,
&
iter
);
tmp
=
get_theme_markup
(
_
(
"Default"
),
FALSE
,
_
(
"Penguin Pimps"
),
_
(
"The default Pidgin buddy list theme"
));
gtk_list_store_set
(
prefs_blist_themes
,
&
iter
,
0
,
pixbuf
,
1
,
tmp
,
2
,
""
,
-1
);
g_free
(
tmp
);
/* status icon themes */
gtk_list_store_clear
(
prefs_status_icon_themes
);
gtk_list_store_append
(
prefs_status_icon_themes
,
&
iter
);
tmp
=
get_theme_markup
(
_
(
"Default"
),
FALSE
,
_
(
"Penguin Pimps"
),
_
(
"The default Pidgin status icon theme"
));
gtk_list_store_set
(
prefs_status_icon_themes
,
&
iter
,
0
,
pixbuf
,
1
,
tmp
,
2
,
""
,
-1
);
g_free
(
tmp
);
if
(
pixbuf
)
g_object_unref
(
G_OBJECT
(
pixbuf
));
/* smiley themes */
gtk_list_store_clear
(
prefs_smiley_themes
);
purple_theme_manager_for_each_theme
(
prefs_themes_sort
);
pref_sound_generate_markup
();
smileys_refresh_theme_list
();
/* set active */
prefs_set_active_theme_combo
(
prefs_sound_themes_combo_box
,
prefs_sound_themes
,
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/sound/theme"
));
prefs_set_active_theme_combo
(
prefs_blist_themes_combo_box
,
prefs_blist_themes
,
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/blist/theme"
));
prefs_set_active_theme_combo
(
prefs_status_themes_combo_box
,
prefs_status_icon_themes
,
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/status/icon-theme"
));
prefs_set_active_theme_combo
(
prefs_smiley_themes_combo_box
,
prefs_smiley_themes
,
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/smileys/theme"
));
prefs_sound_themes_loading
=
FALSE
;
}
/* init all the theme variables so that the themes can be sorted later and used by pref pages */
static
void
prefs_themes_init
(
void
)
{
prefs_sound_themes
=
gtk_list_store_new
(
3
,
GDK_TYPE_PIXBUF
,
G_TYPE_STRING
,
G_TYPE_STRING
);
prefs_blist_themes
=
gtk_list_store_new
(
3
,
GDK_TYPE_PIXBUF
,
G_TYPE_STRING
,
G_TYPE_STRING
);
prefs_status_icon_themes
=
gtk_list_store_new
(
3
,
GDK_TYPE_PIXBUF
,
G_TYPE_STRING
,
G_TYPE_STRING
);
prefs_smiley_themes
=
gtk_list_store_new
(
3
,
GDK_TYPE_PIXBUF
,
G_TYPE_STRING
,
G_TYPE_STRING
);
}
static
PurpleTheme
*
prefs_theme_find_theme
(
const
gchar
*
path
,
const
gchar
*
type
)
{
PurpleTheme
*
theme
=
purple_theme_manager_load_theme
(
path
,
type
);
GDir
*
dir
=
g_dir_open
(
path
,
0
,
NULL
);
const
gchar
*
next
;
while
(
!
PURPLE_IS_THEME
(
theme
)
&&
(
next
=
g_dir_read_name
(
dir
)))
{
gchar
*
next_path
=
g_build_filename
(
path
,
next
,
NULL
);
if
(
g_file_test
(
next_path
,
G_FILE_TEST_IS_DIR
))
theme
=
prefs_theme_find_theme
(
next_path
,
type
);
g_free
(
next_path
);
}
g_dir_close
(
dir
);
return
theme
;
}
/* Eww. Seriously ewww. But thanks, grim! This is taken from guifications2 */
static
gboolean
purple_theme_file_copy
(
const
gchar
*
source
,
const
gchar
*
destination
)
{
FILE
*
src
,
*
dest
;
gint
chr
=
EOF
;
if
(
!
(
src
=
g_fopen
(
source
,
"rb"
)))
return
FALSE
;
if
(
!
(
dest
=
g_fopen
(
destination
,
"wb"
)))
{
fclose
(
src
);
return
FALSE
;
}
while
((
chr
=
fgetc
(
src
))
!=
EOF
)
{
fputc
(
chr
,
dest
);
}
fclose
(
dest
);
fclose
(
src
);
return
TRUE
;
}
static
void
free_theme_info
(
struct
theme_info
*
info
)
{
if
(
info
!=
NULL
)
{
g_free
(
info
->
type
);
g_free
(
info
->
extension
);
g_free
(
info
->
original_name
);
g_free
(
info
);
}
}
/* installs a theme, info is freed by function */
static
void
theme_install_theme
(
char
*
path
,
struct
theme_info
*
info
)
{
#ifndef _WIN32
gchar
*
command
;
#endif
gchar
*
destdir
;
const
char
*
tail
;
gboolean
is_smiley_theme
,
is_archive
;
PurpleTheme
*
theme
=
NULL
;
if
(
info
==
NULL
)
return
;
/* check the extension */
tail
=
info
->
extension
?
info
->
extension
:
strrchr
(
path
,
'.'
);
if
(
!
tail
)
{
free_theme_info
(
info
);
return
;
}
is_archive
=
!
g_ascii_strcasecmp
(
tail
,
".gz"
)
||
!
g_ascii_strcasecmp
(
tail
,
".tgz"
);
/* Just to be safe */
g_strchomp
(
path
);
if
((
is_smiley_theme
=
purple_strequal
(
info
->
type
,
"smiley"
)))
destdir
=
g_build_filename
(
purple_user_dir
(),
"smileys"
,
NULL
);
else
destdir
=
g_build_filename
(
purple_user_dir
(),
"themes"
,
"temp"
,
NULL
);
/* We'll check this just to make sure. This also lets us do something different on
* other platforms, if need be */
if
(
is_archive
)
{
#ifndef _WIN32
gchar
*
path_escaped
=
g_shell_quote
(
path
);
gchar
*
destdir_escaped
=
g_shell_quote
(
destdir
);
if
(
!
g_file_test
(
destdir
,
G_FILE_TEST_IS_DIR
))
purple_build_dir
(
destdir
,
S_IRUSR
|
S_IWUSR
|
S_IXUSR
);
command
=
g_strdup_printf
(
"tar > /dev/null xzf %s -C %s"
,
path_escaped
,
destdir_escaped
);
g_free
(
path_escaped
);
g_free
(
destdir_escaped
);
/* Fire! */
if
(
system
(
command
))
{
purple_notify_error
(
NULL
,
NULL
,
_
(
"Theme failed to unpack."
),
NULL
);
g_free
(
command
);
g_free
(
destdir
);
free_theme_info
(
info
);
return
;
}
#else
if
(
!
winpidgin_gz_untar
(
path
,
destdir
))
{
purple_notify_error
(
NULL
,
NULL
,
_
(
"Theme failed to unpack."
),
NULL
);
g_free
(
destdir
);
free_theme_info
(
info
);
return
;
}
#endif
}
if
(
is_smiley_theme
)
{
/* just extract the folder to the smiley directory */
prefs_themes_refresh
();
}
else
if
(
is_archive
)
{
theme
=
prefs_theme_find_theme
(
destdir
,
info
->
type
);
if
(
PURPLE_IS_THEME
(
theme
))
{
/* create the location for the theme */
gchar
*
theme_dest
=
g_build_filename
(
purple_user_dir
(),
"themes"
,
purple_theme_get_name
(
theme
),
"purple"
,
info
->
type
,
NULL
);
if
(
!
g_file_test
(
theme_dest
,
G_FILE_TEST_IS_DIR
))
purple_build_dir
(
theme_dest
,
S_IRUSR
|
S_IWUSR
|
S_IXUSR
);
g_free
(
theme_dest
);
theme_dest
=
g_build_filename
(
purple_user_dir
(),
"themes"
,
purple_theme_get_name
(
theme
),
"purple"
,
info
->
type
,
NULL
);
/* move the entire directory to new location */
g_rename
(
purple_theme_get_dir
(
theme
),
theme_dest
);
g_free
(
theme_dest
);
if
(
g_remove
(
destdir
)
!=
0
)
{
purple_debug_error
(
"gtkprefs"
,
"couldn't remove temp (dest) path
\n
"
);
}
g_object_unref
(
theme
);
prefs_themes_refresh
();
}
else
{
/* something was wrong with the theme archive */
g_unlink
(
destdir
);
purple_notify_error
(
NULL
,
NULL
,
_
(
"Theme failed to load."
),
NULL
);
}
}
else
{
/* just a single file so copy it to a new temp directory and attempt to load it*/
gchar
*
temp_path
,
*
temp_file
;
temp_path
=
g_build_filename
(
purple_user_dir
(),
"themes"
,
"temp"
,
"sub_folder"
,
NULL
);
if
(
info
->
original_name
!=
NULL
)
{
/* name was changed from the original (probably a dnd) change it back before loading */
temp_file
=
g_build_filename
(
temp_path
,
info
->
original_name
,
NULL
);
}
else
{
gchar
*
source_name
=
g_path_get_basename
(
path
);
temp_file
=
g_build_filename
(
temp_path
,
source_name
,
NULL
);
g_free
(
source_name
);
}
if
(
!
g_file_test
(
temp_path
,
G_FILE_TEST_IS_DIR
))
purple_build_dir
(
temp_path
,
S_IRUSR
|
S_IWUSR
|
S_IXUSR
);
if
(
purple_theme_file_copy
(
path
,
temp_file
))
{
/* find the theme, could be in subfolder */
theme
=
prefs_theme_find_theme
(
temp_path
,
info
->
type
);
if
(
PURPLE_IS_THEME
(
theme
))
{
gchar
*
theme_dest
=
g_build_filename
(
purple_user_dir
(),
"themes"
,
purple_theme_get_name
(
theme
),
"purple"
,
info
->
type
,
NULL
);
if
(
!
g_file_test
(
theme_dest
,
G_FILE_TEST_IS_DIR
))
purple_build_dir
(
theme_dest
,
S_IRUSR
|
S_IWUSR
|
S_IXUSR
);
g_rename
(
purple_theme_get_dir
(
theme
),
theme_dest
);
g_free
(
theme_dest
);
g_object_unref
(
theme
);
prefs_themes_refresh
();
}
else
{
if
(
g_remove
(
temp_path
)
!=
0
)
{
purple_debug_error
(
"gtkprefs"
,
"couldn't remove temp path
\n
"
);
}
purple_notify_error
(
NULL
,
NULL
,
_
(
"Theme failed to load."
),
NULL
);
}
}
else
{
purple_notify_error
(
NULL
,
NULL
,
_
(
"Theme failed to copy."
),
NULL
);
}
g_free
(
temp_file
);
g_free
(
temp_path
);
}
g_free
(
destdir
);
free_theme_info
(
info
);
}
static
void
theme_got_url
(
PurpleUtilFetchUrlData
*
url_data
,
gpointer
user_data
,
const
gchar
*
themedata
,
size_t
len
,
const
gchar
*
error_message
)
{
FILE
*
f
;
gchar
*
path
;
size_t
wc
;
if
((
error_message
!=
NULL
)
||
(
len
==
0
))
{
free_theme_info
(
user_data
);
return
;
}
f
=
purple_mkstemp
(
&
path
,
TRUE
);
wc
=
fwrite
(
themedata
,
len
,
1
,
f
);
if
(
wc
!=
1
)
{
purple_debug_warning
(
"theme_got_url"
,
"Unable to write theme data.
\n
"
);
fclose
(
f
);
g_unlink
(
path
);
g_free
(
path
);
free_theme_info
(
user_data
);
return
;
}
fclose
(
f
);
theme_install_theme
(
path
,
user_data
);
g_unlink
(
path
);
g_free
(
path
);
}
static
void
theme_dnd_recv
(
GtkWidget
*
widget
,
GdkDragContext
*
dc
,
guint
x
,
guint
y
,
GtkSelectionData
*
sd
,
guint
info
,
guint
t
,
gpointer
user_data
)
{
gchar
*
name
=
g_strchomp
((
gchar
*
)
sd
->
data
);
if
((
sd
->
length
>=
0
)
&&
(
sd
->
format
==
8
))
{
/* Well, it looks like the drag event was cool.
* Let's do something with it */
gchar
*
temp
;
struct
theme_info
*
info
=
g_new0
(
struct
theme_info
,
1
);
info
->
type
=
g_strdup
((
gchar
*
)
user_data
);
info
->
extension
=
g_strdup
(
g_strrstr
(
name
,
"."
));
temp
=
g_strrstr
(
name
,
"/"
);
info
->
original_name
=
temp
?
g_strdup
(
++
temp
)
:
NULL
;
if
(
!
g_ascii_strncasecmp
(
name
,
"file://"
,
7
))
{
GError
*
converr
=
NULL
;
gchar
*
tmp
;
/* It looks like we're dealing with a local file. Let's
* just untar it in the right place */
if
(
!
(
tmp
=
g_filename_from_uri
(
name
,
NULL
,
&
converr
)))
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"theme dnd"
,
"%s
\n
"
,
(
converr
?
converr
->
message
:
"g_filename_from_uri error"
));
free_theme_info
(
info
);
return
;
}
theme_install_theme
(
tmp
,
info
);
g_free
(
tmp
);
}
else
if
(
!
g_ascii_strncasecmp
(
name
,
"http://"
,
7
))
{
/* Oo, a web drag and drop. This is where things
* will start to get interesting */
purple_util_fetch_url
(
name
,
TRUE
,
NULL
,
FALSE
,
theme_got_url
,
info
);
}
else
if
(
!
g_ascii_strncasecmp
(
name
,
"https://"
,
8
))
{
/* purple_util_fetch_url() doesn't support HTTPS, but we want users
* to be able to drag and drop links from the SF trackers, so
* we'll try it as an HTTP URL. */
char
*
tmp
=
g_strdup
(
name
+
1
);
tmp
[
0
]
=
'h'
;
tmp
[
1
]
=
't'
;
tmp
[
2
]
=
't'
;
tmp
[
3
]
=
'p'
;
purple_util_fetch_url
(
tmp
,
TRUE
,
NULL
,
FALSE
,
theme_got_url
,
info
);
g_free
(
tmp
);
}
else
free_theme_info
(
info
);
gtk_drag_finish
(
dc
,
TRUE
,
FALSE
,
t
);
}
gtk_drag_finish
(
dc
,
FALSE
,
FALSE
,
t
);
}
/* builds a theme combo box from a list store with colums: icon preview, markup, theme name */
static
GtkWidget
*
prefs_build_theme_combo_box
(
GtkListStore
*
store
,
const
char
*
current_theme
,
const
char
*
type
)
{
GtkCellRenderer
*
cell_rend
;
GtkWidget
*
combo_box
;
GtkTargetEntry
te
[
3
]
=
{
{
"text/plain"
,
0
,
0
},
{
"text/uri-list"
,
0
,
1
},
{
"STRING"
,
0
,
2
}
};
g_return_val_if_fail
(
store
!=
NULL
&&
current_theme
!=
NULL
,
NULL
);
combo_box
=
gtk_combo_box_new_with_model
(
GTK_TREE_MODEL
(
store
));
cell_rend
=
gtk_cell_renderer_pixbuf_new
();
gtk_cell_renderer_set_fixed_size
(
cell_rend
,
PREFS_OPTIMAL_ICON_SIZE
,
PREFS_OPTIMAL_ICON_SIZE
);
gtk_cell_layout_pack_start
(
GTK_CELL_LAYOUT
(
combo_box
),
cell_rend
,
FALSE
);
gtk_cell_layout_set_attributes
(
GTK_CELL_LAYOUT
(
combo_box
),
cell_rend
,
"pixbuf"
,
0
,
NULL
);
cell_rend
=
gtk_cell_renderer_text_new
();
gtk_cell_layout_pack_start
(
GTK_CELL_LAYOUT
(
combo_box
),
cell_rend
,
TRUE
);
gtk_cell_layout_set_attributes
(
GTK_CELL_LAYOUT
(
combo_box
),
cell_rend
,
"markup"
,
1
,
NULL
);
g_object_set
(
cell_rend
,
"ellipsize"
,
PANGO_ELLIPSIZE_END
,
NULL
);
gtk_drag_dest_set
(
combo_box
,
GTK_DEST_DEFAULT_MOTION
|
GTK_DEST_DEFAULT_HIGHLIGHT
|
GTK_DEST_DEFAULT_DROP
,
te
,
sizeof
(
te
)
/
sizeof
(
GtkTargetEntry
)
,
GDK_ACTION_COPY
|
GDK_ACTION_MOVE
);
g_signal_connect
(
G_OBJECT
(
combo_box
),
"drag_data_received"
,
G_CALLBACK
(
theme_dnd_recv
),
(
gpointer
)
type
);
return
combo_box
;
}
/* sets the current sound theme */
static
void
prefs_set_sound_theme_cb
(
GtkComboBox
*
combo_box
,
gpointer
user_data
)
{
gint
i
;
gchar
*
pref
;
gchar
*
new_theme
;
GtkTreeIter
new_iter
;
if
(
gtk_combo_box_get_active_iter
(
combo_box
,
&
new_iter
)
&&
!
prefs_sound_themes_loading
)
{
gtk_tree_model_get
(
GTK_TREE_MODEL
(
prefs_sound_themes
),
&
new_iter
,
2
,
&
new_theme
,
-1
);
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/sound/theme"
,
new_theme
);
/* New theme removes all customization */
for
(
i
=
0
;
i
<
PURPLE_NUM_SOUNDS
;
i
++
){
pref
=
g_strdup_printf
(
PIDGIN_PREFS_ROOT
"/sound/file/%s"
,
pidgin_sound_get_event_option
(
i
));
purple_prefs_set_path
(
pref
,
""
);
g_free
(
pref
);
}
/* gets rid of the "(Custom)" from the last selection */
pref_sound_generate_markup
();
gtk_entry_set_text
(
GTK_ENTRY
(
sound_entry
),
_
(
"(default)"
));
g_free
(
new_theme
);
}
}
/* sets the current smiley theme */
static
void
prefs_set_smiley_theme_cb
(
GtkComboBox
*
combo_box
,
gpointer
user_data
)
{
gchar
*
new_theme
;
GtkTreeIter
new_iter
;
if
(
gtk_combo_box_get_active_iter
(
combo_box
,
&
new_iter
))
{
gtk_tree_model_get
(
GTK_TREE_MODEL
(
prefs_smiley_themes
),
&
new_iter
,
2
,
&
new_theme
,
-1
);
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/smileys/theme"
,
new_theme
);
pidgin_themes_smiley_themeize
(
sample_imhtml
);
g_free
(
new_theme
);
}
}
/* Does same as normal sort, except "none" is sorted first */
static
gint
pidgin_sort_smileys
(
GtkTreeModel
*
model
,
GtkTreeIter
*
a
,
GtkTreeIter
*
b
,
gpointer
userdata
)
{
gint
ret
=
0
;
gchar
*
name1
=
NULL
,
*
name2
=
NULL
;
gtk_tree_model_get
(
model
,
a
,
2
,
&
name1
,
-1
);
gtk_tree_model_get
(
model
,
b
,
2
,
&
name2
,
-1
);
if
(
name1
==
NULL
||
name2
==
NULL
)
{
if
(
!
(
name1
==
NULL
&&
name2
==
NULL
))
ret
=
(
name1
==
NULL
)
?
-1
:
1
;
}
else
if
(
!
g_ascii_strcasecmp
(
name1
,
"none"
))
{
if
(
!
g_utf8_collate
(
name1
,
name2
))
ret
=
0
;
else
/* Sort name1 first */
ret
=
-1
;
}
else
if
(
!
g_ascii_strcasecmp
(
name2
,
"none"
))
{
/* Sort name2 first */
ret
=
1
;
}
else
{
/* Neither string is "none", default to normal sort */
ret
=
purple_utf8_strcasecmp
(
name1
,
name2
);
}
g_free
(
name1
);
g_free
(
name2
);
return
ret
;
}
/* sets the current buddy list theme */
static
void
prefs_set_blist_theme_cb
(
GtkComboBox
*
combo_box
,
gpointer
user_data
)
{
PidginBlistTheme
*
theme
=
NULL
;
GtkTreeIter
iter
;
gchar
*
name
=
NULL
;
if
(
gtk_combo_box_get_active_iter
(
combo_box
,
&
iter
))
{
gtk_tree_model_get
(
GTK_TREE_MODEL
(
prefs_blist_themes
),
&
iter
,
2
,
&
name
,
-1
);
if
(
!
name
||
*
name
)
theme
=
PIDGIN_BLIST_THEME
(
purple_theme_manager_find_theme
(
name
,
"blist"
));
g_free
(
name
);
pidgin_blist_set_theme
(
theme
);
}
}
/* sets the current icon theme */
static
void
prefs_set_status_icon_theme_cb
(
GtkComboBox
*
combo_box
,
gpointer
user_data
)
{
PidginStatusIconTheme
*
theme
=
NULL
;
GtkTreeIter
iter
;
gchar
*
name
=
NULL
;
if
(
gtk_combo_box_get_active_iter
(
combo_box
,
&
iter
))
{
gtk_tree_model_get
(
GTK_TREE_MODEL
(
prefs_status_icon_themes
),
&
iter
,
2
,
&
name
,
-1
);
if
(
!
name
||
*
name
)
theme
=
PIDGIN_STATUS_ICON_THEME
(
purple_theme_manager_find_theme
(
name
,
"status-icon"
));
g_free
(
name
);
pidgin_stock_load_status_icon_theme
(
theme
);
pidgin_blist_refresh
(
purple_get_blist
());
}
}
static
GtkWidget
*
add_theme_prefs_combo
(
GtkWidget
*
vbox
,
GtkSizeGroup
*
combo_sg
,
GtkSizeGroup
*
label_sg
,
GtkListStore
*
theme_store
,
GCallback
combo_box_cb
,
gpointer
combo_box_cb_user_data
,
const
char
*
label_str
,
const
char
*
prefs_path
,
const
char
*
theme_type
)
{
GtkWidget
*
label
;
GtkWidget
*
combo_box
=
NULL
;
GtkWidget
*
themesel_hbox
=
gtk_hbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
label
=
gtk_label_new
(
label_str
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0.5
);
gtk_size_group_add_widget
(
label_sg
,
label
);
gtk_box_pack_start
(
GTK_BOX
(
themesel_hbox
),
label
,
FALSE
,
FALSE
,
0
);
combo_box
=
prefs_build_theme_combo_box
(
theme_store
,
purple_prefs_get_string
(
prefs_path
),
theme_type
);
g_signal_connect
(
G_OBJECT
(
combo_box
),
"changed"
,
(
GCallback
)
combo_box_cb
,
combo_box_cb_user_data
);
gtk_size_group_add_widget
(
combo_sg
,
combo_box
);
gtk_box_pack_start
(
GTK_BOX
(
themesel_hbox
),
combo_box
,
TRUE
,
TRUE
,
0
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
themesel_hbox
,
FALSE
,
FALSE
,
0
);
return
combo_box
;
}
static
GtkWidget
*
theme_page
(
void
)
{
GtkWidget
*
label
;
GtkWidget
*
ret
,
*
vbox
;
GtkSizeGroup
*
label_sg
=
gtk_size_group_new
(
GTK_SIZE_GROUP_HORIZONTAL
);
GtkSizeGroup
*
combo_sg
=
gtk_size_group_new
(
GTK_SIZE_GROUP_HORIZONTAL
);
ret
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_CAT_SPACE
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
ret
),
PIDGIN_HIG_BORDER
);
vbox
=
pidgin_make_frame
(
ret
,
_
(
"Theme Selections"
));
/* Instructions */
label
=
gtk_label_new
(
_
(
"Select a theme that you would like to use from "
"the lists below.
\n
New themes can be installed by "
"dragging and dropping them onto the theme list."
));
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0.5
);
gtk_label_set_justify
(
GTK_LABEL
(
label
),
GTK_JUSTIFY_LEFT
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
label
,
TRUE
,
FALSE
,
0
);
gtk_widget_show
(
label
);
/* Buddy List Themes */
prefs_blist_themes_combo_box
=
add_theme_prefs_combo
(
vbox
,
combo_sg
,
label_sg
,
prefs_blist_themes
,
(
GCallback
)
prefs_set_blist_theme_cb
,
NULL
,
_
(
"Buddy List Theme:"
),
PIDGIN_PREFS_ROOT
"/blist/theme"
,
"blist"
);
/* Status Icon Themes */
prefs_status_themes_combo_box
=
add_theme_prefs_combo
(
vbox
,
combo_sg
,
label_sg
,
prefs_status_icon_themes
,
(
GCallback
)
prefs_set_status_icon_theme_cb
,
NULL
,
_
(
"Status Icon Theme:"
),
PIDGIN_PREFS_ROOT
"/status/icon-theme"
,
"icon"
);
/* Sound Themes */
prefs_sound_themes_combo_box
=
add_theme_prefs_combo
(
vbox
,
combo_sg
,
label_sg
,
prefs_sound_themes
,
(
GCallback
)
prefs_set_sound_theme_cb
,
NULL
,
_
(
"Sound Theme:"
),
PIDGIN_PREFS_ROOT
"/sound/theme"
,
"sound"
);
/* Smiley Themes */
prefs_smiley_themes_combo_box
=
add_theme_prefs_combo
(
vbox
,
combo_sg
,
label_sg
,
prefs_smiley_themes
,
(
GCallback
)
prefs_set_smiley_theme_cb
,
NULL
,
_
(
"Smiley Theme:"
),
PIDGIN_PREFS_ROOT
"/smileys/theme"
,
"smiley"
);
/* Custom sort so "none" theme is at top of list */
gtk_tree_sortable_set_sort_func
(
GTK_TREE_SORTABLE
(
prefs_smiley_themes
),
2
,
pidgin_sort_smileys
,
NULL
,
NULL
);
gtk_tree_sortable_set_sort_column_id
(
GTK_TREE_SORTABLE
(
prefs_smiley_themes
),
2
,
GTK_SORT_ASCENDING
);
gtk_widget_show_all
(
ret
);
return
ret
;
}
static
void
formatting_toggle_cb
(
GtkIMHtml
*
imhtml
,
GtkIMHtmlButtons
buttons
,
void
*
toolbar
)
{
gboolean
bold
,
italic
,
uline
;
gtk_imhtml_get_current_format
(
GTK_IMHTML
(
imhtml
),
&
bold
,
&
italic
,
&
uline
);
if
(
buttons
&
GTK_IMHTML_BOLD
)
purple_prefs_set_bool
(
PIDGIN_PREFS_ROOT
"/conversations/send_bold"
,
bold
);
if
(
buttons
&
GTK_IMHTML_ITALIC
)
purple_prefs_set_bool
(
PIDGIN_PREFS_ROOT
"/conversations/send_italic"
,
italic
);
if
(
buttons
&
GTK_IMHTML_UNDERLINE
)
purple_prefs_set_bool
(
PIDGIN_PREFS_ROOT
"/conversations/send_underline"
,
uline
);
if
(
buttons
&
GTK_IMHTML_GROW
||
buttons
&
GTK_IMHTML_SHRINK
)
purple_prefs_set_int
(
PIDGIN_PREFS_ROOT
"/conversations/font_size"
,
gtk_imhtml_get_current_fontsize
(
GTK_IMHTML
(
imhtml
)));
if
(
buttons
&
GTK_IMHTML_FACE
)
{
char
*
face
=
gtk_imhtml_get_current_fontface
(
GTK_IMHTML
(
imhtml
));
if
(
!
face
)
face
=
g_strdup
(
""
);
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/conversations/font_face"
,
face
);
g_free
(
face
);
}
if
(
buttons
&
GTK_IMHTML_FORECOLOR
)
{
char
*
color
=
gtk_imhtml_get_current_forecolor
(
GTK_IMHTML
(
imhtml
));
if
(
!
color
)
color
=
g_strdup
(
""
);
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/conversations/fgcolor"
,
color
);
g_free
(
color
);
}
if
(
buttons
&
GTK_IMHTML_BACKCOLOR
)
{
char
*
color
;
GObject
*
object
;
color
=
gtk_imhtml_get_current_backcolor
(
GTK_IMHTML
(
imhtml
));
if
(
!
color
)
color
=
g_strdup
(
""
);
/* Block the signal to prevent a loop. */
object
=
g_object_ref
(
G_OBJECT
(
imhtml
));
g_signal_handlers_block_matched
(
object
,
G_SIGNAL_MATCH_DATA
,
0
,
0
,
NULL
,
NULL
,
toolbar
);
/* Clear the backcolor. */
gtk_imhtml_toggle_backcolor
(
GTK_IMHTML
(
imhtml
),
""
);
/* Unblock the signal. */
g_signal_handlers_unblock_matched
(
object
,
G_SIGNAL_MATCH_DATA
,
0
,
0
,
NULL
,
NULL
,
toolbar
);
g_object_unref
(
object
);
/* This will fire a toggle signal and get saved below. */
gtk_imhtml_toggle_background
(
GTK_IMHTML
(
imhtml
),
color
);
g_free
(
color
);
}
if
(
buttons
&
GTK_IMHTML_BACKGROUND
)
{
char
*
color
=
gtk_imhtml_get_current_background
(
GTK_IMHTML
(
imhtml
));
if
(
!
color
)
color
=
g_strdup
(
""
);
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/conversations/bgcolor"
,
color
);
g_free
(
color
);
}
}
static
void
formatting_clear_cb
(
GtkIMHtml
*
imhtml
,
void
*
data
)
{
purple_prefs_set_bool
(
PIDGIN_PREFS_ROOT
"/conversations/send_bold"
,
FALSE
);
purple_prefs_set_bool
(
PIDGIN_PREFS_ROOT
"/conversations/send_italic"
,
FALSE
);
purple_prefs_set_bool
(
PIDGIN_PREFS_ROOT
"/conversations/send_underline"
,
FALSE
);
purple_prefs_set_int
(
PIDGIN_PREFS_ROOT
"/conversations/font_size"
,
3
);
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/conversations/font_face"
,
""
);
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/conversations/fgcolor"
,
""
);
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/conversations/bgcolor"
,
""
);
}
static
void
conversation_usetabs_cb
(
const
char
*
name
,
PurplePrefType
type
,
gconstpointer
value
,
gpointer
data
)
{
gboolean
usetabs
=
GPOINTER_TO_INT
(
value
);
if
(
usetabs
)
gtk_widget_set_sensitive
(
GTK_WIDGET
(
data
),
TRUE
);
else
gtk_widget_set_sensitive
(
GTK_WIDGET
(
data
),
FALSE
);
}
#define CONVERSATION_CLOSE_ACCEL_PATH "<main>/Conversation/Close"
/* Filled in in keyboard_shortcuts(). */
static
GtkAccelKey
ctrl_w
=
{
0
,
0
,
0
};
static
GtkAccelKey
escape
=
{
0
,
0
,
0
};
static
guint
escape_closes_conversation_cb_id
=
0
;
static
gboolean
accel_is_escape
(
GtkAccelKey
*
k
)
{
return
(
k
->
accel_key
==
escape
.
accel_key
&&
k
->
accel_mods
==
escape
.
accel_mods
);
}
/* Update the tickybox in Preferences when the keybinding for Conversation ->
* Close is changed via Gtk.
*/
static
void
conversation_close_accel_changed_cb
(
GtkAccelMap
*
object
,
gchar
*
accel_path
,
guint
accel_key
,
GdkModifierType
accel_mods
,
gpointer
checkbox_
)
{
GtkToggleButton
*
checkbox
=
GTK_TOGGLE_BUTTON
(
checkbox_
);
GtkAccelKey
new
=
{
accel_key
,
accel_mods
,
0
};
g_signal_handler_block
(
checkbox
,
escape_closes_conversation_cb_id
);
gtk_toggle_button_set_active
(
checkbox
,
accel_is_escape
(
&
new
));
g_signal_handler_unblock
(
checkbox
,
escape_closes_conversation_cb_id
);
}
static
void
escape_closes_conversation_cb
(
GtkWidget
*
w
,
gpointer
unused
)
{
gboolean
active
=
gtk_toggle_button_get_active
(
GTK_TOGGLE_BUTTON
(
w
));
gboolean
changed
;
GtkAccelKey
*
new_key
=
active
?
&
escape
:
&
ctrl_w
;
changed
=
gtk_accel_map_change_entry
(
CONVERSATION_CLOSE_ACCEL_PATH
,
new_key
->
accel_key
,
new_key
->
accel_mods
,
TRUE
);
/* If another path is already bound to the new accelerator,
* _change_entry tries to delete that binding (because it was passed
* replace=TRUE). If that other path is locked, then _change_entry
* will fail. We don't ever lock any accelerator paths, so this case
* should never arise.
*/
if
(
!
changed
)
purple_debug_warning
(
"gtkprefs"
,
"Escape accel failed to change
\n
"
);
}
/* Creates preferences for keyboard shortcuts that it's hard to change with the
* standard Gtk accelerator-changing mechanism.
*/
static
void
keyboard_shortcuts
(
GtkWidget
*
page
)
{
GtkWidget
*
vbox
=
pidgin_make_frame
(
page
,
_
(
"Keyboard Shortcuts"
));
GtkWidget
*
checkbox
;
GtkAccelKey
current
=
{
0
,
0
,
0
};
GtkAccelMap
*
map
=
gtk_accel_map_get
();
/* Maybe it would be better just to hardcode the values?
* -- resiak, 2007-04-30
*/
if
(
ctrl_w
.
accel_key
==
0
)
{
gtk_accelerator_parse
(
"<Control>w"
,
&
(
ctrl_w
.
accel_key
),
&
(
ctrl_w
.
accel_mods
));
g_assert
(
ctrl_w
.
accel_key
!=
0
);
gtk_accelerator_parse
(
"Escape"
,
&
(
escape
.
accel_key
),
&
(
escape
.
accel_mods
));
g_assert
(
escape
.
accel_key
!=
0
);
}
checkbox
=
gtk_check_button_new_with_mnemonic
(
_
(
"Cl_ose conversations with the Escape key"
));
gtk_accel_map_lookup_entry
(
CONVERSATION_CLOSE_ACCEL_PATH
,
&
current
);
gtk_toggle_button_set_active
(
GTK_TOGGLE_BUTTON
(
checkbox
),
accel_is_escape
(
&
current
));
escape_closes_conversation_cb_id
=
g_signal_connect
(
checkbox
,
"clicked"
,
G_CALLBACK
(
escape_closes_conversation_cb
),
NULL
);
g_signal_connect_object
(
map
,
"changed::"
CONVERSATION_CLOSE_ACCEL_PATH
,
G_CALLBACK
(
conversation_close_accel_changed_cb
),
checkbox
,
(
GConnectFlags
)
0
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
checkbox
,
FALSE
,
FALSE
,
0
);
}
static
GtkWidget
*
interface_page
(
void
)
{
GtkWidget
*
ret
;
GtkWidget
*
vbox
;
GtkWidget
*
vbox2
;
GtkWidget
*
label
;
GtkSizeGroup
*
sg
;
GList
*
names
=
NULL
;
ret
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_CAT_SPACE
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
ret
),
PIDGIN_HIG_BORDER
);
sg
=
gtk_size_group_new
(
GTK_SIZE_GROUP_HORIZONTAL
);
/* System Tray */
vbox
=
pidgin_make_frame
(
ret
,
_
(
"System Tray Icon"
));
label
=
pidgin_prefs_dropdown
(
vbox
,
_
(
"_Show system tray icon:"
),
PURPLE_PREF_STRING
,
PIDGIN_PREFS_ROOT
"/docklet/show"
,
_
(
"Always"
),
"always"
,
_
(
"On unread messages"
),
"pending"
,
_
(
"Never"
),
"never"
,
NULL
);
gtk_size_group_add_widget
(
sg
,
label
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0.0
,
0.5
);
vbox
=
pidgin_make_frame
(
ret
,
_
(
"Conversation Window"
));
label
=
pidgin_prefs_dropdown
(
vbox
,
_
(
"_Hide new IM conversations:"
),
PURPLE_PREF_STRING
,
PIDGIN_PREFS_ROOT
"/conversations/im/hide_new"
,
_
(
"Never"
),
"never"
,
_
(
"When away"
),
"away"
,
_
(
"Always"
),
"always"
,
NULL
);
gtk_size_group_add_widget
(
sg
,
label
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0.0
,
0.5
);
#ifdef _WIN32
pidgin_prefs_checkbox
(
_
(
"Minimi_ze new conversation windows"
),
PIDGIN_PREFS_ROOT
"/win32/minimize_new_convs"
,
vbox
);
#endif
/* All the tab options! */
vbox
=
pidgin_make_frame
(
ret
,
_
(
"Tabs"
));
pidgin_prefs_checkbox
(
_
(
"Show IMs and chats in _tabbed windows"
),
PIDGIN_PREFS_ROOT
"/conversations/tabs"
,
vbox
);
/*
* Connect a signal to the above preference. When conversations are not
* shown in a tabbed window then all tabbing options should be disabled.
*/
vbox2
=
gtk_vbox_new
(
FALSE
,
9
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
vbox2
,
FALSE
,
FALSE
,
0
);
purple_prefs_connect_callback
(
prefs
,
PIDGIN_PREFS_ROOT
"/conversations/tabs"
,
conversation_usetabs_cb
,
vbox2
);
if
(
!
purple_prefs_get_bool
(
PIDGIN_PREFS_ROOT
"/conversations/tabs"
))
gtk_widget_set_sensitive
(
vbox2
,
FALSE
);
pidgin_prefs_checkbox
(
_
(
"Show close b_utton on tabs"
),
PIDGIN_PREFS_ROOT
"/conversations/close_on_tabs"
,
vbox2
);
label
=
pidgin_prefs_dropdown
(
vbox2
,
_
(
"_Placement:"
),
PURPLE_PREF_INT
,
PIDGIN_PREFS_ROOT
"/conversations/tab_side"
,
_
(
"Top"
),
GTK_POS_TOP
,
_
(
"Bottom"
),
GTK_POS_BOTTOM
,
_
(
"Left"
),
GTK_POS_LEFT
,
_
(
"Right"
),
GTK_POS_RIGHT
,
_
(
"Left Vertical"
),
GTK_POS_LEFT
|
8
,
_
(
"Right Vertical"
),
GTK_POS_RIGHT
|
8
,
NULL
);
gtk_size_group_add_widget
(
sg
,
label
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0.0
,
0.5
);
names
=
pidgin_conv_placement_get_options
();
label
=
pidgin_prefs_dropdown_from_list
(
vbox2
,
_
(
"N_ew conversations:"
),
PURPLE_PREF_STRING
,
PIDGIN_PREFS_ROOT
"/conversations/placement"
,
names
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0.0
,
0.5
);
gtk_size_group_add_widget
(
sg
,
label
);
g_list_free
(
names
);
keyboard_shortcuts
(
ret
);
gtk_widget_show_all
(
ret
);
g_object_unref
(
sg
);
return
ret
;
}
#ifdef _WIN32
static
void
apply_custom_font
(
void
)
{
PangoFontDescription
*
desc
=
NULL
;
if
(
!
purple_prefs_get_bool
(
PIDGIN_PREFS_ROOT
"/conversations/use_theme_font"
))
{
const
char
*
font
=
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/conversations/custom_font"
);
desc
=
pango_font_description_from_string
(
font
);
}
gtk_widget_modify_font
(
sample_imhtml
,
desc
);
if
(
desc
)
pango_font_description_free
(
desc
);
}
static
void
pidgin_custom_font_set
(
GtkFontButton
*
font_button
,
gpointer
nul
)
{
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/conversations/custom_font"
,
gtk_font_button_get_font_name
(
font_button
));
apply_custom_font
();
}
#endif
static
GtkWidget
*
conv_page
(
void
)
{
GtkWidget
*
ret
;
GtkWidget
*
vbox
;
GtkWidget
*
toolbar
;
GtkWidget
*
iconpref1
;
GtkWidget
*
iconpref2
;
GtkWidget
*
imhtml
;
GtkWidget
*
frame
;
GtkWidget
*
hbox
;
GtkWidget
*
checkbox
;
GtkWidget
*
spin_button
;
ret
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_CAT_SPACE
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
ret
),
PIDGIN_HIG_BORDER
);
vbox
=
pidgin_make_frame
(
ret
,
_
(
"Conversations"
));
pidgin_prefs_checkbox
(
_
(
"Show _formatting on incoming messages"
),
PIDGIN_PREFS_ROOT
"/conversations/show_incoming_formatting"
,
vbox
);
pidgin_prefs_checkbox
(
_
(
"Close IMs immediately when the tab is closed"
),
PIDGIN_PREFS_ROOT
"/conversations/im/close_immediately"
,
vbox
);
iconpref1
=
pidgin_prefs_checkbox
(
_
(
"Show _detailed information"
),
PIDGIN_PREFS_ROOT
"/conversations/im/show_buddy_icons"
,
vbox
);
iconpref2
=
pidgin_prefs_checkbox
(
_
(
"Enable buddy ic_on animation"
),
PIDGIN_PREFS_ROOT
"/conversations/im/animate_buddy_icons"
,
vbox
);
if
(
!
purple_prefs_get_bool
(
PIDGIN_PREFS_ROOT
"/conversations/im/show_buddy_icons"
))
gtk_widget_set_sensitive
(
iconpref2
,
FALSE
);
g_signal_connect
(
G_OBJECT
(
iconpref1
),
"clicked"
,
G_CALLBACK
(
pidgin_toggle_sensitive
),
iconpref2
);
pidgin_prefs_checkbox
(
_
(
"_Notify buddies that you are typing to them"
),
"/purple/conversations/im/send_typing"
,
vbox
);
#ifdef USE_GTKSPELL
pidgin_prefs_checkbox
(
_
(
"Highlight _misspelled words"
),
PIDGIN_PREFS_ROOT
"/conversations/spellcheck"
,
vbox
);
#endif
pidgin_prefs_checkbox
(
_
(
"Use smooth-scrolling"
),
PIDGIN_PREFS_ROOT
"/conversations/use_smooth_scrolling"
,
vbox
);
#ifdef _WIN32
pidgin_prefs_checkbox
(
_
(
"F_lash window when IMs are received"
),
PIDGIN_PREFS_ROOT
"/win32/blink_im"
,
vbox
);
#endif
hbox
=
gtk_hbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
checkbox
=
pidgin_prefs_checkbox
(
_
(
"Resize incoming custom smileys"
),
PIDGIN_PREFS_ROOT
"/conversations/resize_custom_smileys"
,
hbox
);
spin_button
=
pidgin_prefs_labeled_spin_button
(
hbox
,
_
(
"Maximum size:"
),
PIDGIN_PREFS_ROOT
"/conversations/custom_smileys_size"
,
16
,
512
,
NULL
);
if
(
!
purple_prefs_get_bool
(
PIDGIN_PREFS_ROOT
"/conversations/resize_custom_smileys"
))
gtk_widget_set_sensitive
(
GTK_WIDGET
(
spin_button
),
FALSE
);
g_signal_connect
(
G_OBJECT
(
checkbox
),
"clicked"
,
G_CALLBACK
(
pidgin_toggle_sensitive
),
spin_button
);
pidgin_add_widget_to_vbox
(
GTK_BOX
(
vbox
),
NULL
,
NULL
,
hbox
,
TRUE
,
NULL
);
pidgin_prefs_labeled_spin_button
(
vbox
,
_
(
"Minimum input area height in lines:"
),
PIDGIN_PREFS_ROOT
"/conversations/minimum_entry_lines"
,
1
,
8
,
NULL
);
#ifdef _WIN32
{
GtkWidget
*
fontpref
,
*
font_button
,
*
hbox
;
const
char
*
font_name
;
vbox
=
pidgin_make_frame
(
ret
,
_
(
"Font"
));
fontpref
=
pidgin_prefs_checkbox
(
_
(
"Use font from _theme"
),
PIDGIN_PREFS_ROOT
"/conversations/use_theme_font"
,
vbox
);
font_name
=
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/conversations/custom_font"
);
if
((
font_name
==
NULL
)
||
(
*
font_name
==
'\0'
))
{
font_button
=
gtk_font_button_new
();
}
else
{
font_button
=
gtk_font_button_new_with_font
(
font_name
);
}
gtk_font_button_set_show_style
(
GTK_FONT_BUTTON
(
font_button
),
TRUE
);
hbox
=
pidgin_add_widget_to_vbox
(
GTK_BOX
(
vbox
),
_
(
"Conversation _font:"
),
NULL
,
font_button
,
FALSE
,
NULL
);
if
(
purple_prefs_get_bool
(
PIDGIN_PREFS_ROOT
"/conversations/use_theme_font"
))
gtk_widget_set_sensitive
(
hbox
,
FALSE
);
g_signal_connect
(
G_OBJECT
(
fontpref
),
"clicked"
,
G_CALLBACK
(
pidgin_toggle_sensitive
),
hbox
);
g_signal_connect
(
G_OBJECT
(
fontpref
),
"clicked"
,
G_CALLBACK
(
apply_custom_font
),
hbox
);
g_signal_connect
(
G_OBJECT
(
font_button
),
"font-set"
,
G_CALLBACK
(
pidgin_custom_font_set
),
NULL
);
}
#endif
vbox
=
pidgin_make_frame
(
ret
,
_
(
"Default Formatting"
));
frame
=
pidgin_create_imhtml
(
TRUE
,
&
imhtml
,
&
toolbar
,
NULL
);
gtk_widget_show
(
frame
);
gtk_widget_set_name
(
imhtml
,
"pidgin_prefs_font_imhtml"
);
gtk_widget_set_size_request
(
frame
,
450
,
-1
);
gtk_imhtml_set_whole_buffer_formatting_only
(
GTK_IMHTML
(
imhtml
),
TRUE
);
gtk_imhtml_set_format_functions
(
GTK_IMHTML
(
imhtml
),
GTK_IMHTML_BOLD
|
GTK_IMHTML_ITALIC
|
GTK_IMHTML_UNDERLINE
|
GTK_IMHTML_GROW
|
GTK_IMHTML_SHRINK
|
GTK_IMHTML_FACE
|
GTK_IMHTML_FORECOLOR
|
GTK_IMHTML_BACKCOLOR
|
GTK_IMHTML_BACKGROUND
);
gtk_imhtml_append_text
(
GTK_IMHTML
(
imhtml
),
_
(
"This is how your outgoing message text will appear when you use protocols that support formatting."
),
0
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
frame
,
TRUE
,
TRUE
,
0
);
gtk_imhtml_setup_entry
(
GTK_IMHTML
(
imhtml
),
PURPLE_CONNECTION_HTML
|
PURPLE_CONNECTION_FORMATTING_WBFO
);
g_signal_connect_after
(
G_OBJECT
(
imhtml
),
"format_function_toggle"
,
G_CALLBACK
(
formatting_toggle_cb
),
toolbar
);
g_signal_connect_after
(
G_OBJECT
(
imhtml
),
"format_function_clear"
,
G_CALLBACK
(
formatting_clear_cb
),
NULL
);
sample_imhtml
=
imhtml
;
gtk_widget_show
(
ret
);
return
ret
;
}
static
void
network_ip_changed
(
GtkEntry
*
entry
,
gpointer
data
)
{
const
gchar
*
text
=
gtk_entry_get_text
(
entry
);
GdkColor
color
;
if
(
text
&&
*
text
)
{
if
(
purple_ip_address_is_valid
(
text
))
{
color
.
red
=
0xAFFF
;
color
.
green
=
0xFFFF
;
color
.
blue
=
0xAFFF
;
purple_network_set_public_ip
(
text
);
}
else
{
color
.
red
=
0xFFFF
;
color
.
green
=
0xAFFF
;
color
.
blue
=
0xAFFF
;
}
gtk_widget_modify_base
(
GTK_WIDGET
(
entry
),
GTK_STATE_NORMAL
,
&
color
);
}
else
{
purple_network_set_public_ip
(
""
);
gtk_widget_modify_base
(
GTK_WIDGET
(
entry
),
GTK_STATE_NORMAL
,
NULL
);
}
}
static
gboolean
network_stun_server_changed_cb
(
GtkWidget
*
widget
,
GdkEventFocus
*
event
,
gpointer
data
)
{
GtkEntry
*
entry
=
GTK_ENTRY
(
widget
);
purple_prefs_set_string
(
"/purple/network/stun_server"
,
gtk_entry_get_text
(
entry
));
purple_network_set_stun_server
(
gtk_entry_get_text
(
entry
));
return
FALSE
;
}
static
gboolean
network_turn_server_changed_cb
(
GtkWidget
*
widget
,
GdkEventFocus
*
event
,
gpointer
data
)
{
GtkEntry
*
entry
=
GTK_ENTRY
(
widget
);
purple_prefs_set_string
(
"/purple/network/turn_server"
,
gtk_entry_get_text
(
entry
));
purple_network_set_turn_server
(
gtk_entry_get_text
(
entry
));
return
FALSE
;
}
static
void
proxy_changed_cb
(
const
char
*
name
,
PurplePrefType
type
,
gconstpointer
value
,
gpointer
data
)
{
GtkWidget
*
frame
=
data
;
const
char
*
proxy
=
value
;
if
(
!
purple_strequal
(
proxy
,
"none"
)
&&
!
purple_strequal
(
proxy
,
"envvar"
))
gtk_widget_show_all
(
frame
);
else
gtk_widget_hide
(
frame
);
}
static
void
proxy_print_option
(
GtkEntry
*
entry
,
int
entrynum
)
{
if
(
entrynum
==
PROXYHOST
)
purple_prefs_set_string
(
"/purple/proxy/host"
,
gtk_entry_get_text
(
entry
));
else
if
(
entrynum
==
PROXYPORT
)
purple_prefs_set_int
(
"/purple/proxy/port"
,
atoi
(
gtk_entry_get_text
(
entry
)));
else
if
(
entrynum
==
PROXYUSER
)
purple_prefs_set_string
(
"/purple/proxy/username"
,
gtk_entry_get_text
(
entry
));
else
if
(
entrynum
==
PROXYPASS
)
purple_prefs_set_string
(
"/purple/proxy/password"
,
gtk_entry_get_text
(
entry
));
}
static
void
proxy_button_clicked_cb
(
GtkWidget
*
button
,
gchar
*
program
)
{
GError
*
err
=
NULL
;
if
(
g_spawn_command_line_async
(
program
,
&
err
))
return
;
purple_notify_error
(
NULL
,
NULL
,
_
(
"Cannot start proxy configuration program."
),
err
->
message
);
g_error_free
(
err
);
}
#ifndef _WIN32
static
void
browser_button_clicked_cb
(
GtkWidget
*
button
,
gchar
*
path
)
{
GError
*
err
=
NULL
;
if
(
g_spawn_command_line_async
(
path
,
&
err
))
return
;
purple_notify_error
(
NULL
,
NULL
,
_
(
"Cannot start browser configuration program."
),
err
->
message
);
g_error_free
(
err
);
}
#endif
static
void
auto_ip_button_clicked_cb
(
GtkWidget
*
button
,
gpointer
null
)
{
const
char
*
ip
;
PurpleStunNatDiscovery
*
stun
;
char
*
auto_ip_text
;
/* purple_network_get_my_ip will return the IP that was set by the user with
purple_network_set_public_ip, so make a lookup for the auto-detected IP
ourselves. */
if
(
purple_prefs_get_bool
(
"/purple/network/auto_ip"
))
{
/* Check if STUN discovery was already done */
stun
=
purple_stun_discover
(
NULL
);
if
((
stun
!=
NULL
)
&&
(
stun
->
status
==
PURPLE_STUN_STATUS_DISCOVERED
))
{
ip
=
stun
->
publicip
;
}
else
{
/* Attempt to get the IP from a NAT device using UPnP */
ip
=
purple_upnp_get_public_ip
();
if
(
ip
==
NULL
)
{
/* Attempt to get the IP from a NAT device using NAT-PMP */
ip
=
purple_pmp_get_public_ip
();
if
(
ip
==
NULL
)
{
/* Just fetch the IP of the local system */
ip
=
purple_network_get_local_system_ip
(
-1
);
}
}
}
}
else
ip
=
_
(
"Disabled"
);
auto_ip_text
=
g_strdup_printf
(
_
(
"Use _automatically detected IP address: %s"
),
ip
);
gtk_button_set_label
(
GTK_BUTTON
(
button
),
auto_ip_text
);
g_free
(
auto_ip_text
);
}
static
GtkWidget
*
network_page
(
void
)
{
GtkWidget
*
ret
;
GtkWidget
*
vbox
,
*
hbox
,
*
entry
;
GtkWidget
*
label
,
*
auto_ip_checkbox
,
*
ports_checkbox
,
*
spin_button
;
GtkSizeGroup
*
sg
;
ret
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_CAT_SPACE
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
ret
),
PIDGIN_HIG_BORDER
);
vbox
=
pidgin_make_frame
(
ret
,
_
(
"IP Address"
));
sg
=
gtk_size_group_new
(
GTK_SIZE_GROUP_HORIZONTAL
);
entry
=
gtk_entry_new
();
gtk_entry_set_text
(
GTK_ENTRY
(
entry
),
purple_prefs_get_string
(
"/purple/network/stun_server"
));
g_signal_connect
(
G_OBJECT
(
entry
),
"focus-out-event"
,
G_CALLBACK
(
network_stun_server_changed_cb
),
NULL
);
gtk_widget_show
(
entry
);
pidgin_add_widget_to_vbox
(
GTK_BOX
(
vbox
),
_
(
"ST_UN server:"
),
sg
,
entry
,
TRUE
,
NULL
);
hbox
=
gtk_hbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
gtk_container_add
(
GTK_CONTAINER
(
vbox
),
hbox
);
label
=
gtk_label_new
(
NULL
);
gtk_container_add
(
GTK_CONTAINER
(
hbox
),
label
);
gtk_size_group_add_widget
(
sg
,
label
);
label
=
gtk_label_new
(
NULL
);
gtk_label_set_markup
(
GTK_LABEL
(
label
),
_
(
"<span style=
\"
italic
\"
>Example: stunserver.org</span>"
));
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0.0
,
0.5
);
gtk_container_add
(
GTK_CONTAINER
(
hbox
),
label
);
auto_ip_checkbox
=
pidgin_prefs_checkbox
(
"Use _automatically detected IP address"
,
"/purple/network/auto_ip"
,
vbox
);
g_signal_connect
(
G_OBJECT
(
auto_ip_checkbox
),
"clicked"
,
G_CALLBACK
(
auto_ip_button_clicked_cb
),
NULL
);
auto_ip_button_clicked_cb
(
auto_ip_checkbox
,
NULL
);
/* Update label */
entry
=
gtk_entry_new
();
gtk_entry_set_text
(
GTK_ENTRY
(
entry
),
purple_network_get_public_ip
());
g_signal_connect
(
G_OBJECT
(
entry
),
"changed"
,
G_CALLBACK
(
network_ip_changed
),
NULL
);
hbox
=
pidgin_add_widget_to_vbox
(
GTK_BOX
(
vbox
),
_
(
"Public _IP:"
),
sg
,
entry
,
TRUE
,
NULL
);
if
(
purple_prefs_get_bool
(
"/purple/network/auto_ip"
))
{
gtk_widget_set_sensitive
(
GTK_WIDGET
(
hbox
),
FALSE
);
}
g_signal_connect
(
G_OBJECT
(
auto_ip_checkbox
),
"clicked"
,
G_CALLBACK
(
pidgin_toggle_sensitive
),
hbox
);
g_object_unref
(
sg
);
vbox
=
pidgin_make_frame
(
ret
,
_
(
"Ports"
));
sg
=
gtk_size_group_new
(
GTK_SIZE_GROUP_HORIZONTAL
);
pidgin_prefs_checkbox
(
_
(
"_Enable automatic router port forwarding"
),
"/purple/network/map_ports"
,
vbox
);
hbox
=
gtk_hbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
ports_checkbox
=
pidgin_prefs_checkbox
(
_
(
"_Manually specify range of ports to listen on:"
),
"/purple/network/ports_range_use"
,
hbox
);
spin_button
=
pidgin_prefs_labeled_spin_button
(
hbox
,
_
(
"_Start:"
),
"/purple/network/ports_range_start"
,
0
,
65535
,
sg
);
if
(
!
purple_prefs_get_bool
(
"/purple/network/ports_range_use"
))
gtk_widget_set_sensitive
(
GTK_WIDGET
(
spin_button
),
FALSE
);
g_signal_connect
(
G_OBJECT
(
ports_checkbox
),
"clicked"
,
G_CALLBACK
(
pidgin_toggle_sensitive
),
spin_button
);
spin_button
=
pidgin_prefs_labeled_spin_button
(
hbox
,
_
(
"_End:"
),
"/purple/network/ports_range_end"
,
0
,
65535
,
sg
);
if
(
!
purple_prefs_get_bool
(
"/purple/network/ports_range_use"
))
gtk_widget_set_sensitive
(
GTK_WIDGET
(
spin_button
),
FALSE
);
g_signal_connect
(
G_OBJECT
(
ports_checkbox
),
"clicked"
,
G_CALLBACK
(
pidgin_toggle_sensitive
),
spin_button
);
pidgin_add_widget_to_vbox
(
GTK_BOX
(
vbox
),
NULL
,
NULL
,
hbox
,
TRUE
,
NULL
);
g_object_unref
(
sg
);
/* TURN server */
vbox
=
pidgin_make_frame
(
ret
,
_
(
"Relay Server (TURN)"
));
sg
=
gtk_size_group_new
(
GTK_SIZE_GROUP_HORIZONTAL
);
entry
=
gtk_entry_new
();
gtk_entry_set_text
(
GTK_ENTRY
(
entry
),
purple_prefs_get_string
(
"/purple/network/turn_server"
));
g_signal_connect
(
G_OBJECT
(
entry
),
"focus-out-event"
,
G_CALLBACK
(
network_turn_server_changed_cb
),
NULL
);
gtk_widget_show
(
entry
);
hbox
=
pidgin_add_widget_to_vbox
(
GTK_BOX
(
vbox
),
_
(
"_TURN server:"
),
sg
,
entry
,
TRUE
,
NULL
);
pidgin_prefs_labeled_spin_button
(
hbox
,
_
(
"_UDP Port:"
),
"/purple/network/turn_port"
,
0
,
65535
,
NULL
);
pidgin_prefs_labeled_spin_button
(
hbox
,
_
(
"T_CP Port:"
),
"/purple/network/turn_port_tcp"
,
0
,
65535
,
NULL
);
hbox
=
pidgin_prefs_labeled_entry
(
vbox
,
_
(
"Use_rname:"
),
"/purple/network/turn_username"
,
sg
);
pidgin_prefs_labeled_password
(
hbox
,
_
(
"Pass_word:"
),
"/purple/network/turn_password"
,
NULL
);
gtk_widget_show_all
(
ret
);
g_object_unref
(
sg
);
return
ret
;
}
#ifndef _WIN32
static
gboolean
manual_browser_set
(
GtkWidget
*
entry
,
GdkEventFocus
*
event
,
gpointer
data
)
{
const
char
*
program
=
gtk_entry_get_text
(
GTK_ENTRY
(
entry
));
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/browsers/manual_command"
,
program
);
/* carry on normally */
return
FALSE
;
}
static
GList
*
get_available_browsers
(
void
)
{
struct
browser
{
char
*
name
;
char
*
command
;
};
/* Sorted reverse alphabetically */
static
const
struct
browser
possible_browsers
[]
=
{
{
N_
(
"Seamonkey"
),
"seamonkey"
},
{
N_
(
"Opera"
),
"opera"
},
{
N_
(
"Netscape"
),
"netscape"
},
{
N_
(
"Mozilla"
),
"mozilla"
},
{
N_
(
"Konqueror"
),
"kfmclient"
},
{
N_
(
"Google Chrome"
),
"google-chrome"
},
/* Do not move the line below. Code below expects gnome-open to be in
* this list immediately after xdg-open! */
{
N_
(
"Desktop Default"
),
"xdg-open"
},
{
N_
(
"GNOME Default"
),
"gnome-open"
},
{
N_
(
"Galeon"
),
"galeon"
},
{
N_
(
"Firefox"
),
"firefox"
},
{
N_
(
"Firebird"
),
"mozilla-firebird"
},
{
N_
(
"Epiphany"
),
"epiphany"
},
/* Translators: please do not translate "chromium-browser" here! */
{
N_
(
"Chromium (chromium-browser)"
),
"chromium-browser"
},
/* Translators: please do not translate "chrome" here! */
{
N_
(
"Chromium (chrome)"
),
"chrome"
}
};
static
const
int
num_possible_browsers
=
G_N_ELEMENTS
(
possible_browsers
);
GList
*
browsers
=
NULL
;
int
i
=
0
;
char
*
browser_setting
=
(
char
*
)
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/browsers/browser"
);
browsers
=
g_list_prepend
(
browsers
,
(
gpointer
)
"custom"
);
browsers
=
g_list_prepend
(
browsers
,
(
gpointer
)
_
(
"Manual"
));
for
(
i
=
0
;
i
<
num_possible_browsers
;
i
++
)
{
if
(
purple_program_is_valid
(
possible_browsers
[
i
].
command
))
{
browsers
=
g_list_prepend
(
browsers
,
possible_browsers
[
i
].
command
);
browsers
=
g_list_prepend
(
browsers
,
(
gpointer
)
_
(
possible_browsers
[
i
].
name
));
if
(
browser_setting
&&
purple_strequal
(
possible_browsers
[
i
].
command
,
browser_setting
))
browser_setting
=
NULL
;
/* If xdg-open is valid, prefer it over gnome-open and skip forward */
if
(
purple_strequal
(
possible_browsers
[
i
].
command
,
"xdg-open"
))
{
if
(
purple_strequal
(
"gnome-open"
,
browser_setting
))
{
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/browsers/browser"
,
possible_browsers
[
i
].
command
);
browser_setting
=
NULL
;
}
i
++
;
}
}
}
if
(
browser_setting
)
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/browsers/browser"
,
"custom"
);
return
browsers
;
}
static
void
browser_changed1_cb
(
const
char
*
name
,
PurplePrefType
type
,
gconstpointer
value
,
gpointer
data
)
{
GtkWidget
*
hbox
=
data
;
const
char
*
browser
=
value
;
gtk_widget_set_sensitive
(
hbox
,
!
purple_strequal
(
browser
,
"custom"
));
}
static
void
browser_changed2_cb
(
const
char
*
name
,
PurplePrefType
type
,
gconstpointer
value
,
gpointer
data
)
{
GtkWidget
*
hbox
=
data
;
const
char
*
browser
=
value
;
gtk_widget_set_sensitive
(
hbox
,
purple_strequal
(
browser
,
"custom"
));
}
static
GtkWidget
*
browser_page
(
void
)
{
GtkWidget
*
ret
,
*
vbox
,
*
hbox
,
*
label
,
*
entry
,
*
browser_button
;
GtkSizeGroup
*
sg
;
GList
*
browsers
=
NULL
;
ret
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_CAT_SPACE
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
ret
),
PIDGIN_HIG_BORDER
);
vbox
=
pidgin_make_frame
(
ret
,
_
(
"Browser Selection"
));
if
(
purple_running_gnome
())
{
gchar
*
path
;
hbox
=
gtk_hbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
label
=
gtk_label_new
(
_
(
"Browser preferences are configured in GNOME preferences"
));
gtk_container_add
(
GTK_CONTAINER
(
vbox
),
hbox
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
label
,
FALSE
,
FALSE
,
0
);
hbox
=
gtk_hbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
gtk_container_add
(
GTK_CONTAINER
(
vbox
),
hbox
);
path
=
g_find_program_in_path
(
"gnome-control-center"
);
if
(
path
!=
NULL
)
{
gchar
*
tmp
=
g_strdup_printf
(
"%s info"
,
path
);
g_free
(
path
);
path
=
tmp
;
}
else
{
path
=
g_find_program_in_path
(
"gnome-default-applications-properties"
);
}
if
(
path
==
NULL
)
{
label
=
gtk_label_new
(
NULL
);
gtk_label_set_markup
(
GTK_LABEL
(
label
),
_
(
"<b>Browser configuration program was not found.</b>"
));
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
label
,
FALSE
,
FALSE
,
0
);
}
else
{
browser_button
=
gtk_button_new_with_mnemonic
(
_
(
"Configure _Browser"
));
g_signal_connect_data
(
G_OBJECT
(
browser_button
),
"clicked"
,
G_CALLBACK
(
browser_button_clicked_cb
),
path
,
(
GClosureNotify
)
g_free
,
0
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
browser_button
,
FALSE
,
FALSE
,
0
);
}
gtk_widget_show_all
(
ret
);
}
else
{
sg
=
gtk_size_group_new
(
GTK_SIZE_GROUP_HORIZONTAL
);
browsers
=
get_available_browsers
();
if
(
browsers
!=
NULL
)
{
label
=
pidgin_prefs_dropdown_from_list
(
vbox
,
_
(
"_Browser:"
),
PURPLE_PREF_STRING
,
PIDGIN_PREFS_ROOT
"/browsers/browser"
,
browsers
);
g_list_free
(
browsers
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0.5
);
gtk_size_group_add_widget
(
sg
,
label
);
hbox
=
gtk_hbox_new
(
FALSE
,
0
);
label
=
pidgin_prefs_dropdown
(
hbox
,
_
(
"_Open link in:"
),
PURPLE_PREF_INT
,
PIDGIN_PREFS_ROOT
"/browsers/place"
,
_
(
"Browser default"
),
PIDGIN_BROWSER_DEFAULT
,
_
(
"Existing window"
),
PIDGIN_BROWSER_CURRENT
,
_
(
"New window"
),
PIDGIN_BROWSER_NEW_WINDOW
,
_
(
"New tab"
),
PIDGIN_BROWSER_NEW_TAB
,
NULL
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0.5
);
gtk_size_group_add_widget
(
sg
,
label
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
hbox
,
FALSE
,
FALSE
,
0
);
if
(
purple_strequal
(
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/browsers/browser"
),
"custom"
))
gtk_widget_set_sensitive
(
hbox
,
FALSE
);
purple_prefs_connect_callback
(
prefs
,
PIDGIN_PREFS_ROOT
"/browsers/browser"
,
browser_changed1_cb
,
hbox
);
}
entry
=
gtk_entry_new
();
gtk_entry_set_text
(
GTK_ENTRY
(
entry
),
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/browsers/manual_command"
));
g_signal_connect
(
G_OBJECT
(
entry
),
"focus-out-event"
,
G_CALLBACK
(
manual_browser_set
),
NULL
);
hbox
=
pidgin_add_widget_to_vbox
(
GTK_BOX
(
vbox
),
_
(
"_Manual:
\n
(%s for URL)"
),
sg
,
entry
,
TRUE
,
NULL
);
if
(
!
purple_strequal
(
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/browsers/browser"
),
"custom"
))
gtk_widget_set_sensitive
(
hbox
,
FALSE
);
purple_prefs_connect_callback
(
prefs
,
PIDGIN_PREFS_ROOT
"/browsers/browser"
,
browser_changed2_cb
,
hbox
);
gtk_widget_show_all
(
ret
);
g_object_unref
(
sg
);
}
return
ret
;
}
#endif
/*_WIN32*/
static
GtkWidget
*
proxy_page
(
void
)
{
GtkWidget
*
ret
=
NULL
,
*
vbox
=
NULL
,
*
hbox
=
NULL
;
GtkWidget
*
table
=
NULL
,
*
entry
=
NULL
,
*
label
=
NULL
,
*
proxy_button
=
NULL
;
GtkWidget
*
prefs_proxy_frame
=
NULL
;
PurpleProxyInfo
*
proxy_info
;
ret
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_CAT_SPACE
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
ret
),
PIDGIN_HIG_BORDER
);
vbox
=
pidgin_make_frame
(
ret
,
_
(
"Proxy Server"
));
prefs_proxy_frame
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
if
(
purple_running_gnome
())
{
gchar
*
path
=
NULL
;
hbox
=
gtk_hbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
label
=
gtk_label_new
(
_
(
"Proxy preferences are configured in GNOME preferences"
));
gtk_container_add
(
GTK_CONTAINER
(
vbox
),
hbox
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
label
,
FALSE
,
FALSE
,
0
);
hbox
=
gtk_hbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
gtk_container_add
(
GTK_CONTAINER
(
vbox
),
hbox
);
path
=
g_find_program_in_path
(
"gnome-network-properties"
);
if
(
path
==
NULL
)
path
=
g_find_program_in_path
(
"gnome-network-preferences"
);
if
(
path
==
NULL
)
{
path
=
g_find_program_in_path
(
"gnome-control-center"
);
if
(
path
!=
NULL
)
{
char
*
tmp
=
g_strdup_printf
(
"%s network"
,
path
);
g_free
(
path
);
path
=
tmp
;
}
}
if
(
path
==
NULL
)
{
label
=
gtk_label_new
(
NULL
);
gtk_label_set_markup
(
GTK_LABEL
(
label
),
_
(
"<b>Proxy configuration program was not found.</b>"
));
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
label
,
FALSE
,
FALSE
,
0
);
}
else
{
proxy_button
=
gtk_button_new_with_mnemonic
(
_
(
"Configure _Proxy"
));
g_signal_connect
(
G_OBJECT
(
proxy_button
),
"clicked"
,
G_CALLBACK
(
proxy_button_clicked_cb
),
path
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
proxy_button
,
FALSE
,
FALSE
,
0
);
}
/* NOTE: path leaks, but only when the prefs window is destroyed,
which is never */
gtk_widget_show_all
(
ret
);
}
else
{
GtkWidget
*
prefs_proxy_subframe
=
gtk_vbox_new
(
FALSE
,
0
);
/* This is a global option that affects SOCKS4 usage even with
* account-specific proxy settings */
pidgin_prefs_checkbox
(
_
(
"Use remote _DNS with SOCKS4 proxies"
),
"/purple/proxy/socks4_remotedns"
,
prefs_proxy_frame
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
prefs_proxy_frame
,
0
,
0
,
0
);
pidgin_prefs_dropdown
(
prefs_proxy_frame
,
_
(
"Proxy t_ype:"
),
PURPLE_PREF_STRING
,
"/purple/proxy/type"
,
_
(
"No proxy"
),
"none"
,
_
(
"SOCKS 4"
),
"socks4"
,
_
(
"SOCKS 5"
),
"socks5"
,
_
(
"Tor/Privacy (SOCKS5)"
),
"tor"
,
_
(
"HTTP"
),
"http"
,
_
(
"Use Environmental Settings"
),
"envvar"
,
NULL
);
gtk_box_pack_start
(
GTK_BOX
(
prefs_proxy_frame
),
prefs_proxy_subframe
,
0
,
0
,
0
);
proxy_info
=
purple_global_proxy_get_info
();
gtk_widget_show_all
(
ret
);
purple_prefs_connect_callback
(
prefs
,
"/purple/proxy/type"
,
proxy_changed_cb
,
prefs_proxy_subframe
);
table
=
gtk_table_new
(
4
,
2
,
FALSE
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
table
),
0
);
gtk_table_set_col_spacings
(
GTK_TABLE
(
table
),
5
);
gtk_table_set_row_spacings
(
GTK_TABLE
(
table
),
10
);
gtk_container_add
(
GTK_CONTAINER
(
prefs_proxy_subframe
),
table
);
label
=
gtk_label_new_with_mnemonic
(
_
(
"_Host:"
));
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
1.0
,
0.5
);
gtk_table_attach
(
GTK_TABLE
(
table
),
label
,
0
,
1
,
0
,
1
,
GTK_FILL
,
0
,
0
,
0
);
entry
=
gtk_entry_new
();
gtk_label_set_mnemonic_widget
(
GTK_LABEL
(
label
),
entry
);
gtk_table_attach
(
GTK_TABLE
(
table
),
entry
,
1
,
2
,
0
,
1
,
GTK_FILL
,
0
,
0
,
0
);
g_signal_connect
(
G_OBJECT
(
entry
),
"changed"
,
G_CALLBACK
(
proxy_print_option
),
(
void
*
)
PROXYHOST
);
if
(
proxy_info
!=
NULL
&&
purple_proxy_info_get_host
(
proxy_info
))
gtk_entry_set_text
(
GTK_ENTRY
(
entry
),
purple_proxy_info_get_host
(
proxy_info
));
hbox
=
gtk_hbox_new
(
TRUE
,
5
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
hbox
,
FALSE
,
FALSE
,
0
);
pidgin_set_accessible_label
(
entry
,
label
);
label
=
gtk_label_new_with_mnemonic
(
_
(
"P_ort:"
));
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
1.0
,
0.5
);
gtk_table_attach
(
GTK_TABLE
(
table
),
label
,
2
,
3
,
0
,
1
,
GTK_FILL
,
0
,
0
,
0
);
entry
=
gtk_spin_button_new_with_range
(
0
,
65535
,
1
);
gtk_label_set_mnemonic_widget
(
GTK_LABEL
(
label
),
entry
);
gtk_table_attach
(
GTK_TABLE
(
table
),
entry
,
3
,
4
,
0
,
1
,
GTK_FILL
,
0
,
0
,
0
);
g_signal_connect
(
G_OBJECT
(
entry
),
"changed"
,
G_CALLBACK
(
proxy_print_option
),
(
void
*
)
PROXYPORT
);
if
(
proxy_info
!=
NULL
&&
purple_proxy_info_get_port
(
proxy_info
)
!=
0
)
{
gtk_spin_button_set_value
(
GTK_SPIN_BUTTON
(
entry
),
purple_proxy_info_get_port
(
proxy_info
));
}
pidgin_set_accessible_label
(
entry
,
label
);
label
=
gtk_label_new_with_mnemonic
(
_
(
"User_name:"
));
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
1.0
,
0.5
);
gtk_table_attach
(
GTK_TABLE
(
table
),
label
,
0
,
1
,
1
,
2
,
GTK_FILL
,
0
,
0
,
0
);
entry
=
gtk_entry_new
();
gtk_label_set_mnemonic_widget
(
GTK_LABEL
(
label
),
entry
);
gtk_table_attach
(
GTK_TABLE
(
table
),
entry
,
1
,
2
,
1
,
2
,
GTK_FILL
,
0
,
0
,
0
);
g_signal_connect
(
G_OBJECT
(
entry
),
"changed"
,
G_CALLBACK
(
proxy_print_option
),
(
void
*
)
PROXYUSER
);
if
(
proxy_info
!=
NULL
&&
purple_proxy_info_get_username
(
proxy_info
)
!=
NULL
)
gtk_entry_set_text
(
GTK_ENTRY
(
entry
),
purple_proxy_info_get_username
(
proxy_info
));
hbox
=
gtk_hbox_new
(
TRUE
,
5
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
hbox
,
FALSE
,
FALSE
,
0
);
pidgin_set_accessible_label
(
entry
,
label
);
label
=
gtk_label_new_with_mnemonic
(
_
(
"Pa_ssword:"
));
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
1.0
,
0.5
);
gtk_table_attach
(
GTK_TABLE
(
table
),
label
,
2
,
3
,
1
,
2
,
GTK_FILL
,
0
,
0
,
0
);
entry
=
gtk_entry_new
();
gtk_label_set_mnemonic_widget
(
GTK_LABEL
(
label
),
entry
);
gtk_table_attach
(
GTK_TABLE
(
table
),
entry
,
3
,
4
,
1
,
2
,
GTK_FILL
,
0
,
0
,
0
);
gtk_entry_set_visibility
(
GTK_ENTRY
(
entry
),
FALSE
);
#if !GTK_CHECK_VERSION(2,16,0)
if
(
gtk_entry_get_invisible_char
(
GTK_ENTRY
(
entry
))
==
'*'
)
gtk_entry_set_invisible_char
(
GTK_ENTRY
(
entry
),
PIDGIN_INVISIBLE_CHAR
);
#endif
/* Less than GTK+ 2.16 */
g_signal_connect
(
G_OBJECT
(
entry
),
"changed"
,
G_CALLBACK
(
proxy_print_option
),
(
void
*
)
PROXYPASS
);
if
(
proxy_info
!=
NULL
&&
purple_proxy_info_get_password
(
proxy_info
)
!=
NULL
)
gtk_entry_set_text
(
GTK_ENTRY
(
entry
),
purple_proxy_info_get_password
(
proxy_info
));
pidgin_set_accessible_label
(
entry
,
label
);
proxy_changed_cb
(
"/purple/proxy/type"
,
PURPLE_PREF_STRING
,
purple_prefs_get_string
(
"/purple/proxy/type"
),
prefs_proxy_subframe
);
}
return
ret
;
}
static
GtkWidget
*
logging_page
(
void
)
{
GtkWidget
*
ret
;
GtkWidget
*
vbox
;
GList
*
names
;
ret
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_CAT_SPACE
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
ret
),
PIDGIN_HIG_BORDER
);
vbox
=
pidgin_make_frame
(
ret
,
_
(
"Logging"
));
names
=
purple_log_logger_get_options
();
pidgin_prefs_dropdown_from_list
(
vbox
,
_
(
"Log _format:"
),
PURPLE_PREF_STRING
,
"/purple/logging/format"
,
names
);
g_list_free
(
names
);
pidgin_prefs_checkbox
(
_
(
"Log all _instant messages"
),
"/purple/logging/log_ims"
,
vbox
);
pidgin_prefs_checkbox
(
_
(
"Log all c_hats"
),
"/purple/logging/log_chats"
,
vbox
);
pidgin_prefs_checkbox
(
_
(
"Log all _status changes to system log"
),
"/purple/logging/log_system"
,
vbox
);
gtk_widget_show_all
(
ret
);
return
ret
;
}
#ifndef _WIN32
static
gint
sound_cmd_yeah
(
GtkEntry
*
entry
,
gpointer
d
)
{
purple_prefs_set_path
(
PIDGIN_PREFS_ROOT
"/sound/command"
,
gtk_entry_get_text
(
GTK_ENTRY
(
entry
)));
return
TRUE
;
}
static
void
sound_changed1_cb
(
const
char
*
name
,
PurplePrefType
type
,
gconstpointer
value
,
gpointer
data
)
{
GtkWidget
*
hbox
=
data
;
const
char
*
method
=
value
;
gtk_widget_set_sensitive
(
hbox
,
purple_strequal
(
method
,
"custom"
));
}
static
void
sound_changed2_cb
(
const
char
*
name
,
PurplePrefType
type
,
gconstpointer
value
,
gpointer
data
)
{
GtkWidget
*
vbox
=
data
;
const
char
*
method
=
value
;
gtk_widget_set_sensitive
(
vbox
,
!
purple_strequal
(
method
,
"none"
));
}
#endif
/* !_WIN32 */
#ifdef USE_GSTREAMER
static
void
sound_changed3_cb
(
const
char
*
name
,
PurplePrefType
type
,
gconstpointer
value
,
gpointer
data
)
{
GtkWidget
*
hbox
=
data
;
const
char
*
method
=
value
;
gtk_widget_set_sensitive
(
hbox
,
purple_strequal
(
method
,
"automatic"
)
||
purple_strequal
(
method
,
"alsa"
)
||
purple_strequal
(
method
,
"esd"
));
}
#endif
/* USE_GSTREAMER */
static
void
event_toggled
(
GtkCellRendererToggle
*
cell
,
gchar
*
pth
,
gpointer
data
)
{
GtkTreeModel
*
model
=
(
GtkTreeModel
*
)
data
;
GtkTreeIter
iter
;
GtkTreePath
*
path
=
gtk_tree_path_new_from_string
(
pth
);
char
*
pref
;
gtk_tree_model_get_iter
(
model
,
&
iter
,
path
);
gtk_tree_model_get
(
model
,
&
iter
,
2
,
&
pref
,
-1
);
purple_prefs_set_bool
(
pref
,
!
gtk_cell_renderer_toggle_get_active
(
cell
));
g_free
(
pref
);
gtk_list_store_set
(
GTK_LIST_STORE
(
model
),
&
iter
,
0
,
!
gtk_cell_renderer_toggle_get_active
(
cell
),
-1
);
gtk_tree_path_free
(
path
);
}
static
void
test_sound
(
GtkWidget
*
button
,
gpointer
i_am_NULL
)
{
char
*
pref
;
gboolean
temp_enabled
;
gboolean
temp_mute
;
pref
=
g_strdup_printf
(
PIDGIN_PREFS_ROOT
"/sound/enabled/%s"
,
pidgin_sound_get_event_option
(
sound_row_sel
));
temp_enabled
=
purple_prefs_get_bool
(
pref
);
temp_mute
=
purple_prefs_get_bool
(
PIDGIN_PREFS_ROOT
"/sound/mute"
);
if
(
!
temp_enabled
)
purple_prefs_set_bool
(
pref
,
TRUE
);
if
(
temp_mute
)
purple_prefs_set_bool
(
PIDGIN_PREFS_ROOT
"/sound/mute"
,
FALSE
);
purple_sound_play_event
(
sound_row_sel
,
NULL
);
if
(
!
temp_enabled
)
purple_prefs_set_bool
(
pref
,
FALSE
);
if
(
temp_mute
)
purple_prefs_set_bool
(
PIDGIN_PREFS_ROOT
"/sound/mute"
,
TRUE
);
g_free
(
pref
);
}
/*
* Resets a sound file back to default.
*/
static
void
reset_sound
(
GtkWidget
*
button
,
gpointer
i_am_also_NULL
)
{
gchar
*
pref
;
pref
=
g_strdup_printf
(
PIDGIN_PREFS_ROOT
"/sound/file/%s"
,
pidgin_sound_get_event_option
(
sound_row_sel
));
purple_prefs_set_path
(
pref
,
""
);
g_free
(
pref
);
gtk_entry_set_text
(
GTK_ENTRY
(
sound_entry
),
_
(
"(default)"
));
pref_sound_generate_markup
();
}
static
void
sound_chosen_cb
(
void
*
user_data
,
const
char
*
filename
)
{
gchar
*
pref
;
int
sound
;
sound
=
GPOINTER_TO_INT
(
user_data
);
/* Set it -- and forget it */
pref
=
g_strdup_printf
(
PIDGIN_PREFS_ROOT
"/sound/file/%s"
,
pidgin_sound_get_event_option
(
sound
));
purple_prefs_set_path
(
pref
,
filename
);
g_free
(
pref
);
/*
* If the sound we just changed is still the currently selected
* sound, then update the box showing the file name.
*/
if
(
sound
==
sound_row_sel
)
gtk_entry_set_text
(
GTK_ENTRY
(
sound_entry
),
filename
);
pref_sound_generate_markup
();
}
static
void
select_sound
(
GtkWidget
*
button
,
gpointer
being_NULL_is_fun
)
{
gchar
*
pref
;
const
char
*
filename
;
pref
=
g_strdup_printf
(
PIDGIN_PREFS_ROOT
"/sound/file/%s"
,
pidgin_sound_get_event_option
(
sound_row_sel
));
filename
=
purple_prefs_get_path
(
pref
);
g_free
(
pref
);
if
(
*
filename
==
'\0'
)
filename
=
NULL
;
purple_request_file
(
prefs
,
_
(
"Sound Selection"
),
filename
,
FALSE
,
G_CALLBACK
(
sound_chosen_cb
),
NULL
,
NULL
,
NULL
,
NULL
,
GINT_TO_POINTER
(
sound_row_sel
));
}
#ifdef USE_GSTREAMER
static
gchar
*
prefs_sound_volume_format
(
GtkScale
*
scale
,
gdouble
val
)
{
if
(
val
<
15
)
{
return
g_strdup_printf
(
_
(
"Quietest"
));
}
else
if
(
val
<
30
)
{
return
g_strdup_printf
(
_
(
"Quieter"
));
}
else
if
(
val
<
45
)
{
return
g_strdup_printf
(
_
(
"Quiet"
));
}
else
if
(
val
<
55
)
{
return
g_strdup_printf
(
_
(
"Normal"
));
}
else
if
(
val
<
70
)
{
return
g_strdup_printf
(
_
(
"Loud"
));
}
else
if
(
val
<
85
)
{
return
g_strdup_printf
(
_
(
"Louder"
));
}
else
{
return
g_strdup_printf
(
_
(
"Loudest"
));
}
}
static
void
prefs_sound_volume_changed
(
GtkRange
*
range
)
{
int
val
=
(
int
)
gtk_range_get_value
(
GTK_RANGE
(
range
));
purple_prefs_set_int
(
PIDGIN_PREFS_ROOT
"/sound/volume"
,
val
);
}
#endif
static
void
prefs_sound_sel
(
GtkTreeSelection
*
sel
,
GtkTreeModel
*
model
)
{
GtkTreeIter
iter
;
GValue
val
;
const
char
*
file
;
char
*
pref
;
if
(
!
gtk_tree_selection_get_selected
(
sel
,
&
model
,
&
iter
))
return
;
val
.
g_type
=
0
;
gtk_tree_model_get_value
(
model
,
&
iter
,
3
,
&
val
);
sound_row_sel
=
g_value_get_uint
(
&
val
);
pref
=
g_strdup_printf
(
PIDGIN_PREFS_ROOT
"/sound/file/%s"
,
pidgin_sound_get_event_option
(
sound_row_sel
));
file
=
purple_prefs_get_path
(
pref
);
g_free
(
pref
);
if
(
sound_entry
)
gtk_entry_set_text
(
GTK_ENTRY
(
sound_entry
),
(
file
&&
*
file
!=
'\0'
)
?
file
:
_
(
"(default)"
));
g_value_unset
(
&
val
);
pref_sound_generate_markup
();
}
static
void
mute_changed_cb
(
const
char
*
pref_name
,
PurplePrefType
pref_type
,
gconstpointer
val
,
gpointer
data
)
{
GtkToggleButton
*
button
=
data
;
gboolean
muted
=
GPOINTER_TO_INT
(
val
);
g_return_if_fail
(
purple_strequal
(
pref_name
,
PIDGIN_PREFS_ROOT
"/sound/mute"
));
/* Block the handler that re-sets the preference. */
g_signal_handlers_block_matched
(
button
,
G_SIGNAL_MATCH_DATA
,
0
,
0
,
NULL
,
NULL
,
(
gpointer
)
pref_name
);
gtk_toggle_button_set_active
(
button
,
muted
);
g_signal_handlers_unblock_matched
(
button
,
G_SIGNAL_MATCH_DATA
,
0
,
0
,
NULL
,
NULL
,
(
gpointer
)
pref_name
);
}
static
GtkWidget
*
sound_page
(
void
)
{
GtkWidget
*
ret
;
GtkWidget
*
vbox
,
*
vbox2
,
*
sw
,
*
button
;
GtkSizeGroup
*
sg
;
GtkTreeIter
iter
;
GtkWidget
*
event_view
;
GtkListStore
*
event_store
;
GtkCellRenderer
*
rend
;
GtkTreeViewColumn
*
col
;
GtkTreeSelection
*
sel
;
GtkTreePath
*
path
;
GtkWidget
*
hbox
;
int
j
;
const
char
*
file
;
char
*
pref
;
#ifndef _WIN32
GtkWidget
*
dd
;
GtkWidget
*
entry
;
const
char
*
cmd
;
#endif
ret
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_CAT_SPACE
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
ret
),
PIDGIN_HIG_BORDER
);
sg
=
gtk_size_group_new
(
GTK_SIZE_GROUP_HORIZONTAL
);
vbox2
=
pidgin_make_frame
(
ret
,
_
(
"Sound Options"
));
vbox
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
gtk_box_pack_start
(
GTK_BOX
(
vbox2
),
vbox
,
FALSE
,
FALSE
,
0
);
#ifndef _WIN32
dd
=
pidgin_prefs_dropdown
(
vbox2
,
_
(
"_Method:"
),
PURPLE_PREF_STRING
,
PIDGIN_PREFS_ROOT
"/sound/method"
,
_
(
"Console beep"
),
"beep"
,
#ifdef USE_GSTREAMER
_
(
"Automatic"
),
"automatic"
,
"ESD"
,
"esd"
,
"ALSA"
,
"alsa"
,
#endif
_
(
"Command"
),
"custom"
,
_
(
"No sounds"
),
"none"
,
NULL
);
gtk_size_group_add_widget
(
sg
,
dd
);
gtk_misc_set_alignment
(
GTK_MISC
(
dd
),
0
,
0.5
);
entry
=
gtk_entry_new
();
gtk_editable_set_editable
(
GTK_EDITABLE
(
entry
),
TRUE
);
cmd
=
purple_prefs_get_path
(
PIDGIN_PREFS_ROOT
"/sound/command"
);
if
(
cmd
)
gtk_entry_set_text
(
GTK_ENTRY
(
entry
),
cmd
);
g_signal_connect
(
G_OBJECT
(
entry
),
"changed"
,
G_CALLBACK
(
sound_cmd_yeah
),
NULL
);
hbox
=
pidgin_add_widget_to_vbox
(
GTK_BOX
(
vbox
),
_
(
"Sound c_ommand:
\n
(%s for filename)"
),
sg
,
entry
,
TRUE
,
NULL
);
purple_prefs_connect_callback
(
prefs
,
PIDGIN_PREFS_ROOT
"/sound/method"
,
sound_changed1_cb
,
hbox
);
gtk_widget_set_sensitive
(
hbox
,
purple_strequal
(
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/sound/method"
),
"custom"
));
#endif
/* _WIN32 */
button
=
pidgin_prefs_checkbox
(
_
(
"M_ute sounds"
),
PIDGIN_PREFS_ROOT
"/sound/mute"
,
vbox
);
purple_prefs_connect_callback
(
prefs
,
PIDGIN_PREFS_ROOT
"/sound/mute"
,
mute_changed_cb
,
button
);
pidgin_prefs_checkbox
(
_
(
"Sounds when conversation has _focus"
),
PIDGIN_PREFS_ROOT
"/sound/conv_focus"
,
vbox
);
pidgin_prefs_dropdown
(
vbox
,
_
(
"_Enable sounds:"
),
PURPLE_PREF_INT
,
"/purple/sound/while_status"
,
_
(
"Only when available"
),
1
,
_
(
"Only when not available"
),
2
,
_
(
"Always"
),
3
,
NULL
);
#ifdef USE_GSTREAMER
sw
=
gtk_hscale_new_with_range
(
0.0
,
100.0
,
5.0
);
gtk_range_set_increments
(
GTK_RANGE
(
sw
),
5.0
,
25.0
);
gtk_range_set_value
(
GTK_RANGE
(
sw
),
purple_prefs_get_int
(
PIDGIN_PREFS_ROOT
"/sound/volume"
));
g_signal_connect
(
G_OBJECT
(
sw
),
"format-value"
,
G_CALLBACK
(
prefs_sound_volume_format
),
NULL
);
g_signal_connect
(
G_OBJECT
(
sw
),
"value-changed"
,
G_CALLBACK
(
prefs_sound_volume_changed
),
NULL
);
hbox
=
pidgin_add_widget_to_vbox
(
GTK_BOX
(
vbox
),
_
(
"V_olume:"
),
NULL
,
sw
,
TRUE
,
NULL
);
purple_prefs_connect_callback
(
prefs
,
PIDGIN_PREFS_ROOT
"/sound/method"
,
sound_changed3_cb
,
hbox
);
sound_changed3_cb
(
PIDGIN_PREFS_ROOT
"/sound/method"
,
PURPLE_PREF_STRING
,
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/sound/method"
),
hbox
);
#endif
#ifndef _WIN32
gtk_widget_set_sensitive
(
vbox
,
!
purple_strequal
(
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/sound/method"
),
"none"
));
purple_prefs_connect_callback
(
prefs
,
PIDGIN_PREFS_ROOT
"/sound/method"
,
sound_changed2_cb
,
vbox
);
#endif
vbox
=
pidgin_make_frame
(
ret
,
_
(
"Sound Events"
));
/* The following is an ugly hack to make the frame expand so the
* sound events list is big enough to be usable */
gtk_box_set_child_packing
(
GTK_BOX
(
vbox
->
parent
),
vbox
,
TRUE
,
TRUE
,
0
,
GTK_PACK_START
);
gtk_box_set_child_packing
(
GTK_BOX
(
vbox
->
parent
->
parent
),
vbox
->
parent
,
TRUE
,
TRUE
,
0
,
GTK_PACK_START
);
gtk_box_set_child_packing
(
GTK_BOX
(
vbox
->
parent
->
parent
->
parent
),
vbox
->
parent
->
parent
,
TRUE
,
TRUE
,
0
,
GTK_PACK_START
);
/* SOUND SELECTION */
event_store
=
gtk_list_store_new
(
4
,
G_TYPE_BOOLEAN
,
G_TYPE_STRING
,
G_TYPE_STRING
,
G_TYPE_UINT
);
for
(
j
=
0
;
j
<
PURPLE_NUM_SOUNDS
;
j
++
)
{
char
*
pref
=
g_strdup_printf
(
PIDGIN_PREFS_ROOT
"/sound/enabled/%s"
,
pidgin_sound_get_event_option
(
j
));
const
char
*
label
=
pidgin_sound_get_event_label
(
j
);
if
(
label
==
NULL
)
{
g_free
(
pref
);
continue
;
}
gtk_list_store_append
(
event_store
,
&
iter
);
gtk_list_store_set
(
event_store
,
&
iter
,
0
,
purple_prefs_get_bool
(
pref
),
1
,
_
(
label
),
2
,
pref
,
3
,
j
,
-1
);
g_free
(
pref
);
}
event_view
=
gtk_tree_view_new_with_model
(
GTK_TREE_MODEL
(
event_store
));
rend
=
gtk_cell_renderer_toggle_new
();
sel
=
gtk_tree_view_get_selection
(
GTK_TREE_VIEW
(
event_view
));
g_signal_connect
(
G_OBJECT
(
sel
),
"changed"
,
G_CALLBACK
(
prefs_sound_sel
),
NULL
);
g_signal_connect
(
G_OBJECT
(
rend
),
"toggled"
,
G_CALLBACK
(
event_toggled
),
event_store
);
path
=
gtk_tree_path_new_first
();
gtk_tree_selection_select_path
(
sel
,
path
);
gtk_tree_path_free
(
path
);
col
=
gtk_tree_view_column_new_with_attributes
(
_
(
"Play"
),
rend
,
"active"
,
0
,
NULL
);
gtk_tree_view_append_column
(
GTK_TREE_VIEW
(
event_view
),
col
);
rend
=
gtk_cell_renderer_text_new
();
col
=
gtk_tree_view_column_new_with_attributes
(
_
(
"Event"
),
rend
,
"text"
,
1
,
NULL
);
gtk_tree_view_append_column
(
GTK_TREE_VIEW
(
event_view
),
col
);
g_object_unref
(
G_OBJECT
(
event_store
));
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
pidgin_make_scrollable
(
event_view
,
GTK_POLICY_NEVER
,
GTK_POLICY_AUTOMATIC
,
GTK_SHADOW_IN
,
-1
,
100
),
TRUE
,
TRUE
,
0
);
hbox
=
gtk_hbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
hbox
,
FALSE
,
FALSE
,
0
);
sound_entry
=
gtk_entry_new
();
pref
=
g_strdup_printf
(
PIDGIN_PREFS_ROOT
"/sound/file/%s"
,
pidgin_sound_get_event_option
(
0
));
file
=
purple_prefs_get_path
(
pref
);
g_free
(
pref
);
gtk_entry_set_text
(
GTK_ENTRY
(
sound_entry
),
(
file
&&
*
file
!=
'\0'
)
?
file
:
_
(
"(default)"
));
gtk_editable_set_editable
(
GTK_EDITABLE
(
sound_entry
),
FALSE
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
sound_entry
,
FALSE
,
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
button
=
gtk_button_new_with_mnemonic
(
_
(
"_Browse..."
));
g_signal_connect
(
G_OBJECT
(
button
),
"clicked"
,
G_CALLBACK
(
select_sound
),
NULL
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
button
,
FALSE
,
FALSE
,
1
);
button
=
gtk_button_new_with_mnemonic
(
_
(
"Pre_view"
));
g_signal_connect
(
G_OBJECT
(
button
),
"clicked"
,
G_CALLBACK
(
test_sound
),
NULL
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
button
,
FALSE
,
FALSE
,
1
);
button
=
gtk_button_new_with_mnemonic
(
_
(
"_Reset"
));
g_signal_connect
(
G_OBJECT
(
button
),
"clicked"
,
G_CALLBACK
(
reset_sound
),
NULL
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
button
,
FALSE
,
FALSE
,
1
);
gtk_widget_show_all
(
ret
);
g_object_unref
(
sg
);
return
ret
;
}
static
void
set_idle_away
(
PurpleSavedStatus
*
status
)
{
purple_prefs_set_int
(
"/purple/savedstatus/idleaway"
,
purple_savedstatus_get_creation_time
(
status
));
}
static
void
set_startupstatus
(
PurpleSavedStatus
*
status
)
{
purple_prefs_set_int
(
"/purple/savedstatus/startup"
,
purple_savedstatus_get_creation_time
(
status
));
}
static
GtkWidget
*
away_page
(
void
)
{
GtkWidget
*
ret
;
GtkWidget
*
vbox
;
GtkWidget
*
hbox
;
GtkWidget
*
dd
;
GtkWidget
*
label
;
GtkWidget
*
button
;
GtkWidget
*
menu
;
GtkSizeGroup
*
sg
;
ret
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_CAT_SPACE
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
ret
),
PIDGIN_HIG_BORDER
);
sg
=
gtk_size_group_new
(
GTK_SIZE_GROUP_HORIZONTAL
);
/* Idle stuff */
vbox
=
pidgin_make_frame
(
ret
,
_
(
"Idle"
));
dd
=
pidgin_prefs_dropdown
(
vbox
,
_
(
"_Report idle time:"
),
PURPLE_PREF_STRING
,
"/purple/away/idle_reporting"
,
_
(
"Never"
),
"none"
,
_
(
"From last sent message"
),
"purple"
,
#if defined(USE_SCREENSAVER) || defined(HAVE_IOKIT)
_
(
"Based on keyboard or mouse use"
),
"system"
,
#endif
NULL
);
gtk_size_group_add_widget
(
sg
,
dd
);
gtk_misc_set_alignment
(
GTK_MISC
(
dd
),
0
,
0.5
);
pidgin_prefs_labeled_spin_button
(
vbox
,
_
(
"_Minutes before becoming idle:"
),
"/purple/away/mins_before_away"
,
1
,
24
*
60
,
sg
);
hbox
=
gtk_hbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
hbox
,
FALSE
,
FALSE
,
0
);
button
=
pidgin_prefs_checkbox
(
_
(
"Change to this status when _idle:"
),
"/purple/away/away_when_idle"
,
hbox
);
gtk_size_group_add_widget
(
sg
,
button
);
/* TODO: Show something useful if we don't have any saved statuses. */
menu
=
pidgin_status_menu
(
purple_savedstatus_get_idleaway
(),
G_CALLBACK
(
set_idle_away
));
gtk_size_group_add_widget
(
sg
,
menu
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
menu
,
FALSE
,
FALSE
,
0
);
g_signal_connect
(
G_OBJECT
(
button
),
"clicked"
,
G_CALLBACK
(
pidgin_toggle_sensitive
),
menu
);
if
(
!
purple_prefs_get_bool
(
"/purple/away/away_when_idle"
))
gtk_widget_set_sensitive
(
GTK_WIDGET
(
menu
),
FALSE
);
/* Away stuff */
vbox
=
pidgin_make_frame
(
ret
,
_
(
"Away"
));
dd
=
pidgin_prefs_dropdown
(
vbox
,
_
(
"_Auto-reply:"
),
PURPLE_PREF_STRING
,
"/purple/away/auto_reply"
,
_
(
"Never"
),
"never"
,
_
(
"When away"
),
"away"
,
_
(
"When both away and idle"
),
"awayidle"
,
NULL
);
gtk_size_group_add_widget
(
sg
,
dd
);
gtk_misc_set_alignment
(
GTK_MISC
(
dd
),
0
,
0.5
);
/* Signon status stuff */
vbox
=
pidgin_make_frame
(
ret
,
_
(
"Status at Startup"
));
button
=
pidgin_prefs_checkbox
(
_
(
"Use status from last _exit at startup"
),
"/purple/savedstatus/startup_current_status"
,
vbox
);
gtk_size_group_add_widget
(
sg
,
button
);
/* TODO: Show something useful if we don't have any saved statuses. */
menu
=
pidgin_status_menu
(
purple_savedstatus_get_startup
(),
G_CALLBACK
(
set_startupstatus
));
gtk_size_group_add_widget
(
sg
,
menu
);
g_signal_connect
(
G_OBJECT
(
button
),
"clicked"
,
G_CALLBACK
(
pidgin_toggle_sensitive
),
menu
);
pidgin_add_widget_to_vbox
(
GTK_BOX
(
vbox
),
_
(
"Status to a_pply at startup:"
),
sg
,
menu
,
TRUE
,
&
label
);
g_signal_connect
(
G_OBJECT
(
button
),
"clicked"
,
G_CALLBACK
(
pidgin_toggle_sensitive
),
label
);
if
(
purple_prefs_get_bool
(
"/purple/savedstatus/startup_current_status"
))
{
gtk_widget_set_sensitive
(
GTK_WIDGET
(
menu
),
FALSE
);
gtk_widget_set_sensitive
(
GTK_WIDGET
(
label
),
FALSE
);
}
gtk_widget_show_all
(
ret
);
g_object_unref
(
sg
);
return
ret
;
}
#ifdef USE_VV
static
GList
*
get_vv_device_menuitems
(
PurpleMediaElementType
type
)
{
GList
*
result
=
NULL
;
GList
*
i
;
i
=
purple_media_manager_enumerate_elements
(
purple_media_manager_get
(),
type
);
for
(;
i
;
i
=
g_list_delete_link
(
i
,
i
))
{
PurpleMediaElementInfo
*
info
=
i
->
data
;
result
=
g_list_append
(
result
,
purple_media_element_info_get_name
(
info
));
result
=
g_list_append
(
result
,
purple_media_element_info_get_id
(
info
));
g_object_unref
(
info
);
}
return
result
;
}
static
GstElement
*
create_test_element
(
PurpleMediaElementType
type
)
{
PurpleMediaElementInfo
*
element_info
;
element_info
=
purple_media_manager_get_active_element
(
purple_media_manager_get
(),
type
);
g_return_val_if_fail
(
element_info
,
NULL
);
return
purple_media_element_info_call_create
(
element_info
,
NULL
,
NULL
,
NULL
);
}
static
void
vv_test_switch_page_cb
(
GtkNotebook
*
notebook
,
GtkWidget
*
page
,
guint
num
,
gpointer
data
)
{
GtkWidget
*
test
=
data
;
gtk_toggle_button_set_active
(
GTK_TOGGLE_BUTTON
(
test
),
FALSE
);
}
static
GstElement
*
create_voice_pipeline
(
void
)
{
GstElement
*
pipeline
;
GstElement
*
src
,
*
sink
;
GstElement
*
volume
;
GstElement
*
level
;
GstElement
*
valve
;
pipeline
=
gst_pipeline_new
(
"voicetest"
);
src
=
create_test_element
(
PURPLE_MEDIA_ELEMENT_AUDIO
|
PURPLE_MEDIA_ELEMENT_SRC
);
sink
=
create_test_element
(
PURPLE_MEDIA_ELEMENT_AUDIO
|
PURPLE_MEDIA_ELEMENT_SINK
);
volume
=
gst_element_factory_make
(
"volume"
,
"volume"
);
level
=
gst_element_factory_make
(
"level"
,
"level"
);
valve
=
gst_element_factory_make
(
"valve"
,
"valve"
);
gst_bin_add_many
(
GST_BIN
(
pipeline
),
src
,
volume
,
level
,
valve
,
sink
,
NULL
);
gst_element_link_many
(
src
,
volume
,
level
,
valve
,
sink
,
NULL
);
purple_debug_info
(
"gtkprefs"
,
"create_voice_pipeline: setting pipeline "
"state to GST_STATE_PLAYING - it may hang here on win32
\n
"
);
gst_element_set_state
(
GST_ELEMENT
(
pipeline
),
GST_STATE_PLAYING
);
purple_debug_info
(
"gtkprefs"
,
"create_voice_pipeline: state is set
\n
"
);
return
pipeline
;
}
static
void
on_volume_change_cb
(
GtkWidget
*
w
,
gdouble
value
,
gpointer
data
)
{
GstElement
*
volume
;
if
(
!
voice_pipeline
)
return
;
volume
=
gst_bin_get_by_name
(
GST_BIN
(
voice_pipeline
),
"volume"
);
g_object_set
(
volume
,
"volume"
,
gtk_scale_button_get_value
(
GTK_SCALE_BUTTON
(
w
))
*
10.0
,
NULL
);
}
static
gdouble
gst_msg_db_to_percent
(
GstMessage
*
msg
,
gchar
*
value_name
)
{
const
GValue
*
list
;
const
GValue
*
value
;
gdouble
value_db
;
gdouble
percent
;
list
=
gst_structure_get_value
(
gst_message_get_structure
(
msg
),
value_name
);
#if GST_CHECK_VERSION(1,0,0)
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
value
=
g_value_array_get_nth
(
g_value_get_boxed
(
list
),
0
);
G_GNUC_END_IGNORE_DEPRECATIONS
#else
value
=
gst_value_list_get_value
(
list
,
0
);
#endif
value_db
=
g_value_get_double
(
value
);
percent
=
pow
(
10
,
value_db
/
20
);
return
(
percent
>
1.0
)
?
1.0
:
percent
;
}
static
gboolean
gst_bus_cb
(
GstBus
*
bus
,
GstMessage
*
msg
,
gpointer
data
)
{
if
(
GST_MESSAGE_TYPE
(
msg
)
==
GST_MESSAGE_ELEMENT
&&
gst_structure_has_name
(
gst_message_get_structure
(
msg
),
"level"
))
{
GstElement
*
src
=
GST_ELEMENT
(
GST_MESSAGE_SRC
(
msg
));
gchar
*
name
=
gst_element_get_name
(
src
);
if
(
purple_strequal
(
name
,
"level"
))
{
gdouble
percent
;
gdouble
threshold
;
GstElement
*
valve
;
percent
=
gst_msg_db_to_percent
(
msg
,
"rms"
);
gtk_progress_bar_set_fraction
(
GTK_PROGRESS_BAR
(
voice_level
),
percent
);
percent
=
gst_msg_db_to_percent
(
msg
,
"decay"
);
threshold
=
gtk_range_get_value
(
GTK_RANGE
(
voice_threshold
))
/
100.0
;
valve
=
gst_bin_get_by_name
(
GST_BIN
(
GST_ELEMENT_PARENT
(
src
)),
"valve"
);
g_object_set
(
valve
,
"drop"
,
(
percent
<
threshold
),
NULL
);
g_object_set
(
voice_level
,
"text"
,
(
percent
<
threshold
)
?
_
(
"DROP"
)
:
" "
,
NULL
);
}
g_free
(
name
);
}
return
TRUE
;
}
static
void
voice_test_destroy_cb
(
GtkWidget
*
w
,
gpointer
data
)
{
if
(
!
voice_pipeline
)
return
;
gst_element_set_state
(
voice_pipeline
,
GST_STATE_NULL
);
gst_object_unref
(
voice_pipeline
);
voice_pipeline
=
NULL
;
}
static
void
enable_voice_test
(
void
)
{
GstBus
*
bus
;
voice_pipeline
=
create_voice_pipeline
();
bus
=
gst_pipeline_get_bus
(
GST_PIPELINE
(
voice_pipeline
));
gst_bus_add_signal_watch
(
bus
);
g_signal_connect
(
bus
,
"message"
,
G_CALLBACK
(
gst_bus_cb
),
NULL
);
gst_object_unref
(
bus
);
}
static
void
toggle_voice_test_cb
(
GtkToggleButton
*
test
,
gpointer
data
)
{
if
(
gtk_toggle_button_get_active
(
test
))
{
gtk_widget_set_sensitive
(
voice_level
,
TRUE
);
enable_voice_test
();
g_signal_connect
(
voice_volume
,
"value-changed"
,
G_CALLBACK
(
on_volume_change_cb
),
NULL
);
g_signal_connect
(
test
,
"destroy"
,
G_CALLBACK
(
voice_test_destroy_cb
),
NULL
);
g_signal_connect
(
prefsnotebook
,
"switch-page"
,
G_CALLBACK
(
vv_test_switch_page_cb
),
test
);
}
else
{
gtk_progress_bar_set_fraction
(
GTK_PROGRESS_BAR
(
voice_level
),
0.0
);
gtk_widget_set_sensitive
(
voice_level
,
FALSE
);
g_object_disconnect
(
voice_volume
,
"any-signal::value-changed"
,
G_CALLBACK
(
on_volume_change_cb
),
NULL
,
NULL
);
g_object_disconnect
(
test
,
"any-signal::destroy"
,
G_CALLBACK
(
voice_test_destroy_cb
),
NULL
,
NULL
);
g_object_disconnect
(
prefsnotebook
,
"any-signal::switch-page"
,
G_CALLBACK
(
vv_test_switch_page_cb
),
test
,
NULL
);
voice_test_destroy_cb
(
NULL
,
NULL
);
}
}
static
void
volume_changed_cb
(
GtkScaleButton
*
button
,
gdouble
value
,
gpointer
data
)
{
purple_prefs_set_int
(
"/purple/media/audio/volume/input"
,
value
*
100
);
}
static
void
threshold_value_changed_cb
(
GtkScale
*
scale
,
GtkWidget
*
label
)
{
int
value
;
char
*
tmp
;
value
=
(
int
)
gtk_range_get_value
(
GTK_RANGE
(
scale
));
tmp
=
g_strdup_printf
(
_
(
"Silence threshold: %d%%"
),
value
);
gtk_label_set_label
(
GTK_LABEL
(
label
),
tmp
);
g_free
(
tmp
);
purple_prefs_set_int
(
"/purple/media/audio/silence_threshold"
,
value
);
}
static
void
make_voice_test
(
GtkWidget
*
vbox
)
{
GtkWidget
*
test
;
GtkWidget
*
hbox
;
GtkWidget
*
label
;
GtkWidget
*
level
;
GtkWidget
*
volume
;
GtkWidget
*
threshold
;
char
*
tmp
;
label
=
gtk_label_new
(
NULL
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
label
,
TRUE
,
TRUE
,
0
);
hbox
=
gtk_hbox_new
(
FALSE
,
PIDGIN_HIG_CAT_SPACE
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
hbox
,
FALSE
,
FALSE
,
0
);
label
=
gtk_label_new
(
_
(
"Volume:"
));
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
label
,
FALSE
,
FALSE
,
0
);
volume
=
gtk_volume_button_new
();
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
volume
,
TRUE
,
TRUE
,
0
);
gtk_scale_button_set_value
(
GTK_SCALE_BUTTON
(
volume
),
purple_prefs_get_int
(
"/purple/media/audio/volume/input"
)
/
100.0
);
g_signal_connect
(
volume
,
"value-changed"
,
G_CALLBACK
(
volume_changed_cb
),
NULL
);
tmp
=
g_strdup_printf
(
_
(
"Silence threshold: %d%%"
),
purple_prefs_get_int
(
"/purple/media/audio/silence_threshold"
));
label
=
gtk_label_new
(
tmp
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
label
,
FALSE
,
FALSE
,
0
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0.5
);
g_free
(
tmp
);
threshold
=
gtk_hscale_new_with_range
(
0
,
100
,
1
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
threshold
,
FALSE
,
FALSE
,
0
);
gtk_range_set_value
(
GTK_RANGE
(
threshold
),
purple_prefs_get_int
(
"/purple/media/audio/silence_threshold"
));
gtk_scale_set_draw_value
(
GTK_SCALE
(
threshold
),
FALSE
);
g_signal_connect
(
threshold
,
"value-changed"
,
G_CALLBACK
(
threshold_value_changed_cb
),
label
);
test
=
gtk_toggle_button_new_with_label
(
_
(
"Test Audio"
));
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
test
,
FALSE
,
FALSE
,
0
);
level
=
gtk_progress_bar_new
();
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
level
,
FALSE
,
FALSE
,
0
);
gtk_widget_set_sensitive
(
level
,
FALSE
);
voice_volume
=
volume
;
voice_level
=
level
;
voice_threshold
=
threshold
;
g_signal_connect
(
test
,
"toggled"
,
G_CALLBACK
(
toggle_voice_test_cb
),
NULL
);
}
static
GstElement
*
create_video_pipeline
(
void
)
{
GstElement
*
pipeline
;
GstElement
*
src
,
*
sink
;
GstElement
*
videoconvert
;
GstElement
*
videoscale
;
pipeline
=
gst_pipeline_new
(
"videotest"
);
src
=
create_test_element
(
PURPLE_MEDIA_ELEMENT_VIDEO
|
PURPLE_MEDIA_ELEMENT_SRC
);
sink
=
create_test_element
(
PURPLE_MEDIA_ELEMENT_VIDEO
|
PURPLE_MEDIA_ELEMENT_SINK
);
videoconvert
=
gst_element_factory_make
(
"videoconvert"
,
NULL
);
videoscale
=
gst_element_factory_make
(
"videoscale"
,
NULL
);
g_object_set_data
(
G_OBJECT
(
pipeline
),
"sink"
,
sink
);
gst_bin_add_many
(
GST_BIN
(
pipeline
),
src
,
videoconvert
,
videoscale
,
sink
,
NULL
);
gst_element_link_many
(
src
,
videoconvert
,
videoscale
,
sink
,
NULL
);
return
pipeline
;
}
static
void
video_test_destroy_cb
(
GtkWidget
*
w
,
gpointer
data
)
{
if
(
!
video_pipeline
)
return
;
gst_element_set_state
(
video_pipeline
,
GST_STATE_NULL
);
gst_object_unref
(
video_pipeline
);
video_pipeline
=
NULL
;
}
static
void
window_id_cb
(
GstBus
*
bus
,
GstMessage
*
msg
,
gulong
window_id
)
{
if
(
GST_MESSAGE_TYPE
(
msg
)
!=
GST_MESSAGE_ELEMENT
#if GST_CHECK_VERSION(1,0,0)
||
!
gst_is_video_overlay_prepare_window_handle_message
(
msg
))
#else
/* there may be have-xwindow-id also, in case something went wrong */
||
!
gst_structure_has_name
(
msg
->
structure
,
"prepare-xwindow-id"
))
#endif
return
;
g_signal_handlers_disconnect_matched
(
bus
,
G_SIGNAL_MATCH_FUNC
|
G_SIGNAL_MATCH_DATA
,
0
,
0
,
NULL
,
window_id_cb
,
(
gpointer
)
window_id
);
#if GST_CHECK_VERSION(1,0,0)
gst_video_overlay_set_window_handle
(
GST_VIDEO_OVERLAY
(
GST_MESSAGE_SRC
(
msg
)),
window_id
);
#elif GST_CHECK_VERSION(0,10,31)
gst_x_overlay_set_window_handle
(
GST_X_OVERLAY
(
GST_MESSAGE_SRC
(
msg
)),
window_id
);
#else
gst_x_overlay_set_xwindow_id
(
GST_X_OVERLAY
(
GST_MESSAGE_SRC
(
msg
)),
window_id
);
#endif
}
static
void
enable_video_test
(
void
)
{
GstBus
*
bus
;
GdkWindow
*
window
=
gtk_widget_get_window
(
video_drawing_area
);
gulong
window_id
=
0
;
#if defined(GDK_WINDOWING_WIN32)
window_id
=
GPOINTER_TO_UINT
(
GDK_WINDOW_HWND
(
window
));
#elif defined (GDK_WINDOWING_X11)
window_id
=
GDK_WINDOW_XID
(
window
);
#elif defined(GDK_WINDOWING_QUARTZ)
window_id
=
(
gulong
)
gdk_quartz_window_get_nsview
(
window
);
#else
# error "Unsupported GDK windowing system"
#endif
video_pipeline
=
create_video_pipeline
();
bus
=
gst_pipeline_get_bus
(
GST_PIPELINE
(
video_pipeline
));
#if GST_CHECK_VERSION(1,0,0)
gst_bus_set_sync_handler
(
bus
,
gst_bus_sync_signal_handler
,
NULL
,
NULL
);
#else
gst_bus_set_sync_handler
(
bus
,
gst_bus_sync_signal_handler
,
NULL
);
#endif
g_signal_connect
(
bus
,
"sync-message::element"
,
G_CALLBACK
(
window_id_cb
),
(
gpointer
)
window_id
);
gst_object_unref
(
bus
);
gst_element_set_state
(
GST_ELEMENT
(
video_pipeline
),
GST_STATE_PLAYING
);
}
static
void
toggle_video_test_cb
(
GtkToggleButton
*
test
,
gpointer
data
)
{
if
(
gtk_toggle_button_get_active
(
test
))
{
enable_video_test
();
g_signal_connect
(
test
,
"destroy"
,
G_CALLBACK
(
video_test_destroy_cb
),
NULL
);
g_signal_connect
(
prefsnotebook
,
"switch-page"
,
G_CALLBACK
(
vv_test_switch_page_cb
),
test
);
}
else
{
g_object_disconnect
(
test
,
"any-signal::destroy"
,
G_CALLBACK
(
video_test_destroy_cb
),
NULL
,
NULL
);
g_object_disconnect
(
prefsnotebook
,
"any-signal::switch-page"
,
G_CALLBACK
(
vv_test_switch_page_cb
),
test
,
NULL
);
video_test_destroy_cb
(
NULL
,
NULL
);
}
}
static
GtkWidget
*
pidgin_create_video_widget
(
void
)
{
GtkWidget
*
video
=
NULL
;
video
=
gtk_drawing_area_new
();
/* In order to enable client shadow decorations, GtkDialog from GTK+ 3.0
* uses ARGB visual which by default gets inherited by its child widgets.
* XVideo adaptors on the other hand often support just depth 24 and
* rendering video through xvimagesink onto a widget inside a GtkDialog
* then results in no visible output.
*
* This ensures the default system visual of the drawing area doesn't get
* overridden by the widget's parent.
*/
gtk_widget_set_visual
(
video
,
gdk_screen_get_system_visual
(
gtk_widget_get_screen
(
video
)));
return
video
;
}
static
void
make_video_test
(
GtkWidget
*
vbox
)
{
GtkWidget
*
test
;
GtkWidget
*
video
;
video_drawing_area
=
video
=
pidgin_create_video_widget
();
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
video
,
TRUE
,
TRUE
,
0
);
gtk_widget_set_size_request
(
GTK_WIDGET
(
video
),
240
,
180
);
test
=
gtk_toggle_button_new_with_label
(
_
(
"Test Video"
));
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
test
,
FALSE
,
FALSE
,
0
);
g_signal_connect
(
test
,
"toggled"
,
G_CALLBACK
(
toggle_video_test_cb
),
NULL
);
}
static
void
vv_device_changed_cb
(
const
gchar
*
name
,
PurplePrefType
type
,
gconstpointer
value
,
gpointer
data
)
{
PurpleMediaManager
*
manager
;
PurpleMediaElementInfo
*
info
;
manager
=
purple_media_manager_get
();
info
=
purple_media_manager_get_element_info
(
manager
,
value
);
purple_media_manager_set_active_element
(
manager
,
info
);
/* Refresh test viewers */
if
(
strstr
(
name
,
"audio"
)
&&
voice_pipeline
)
{
voice_test_destroy_cb
(
NULL
,
NULL
);
enable_voice_test
();
}
else
if
(
strstr
(
name
,
"video"
)
&&
video_pipeline
)
{
video_test_destroy_cb
(
NULL
,
NULL
);
enable_video_test
();
}
}
static
const
char
*
purple_media_type_to_preference_key
(
PurpleMediaElementType
type
)
{
if
(
type
&
PURPLE_MEDIA_ELEMENT_AUDIO
)
{
if
(
type
&
PURPLE_MEDIA_ELEMENT_SRC
)
{
return
PIDGIN_PREFS_ROOT
"/vvconfig/audio/src/device"
;
}
else
if
(
type
&
PURPLE_MEDIA_ELEMENT_SINK
)
{
return
PIDGIN_PREFS_ROOT
"/vvconfig/audio/sink/device"
;
}
}
else
if
(
type
&
PURPLE_MEDIA_ELEMENT_VIDEO
)
{
if
(
type
&
PURPLE_MEDIA_ELEMENT_SRC
)
{
return
PIDGIN_PREFS_ROOT
"/vvconfig/video/src/device"
;
}
else
if
(
type
&
PURPLE_MEDIA_ELEMENT_SINK
)
{
return
PIDGIN_PREFS_ROOT
"/vvconfig/video/sink/device"
;
}
}
return
NULL
;
}
static
GtkWidget
*
make_vv_dropdown
(
GtkWidget
*
parent
,
GtkSizeGroup
*
size_group
,
PurpleMediaElementType
element_type
)
{
GtkWidget
*
label
;
const
gchar
*
preference_key
;
GList
*
devices
;
preference_key
=
purple_media_type_to_preference_key
(
element_type
);
devices
=
get_vv_device_menuitems
(
element_type
);
if
(
g_list_find_custom
(
devices
,
purple_prefs_get_string
(
preference_key
),
(
GCompareFunc
)
g_strcmp0
)
==
NULL
)
{
GList
*
next
=
g_list_next
(
devices
);
if
(
next
)
purple_prefs_set_string
(
preference_key
,
next
->
data
);
}
label
=
pidgin_prefs_dropdown_from_list
(
parent
,
_
(
"_Device"
),
PURPLE_PREF_STRING
,
preference_key
,
devices
);
g_list_free
(
devices
);
gtk_size_group_add_widget
(
size_group
,
label
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0.5
);
/* Return the parent GtkBox of dropdown and label, which was created
* in pidgin_prefs_dropdown_from_list(). */
return
gtk_widget_get_parent
(
label
);
}
static
GtkWidget
*
make_vv_frame
(
GtkWidget
*
parent
,
GtkSizeGroup
*
sg
,
const
gchar
*
name
,
PurpleMediaElementType
type
)
{
GtkWidget
*
vbox
;
GtkWidget
*
dropdown
;
vbox
=
pidgin_make_frame
(
parent
,
name
);
dropdown
=
make_vv_dropdown
(
vbox
,
sg
,
type
);
purple_prefs_connect_callback
(
vbox
,
purple_media_type_to_preference_key
(
type
),
vv_device_changed_cb
,
vbox
);
g_signal_connect_swapped
(
vbox
,
"destroy"
,
G_CALLBACK
(
purple_prefs_disconnect_by_handle
),
vbox
);
g_object_set_data
(
G_OBJECT
(
vbox
),
"vv_frame"
,
vbox
);
g_object_set_data
(
G_OBJECT
(
vbox
),
"vv_dropdown"
,
dropdown
);
g_object_set_data
(
G_OBJECT
(
vbox
),
"vv_size_group"
,
sg
);
g_object_set_data
(
G_OBJECT
(
vbox
),
"vv_media_type"
,
(
gpointer
)
type
);
return
vbox
;
}
static
void
device_list_changed_cb
(
PurpleMediaManager
*
manager
,
GtkWidget
*
widget
)
{
GtkWidget
*
frame
;
GtkWidget
*
dropdown
;
PurpleMediaElementType
media_type
;
gtk_widget_destroy
(
g_object_get_data
(
G_OBJECT
(
widget
),
"vv_dropdown"
));
frame
=
g_object_get_data
(
G_OBJECT
(
widget
),
"vv_frame"
);
media_type
=
(
PurpleMediaElementType
)
g_object_get_data
(
G_OBJECT
(
widget
),
"vv_media_type"
);
dropdown
=
make_vv_dropdown
(
frame
,
g_object_get_data
(
G_OBJECT
(
widget
),
"vv_size_group"
),
media_type
);
g_object_set_data
(
G_OBJECT
(
widget
),
"vv_dropdown"
,
dropdown
);
}
static
GtkWidget
*
vv_page
(
void
)
{
GtkWidget
*
ret
;
GtkWidget
*
vbox
;
GtkWidget
*
frame
;
GtkSizeGroup
*
sg
;
PurpleMediaManager
*
manager
;
ret
=
gtk_hbox_new
(
FALSE
,
PIDGIN_HIG_CAT_SPACE
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
ret
),
PIDGIN_HIG_BORDER
);
sg
=
gtk_size_group_new
(
GTK_SIZE_GROUP_HORIZONTAL
);
manager
=
purple_media_manager_get
();
vbox
=
pidgin_make_frame
(
ret
,
_
(
"Audio"
));
frame
=
make_vv_frame
(
vbox
,
sg
,
_
(
"Input"
),
PURPLE_MEDIA_ELEMENT_AUDIO
|
PURPLE_MEDIA_ELEMENT_SRC
);
g_signal_connect_object
(
manager
,
"elements-changed::audiosrc"
,
G_CALLBACK
(
device_list_changed_cb
),
frame
,
0
);
frame
=
make_vv_frame
(
vbox
,
sg
,
_
(
"Output"
),
PURPLE_MEDIA_ELEMENT_AUDIO
|
PURPLE_MEDIA_ELEMENT_SINK
);
g_signal_connect_object
(
manager
,
"elements-changed::audiosink"
,
G_CALLBACK
(
device_list_changed_cb
),
frame
,
0
);
make_voice_test
(
vbox
);
vbox
=
pidgin_make_frame
(
ret
,
_
(
"Video"
));
frame
=
make_vv_frame
(
vbox
,
sg
,
_
(
"Input"
),
PURPLE_MEDIA_ELEMENT_VIDEO
|
PURPLE_MEDIA_ELEMENT_SRC
);
g_signal_connect_object
(
manager
,
"elements-changed::videosrc"
,
G_CALLBACK
(
device_list_changed_cb
),
frame
,
0
);
frame
=
make_vv_frame
(
vbox
,
sg
,
_
(
"Output"
),
PURPLE_MEDIA_ELEMENT_VIDEO
|
PURPLE_MEDIA_ELEMENT_SINK
);
g_signal_connect_object
(
manager
,
"elements-changed::videosink"
,
G_CALLBACK
(
device_list_changed_cb
),
frame
,
0
);
make_video_test
(
vbox
);
gtk_widget_show_all
(
ret
);
return
ret
;
}
#endif
static
int
prefs_notebook_add_page
(
const
char
*
text
,
GtkWidget
*
page
,
int
ind
)
{
return
gtk_notebook_append_page
(
GTK_NOTEBOOK
(
prefsnotebook
),
page
,
gtk_label_new
(
text
));
}
static
void
prefs_notebook_init
(
void
)
{
prefs_notebook_add_page
(
_
(
"Interface"
),
interface_page
(),
notebook_page
++
);
#ifndef _WIN32
/* We use the registered default browser in windows */
/* if the user is running Mac OS X, hide the browsers tab */
if
(
purple_running_osx
()
==
FALSE
)
prefs_notebook_add_page
(
_
(
"Browser"
),
browser_page
(),
notebook_page
++
);
#endif
prefs_notebook_add_page
(
_
(
"Conversations"
),
conv_page
(),
notebook_page
++
);
prefs_notebook_add_page
(
_
(
"Logging"
),
logging_page
(),
notebook_page
++
);
prefs_notebook_add_page
(
_
(
"Network"
),
network_page
(),
notebook_page
++
);
prefs_notebook_add_page
(
_
(
"Proxy"
),
proxy_page
(),
notebook_page
++
);
prefs_notebook_add_page
(
_
(
"Sounds"
),
sound_page
(),
notebook_page
++
);
prefs_notebook_add_page
(
_
(
"Status / Idle"
),
away_page
(),
notebook_page
++
);
prefs_notebook_add_page
(
_
(
"Themes"
),
theme_page
(),
notebook_page
++
);
#ifdef USE_VV
prefs_notebook_add_page
(
_
(
"Voice/Video"
),
vv_page
(),
notebook_page
++
);
#endif
}
void
pidgin_prefs_show
(
void
)
{
GtkWidget
*
vbox
;
GtkWidget
*
notebook
;
GtkWidget
*
button
;
if
(
prefs
)
{
gtk_window_present
(
GTK_WINDOW
(
prefs
));
return
;
}
/* copy the preferences to tmp values...
* I liked "take affect immediately" Oh well :-( */
/* (that should have been "effect," right?) */
/* Back to instant-apply! I win! BU-HAHAHA! */
/* Create the window */
prefs
=
pidgin_create_dialog
(
_
(
"Preferences"
),
PIDGIN_HIG_BORDER
,
"preferences"
,
FALSE
);
g_signal_connect
(
G_OBJECT
(
prefs
),
"destroy"
,
G_CALLBACK
(
delete_prefs
),
NULL
);
vbox
=
pidgin_dialog_get_vbox_with_properties
(
GTK_DIALOG
(
prefs
),
FALSE
,
PIDGIN_HIG_BORDER
);
/* The notebook */
prefsnotebook
=
notebook
=
gtk_notebook_new
();
gtk_notebook_set_tab_pos
(
GTK_NOTEBOOK
(
notebook
),
GTK_POS_LEFT
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
notebook
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
prefsnotebook
);
button
=
pidgin_dialog_add_button
(
GTK_DIALOG
(
prefs
),
GTK_STOCK_CLOSE
,
NULL
,
NULL
);
g_signal_connect_swapped
(
G_OBJECT
(
button
),
"clicked"
,
G_CALLBACK
(
gtk_widget_destroy
),
prefs
);
prefs_notebook_init
();
/* Refresh the list of themes before showing the preferences window */
prefs_themes_refresh
();
/* Show everything. */
gtk_widget_show
(
prefs
);
}
static
void
set_bool_pref
(
GtkWidget
*
w
,
const
char
*
key
)
{
purple_prefs_set_bool
(
key
,
gtk_toggle_button_get_active
(
GTK_TOGGLE_BUTTON
(
w
)));
}
GtkWidget
*
pidgin_prefs_checkbox
(
const
char
*
text
,
const
char
*
key
,
GtkWidget
*
page
)
{
GtkWidget
*
button
;
button
=
gtk_check_button_new_with_mnemonic
(
text
);
gtk_toggle_button_set_active
(
GTK_TOGGLE_BUTTON
(
button
),
purple_prefs_get_bool
(
key
));
gtk_box_pack_start
(
GTK_BOX
(
page
),
button
,
FALSE
,
FALSE
,
0
);
g_signal_connect
(
G_OBJECT
(
button
),
"clicked"
,
G_CALLBACK
(
set_bool_pref
),
(
char
*
)
key
);
gtk_widget_show
(
button
);
return
button
;
}
static
void
smiley_theme_pref_cb
(
const
char
*
name
,
PurplePrefType
type
,
gconstpointer
value
,
gpointer
data
)
{
const
char
*
themename
=
value
;
GSList
*
themes
;
for
(
themes
=
smiley_themes
;
themes
;
themes
=
themes
->
next
)
{
struct
smiley_theme
*
smile
=
themes
->
data
;
if
(
smile
->
name
&&
purple_strequal
(
themename
,
smile
->
name
))
{
pidgin_themes_load_smiley_theme
(
smile
->
path
,
TRUE
);
break
;
}
}
}
void
pidgin_prefs_init
(
void
)
{
purple_prefs_add_none
(
PIDGIN_PREFS_ROOT
""
);
purple_prefs_add_none
(
"/plugins/gtk"
);
#ifndef _WIN32
/* Browsers */
purple_prefs_add_none
(
PIDGIN_PREFS_ROOT
"/browsers"
);
purple_prefs_add_int
(
PIDGIN_PREFS_ROOT
"/browsers/place"
,
PIDGIN_BROWSER_DEFAULT
);
purple_prefs_add_string
(
PIDGIN_PREFS_ROOT
"/browsers/manual_command"
,
""
);
purple_prefs_add_string
(
PIDGIN_PREFS_ROOT
"/browsers/browser"
,
"xdg-open"
);
#endif
/* Plugins */
purple_prefs_add_none
(
PIDGIN_PREFS_ROOT
"/plugins"
);
purple_prefs_add_path_list
(
PIDGIN_PREFS_ROOT
"/plugins/loaded"
,
NULL
);
/* File locations */
purple_prefs_add_none
(
PIDGIN_PREFS_ROOT
"/filelocations"
);
purple_prefs_add_path
(
PIDGIN_PREFS_ROOT
"/filelocations/last_save_folder"
,
""
);
purple_prefs_add_path
(
PIDGIN_PREFS_ROOT
"/filelocations/last_open_folder"
,
""
);
purple_prefs_add_path
(
PIDGIN_PREFS_ROOT
"/filelocations/last_icon_folder"
,
""
);
/* Themes */
prefs_themes_init
();
/* Smiley Themes */
purple_prefs_add_none
(
PIDGIN_PREFS_ROOT
"/smileys"
);
purple_prefs_add_string
(
PIDGIN_PREFS_ROOT
"/smileys/theme"
,
"Default"
);
/* Smiley Callbacks */
purple_prefs_connect_callback
(
&
prefs
,
PIDGIN_PREFS_ROOT
"/smileys/theme"
,
smiley_theme_pref_cb
,
NULL
);
#ifdef USE_VV
/* Voice/Video */
purple_prefs_add_none
(
PIDGIN_PREFS_ROOT
"/vvconfig"
);
purple_prefs_add_none
(
PIDGIN_PREFS_ROOT
"/vvconfig/audio"
);
purple_prefs_add_none
(
PIDGIN_PREFS_ROOT
"/vvconfig/audio/src"
);
purple_prefs_add_string
(
PIDGIN_PREFS_ROOT
"/vvconfig/audio/src/device"
,
""
);
purple_prefs_add_none
(
PIDGIN_PREFS_ROOT
"/vvconfig/audio/sink"
);
purple_prefs_add_string
(
PIDGIN_PREFS_ROOT
"/vvconfig/audio/sink/device"
,
""
);
purple_prefs_add_none
(
PIDGIN_PREFS_ROOT
"/vvconfig/video"
);
purple_prefs_add_none
(
PIDGIN_PREFS_ROOT
"/vvconfig/video/src"
);
purple_prefs_add_string
(
PIDGIN_PREFS_ROOT
"/vvconfig/video/src/device"
,
""
);
purple_prefs_add_none
(
PIDGIN_PREFS_ROOT
"/vvconfig/video"
);
purple_prefs_add_none
(
PIDGIN_PREFS_ROOT
"/vvconfig/video/sink"
);
purple_prefs_add_string
(
PIDGIN_PREFS_ROOT
"/vvconfig/video/sink/device"
,
""
);
#endif
pidgin_prefs_update_old
();
}
void
pidgin_prefs_update_old
(
void
)
{
const
char
*
str
=
NULL
;
purple_prefs_rename
(
"/gaim/gtk"
,
PIDGIN_PREFS_ROOT
);
/* Rename some old prefs */
purple_prefs_rename
(
PIDGIN_PREFS_ROOT
"/logging/log_ims"
,
"/purple/logging/log_ims"
);
purple_prefs_rename
(
PIDGIN_PREFS_ROOT
"/logging/log_chats"
,
"/purple/logging/log_chats"
);
purple_prefs_rename
(
"/purple/conversations/placement"
,
PIDGIN_PREFS_ROOT
"/conversations/placement"
);
purple_prefs_rename
(
PIDGIN_PREFS_ROOT
"/debug/timestamps"
,
"/purple/debug/timestamps"
);
purple_prefs_rename
(
PIDGIN_PREFS_ROOT
"/conversations/im/raise_on_events"
,
"/plugins/gtk/X11/notify/method_raise"
);
purple_prefs_rename_boolean_toggle
(
PIDGIN_PREFS_ROOT
"/conversations/ignore_colors"
,
PIDGIN_PREFS_ROOT
"/conversations/show_incoming_formatting"
);
/*
* this path pref changed to a string, so migrate. I know this will break
* things for and confuse users that use multiple versions with the same
* config directory, but I'm not inclined to want to deal with that at the
* moment. -- rekkanoryo
*/
if
((
str
=
purple_prefs_get_path
(
PIDGIN_PREFS_ROOT
"/browsers/command"
))
!=
NULL
)
{
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/browsers/manual_command"
,
str
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/browsers/command"
);
}
/* this string pref moved into the core, try to be friendly */
purple_prefs_rename
(
PIDGIN_PREFS_ROOT
"/idle/reporting_method"
,
"/purple/away/idle_reporting"
);
if
((
str
=
purple_prefs_get_string
(
"/purple/away/idle_reporting"
))
&&
purple_strequal
(
str
,
"gaim"
))
purple_prefs_set_string
(
"/purple/away/idle_reporting"
,
"purple"
);
/* Remove some no-longer-used prefs */
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/blist/auto_expand_contacts"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/blist/button_style"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/blist/grey_idle_buddies"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/blist/raise_on_events"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/blist/show_group_count"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/blist/show_warning_level"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/button_type"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/ctrl_enter_sends"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/enter_sends"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/escape_closes"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/html_shortcuts"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/icons_on_tabs"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/send_formatting"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/show_smileys"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/show_urls_as_links"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/smiley_shortcuts"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/use_custom_bgcolor"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/use_custom_fgcolor"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/use_custom_font"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/use_custom_size"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/chat/old_tab_complete"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/chat/tab_completion"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/im/hide_on_send"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/chat/color_nicks"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/chat/raise_on_events"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/ignore_fonts"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/ignore_font_sizes"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/passthrough_unknown_commands"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/idle"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/logging/individual_logs"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/sound/signon"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/sound/silent_signon"
);
/* Convert old queuing prefs to hide_new 3-way pref. */
if
(
purple_prefs_exists
(
"/plugins/gtk/docklet/queue_messages"
)
&&
purple_prefs_get_bool
(
"/plugins/gtk/docklet/queue_messages"
))
{
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/conversations/im/hide_new"
,
"always"
);
}
else
if
(
purple_prefs_exists
(
PIDGIN_PREFS_ROOT
"/away/queue_messages"
)
&&
purple_prefs_get_bool
(
PIDGIN_PREFS_ROOT
"/away/queue_messages"
))
{
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/conversations/im/hide_new"
,
"away"
);
}
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/away/queue_messages"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/away"
);
purple_prefs_remove
(
"/plugins/gtk/docklet/queue_messages"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/chat/default_width"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/chat/default_height"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/im/default_width"
);
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/im/default_height"
);
purple_prefs_rename
(
PIDGIN_PREFS_ROOT
"/conversations/x"
,
PIDGIN_PREFS_ROOT
"/conversations/im/x"
);
purple_prefs_rename
(
PIDGIN_PREFS_ROOT
"/conversations/y"
,
PIDGIN_PREFS_ROOT
"/conversations/im/y"
);
}