qulogic/pidgin
Clone
Summary
Browse
Changes
Graph
merge of '0f5cf0aaeb07300ee4f118bca066df8a6736a789'
release-2.2.1
2007-10-01, Luke Schierer
466855db01f9
merge of '0f5cf0aaeb07300ee4f118bca066df8a6736a789'
and '536822f0bc014a13db391400482c7d16d8a15796'
/**
* @file gtkutils.c GTK+ utility functions
* @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"
#ifndef _WIN32
#
include
<X11/Xlib.h>
#else
# ifdef small
# undef small
# endif
#endif
/*_WIN32*/
#ifdef USE_GTKSPELL
#
include
<gtkspell/gtkspell.h>
# ifdef _WIN32
#
include
"wspell.h"
# endif
#endif
#include
<gdk/gdkkeysyms.h>
#include
"conversation.h"
#include
"debug.h"
#include
"desktopitem.h"
#include
"imgstore.h"
#include
"notify.h"
#include
"prefs.h"
#include
"prpl.h"
#include
"request.h"
#include
"signals.h"
#include
"util.h"
#include
"gtkconv.h"
#include
"gtkdialogs.h"
#include
"gtkimhtml.h"
#include
"gtkimhtmltoolbar.h"
#include
"pidginstock.h"
#include
"gtkthemes.h"
#include
"gtkutils.h"
typedef
struct
{
GtkWidget
*
menu
;
gint
default_item
;
}
AopMenu
;
static
guint
accels_save_timer
=
0
;
static
gboolean
url_clicked_idle_cb
(
gpointer
data
)
{
purple_notify_uri
(
NULL
,
data
);
g_free
(
data
);
return
FALSE
;
}
static
void
url_clicked_cb
(
GtkWidget
*
w
,
const
char
*
uri
)
{
g_idle_add
(
url_clicked_idle_cb
,
g_strdup
(
uri
));
}
static
GtkIMHtmlFuncs
gtkimhtml_cbs
=
{
(
GtkIMHtmlGetImageFunc
)
purple_imgstore_find_by_id
,
(
GtkIMHtmlGetImageDataFunc
)
purple_imgstore_get_data
,
(
GtkIMHtmlGetImageSizeFunc
)
purple_imgstore_get_size
,
(
GtkIMHtmlGetImageFilenameFunc
)
purple_imgstore_get_filename
,
purple_imgstore_ref_by_id
,
purple_imgstore_unref_by_id
,
};
void
pidgin_setup_imhtml
(
GtkWidget
*
imhtml
)
{
PangoFontDescription
*
desc
=
NULL
;
g_return_if_fail
(
imhtml
!=
NULL
);
g_return_if_fail
(
GTK_IS_IMHTML
(
imhtml
));
g_signal_connect
(
G_OBJECT
(
imhtml
),
"url_clicked"
,
G_CALLBACK
(
url_clicked_cb
),
NULL
);
pidgin_themes_smiley_themeize
(
imhtml
);
gtk_imhtml_set_funcs
(
GTK_IMHTML
(
imhtml
),
&
gtkimhtml_cbs
);
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
);
}
else
if
(
purple_running_gnome
())
{
/* Use the GNOME "document" font, if applicable */
char
*
path
,
*
font
;
if
((
path
=
g_find_program_in_path
(
"gconftool-2"
)))
{
g_free
(
path
);
if
(
!
g_spawn_command_line_sync
(
"gconftool-2 -g /desktop/gnome/interface/document_font_name"
,
&
font
,
NULL
,
NULL
,
NULL
))
return
;
}
desc
=
pango_font_description_from_string
(
font
);
g_free
(
font
);
}
if
(
desc
)
{
gtk_widget_modify_font
(
imhtml
,
desc
);
pango_font_description_free
(
desc
);
}
}
GtkWidget
*
pidgin_create_window
(
const
char
*
title
,
guint
border_width
,
const
char
*
role
,
gboolean
resizable
)
{
GtkWindow
*
wnd
=
NULL
;
wnd
=
GTK_WINDOW
(
gtk_window_new
(
GTK_WINDOW_TOPLEVEL
));
if
(
title
)
gtk_window_set_title
(
wnd
,
title
);
#ifdef _WIN32
else
gtk_window_set_title
(
wnd
,
PIDGIN_ALERT_TITLE
);
#endif
gtk_container_set_border_width
(
GTK_CONTAINER
(
wnd
),
border_width
);
if
(
role
)
gtk_window_set_role
(
wnd
,
role
);
gtk_window_set_resizable
(
wnd
,
resizable
);
return
GTK_WIDGET
(
wnd
);
}
GtkWidget
*
pidgin_create_imhtml
(
gboolean
editable
,
GtkWidget
**
imhtml_ret
,
GtkWidget
**
toolbar_ret
,
GtkWidget
**
sw_ret
)
{
GtkWidget
*
frame
;
GtkWidget
*
imhtml
;
GtkWidget
*
sep
;
GtkWidget
*
sw
;
GtkWidget
*
toolbar
=
NULL
;
GtkWidget
*
vbox
;
frame
=
gtk_frame_new
(
NULL
);
gtk_frame_set_shadow_type
(
GTK_FRAME
(
frame
),
GTK_SHADOW_IN
);
vbox
=
gtk_vbox_new
(
FALSE
,
0
);
gtk_container_add
(
GTK_CONTAINER
(
frame
),
vbox
);
gtk_widget_show
(
vbox
);
if
(
editable
)
{
toolbar
=
gtk_imhtmltoolbar_new
();
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
toolbar
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
toolbar
);
sep
=
gtk_hseparator_new
();
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
sep
,
FALSE
,
FALSE
,
0
);
g_signal_connect_swapped
(
G_OBJECT
(
toolbar
),
"show"
,
G_CALLBACK
(
gtk_widget_show
),
sep
);
g_signal_connect_swapped
(
G_OBJECT
(
toolbar
),
"hide"
,
G_CALLBACK
(
gtk_widget_hide
),
sep
);
gtk_widget_show
(
sep
);
}
sw
=
gtk_scrolled_window_new
(
NULL
,
NULL
);
gtk_scrolled_window_set_policy
(
GTK_SCROLLED_WINDOW
(
sw
),
GTK_POLICY_AUTOMATIC
,
GTK_POLICY_AUTOMATIC
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
sw
,
TRUE
,
TRUE
,
0
);
gtk_widget_show
(
sw
);
imhtml
=
gtk_imhtml_new
(
NULL
,
NULL
);
gtk_imhtml_set_editable
(
GTK_IMHTML
(
imhtml
),
editable
);
gtk_imhtml_set_format_functions
(
GTK_IMHTML
(
imhtml
),
GTK_IMHTML_ALL
^
GTK_IMHTML_IMAGE
);
gtk_text_view_set_wrap_mode
(
GTK_TEXT_VIEW
(
imhtml
),
GTK_WRAP_WORD_CHAR
);
#ifdef USE_GTKSPELL
if
(
editable
&&
purple_prefs_get_bool
(
PIDGIN_PREFS_ROOT
"/conversations/spellcheck"
))
pidgin_setup_gtkspell
(
GTK_TEXT_VIEW
(
imhtml
));
#endif
gtk_widget_show
(
imhtml
);
if
(
editable
)
{
gtk_imhtmltoolbar_attach
(
GTK_IMHTMLTOOLBAR
(
toolbar
),
imhtml
);
gtk_imhtmltoolbar_associate_smileys
(
GTK_IMHTMLTOOLBAR
(
toolbar
),
"default"
);
}
pidgin_setup_imhtml
(
imhtml
);
gtk_container_add
(
GTK_CONTAINER
(
sw
),
imhtml
);
if
(
imhtml_ret
!=
NULL
)
*
imhtml_ret
=
imhtml
;
if
(
editable
&&
(
toolbar_ret
!=
NULL
))
*
toolbar_ret
=
toolbar
;
if
(
sw_ret
!=
NULL
)
*
sw_ret
=
sw
;
return
frame
;
}
void
pidgin_set_sensitive_if_input
(
GtkWidget
*
entry
,
GtkWidget
*
dialog
)
{
const
char
*
text
=
gtk_entry_get_text
(
GTK_ENTRY
(
entry
));
gtk_dialog_set_response_sensitive
(
GTK_DIALOG
(
dialog
),
GTK_RESPONSE_OK
,
(
*
text
!=
'\0'
));
}
void
pidgin_toggle_sensitive
(
GtkWidget
*
widget
,
GtkWidget
*
to_toggle
)
{
gboolean
sensitivity
;
if
(
to_toggle
==
NULL
)
return
;
sensitivity
=
GTK_WIDGET_IS_SENSITIVE
(
to_toggle
);
gtk_widget_set_sensitive
(
to_toggle
,
!
sensitivity
);
}
void
pidgin_toggle_sensitive_array
(
GtkWidget
*
w
,
GPtrArray
*
data
)
{
gboolean
sensitivity
;
gpointer
element
;
int
i
;
for
(
i
=
0
;
i
<
data
->
len
;
i
++
)
{
element
=
g_ptr_array_index
(
data
,
i
);
if
(
element
==
NULL
)
continue
;
sensitivity
=
GTK_WIDGET_IS_SENSITIVE
(
element
);
gtk_widget_set_sensitive
(
element
,
!
sensitivity
);
}
}
void
pidgin_toggle_showhide
(
GtkWidget
*
widget
,
GtkWidget
*
to_toggle
)
{
if
(
to_toggle
==
NULL
)
return
;
if
(
GTK_WIDGET_VISIBLE
(
to_toggle
))
gtk_widget_hide
(
to_toggle
);
else
gtk_widget_show
(
to_toggle
);
}
GtkWidget
*
pidgin_separator
(
GtkWidget
*
menu
)
{
GtkWidget
*
menuitem
;
menuitem
=
gtk_separator_menu_item_new
();
gtk_widget_show
(
menuitem
);
gtk_menu_shell_append
(
GTK_MENU_SHELL
(
menu
),
menuitem
);
return
menuitem
;
}
GtkWidget
*
pidgin_new_item
(
GtkWidget
*
menu
,
const
char
*
str
)
{
GtkWidget
*
menuitem
;
GtkWidget
*
label
;
menuitem
=
gtk_menu_item_new
();
if
(
menu
)
gtk_menu_shell_append
(
GTK_MENU_SHELL
(
menu
),
menuitem
);
gtk_widget_show
(
menuitem
);
label
=
gtk_label_new
(
str
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0.5
);
gtk_label_set_pattern
(
GTK_LABEL
(
label
),
"_"
);
gtk_container_add
(
GTK_CONTAINER
(
menuitem
),
label
);
gtk_widget_show
(
label
);
/* FIXME: Go back and fix this
gtk_widget_add_accelerator(menuitem, "activate", accel, str[0],
GDK_MOD1_MASK, GTK_ACCEL_LOCKED);
*/
pidgin_set_accessible_label
(
menuitem
,
label
);
return
menuitem
;
}
GtkWidget
*
pidgin_new_check_item
(
GtkWidget
*
menu
,
const
char
*
str
,
GtkSignalFunc
sf
,
gpointer
data
,
gboolean
checked
)
{
GtkWidget
*
menuitem
;
menuitem
=
gtk_check_menu_item_new_with_mnemonic
(
str
);
if
(
menu
)
gtk_menu_shell_append
(
GTK_MENU_SHELL
(
menu
),
menuitem
);
gtk_check_menu_item_set_active
(
GTK_CHECK_MENU_ITEM
(
menuitem
),
checked
);
if
(
sf
)
g_signal_connect
(
G_OBJECT
(
menuitem
),
"activate"
,
sf
,
data
);
gtk_widget_show_all
(
menuitem
);
return
menuitem
;
}
GtkWidget
*
pidgin_pixbuf_toolbar_button_from_stock
(
const
char
*
icon
)
{
GtkWidget
*
button
,
*
image
,
*
bbox
;
button
=
gtk_toggle_button_new
();
gtk_button_set_relief
(
GTK_BUTTON
(
button
),
GTK_RELIEF_NONE
);
bbox
=
gtk_vbox_new
(
FALSE
,
0
);
gtk_container_add
(
GTK_CONTAINER
(
button
),
bbox
);
image
=
gtk_image_new_from_stock
(
icon
,
gtk_icon_size_from_name
(
PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL
));
gtk_box_pack_start
(
GTK_BOX
(
bbox
),
image
,
FALSE
,
FALSE
,
0
);
gtk_widget_show_all
(
bbox
);
return
button
;
}
GtkWidget
*
pidgin_pixbuf_button_from_stock
(
const
char
*
text
,
const
char
*
icon
,
PidginButtonOrientation
style
)
{
GtkWidget
*
button
,
*
image
,
*
label
,
*
bbox
,
*
ibox
,
*
lbox
=
NULL
;
button
=
gtk_button_new
();
if
(
style
==
PIDGIN_BUTTON_HORIZONTAL
)
{
bbox
=
gtk_hbox_new
(
FALSE
,
0
);
ibox
=
gtk_hbox_new
(
FALSE
,
0
);
if
(
text
)
lbox
=
gtk_hbox_new
(
FALSE
,
0
);
}
else
{
bbox
=
gtk_vbox_new
(
FALSE
,
0
);
ibox
=
gtk_vbox_new
(
FALSE
,
0
);
if
(
text
)
lbox
=
gtk_vbox_new
(
FALSE
,
0
);
}
gtk_container_add
(
GTK_CONTAINER
(
button
),
bbox
);
if
(
icon
)
{
gtk_box_pack_start_defaults
(
GTK_BOX
(
bbox
),
ibox
);
image
=
gtk_image_new_from_stock
(
icon
,
GTK_ICON_SIZE_BUTTON
);
gtk_box_pack_end
(
GTK_BOX
(
ibox
),
image
,
FALSE
,
TRUE
,
0
);
}
if
(
text
)
{
gtk_box_pack_start_defaults
(
GTK_BOX
(
bbox
),
lbox
);
label
=
gtk_label_new
(
NULL
);
gtk_label_set_text_with_mnemonic
(
GTK_LABEL
(
label
),
text
);
gtk_label_set_mnemonic_widget
(
GTK_LABEL
(
label
),
button
);
gtk_box_pack_start
(
GTK_BOX
(
lbox
),
label
,
FALSE
,
TRUE
,
0
);
pidgin_set_accessible_label
(
button
,
label
);
}
gtk_widget_show_all
(
bbox
);
return
button
;
}
GtkWidget
*
pidgin_new_item_from_stock
(
GtkWidget
*
menu
,
const
char
*
str
,
const
char
*
icon
,
GtkSignalFunc
sf
,
gpointer
data
,
guint
accel_key
,
guint
accel_mods
,
char
*
mod
)
{
GtkWidget
*
menuitem
;
/*
GtkWidget *hbox;
GtkWidget *label;
*/
GtkWidget
*
image
;
if
(
icon
==
NULL
)
menuitem
=
gtk_menu_item_new_with_mnemonic
(
str
);
else
menuitem
=
gtk_image_menu_item_new_with_mnemonic
(
str
);
if
(
menu
)
gtk_menu_shell_append
(
GTK_MENU_SHELL
(
menu
),
menuitem
);
if
(
sf
)
g_signal_connect
(
G_OBJECT
(
menuitem
),
"activate"
,
sf
,
data
);
if
(
icon
!=
NULL
)
{
image
=
gtk_image_new_from_stock
(
icon
,
gtk_icon_size_from_name
(
PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL
));
gtk_image_menu_item_set_image
(
GTK_IMAGE_MENU_ITEM
(
menuitem
),
image
);
}
/* FIXME: this isn't right
if (mod) {
label = gtk_label_new(mod);
gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 2);
gtk_widget_show(label);
}
*/
/*
if (accel_key) {
gtk_widget_add_accelerator(menuitem, "activate", accel, accel_key,
accel_mods, GTK_ACCEL_LOCKED);
}
*/
gtk_widget_show_all
(
menuitem
);
return
menuitem
;
}
GtkWidget
*
pidgin_make_frame
(
GtkWidget
*
parent
,
const
char
*
title
)
{
GtkWidget
*
vbox
,
*
label
,
*
hbox
;
char
*
labeltitle
;
vbox
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
gtk_box_pack_start
(
GTK_BOX
(
parent
),
vbox
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
vbox
);
label
=
gtk_label_new
(
NULL
);
labeltitle
=
g_strdup_printf
(
"<span weight=
\"
bold
\"
>%s</span>"
,
title
);
gtk_label_set_markup
(
GTK_LABEL
(
label
),
labeltitle
);
g_free
(
labeltitle
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
label
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
label
);
pidgin_set_accessible_label
(
vbox
,
label
);
hbox
=
gtk_hbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
hbox
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
hbox
);
label
=
gtk_label_new
(
" "
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
label
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
label
);
vbox
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
vbox
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
vbox
);
return
vbox
;
}
static
gpointer
aop_option_menu_get_selected
(
GtkWidget
*
optmenu
,
GtkWidget
**
p_item
)
{
GtkWidget
*
menu
=
gtk_option_menu_get_menu
(
GTK_OPTION_MENU
(
optmenu
));
GtkWidget
*
item
=
gtk_menu_get_active
(
GTK_MENU
(
menu
));
if
(
p_item
)
(
*
p_item
)
=
item
;
return
g_object_get_data
(
G_OBJECT
(
item
),
"aop_per_item_data"
);
}
static
void
aop_menu_cb
(
GtkWidget
*
optmenu
,
GCallback
cb
)
{
GtkWidget
*
item
;
gpointer
per_item_data
;
per_item_data
=
aop_option_menu_get_selected
(
optmenu
,
&
item
);
if
(
cb
!=
NULL
)
{
((
void
(
*
)(
GtkWidget
*
,
gpointer
,
gpointer
))
cb
)(
item
,
per_item_data
,
g_object_get_data
(
G_OBJECT
(
optmenu
),
"user_data"
));
}
}
static
GtkWidget
*
aop_menu_item_new
(
GtkSizeGroup
*
sg
,
GdkPixbuf
*
pixbuf
,
const
char
*
lbl
,
gpointer
per_item_data
,
const
char
*
data
)
{
GtkWidget
*
item
;
GtkWidget
*
hbox
;
GtkWidget
*
image
;
GtkWidget
*
label
;
item
=
gtk_menu_item_new
();
gtk_widget_show
(
item
);
hbox
=
gtk_hbox_new
(
FALSE
,
4
);
gtk_widget_show
(
hbox
);
/* Create the image */
if
(
pixbuf
==
NULL
)
image
=
gtk_image_new
();
else
image
=
gtk_image_new_from_pixbuf
(
pixbuf
);
gtk_widget_show
(
image
);
if
(
sg
)
gtk_size_group_add_widget
(
sg
,
image
);
/* Create the label */
label
=
gtk_label_new
(
lbl
);
gtk_widget_show
(
label
);
gtk_label_set_justify
(
GTK_LABEL
(
label
),
GTK_JUSTIFY_LEFT
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0.0
,
0.5
);
gtk_container_add
(
GTK_CONTAINER
(
item
),
hbox
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
image
,
FALSE
,
FALSE
,
0
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
label
,
TRUE
,
TRUE
,
0
);
g_object_set_data
(
G_OBJECT
(
item
),
data
,
per_item_data
);
g_object_set_data
(
G_OBJECT
(
item
),
"aop_per_item_data"
,
per_item_data
);
pidgin_set_accessible_label
(
item
,
label
);
return
item
;
}
static
GdkPixbuf
*
pidgin_create_prpl_icon_from_prpl
(
PurplePlugin
*
prpl
,
PidginPrplIconSize
size
,
PurpleAccount
*
account
)
{
PurplePluginProtocolInfo
*
prpl_info
;
const
char
*
protoname
=
NULL
;
char
*
tmp
;
char
*
filename
=
NULL
;
GdkPixbuf
*
pixbuf
;
prpl_info
=
PURPLE_PLUGIN_PROTOCOL_INFO
(
prpl
);
if
(
prpl_info
->
list_icon
==
NULL
)
return
NULL
;
protoname
=
prpl_info
->
list_icon
(
account
,
NULL
);
if
(
protoname
==
NULL
)
return
NULL
;
/*
* Status icons will be themeable too, and then it will look up
* protoname from the theme
*/
tmp
=
g_strconcat
(
protoname
,
".png"
,
NULL
);
filename
=
g_build_filename
(
DATADIR
,
"pixmaps"
,
"pidgin"
,
"protocols"
,
size
==
PIDGIN_PRPL_ICON_SMALL
?
"16"
:
size
==
PIDGIN_PRPL_ICON_MEDIUM
?
"22"
:
"48"
,
tmp
,
NULL
);
g_free
(
tmp
);
pixbuf
=
gdk_pixbuf_new_from_file
(
filename
,
NULL
);
g_free
(
filename
);
return
pixbuf
;
}
static
GtkWidget
*
aop_option_menu_new
(
AopMenu
*
aop_menu
,
GCallback
cb
,
gpointer
user_data
)
{
GtkWidget
*
optmenu
;
optmenu
=
gtk_option_menu_new
();
gtk_widget_show
(
optmenu
);
gtk_option_menu_set_menu
(
GTK_OPTION_MENU
(
optmenu
),
aop_menu
->
menu
);
if
(
aop_menu
->
default_item
!=
-1
)
gtk_option_menu_set_history
(
GTK_OPTION_MENU
(
optmenu
),
aop_menu
->
default_item
);
g_object_set_data_full
(
G_OBJECT
(
optmenu
),
"aop_menu"
,
aop_menu
,
(
GDestroyNotify
)
g_free
);
g_object_set_data
(
G_OBJECT
(
optmenu
),
"user_data"
,
user_data
);
g_signal_connect
(
G_OBJECT
(
optmenu
),
"changed"
,
G_CALLBACK
(
aop_menu_cb
),
cb
);
return
optmenu
;
}
static
void
aop_option_menu_replace_menu
(
GtkWidget
*
optmenu
,
AopMenu
*
new_aop_menu
)
{
if
(
gtk_option_menu_get_menu
(
GTK_OPTION_MENU
(
optmenu
)))
gtk_option_menu_remove_menu
(
GTK_OPTION_MENU
(
optmenu
));
gtk_option_menu_set_menu
(
GTK_OPTION_MENU
(
optmenu
),
new_aop_menu
->
menu
);
if
(
new_aop_menu
->
default_item
!=
-1
)
gtk_option_menu_set_history
(
GTK_OPTION_MENU
(
optmenu
),
new_aop_menu
->
default_item
);
g_object_set_data_full
(
G_OBJECT
(
optmenu
),
"aop_menu"
,
new_aop_menu
,
(
GDestroyNotify
)
g_free
);
}
static
void
aop_option_menu_select_by_data
(
GtkWidget
*
optmenu
,
gpointer
data
)
{
guint
idx
;
GList
*
llItr
=
NULL
;
for
(
idx
=
0
,
llItr
=
GTK_MENU_SHELL
(
gtk_option_menu_get_menu
(
GTK_OPTION_MENU
(
optmenu
)))
->
children
;
llItr
!=
NULL
;
llItr
=
llItr
->
next
,
idx
++
)
{
if
(
data
==
g_object_get_data
(
G_OBJECT
(
llItr
->
data
),
"aop_per_item_data"
))
{
gtk_option_menu_set_history
(
GTK_OPTION_MENU
(
optmenu
),
idx
);
break
;
}
}
}
static
AopMenu
*
create_protocols_menu
(
const
char
*
default_proto_id
)
{
AopMenu
*
aop_menu
=
NULL
;
PurplePluginProtocolInfo
*
prpl_info
;
PurplePlugin
*
plugin
;
GdkPixbuf
*
pixbuf
=
NULL
;
GtkSizeGroup
*
sg
;
GList
*
p
;
const
char
*
gtalk_name
=
NULL
;
int
i
;
aop_menu
=
g_malloc0
(
sizeof
(
AopMenu
));
aop_menu
->
default_item
=
-1
;
aop_menu
->
menu
=
gtk_menu_new
();
gtk_widget_show
(
aop_menu
->
menu
);
sg
=
gtk_size_group_new
(
GTK_SIZE_GROUP_HORIZONTAL
);
if
(
purple_find_prpl
(
"prpl-jabber"
))
gtalk_name
=
_
(
"Google Talk"
);
for
(
p
=
purple_plugins_get_protocols
(),
i
=
0
;
p
!=
NULL
;
p
=
p
->
next
,
i
++
)
{
plugin
=
(
PurplePlugin
*
)
p
->
data
;
prpl_info
=
PURPLE_PLUGIN_PROTOCOL_INFO
(
plugin
);
if
(
gtalk_name
&&
strcmp
(
gtalk_name
,
plugin
->
info
->
name
)
<
0
)
{
char
*
filename
=
g_build_filename
(
DATADIR
,
"pixmaps"
,
"pidgin"
,
"protocols"
,
"16"
,
"google-talk.png"
,
NULL
);
GtkWidget
*
item
;
pixbuf
=
gdk_pixbuf_new_from_file
(
filename
,
NULL
);
g_free
(
filename
);
gtk_menu_shell_append
(
GTK_MENU_SHELL
(
aop_menu
->
menu
),
item
=
aop_menu_item_new
(
sg
,
pixbuf
,
gtalk_name
,
"prpl-jabber"
,
"protocol"
));
g_object_set_data
(
G_OBJECT
(
item
),
"fake"
,
GINT_TO_POINTER
(
1
));
if
(
pixbuf
)
g_object_unref
(
pixbuf
);
gtalk_name
=
NULL
;
i
++
;
}
pixbuf
=
pidgin_create_prpl_icon_from_prpl
(
plugin
,
PIDGIN_PRPL_ICON_SMALL
,
NULL
);
gtk_menu_shell_append
(
GTK_MENU_SHELL
(
aop_menu
->
menu
),
aop_menu_item_new
(
sg
,
pixbuf
,
plugin
->
info
->
name
,
plugin
->
info
->
id
,
"protocol"
));
if
(
pixbuf
)
g_object_unref
(
pixbuf
);
if
(
default_proto_id
!=
NULL
&&
!
strcmp
(
plugin
->
info
->
id
,
default_proto_id
))
aop_menu
->
default_item
=
i
;
}
g_object_unref
(
sg
);
return
aop_menu
;
}
GtkWidget
*
pidgin_protocol_option_menu_new
(
const
char
*
id
,
GCallback
cb
,
gpointer
user_data
)
{
return
aop_option_menu_new
(
create_protocols_menu
(
id
),
cb
,
user_data
);
}
const
char
*
pidgin_protocol_option_menu_get_selected
(
GtkWidget
*
optmenu
)
{
return
(
const
char
*
)
aop_option_menu_get_selected
(
optmenu
,
NULL
);
}
PurpleAccount
*
pidgin_account_option_menu_get_selected
(
GtkWidget
*
optmenu
)
{
return
(
PurpleAccount
*
)
aop_option_menu_get_selected
(
optmenu
,
NULL
);
}
static
AopMenu
*
create_account_menu
(
PurpleAccount
*
default_account
,
PurpleFilterAccountFunc
filter_func
,
gboolean
show_all
)
{
AopMenu
*
aop_menu
=
NULL
;
PurpleAccount
*
account
;
GdkPixbuf
*
pixbuf
=
NULL
;
GList
*
list
;
GList
*
p
;
GtkSizeGroup
*
sg
;
int
i
;
char
buf
[
256
];
if
(
show_all
)
list
=
purple_accounts_get_all
();
else
list
=
purple_connections_get_all
();
aop_menu
=
g_malloc0
(
sizeof
(
AopMenu
));
aop_menu
->
default_item
=
-1
;
aop_menu
->
menu
=
gtk_menu_new
();
gtk_widget_show
(
aop_menu
->
menu
);
sg
=
gtk_size_group_new
(
GTK_SIZE_GROUP_HORIZONTAL
);
for
(
p
=
list
,
i
=
0
;
p
!=
NULL
;
p
=
p
->
next
,
i
++
)
{
PurplePlugin
*
plugin
;
if
(
show_all
)
account
=
(
PurpleAccount
*
)
p
->
data
;
else
{
PurpleConnection
*
gc
=
(
PurpleConnection
*
)
p
->
data
;
account
=
purple_connection_get_account
(
gc
);
}
if
(
filter_func
&&
!
filter_func
(
account
))
{
i
--
;
continue
;
}
plugin
=
purple_find_prpl
(
purple_account_get_protocol_id
(
account
));
pixbuf
=
pidgin_create_prpl_icon
(
account
,
PIDGIN_PRPL_ICON_SMALL
);
if
(
pixbuf
)
{
if
(
purple_account_is_disconnected
(
account
)
&&
show_all
&&
purple_connections_get_all
())
gdk_pixbuf_saturate_and_pixelate
(
pixbuf
,
pixbuf
,
0.0
,
FALSE
);
}
if
(
purple_account_get_alias
(
account
))
{
g_snprintf
(
buf
,
sizeof
(
buf
),
"%s (%s) (%s)"
,
purple_account_get_username
(
account
),
purple_account_get_alias
(
account
),
purple_account_get_protocol_name
(
account
));
}
else
{
g_snprintf
(
buf
,
sizeof
(
buf
),
"%s (%s)"
,
purple_account_get_username
(
account
),
purple_account_get_protocol_name
(
account
));
}
gtk_menu_shell_append
(
GTK_MENU_SHELL
(
aop_menu
->
menu
),
aop_menu_item_new
(
sg
,
pixbuf
,
buf
,
account
,
"account"
));
if
(
pixbuf
)
g_object_unref
(
pixbuf
);
if
(
default_account
&&
account
==
default_account
)
aop_menu
->
default_item
=
i
;
}
g_object_unref
(
sg
);
return
aop_menu
;
}
static
void
regenerate_account_menu
(
GtkWidget
*
optmenu
)
{
gboolean
show_all
;
PurpleAccount
*
account
;
PurpleFilterAccountFunc
filter_func
;
account
=
(
PurpleAccount
*
)
aop_option_menu_get_selected
(
optmenu
,
NULL
);
show_all
=
GPOINTER_TO_INT
(
g_object_get_data
(
G_OBJECT
(
optmenu
),
"show_all"
));
filter_func
=
g_object_get_data
(
G_OBJECT
(
optmenu
),
"filter_func"
);
aop_option_menu_replace_menu
(
optmenu
,
create_account_menu
(
account
,
filter_func
,
show_all
));
}
static
void
account_menu_sign_on_off_cb
(
PurpleConnection
*
gc
,
GtkWidget
*
optmenu
)
{
regenerate_account_menu
(
optmenu
);
}
static
void
account_menu_added_removed_cb
(
PurpleAccount
*
account
,
GtkWidget
*
optmenu
)
{
regenerate_account_menu
(
optmenu
);
}
static
gboolean
account_menu_destroyed_cb
(
GtkWidget
*
optmenu
,
GdkEvent
*
event
,
void
*
user_data
)
{
purple_signals_disconnect_by_handle
(
optmenu
);
return
FALSE
;
}
void
pidgin_account_option_menu_set_selected
(
GtkWidget
*
optmenu
,
PurpleAccount
*
account
)
{
aop_option_menu_select_by_data
(
optmenu
,
account
);
}
GtkWidget
*
pidgin_account_option_menu_new
(
PurpleAccount
*
default_account
,
gboolean
show_all
,
GCallback
cb
,
PurpleFilterAccountFunc
filter_func
,
gpointer
user_data
)
{
GtkWidget
*
optmenu
;
/* Create the option menu */
optmenu
=
aop_option_menu_new
(
create_account_menu
(
default_account
,
filter_func
,
show_all
),
cb
,
user_data
);
g_signal_connect
(
G_OBJECT
(
optmenu
),
"destroy"
,
G_CALLBACK
(
account_menu_destroyed_cb
),
NULL
);
/* Register the purple sign on/off event callbacks. */
purple_signal_connect
(
purple_connections_get_handle
(),
"signed-on"
,
optmenu
,
PURPLE_CALLBACK
(
account_menu_sign_on_off_cb
),
optmenu
);
purple_signal_connect
(
purple_connections_get_handle
(),
"signed-off"
,
optmenu
,
PURPLE_CALLBACK
(
account_menu_sign_on_off_cb
),
optmenu
);
purple_signal_connect
(
purple_accounts_get_handle
(),
"account-added"
,
optmenu
,
PURPLE_CALLBACK
(
account_menu_added_removed_cb
),
optmenu
);
purple_signal_connect
(
purple_accounts_get_handle
(),
"account-removed"
,
optmenu
,
PURPLE_CALLBACK
(
account_menu_added_removed_cb
),
optmenu
);
/* Set some data. */
g_object_set_data
(
G_OBJECT
(
optmenu
),
"user_data"
,
user_data
);
g_object_set_data
(
G_OBJECT
(
optmenu
),
"show_all"
,
GINT_TO_POINTER
(
show_all
));
g_object_set_data
(
G_OBJECT
(
optmenu
),
"filter_func"
,
filter_func
);
return
optmenu
;
}
gboolean
pidgin_check_if_dir
(
const
char
*
path
,
GtkFileSelection
*
filesel
)
{
char
*
dirname
;
if
(
g_file_test
(
path
,
G_FILE_TEST_IS_DIR
))
{
/* append a / if needed */
if
(
path
[
strlen
(
path
)
-
1
]
!=
G_DIR_SEPARATOR
)
{
dirname
=
g_strconcat
(
path
,
G_DIR_SEPARATOR_S
,
NULL
);
}
else
{
dirname
=
g_strdup
(
path
);
}
gtk_file_selection_set_filename
(
filesel
,
dirname
);
g_free
(
dirname
);
return
TRUE
;
}
return
FALSE
;
}
void
pidgin_setup_gtkspell
(
GtkTextView
*
textview
)
{
#ifdef USE_GTKSPELL
GError
*
error
=
NULL
;
char
*
locale
=
NULL
;
g_return_if_fail
(
textview
!=
NULL
);
g_return_if_fail
(
GTK_IS_TEXT_VIEW
(
textview
));
if
(
gtkspell_new_attach
(
textview
,
locale
,
&
error
)
==
NULL
&&
error
)
{
purple_debug_warning
(
"gtkspell"
,
"Failed to setup GtkSpell: %s
\n
"
,
error
->
message
);
g_error_free
(
error
);
}
#endif
/* USE_GTKSPELL */
}
void
pidgin_save_accels_cb
(
GtkAccelGroup
*
accel_group
,
guint
arg1
,
GdkModifierType
arg2
,
GClosure
*
arg3
,
gpointer
data
)
{
purple_debug
(
PURPLE_DEBUG_MISC
,
"accels"
,
"accel changed, scheduling save.
\n
"
);
if
(
!
accels_save_timer
)
accels_save_timer
=
g_timeout_add
(
5000
,
pidgin_save_accels
,
NULL
);
}
gboolean
pidgin_save_accels
(
gpointer
data
)
{
char
*
filename
=
NULL
;
filename
=
g_build_filename
(
purple_user_dir
(),
G_DIR_SEPARATOR_S
,
"accels"
,
NULL
);
purple_debug
(
PURPLE_DEBUG_MISC
,
"accels"
,
"saving accels to %s
\n
"
,
filename
);
gtk_accel_map_save
(
filename
);
g_free
(
filename
);
accels_save_timer
=
0
;
return
FALSE
;
}
void
pidgin_load_accels
()
{
char
*
filename
=
NULL
;
filename
=
g_build_filename
(
purple_user_dir
(),
G_DIR_SEPARATOR_S
,
"accels"
,
NULL
);
gtk_accel_map_load
(
filename
);
g_free
(
filename
);
}
static
void
show_retrieveing_info
(
PurpleConnection
*
conn
,
const
char
*
name
)
{
PurpleNotifyUserInfo
*
info
=
purple_notify_user_info_new
();
purple_notify_user_info_add_pair
(
info
,
_
(
"Information"
),
_
(
"Retrieving..."
));
purple_notify_userinfo
(
conn
,
name
,
info
,
NULL
,
NULL
);
purple_notify_user_info_destroy
(
info
);
}
void
pidgin_retrieve_user_info
(
PurpleConnection
*
conn
,
const
char
*
name
)
{
show_retrieveing_info
(
conn
,
name
);
serv_get_info
(
conn
,
name
);
}
void
pidgin_retrieve_user_info_in_chat
(
PurpleConnection
*
conn
,
const
char
*
name
,
int
chat
)
{
char
*
who
=
NULL
;
PurplePluginProtocolInfo
*
prpl_info
=
NULL
;
if
(
chat
<
0
)
{
pidgin_retrieve_user_info
(
conn
,
name
);
return
;
}
prpl_info
=
PURPLE_PLUGIN_PROTOCOL_INFO
(
conn
->
prpl
);
if
(
prpl_info
==
NULL
||
prpl_info
->
get_cb_info
==
NULL
)
{
pidgin_retrieve_user_info
(
conn
,
name
);
return
;
}
if
(
prpl_info
->
get_cb_real_name
)
who
=
prpl_info
->
get_cb_real_name
(
conn
,
chat
,
name
);
show_retrieveing_info
(
conn
,
who
?
who
:
name
);
prpl_info
->
get_cb_info
(
conn
,
chat
,
name
);
g_free
(
who
);
}
gboolean
pidgin_parse_x_im_contact
(
const
char
*
msg
,
gboolean
all_accounts
,
PurpleAccount
**
ret_account
,
char
**
ret_protocol
,
char
**
ret_username
,
char
**
ret_alias
)
{
char
*
protocol
=
NULL
;
char
*
username
=
NULL
;
char
*
alias
=
NULL
;
char
*
str
;
char
*
c
,
*
s
;
gboolean
valid
;
g_return_val_if_fail
(
msg
!=
NULL
,
FALSE
);
g_return_val_if_fail
(
ret_protocol
!=
NULL
,
FALSE
);
g_return_val_if_fail
(
ret_username
!=
NULL
,
FALSE
);
s
=
str
=
g_strdup
(
msg
);
while
(
*
s
!=
'\r'
&&
*
s
!=
'\n'
&&
*
s
!=
'\0'
)
{
char
*
key
,
*
value
;
key
=
s
;
/* Grab the key */
while
(
*
s
!=
'\r'
&&
*
s
!=
'\n'
&&
*
s
!=
'\0'
&&
*
s
!=
' '
)
s
++
;
if
(
*
s
==
'\r'
)
s
++
;
if
(
*
s
==
'\n'
)
{
s
++
;
continue
;
}
if
(
*
s
!=
'\0'
)
*
s
++
=
'\0'
;
/* Clear past any whitespace */
while
(
*
s
!=
'\0'
&&
*
s
==
' '
)
s
++
;
/* Now let's grab until the end of the line. */
value
=
s
;
while
(
*
s
!=
'\r'
&&
*
s
!=
'\n'
&&
*
s
!=
'\0'
)
s
++
;
if
(
*
s
==
'\r'
)
*
s
++
=
'\0'
;
if
(
*
s
==
'\n'
)
*
s
++
=
'\0'
;
if
((
c
=
strchr
(
key
,
':'
))
!=
NULL
)
{
if
(
!
g_ascii_strcasecmp
(
key
,
"X-IM-Username:"
))
username
=
g_strdup
(
value
);
else
if
(
!
g_ascii_strcasecmp
(
key
,
"X-IM-Protocol:"
))
protocol
=
g_strdup
(
value
);
else
if
(
!
g_ascii_strcasecmp
(
key
,
"X-IM-Alias:"
))
alias
=
g_strdup
(
value
);
}
}
if
(
username
!=
NULL
&&
protocol
!=
NULL
)
{
valid
=
TRUE
;
*
ret_username
=
username
;
*
ret_protocol
=
protocol
;
if
(
ret_alias
!=
NULL
)
*
ret_alias
=
alias
;
/* Check for a compatible account. */
if
(
ret_account
!=
NULL
)
{
GList
*
list
;
PurpleAccount
*
account
=
NULL
;
GList
*
l
;
const
char
*
protoname
;
if
(
all_accounts
)
list
=
purple_accounts_get_all
();
else
list
=
purple_connections_get_all
();
for
(
l
=
list
;
l
!=
NULL
;
l
=
l
->
next
)
{
PurpleConnection
*
gc
;
PurplePluginProtocolInfo
*
prpl_info
=
NULL
;
PurplePlugin
*
plugin
;
if
(
all_accounts
)
{
account
=
(
PurpleAccount
*
)
l
->
data
;
plugin
=
purple_plugins_find_with_id
(
purple_account_get_protocol_id
(
account
));
if
(
plugin
==
NULL
)
{
account
=
NULL
;
continue
;
}
prpl_info
=
PURPLE_PLUGIN_PROTOCOL_INFO
(
plugin
);
}
else
{
gc
=
(
PurpleConnection
*
)
l
->
data
;
account
=
purple_connection_get_account
(
gc
);
prpl_info
=
PURPLE_PLUGIN_PROTOCOL_INFO
(
gc
->
prpl
);
}
protoname
=
prpl_info
->
list_icon
(
account
,
NULL
);
if
(
!
strcmp
(
protoname
,
protocol
))
break
;
account
=
NULL
;
}
/* Special case for AIM and ICQ */
if
(
account
==
NULL
&&
(
!
strcmp
(
protocol
,
"aim"
)
||
!
strcmp
(
protocol
,
"icq"
)))
{
for
(
l
=
list
;
l
!=
NULL
;
l
=
l
->
next
)
{
PurpleConnection
*
gc
;
PurplePluginProtocolInfo
*
prpl_info
=
NULL
;
PurplePlugin
*
plugin
;
if
(
all_accounts
)
{
account
=
(
PurpleAccount
*
)
l
->
data
;
plugin
=
purple_plugins_find_with_id
(
purple_account_get_protocol_id
(
account
));
if
(
plugin
==
NULL
)
{
account
=
NULL
;
continue
;
}
prpl_info
=
PURPLE_PLUGIN_PROTOCOL_INFO
(
plugin
);
}
else
{
gc
=
(
PurpleConnection
*
)
l
->
data
;
account
=
purple_connection_get_account
(
gc
);
prpl_info
=
PURPLE_PLUGIN_PROTOCOL_INFO
(
gc
->
prpl
);
}
protoname
=
prpl_info
->
list_icon
(
account
,
NULL
);
if
(
!
strcmp
(
protoname
,
"aim"
)
||
!
strcmp
(
protoname
,
"icq"
))
break
;
account
=
NULL
;
}
}
*
ret_account
=
account
;
}
}
else
{
valid
=
FALSE
;
g_free
(
username
);
g_free
(
protocol
);
g_free
(
alias
);
}
g_free
(
str
);
return
valid
;
}
void
pidgin_set_accessible_label
(
GtkWidget
*
w
,
GtkWidget
*
l
)
{
AtkObject
*
acc
;
const
gchar
*
label_text
;
const
gchar
*
existing_name
;
acc
=
gtk_widget_get_accessible
(
w
);
/* If this object has no name, set it's name with the label text */
existing_name
=
atk_object_get_name
(
acc
);
if
(
!
existing_name
)
{
label_text
=
gtk_label_get_text
(
GTK_LABEL
(
l
));
if
(
label_text
)
atk_object_set_name
(
acc
,
label_text
);
}
pidgin_set_accessible_relations
(
w
,
l
);
}
void
pidgin_set_accessible_relations
(
GtkWidget
*
w
,
GtkWidget
*
l
)
{
AtkObject
*
acc
,
*
label
;
AtkObject
*
rel_obj
[
1
];
AtkRelationSet
*
set
;
AtkRelation
*
relation
;
acc
=
gtk_widget_get_accessible
(
w
);
label
=
gtk_widget_get_accessible
(
l
);
/* Make sure mnemonics work */
gtk_label_set_mnemonic_widget
(
GTK_LABEL
(
l
),
w
);
/* Create the labeled-by relation */
set
=
atk_object_ref_relation_set
(
acc
);
rel_obj
[
0
]
=
label
;
relation
=
atk_relation_new
(
rel_obj
,
1
,
ATK_RELATION_LABELLED_BY
);
atk_relation_set_add
(
set
,
relation
);
g_object_unref
(
relation
);
/* Create the label-for relation */
set
=
atk_object_ref_relation_set
(
label
);
rel_obj
[
0
]
=
acc
;
relation
=
atk_relation_new
(
rel_obj
,
1
,
ATK_RELATION_LABEL_FOR
);
atk_relation_set_add
(
set
,
relation
);
g_object_unref
(
relation
);
}
void
pidgin_menu_position_func_helper
(
GtkMenu
*
menu
,
gint
*
x
,
gint
*
y
,
gboolean
*
push_in
,
gpointer
data
)
{
#if GTK_CHECK_VERSION(2,2,0)
GtkWidget
*
widget
;
GtkRequisition
requisition
;
GdkScreen
*
screen
;
GdkRectangle
monitor
;
gint
monitor_num
;
gint
space_left
,
space_right
,
space_above
,
space_below
;
gint
needed_width
;
gint
needed_height
;
gint
xthickness
;
gint
ythickness
;
gboolean
rtl
;
g_return_if_fail
(
GTK_IS_MENU
(
menu
));
widget
=
GTK_WIDGET
(
menu
);
screen
=
gtk_widget_get_screen
(
widget
);
xthickness
=
widget
->
style
->
xthickness
;
ythickness
=
widget
->
style
->
ythickness
;
rtl
=
(
gtk_widget_get_direction
(
widget
)
==
GTK_TEXT_DIR_RTL
);
/*
* We need the requisition to figure out the right place to
* popup the menu. In fact, we always need to ask here, since
* if a size_request was queued while we weren't popped up,
* the requisition won't have been recomputed yet.
*/
gtk_widget_size_request
(
widget
,
&
requisition
);
monitor_num
=
gdk_screen_get_monitor_at_point
(
screen
,
*
x
,
*
y
);
push_in
=
FALSE
;
/*
* The placement of popup menus horizontally works like this (with
* RTL in parentheses)
*
* - If there is enough room to the right (left) of the mouse cursor,
* position the menu there.
*
* - Otherwise, if if there is enough room to the left (right) of the
* mouse cursor, position the menu there.
*
* - Otherwise if the menu is smaller than the monitor, position it
* on the side of the mouse cursor that has the most space available
*
* - Otherwise (if there is simply not enough room for the menu on the
* monitor), position it as far left (right) as possible.
*
* Positioning in the vertical direction is similar: first try below
* mouse cursor, then above.
*/
gdk_screen_get_monitor_geometry
(
screen
,
monitor_num
,
&
monitor
);
space_left
=
*
x
-
monitor
.
x
;
space_right
=
monitor
.
x
+
monitor
.
width
-
*
x
-
1
;
space_above
=
*
y
-
monitor
.
y
;
space_below
=
monitor
.
y
+
monitor
.
height
-
*
y
-
1
;
/* position horizontally */
/* the amount of space we need to position the menu. Note the
* menu is offset "xthickness" pixels
*/
needed_width
=
requisition
.
width
-
xthickness
;
if
(
needed_width
<=
space_left
||
needed_width
<=
space_right
)
{
if
((
rtl
&&
needed_width
<=
space_left
)
||
(
!
rtl
&&
needed_width
>
space_right
))
{
/* position left */
*
x
=
*
x
+
xthickness
-
requisition
.
width
+
1
;
}
else
{
/* position right */
*
x
=
*
x
-
xthickness
;
}
/* x is clamped on-screen further down */
}
else
if
(
requisition
.
width
<=
monitor
.
width
)
{
/* the menu is too big to fit on either side of the mouse
* cursor, but smaller than the monitor. Position it on
* the side that has the most space
*/
if
(
space_left
>
space_right
)
{
/* left justify */
*
x
=
monitor
.
x
;
}
else
{
/* right justify */
*
x
=
monitor
.
x
+
monitor
.
width
-
requisition
.
width
;
}
}
else
/* menu is simply too big for the monitor */
{
if
(
rtl
)
{
/* right justify */
*
x
=
monitor
.
x
+
monitor
.
width
-
requisition
.
width
;
}
else
{
/* left justify */
*
x
=
monitor
.
x
;
}
}
/* Position vertically. The algorithm is the same as above, but
* simpler because we don't have to take RTL into account.
*/
needed_height
=
requisition
.
height
-
ythickness
;
if
(
needed_height
<=
space_above
||
needed_height
<=
space_below
)
{
if
(
needed_height
<=
space_below
)
*
y
=
*
y
-
ythickness
;
else
*
y
=
*
y
+
ythickness
-
requisition
.
height
+
1
;
*
y
=
CLAMP
(
*
y
,
monitor
.
y
,
monitor
.
y
+
monitor
.
height
-
requisition
.
height
);
}
else
if
(
needed_height
>
space_below
&&
needed_height
>
space_above
)
{
if
(
space_below
>=
space_above
)
*
y
=
monitor
.
y
+
monitor
.
height
-
requisition
.
height
;
else
*
y
=
monitor
.
y
;
}
else
{
*
y
=
monitor
.
y
;
}
#endif
}
void
pidgin_treeview_popup_menu_position_func
(
GtkMenu
*
menu
,
gint
*
x
,
gint
*
y
,
gboolean
*
push_in
,
gpointer
data
)
{
GtkWidget
*
widget
=
GTK_WIDGET
(
data
);
GtkTreeView
*
tv
=
GTK_TREE_VIEW
(
data
);
GtkTreePath
*
path
;
GtkTreeViewColumn
*
col
;
GdkRectangle
rect
;
gint
ythickness
=
GTK_WIDGET
(
menu
)
->
style
->
ythickness
;
gdk_window_get_origin
(
widget
->
window
,
x
,
y
);
gtk_tree_view_get_cursor
(
tv
,
&
path
,
&
col
);
gtk_tree_view_get_cell_area
(
tv
,
path
,
col
,
&
rect
);
*
x
+=
rect
.
x
+
rect
.
width
;
*
y
+=
rect
.
y
+
rect
.
height
+
ythickness
;
pidgin_menu_position_func_helper
(
menu
,
x
,
y
,
push_in
,
data
);
}
enum
{
DND_FILE_TRANSFER
,
DND_IM_IMAGE
,
DND_BUDDY_ICON
};
typedef
struct
{
char
*
filename
;
PurpleAccount
*
account
;
char
*
who
;
}
_DndData
;
static
void
dnd_image_ok_callback
(
_DndData
*
data
,
int
choice
)
{
char
*
filedata
;
size_t
size
;
struct
stat
st
;
GError
*
err
=
NULL
;
PurpleConversation
*
conv
;
PidginConversation
*
gtkconv
;
GtkTextIter
iter
;
int
id
;
switch
(
choice
)
{
case
DND_BUDDY_ICON
:
if
(
g_stat
(
data
->
filename
,
&
st
))
{
char
*
str
;
str
=
g_strdup_printf
(
_
(
"The following error has occurred loading %s: %s"
),
data
->
filename
,
strerror
(
errno
));
purple_notify_error
(
NULL
,
NULL
,
_
(
"Failed to load image"
),
str
);
g_free
(
str
);
return
;
}
pidgin_set_custom_buddy_icon
(
data
->
account
,
data
->
who
,
data
->
filename
);
break
;
case
DND_FILE_TRANSFER
:
serv_send_file
(
purple_account_get_connection
(
data
->
account
),
data
->
who
,
data
->
filename
);
break
;
case
DND_IM_IMAGE
:
conv
=
purple_conversation_new
(
PURPLE_CONV_TYPE_IM
,
data
->
account
,
data
->
who
);
gtkconv
=
PIDGIN_CONVERSATION
(
conv
);
if
(
!
g_file_get_contents
(
data
->
filename
,
&
filedata
,
&
size
,
&
err
))
{
char
*
str
;
str
=
g_strdup_printf
(
_
(
"The following error has occurred loading %s: %s"
),
data
->
filename
,
err
->
message
);
purple_notify_error
(
NULL
,
NULL
,
_
(
"Failed to load image"
),
str
);
g_error_free
(
err
);
g_free
(
str
);
return
;
}
id
=
purple_imgstore_add_with_id
(
filedata
,
size
,
data
->
filename
);
gtk_text_buffer_get_iter_at_mark
(
GTK_IMHTML
(
gtkconv
->
entry
)
->
text_buffer
,
&
iter
,
gtk_text_buffer_get_insert
(
GTK_IMHTML
(
gtkconv
->
entry
)
->
text_buffer
));
gtk_imhtml_insert_image_at_iter
(
GTK_IMHTML
(
gtkconv
->
entry
),
id
,
&
iter
);
purple_imgstore_unref_by_id
(
id
);
break
;
}
free
(
data
->
filename
);
free
(
data
->
who
);
free
(
data
);
}
static
void
dnd_image_cancel_callback
(
_DndData
*
data
,
int
choice
)
{
free
(
data
->
filename
);
free
(
data
->
who
);
free
(
data
);
}
static
void
dnd_set_icon_ok_cb
(
_DndData
*
data
)
{
dnd_image_ok_callback
(
data
,
DND_BUDDY_ICON
);
}
static
void
dnd_set_icon_cancel_cb
(
_DndData
*
data
)
{
free
(
data
->
filename
);
free
(
data
->
who
);
free
(
data
);
}
void
pidgin_dnd_file_manage
(
GtkSelectionData
*
sd
,
PurpleAccount
*
account
,
const
char
*
who
)
{
GList
*
tmp
;
GdkPixbuf
*
pb
;
GList
*
files
=
purple_uri_list_extract_filenames
((
const
gchar
*
)
sd
->
data
);
PurpleConnection
*
gc
=
purple_account_get_connection
(
account
);
PurplePluginProtocolInfo
*
prpl_info
=
NULL
;
gboolean
file_send_ok
=
FALSE
;
#ifndef _WIN32
PurpleDesktopItem
*
item
;
#endif
g_return_if_fail
(
account
!=
NULL
);
g_return_if_fail
(
who
!=
NULL
);
for
(
tmp
=
files
;
tmp
!=
NULL
;
tmp
=
g_list_next
(
tmp
))
{
gchar
*
filename
=
tmp
->
data
;
gchar
*
basename
=
g_path_get_basename
(
filename
);
/* Set the default action: don't send anything */
file_send_ok
=
FALSE
;
/* XXX - Make ft API support creating a transfer with more than one file */
if
(
!
g_file_test
(
filename
,
G_FILE_TEST_EXISTS
))
{
continue
;
}
/* XXX - make ft api suupport sending a directory */
/* Are we dealing with a directory? */
if
(
g_file_test
(
filename
,
G_FILE_TEST_IS_DIR
))
{
char
*
str
,
*
str2
;
str
=
g_strdup_printf
(
_
(
"Cannot send folder %s."
),
basename
);
str2
=
g_strdup_printf
(
_
(
"%s cannot transfer a folder. You will need to send the files within individually."
),
PIDGIN_NAME
);
purple_notify_error
(
NULL
,
NULL
,
str
,
str2
);
g_free
(
str
);
g_free
(
str2
);
continue
;
}
/* Are we dealing with an image? */
pb
=
gdk_pixbuf_new_from_file
(
filename
,
NULL
);
if
(
pb
)
{
_DndData
*
data
=
g_malloc
(
sizeof
(
_DndData
));
gboolean
ft
=
FALSE
,
im
=
FALSE
;
data
->
who
=
g_strdup
(
who
);
data
->
filename
=
g_strdup
(
filename
);
data
->
account
=
account
;
if
(
gc
)
prpl_info
=
PURPLE_PLUGIN_PROTOCOL_INFO
(
gc
->
prpl
);
if
(
prpl_info
&&
prpl_info
->
options
&
OPT_PROTO_IM_IMAGE
)
im
=
TRUE
;
if
(
prpl_info
&&
prpl_info
->
can_receive_file
)
ft
=
prpl_info
->
can_receive_file
(
gc
,
who
);
if
(
im
&&
ft
)
purple_request_choice
(
NULL
,
NULL
,
_
(
"You have dragged an image"
),
_
(
"You can send this image as a file transfer, "
"embed it into this message, or use it as the buddy icon for this user."
),
DND_FILE_TRANSFER
,
"OK"
,
(
GCallback
)
dnd_image_ok_callback
,
"Cancel"
,
(
GCallback
)
dnd_image_cancel_callback
,
account
,
who
,
NULL
,
data
,
_
(
"Set as buddy icon"
),
DND_BUDDY_ICON
,
_
(
"Send image file"
),
DND_FILE_TRANSFER
,
_
(
"Insert in message"
),
DND_IM_IMAGE
,
NULL
);
else
if
(
!
(
im
||
ft
))
purple_request_yes_no
(
NULL
,
NULL
,
_
(
"You have dragged an image"
),
_
(
"Would you like to set it as the buddy icon for this user?"
),
0
,
account
,
who
,
NULL
,
data
,
(
GCallback
)
dnd_set_icon_ok_cb
,
(
GCallback
)
dnd_set_icon_cancel_cb
);
else
purple_request_choice
(
NULL
,
NULL
,
_
(
"You have dragged an image"
),
(
ft
?
_
(
"You can send this image as a file transfer, or use it as the buddy icon for this user."
)
:
_
(
"You can insert this image into this message, or use it as the buddy icon for this user"
)),
(
ft
?
DND_FILE_TRANSFER
:
DND_IM_IMAGE
),
"OK"
,
(
GCallback
)
dnd_image_ok_callback
,
"Cancel"
,
(
GCallback
)
dnd_image_cancel_callback
,
account
,
who
,
NULL
,
data
,
_
(
"Set as buddy icon"
),
DND_BUDDY_ICON
,
(
ft
?
_
(
"Send image file"
)
:
_
(
"Insert in message"
)),
(
ft
?
DND_FILE_TRANSFER
:
DND_IM_IMAGE
),
NULL
);
return
;
}
#ifndef _WIN32
/* Are we trying to send a .desktop file? */
else
if
(
purple_str_has_suffix
(
basename
,
".desktop"
)
&&
(
item
=
purple_desktop_item_new_from_file
(
filename
)))
{
PurpleDesktopItemType
dtype
;
char
key
[
64
];
const
char
*
itemname
=
NULL
;
#if GTK_CHECK_VERSION(2,6,0)
const
char
*
const
*
langs
;
int
i
;
langs
=
g_get_language_names
();
for
(
i
=
0
;
langs
[
i
];
i
++
)
{
g_snprintf
(
key
,
sizeof
(
key
),
"Name[%s]"
,
langs
[
i
]);
itemname
=
purple_desktop_item_get_string
(
item
,
key
);
break
;
}
#else
const
char
*
lang
=
g_getenv
(
"LANG"
);
char
*
dot
;
dot
=
strchr
(
lang
,
'.'
);
if
(
dot
)
*
dot
=
'\0'
;
g_snprintf
(
key
,
sizeof
(
key
),
"Name[%s]"
,
lang
);
itemname
=
purple_desktop_item_get_string
(
item
,
key
);
#endif
if
(
!
itemname
)
itemname
=
purple_desktop_item_get_string
(
item
,
"Name"
);
dtype
=
purple_desktop_item_get_entry_type
(
item
);
switch
(
dtype
)
{
PurpleConversation
*
conv
;
PidginConversation
*
gtkconv
;
case
PURPLE_DESKTOP_ITEM_TYPE_LINK
:
conv
=
purple_conversation_new
(
PURPLE_CONV_TYPE_IM
,
account
,
who
);
gtkconv
=
PIDGIN_CONVERSATION
(
conv
);
gtk_imhtml_insert_link
(
GTK_IMHTML
(
gtkconv
->
entry
),
gtk_text_buffer_get_insert
(
GTK_IMHTML
(
gtkconv
->
entry
)
->
text_buffer
),
purple_desktop_item_get_string
(
item
,
"URL"
),
itemname
);
break
;
default
:
/* I don't know if we really want to do anything here. Most of the desktop item types are crap like
* "MIME Type" (I have no clue how that would be a desktop item) and "Comment"... nothing we can really
* send. The only logical one is "Application," but do we really want to send a binary and nothing else?
* Probably not. I'll just give an error and return. */
/* The original patch sent the icon used by the launcher. That's probably wrong */
purple_notify_error
(
NULL
,
NULL
,
_
(
"Cannot send launcher"
),
_
(
"You dragged a desktop launcher. "
"Most likely you wanted to send whatever this launcher points to instead of this launcher"
" itself."
));
break
;
}
purple_desktop_item_unref
(
item
);
return
;
}
#endif
/* _WIN32 */
/* Everything is fine, let's send */
serv_send_file
(
gc
,
who
,
filename
);
g_free
(
filename
);
}
g_list_free
(
files
);
}
void
pidgin_buddy_icon_get_scale_size
(
GdkPixbuf
*
buf
,
PurpleBuddyIconSpec
*
spec
,
PurpleIconScaleRules
rules
,
int
*
width
,
int
*
height
)
{
*
width
=
gdk_pixbuf_get_width
(
buf
);
*
height
=
gdk_pixbuf_get_height
(
buf
);
if
((
spec
==
NULL
)
||
!
(
spec
->
scale_rules
&
rules
))
return
;
purple_buddy_icon_get_scale_size
(
spec
,
width
,
height
);
/* and now for some arbitrary sanity checks */
if
(
*
width
>
100
)
*
width
=
100
;
if
(
*
height
>
100
)
*
height
=
100
;
}
GdkPixbuf
*
pidgin_create_status_icon
(
PurpleStatusPrimitive
prim
,
GtkWidget
*
w
,
const
char
*
size
)
{
GtkIconSize
icon_size
=
gtk_icon_size_from_name
(
size
);
GdkPixbuf
*
pixbuf
=
NULL
;
if
(
prim
==
PURPLE_STATUS_UNAVAILABLE
)
pixbuf
=
gtk_widget_render_icon
(
w
,
PIDGIN_STOCK_STATUS_BUSY
,
icon_size
,
"GtkWidget"
);
else
if
(
prim
==
PURPLE_STATUS_AWAY
)
pixbuf
=
gtk_widget_render_icon
(
w
,
PIDGIN_STOCK_STATUS_AWAY
,
icon_size
,
"GtkWidget"
);
else
if
(
prim
==
PURPLE_STATUS_EXTENDED_AWAY
)
pixbuf
=
gtk_widget_render_icon
(
w
,
PIDGIN_STOCK_STATUS_XA
,
icon_size
,
"GtkWidget"
);
else
if
(
prim
==
PURPLE_STATUS_INVISIBLE
)
pixbuf
=
gtk_widget_render_icon
(
w
,
PIDGIN_STOCK_STATUS_INVISIBLE
,
icon_size
,
"GtkWidget"
);
else
if
(
prim
==
PURPLE_STATUS_OFFLINE
)
pixbuf
=
gtk_widget_render_icon
(
w
,
PIDGIN_STOCK_STATUS_OFFLINE
,
icon_size
,
"GtkWidget"
);
else
pixbuf
=
gtk_widget_render_icon
(
w
,
PIDGIN_STOCK_STATUS_AVAILABLE
,
icon_size
,
"GtkWidget"
);
return
pixbuf
;
}
GdkPixbuf
*
pidgin_create_prpl_icon
(
PurpleAccount
*
account
,
PidginPrplIconSize
size
)
{
PurplePlugin
*
prpl
;
g_return_val_if_fail
(
account
!=
NULL
,
NULL
);
prpl
=
purple_find_prpl
(
purple_account_get_protocol_id
(
account
));
if
(
prpl
==
NULL
)
return
NULL
;
return
pidgin_create_prpl_icon_from_prpl
(
prpl
,
size
,
account
);
}
static
void
menu_action_cb
(
GtkMenuItem
*
item
,
gpointer
object
)
{
gpointer
data
;
void
(
*
callback
)(
gpointer
,
gpointer
);
callback
=
g_object_get_data
(
G_OBJECT
(
item
),
"purplecallback"
);
data
=
g_object_get_data
(
G_OBJECT
(
item
),
"purplecallbackdata"
);
if
(
callback
)
callback
(
object
,
data
);
}
GtkWidget
*
pidgin_append_menu_action
(
GtkWidget
*
menu
,
PurpleMenuAction
*
act
,
gpointer
object
)
{
GtkWidget
*
menuitem
;
if
(
act
==
NULL
)
{
return
pidgin_separator
(
menu
);
}
if
(
act
->
children
==
NULL
)
{
menuitem
=
gtk_menu_item_new_with_mnemonic
(
act
->
label
);
if
(
act
->
callback
!=
NULL
)
{
g_object_set_data
(
G_OBJECT
(
menuitem
),
"purplecallback"
,
act
->
callback
);
g_object_set_data
(
G_OBJECT
(
menuitem
),
"purplecallbackdata"
,
act
->
data
);
g_signal_connect
(
G_OBJECT
(
menuitem
),
"activate"
,
G_CALLBACK
(
menu_action_cb
),
object
);
}
else
{
gtk_widget_set_sensitive
(
menuitem
,
FALSE
);
}
gtk_menu_shell_append
(
GTK_MENU_SHELL
(
menu
),
menuitem
);
}
else
{
GList
*
l
=
NULL
;
GtkWidget
*
submenu
=
NULL
;
GtkAccelGroup
*
group
;
menuitem
=
gtk_menu_item_new_with_mnemonic
(
act
->
label
);
gtk_menu_shell_append
(
GTK_MENU_SHELL
(
menu
),
menuitem
);
submenu
=
gtk_menu_new
();
gtk_menu_item_set_submenu
(
GTK_MENU_ITEM
(
menuitem
),
submenu
);
group
=
gtk_menu_get_accel_group
(
GTK_MENU
(
menu
));
if
(
group
)
{
char
*
path
=
g_strdup_printf
(
"%s/%s"
,
GTK_MENU_ITEM
(
menuitem
)
->
accel_path
,
act
->
label
);
gtk_menu_set_accel_path
(
GTK_MENU
(
submenu
),
path
);
g_free
(
path
);
gtk_menu_set_accel_group
(
GTK_MENU
(
submenu
),
group
);
}
for
(
l
=
act
->
children
;
l
;
l
=
l
->
next
)
{
PurpleMenuAction
*
act
=
(
PurpleMenuAction
*
)
l
->
data
;
pidgin_append_menu_action
(
submenu
,
act
,
object
);
}
g_list_free
(
act
->
children
);
act
->
children
=
NULL
;
}
purple_menu_action_free
(
act
);
return
menuitem
;
}
#if GTK_CHECK_VERSION(2,3,0)
# define NEW_STYLE_COMPLETION
#endif
typedef
struct
{
GtkWidget
*
entry
;
GtkWidget
*
accountopt
;
PidginFilterBuddyCompletionEntryFunc
filter_func
;
gpointer
filter_func_user_data
;
#ifdef NEW_STYLE_COMPLETION
GtkListStore
*
store
;
#else
GCompletion
*
completion
;
gboolean
completion_started
;
GList
*
log_items
;
#endif
/* NEW_STYLE_COMPLETION */
}
PidginCompletionData
;
#ifndef NEW_STYLE_COMPLETION
static
gboolean
completion_entry_event
(
GtkEditable
*
entry
,
GdkEventKey
*
event
,
PidginCompletionData
*
data
)
{
int
pos
,
end_pos
;
if
(
event
->
type
==
GDK_KEY_PRESS
&&
event
->
keyval
==
GDK_Tab
)
{
gtk_editable_get_selection_bounds
(
entry
,
&
pos
,
&
end_pos
);
if
(
data
->
completion_started
&&
pos
!=
end_pos
&&
pos
>
1
&&
end_pos
==
strlen
(
gtk_entry_get_text
(
GTK_ENTRY
(
entry
))))
{
gtk_editable_select_region
(
entry
,
0
,
0
);
gtk_editable_set_position
(
entry
,
-1
);
return
TRUE
;
}
}
else
if
(
event
->
type
==
GDK_KEY_PRESS
&&
event
->
length
>
0
)
{
char
*
prefix
,
*
nprefix
;
gtk_editable_get_selection_bounds
(
entry
,
&
pos
,
&
end_pos
);
if
(
data
->
completion_started
&&
pos
!=
end_pos
&&
pos
>
1
&&
end_pos
==
strlen
(
gtk_entry_get_text
(
GTK_ENTRY
(
entry
))))
{
char
*
temp
;
temp
=
gtk_editable_get_chars
(
entry
,
0
,
pos
);
prefix
=
g_strconcat
(
temp
,
event
->
string
,
NULL
);
g_free
(
temp
);
}
else
if
(
pos
==
end_pos
&&
pos
>
1
&&
end_pos
==
strlen
(
gtk_entry_get_text
(
GTK_ENTRY
(
entry
))))
{
prefix
=
g_strconcat
(
gtk_entry_get_text
(
GTK_ENTRY
(
entry
)),
event
->
string
,
NULL
);
}
else
return
FALSE
;
pos
=
strlen
(
prefix
);
nprefix
=
NULL
;
g_completion_complete
(
data
->
completion
,
prefix
,
&
nprefix
);
if
(
nprefix
!=
NULL
)
{
gtk_entry_set_text
(
GTK_ENTRY
(
entry
),
nprefix
);
gtk_editable_set_position
(
entry
,
pos
);
gtk_editable_select_region
(
entry
,
pos
,
-1
);
data
->
completion_started
=
TRUE
;
g_free
(
nprefix
);
g_free
(
prefix
);
return
TRUE
;
}
g_free
(
prefix
);
}
return
FALSE
;
}
static
void
destroy_completion_data
(
GtkWidget
*
w
,
PidginCompletionData
*
data
)
{
g_list_foreach
(
data
->
completion
->
items
,
(
GFunc
)
g_free
,
NULL
);
g_completion_free
(
data
->
completion
);
g_free
(
data
);
}
#endif
/* !NEW_STYLE_COMPLETION */
#ifdef NEW_STYLE_COMPLETION
static
gboolean
screenname_completion_match_func
(
GtkEntryCompletion
*
completion
,
const
gchar
*
key
,
GtkTreeIter
*
iter
,
gpointer
user_data
)
{
GtkTreeModel
*
model
;
GValue
val1
;
GValue
val2
;
const
char
*
tmp
;
model
=
gtk_entry_completion_get_model
(
completion
);
val1
.
g_type
=
0
;
gtk_tree_model_get_value
(
model
,
iter
,
2
,
&
val1
);
tmp
=
g_value_get_string
(
&
val1
);
if
(
tmp
!=
NULL
&&
purple_str_has_prefix
(
tmp
,
key
))
{
g_value_unset
(
&
val1
);
return
TRUE
;
}
g_value_unset
(
&
val1
);
val2
.
g_type
=
0
;
gtk_tree_model_get_value
(
model
,
iter
,
3
,
&
val2
);
tmp
=
g_value_get_string
(
&
val2
);
if
(
tmp
!=
NULL
&&
purple_str_has_prefix
(
tmp
,
key
))
{
g_value_unset
(
&
val2
);
return
TRUE
;
}
g_value_unset
(
&
val2
);
return
FALSE
;
}
static
gboolean
screenname_completion_match_selected_cb
(
GtkEntryCompletion
*
completion
,
GtkTreeModel
*
model
,
GtkTreeIter
*
iter
,
PidginCompletionData
*
data
)
{
GValue
val
;
GtkWidget
*
optmenu
=
data
->
accountopt
;
PurpleAccount
*
account
;
val
.
g_type
=
0
;
gtk_tree_model_get_value
(
model
,
iter
,
1
,
&
val
);
gtk_entry_set_text
(
GTK_ENTRY
(
data
->
entry
),
g_value_get_string
(
&
val
));
g_value_unset
(
&
val
);
gtk_tree_model_get_value
(
model
,
iter
,
4
,
&
val
);
account
=
g_value_get_pointer
(
&
val
);
g_value_unset
(
&
val
);
if
(
account
==
NULL
)
return
TRUE
;
if
(
optmenu
!=
NULL
)
aop_option_menu_select_by_data
(
optmenu
,
account
);
return
TRUE
;
}
static
void
add_screenname_autocomplete_entry
(
GtkListStore
*
store
,
const
char
*
buddy_alias
,
const
char
*
contact_alias
,
const
PurpleAccount
*
account
,
const
char
*
screenname
)
{
GtkTreeIter
iter
;
gboolean
completion_added
=
FALSE
;
gchar
*
normalized_screenname
;
gchar
*
tmp
;
tmp
=
g_utf8_normalize
(
screenname
,
-1
,
G_NORMALIZE_DEFAULT
);
normalized_screenname
=
g_utf8_casefold
(
tmp
,
-1
);
g_free
(
tmp
);
/* There's no sense listing things like: 'xxx "xxx"'
when the screenname and buddy alias match. */
if
(
buddy_alias
&&
strcmp
(
buddy_alias
,
screenname
))
{
char
*
completion_entry
=
g_strdup_printf
(
"%s
\"
%s
\"
"
,
screenname
,
buddy_alias
);
char
*
tmp2
=
g_utf8_normalize
(
buddy_alias
,
-1
,
G_NORMALIZE_DEFAULT
);
tmp
=
g_utf8_casefold
(
tmp2
,
-1
);
g_free
(
tmp2
);
gtk_list_store_append
(
store
,
&
iter
);
gtk_list_store_set
(
store
,
&
iter
,
0
,
completion_entry
,
1
,
screenname
,
2
,
normalized_screenname
,
3
,
tmp
,
4
,
account
,
-1
);
g_free
(
completion_entry
);
g_free
(
tmp
);
completion_added
=
TRUE
;
}
/* There's no sense listing things like: 'xxx "xxx"'
when the screenname and contact alias match. */
if
(
contact_alias
&&
strcmp
(
contact_alias
,
screenname
))
{
/* We don't want duplicates when the contact and buddy alias match. */
if
(
!
buddy_alias
||
strcmp
(
contact_alias
,
buddy_alias
))
{
char
*
completion_entry
=
g_strdup_printf
(
"%s
\"
%s
\"
"
,
screenname
,
contact_alias
);
char
*
tmp2
=
g_utf8_normalize
(
contact_alias
,
-1
,
G_NORMALIZE_DEFAULT
);
tmp
=
g_utf8_casefold
(
tmp2
,
-1
);
g_free
(
tmp2
);
gtk_list_store_append
(
store
,
&
iter
);
gtk_list_store_set
(
store
,
&
iter
,
0
,
completion_entry
,
1
,
screenname
,
2
,
normalized_screenname
,
3
,
tmp
,
4
,
account
,
-1
);
g_free
(
completion_entry
);
g_free
(
tmp
);
completion_added
=
TRUE
;
}
}
if
(
completion_added
==
FALSE
)
{
/* Add the buddy's screenname. */
gtk_list_store_append
(
store
,
&
iter
);
gtk_list_store_set
(
store
,
&
iter
,
0
,
screenname
,
1
,
screenname
,
2
,
normalized_screenname
,
3
,
NULL
,
4
,
account
,
-1
);
}
g_free
(
normalized_screenname
);
}
#endif
/* NEW_STYLE_COMPLETION */
static
void
get_log_set_name
(
PurpleLogSet
*
set
,
gpointer
value
,
PidginCompletionData
*
data
)
{
PidginFilterBuddyCompletionEntryFunc
filter_func
=
data
->
filter_func
;
gpointer
user_data
=
data
->
filter_func_user_data
;
/* 1. Don't show buddies because we will have gotten them already.
* 2. The boxes that use this autocomplete code handle only IMs. */
if
(
!
set
->
buddy
&&
set
->
type
==
PURPLE_LOG_IM
)
{
PidginBuddyCompletionEntry
entry
;
entry
.
is_buddy
=
FALSE
;
entry
.
entry
.
logged_buddy
=
set
;
if
(
filter_func
(
&
entry
,
user_data
))
{
#ifdef NEW_STYLE_COMPLETION
add_screenname_autocomplete_entry
(
data
->
store
,
NULL
,
NULL
,
set
->
account
,
set
->
name
);
#else
/* Steal the name for the GCompletion. */
data
->
log_items
=
g_list_append
(
data
->
log_items
,
set
->
name
);
set
->
name
=
set
->
normalized_name
=
NULL
;
#endif
/* NEW_STYLE_COMPLETION */
}
}
}
static
void
add_completion_list
(
PidginCompletionData
*
data
)
{
PurpleBlistNode
*
gnode
,
*
cnode
,
*
bnode
;
PidginFilterBuddyCompletionEntryFunc
filter_func
=
data
->
filter_func
;
gpointer
user_data
=
data
->
filter_func_user_data
;
GHashTable
*
sets
;
#ifdef NEW_STYLE_COMPLETION
gtk_list_store_clear
(
data
->
store
);
#else
GList
*
item
=
g_list_append
(
NULL
,
NULL
);
g_list_foreach
(
data
->
completion
->
items
,
(
GFunc
)
g_free
,
NULL
);
g_completion_clear_items
(
data
->
completion
);
#endif
/* NEW_STYLE_COMPLETION */
for
(
gnode
=
purple_get_blist
()
->
root
;
gnode
!=
NULL
;
gnode
=
gnode
->
next
)
{
if
(
!
PURPLE_BLIST_NODE_IS_GROUP
(
gnode
))
continue
;
for
(
cnode
=
gnode
->
child
;
cnode
!=
NULL
;
cnode
=
cnode
->
next
)
{
if
(
!
PURPLE_BLIST_NODE_IS_CONTACT
(
cnode
))
continue
;
for
(
bnode
=
cnode
->
child
;
bnode
!=
NULL
;
bnode
=
bnode
->
next
)
{
PidginBuddyCompletionEntry
entry
;
entry
.
is_buddy
=
TRUE
;
entry
.
entry
.
buddy
=
(
PurpleBuddy
*
)
bnode
;
if
(
filter_func
(
&
entry
,
user_data
))
{
#ifdef NEW_STYLE_COMPLETION
add_screenname_autocomplete_entry
(
data
->
store
,
((
PurpleContact
*
)
cnode
)
->
alias
,
purple_buddy_get_contact_alias
(
entry
.
entry
.
buddy
),
entry
.
entry
.
buddy
->
account
,
entry
.
entry
.
buddy
->
name
);
#else
item
->
data
=
g_strdup
(
entry
.
entry
.
buddy
->
name
);
g_completion_add_items
(
data
->
completion
,
item
);
#endif
/* NEW_STYLE_COMPLETION */
}
}
}
}
#ifndef NEW_STYLE_COMPLETION
g_list_free
(
item
);
data
->
log_items
=
NULL
;
#endif
/* NEW_STYLE_COMPLETION */
sets
=
purple_log_get_log_sets
();
g_hash_table_foreach
(
sets
,
(
GHFunc
)
get_log_set_name
,
data
);
g_hash_table_destroy
(
sets
);
#ifndef NEW_STYLE_COMPLETION
g_completion_add_items
(
data
->
completion
,
data
->
log_items
);
g_list_free
(
data
->
log_items
);
#endif
/* NEW_STYLE_COMPLETION */
}
static
void
screenname_autocomplete_destroyed_cb
(
GtkWidget
*
widget
,
gpointer
data
)
{
g_free
(
data
);
purple_signals_disconnect_by_handle
(
widget
);
}
static
void
repopulate_autocomplete
(
gpointer
something
,
gpointer
data
)
{
add_completion_list
(
data
);
}
void
pidgin_setup_screenname_autocomplete_with_filter
(
GtkWidget
*
entry
,
GtkWidget
*
accountopt
,
PidginFilterBuddyCompletionEntryFunc
filter_func
,
gpointer
user_data
)
{
PidginCompletionData
*
data
;
#ifdef NEW_STYLE_COMPLETION
/* Store the displayed completion value, the screenname, the UTF-8 normalized & casefolded screenname,
* the UTF-8 normalized & casefolded value for comparison, and the account. */
GtkListStore
*
store
;
GtkEntryCompletion
*
completion
;
data
=
g_new0
(
PidginCompletionData
,
1
);
store
=
gtk_list_store_new
(
5
,
G_TYPE_STRING
,
G_TYPE_STRING
,
G_TYPE_STRING
,
G_TYPE_STRING
,
G_TYPE_POINTER
);
data
->
entry
=
entry
;
data
->
accountopt
=
accountopt
;
if
(
filter_func
==
NULL
)
{
data
->
filter_func
=
pidgin_screenname_autocomplete_default_filter
;
data
->
filter_func_user_data
=
NULL
;
}
else
{
data
->
filter_func
=
filter_func
;
data
->
filter_func_user_data
=
user_data
;
}
data
->
store
=
store
;
add_completion_list
(
data
);
/* Sort the completion list by screenname. */
gtk_tree_sortable_set_sort_column_id
(
GTK_TREE_SORTABLE
(
store
),
1
,
GTK_SORT_ASCENDING
);
completion
=
gtk_entry_completion_new
();
gtk_entry_completion_set_match_func
(
completion
,
screenname_completion_match_func
,
NULL
,
NULL
);
g_signal_connect
(
G_OBJECT
(
completion
),
"match-selected"
,
G_CALLBACK
(
screenname_completion_match_selected_cb
),
data
);
gtk_entry_set_completion
(
GTK_ENTRY
(
entry
),
completion
);
g_object_unref
(
completion
);
gtk_entry_completion_set_model
(
completion
,
GTK_TREE_MODEL
(
store
));
g_object_unref
(
store
);
gtk_entry_completion_set_text_column
(
completion
,
0
);
#else
/* !NEW_STYLE_COMPLETION */
data
=
g_new0
(
PidginCompletionData
,
1
);
data
->
entry
=
entry
;
data
->
accountopt
=
accountopt
;
if
(
filter_func
==
NULL
)
{
data
->
filter_func
=
pidgin_screenname_autocomplete_default_filter
;
data
->
filter_func_user_data
=
NULL
;
}
else
{
data
->
filter_func
=
filter_func
;
data
->
filter_func_user_data
=
user_data
;
}
data
->
completion
=
g_completion_new
(
NULL
);
data
->
completion_started
=
FALSE
;
add_completion_list
(
data
);
g_completion_set_compare
(
data
->
completion
,
g_ascii_strncasecmp
);
g_signal_connect
(
G_OBJECT
(
entry
),
"event"
,
G_CALLBACK
(
completion_entry_event
),
data
);
g_signal_connect
(
G_OBJECT
(
entry
),
"destroy"
,
G_CALLBACK
(
destroy_completion_data
),
data
);
#endif
/* !NEW_STYLE_COMPLETION */
purple_signal_connect
(
purple_connections_get_handle
(),
"signed-on"
,
entry
,
PURPLE_CALLBACK
(
repopulate_autocomplete
),
data
);
purple_signal_connect
(
purple_connections_get_handle
(),
"signed-off"
,
entry
,
PURPLE_CALLBACK
(
repopulate_autocomplete
),
data
);
purple_signal_connect
(
purple_accounts_get_handle
(),
"account-added"
,
entry
,
PURPLE_CALLBACK
(
repopulate_autocomplete
),
data
);
purple_signal_connect
(
purple_accounts_get_handle
(),
"account-removed"
,
entry
,
PURPLE_CALLBACK
(
repopulate_autocomplete
),
data
);
g_signal_connect
(
G_OBJECT
(
entry
),
"destroy"
,
G_CALLBACK
(
screenname_autocomplete_destroyed_cb
),
data
);
}
gboolean
pidgin_screenname_autocomplete_default_filter
(
const
PidginBuddyCompletionEntry
*
completion_entry
,
gpointer
all_accounts
)
{
gboolean
all
=
GPOINTER_TO_INT
(
all_accounts
);
if
(
completion_entry
->
is_buddy
)
{
return
all
||
purple_account_is_connected
(
completion_entry
->
entry
.
buddy
->
account
);
}
else
{
return
all
||
(
completion_entry
->
entry
.
logged_buddy
->
account
!=
NULL
&&
purple_account_is_connected
(
completion_entry
->
entry
.
logged_buddy
->
account
));
}
}
void
pidgin_setup_screenname_autocomplete
(
GtkWidget
*
entry
,
GtkWidget
*
accountopt
,
gboolean
all
)
{
pidgin_setup_screenname_autocomplete_with_filter
(
entry
,
accountopt
,
pidgin_screenname_autocomplete_default_filter
,
GINT_TO_POINTER
(
all
));
}
void
pidgin_set_cursor
(
GtkWidget
*
widget
,
GdkCursorType
cursor_type
)
{
GdkCursor
*
cursor
;
g_return_if_fail
(
widget
!=
NULL
);
if
(
widget
->
window
==
NULL
)
return
;
cursor
=
gdk_cursor_new
(
GDK_WATCH
);
gdk_window_set_cursor
(
widget
->
window
,
cursor
);
gdk_cursor_unref
(
cursor
);
#if GTK_CHECK_VERSION(2,4,0)
gdk_display_flush
(
gdk_drawable_get_display
(
GDK_DRAWABLE
(
widget
->
window
)));
#else
gdk_flush
();
#endif
}
void
pidgin_clear_cursor
(
GtkWidget
*
widget
)
{
g_return_if_fail
(
widget
!=
NULL
);
if
(
widget
->
window
==
NULL
)
return
;
gdk_window_set_cursor
(
widget
->
window
,
NULL
);
}
struct
_icon_chooser
{
GtkWidget
*
icon_filesel
;
GtkWidget
*
icon_preview
;
GtkWidget
*
icon_text
;
void
(
*
callback
)(
const
char
*
,
gpointer
);
gpointer
data
;
};
#if !GTK_CHECK_VERSION(2,4,0)
/* FILECHOOSER */
static
void
icon_filesel_delete_cb
(
GtkWidget
*
w
,
struct
_icon_chooser
*
dialog
)
{
if
(
dialog
->
icon_filesel
!=
NULL
)
gtk_widget_destroy
(
dialog
->
icon_filesel
);
if
(
dialog
->
callback
)
dialog
->
callback
(
NULL
,
dialog
->
data
);
g_free
(
dialog
);
}
#endif
/* FILECHOOSER */
#if GTK_CHECK_VERSION(2,4,0)
/* FILECHOOSER */
static
void
icon_filesel_choose_cb
(
GtkWidget
*
widget
,
gint
response
,
struct
_icon_chooser
*
dialog
)
{
char
*
filename
,
*
current_folder
;
if
(
response
!=
GTK_RESPONSE_ACCEPT
)
{
if
(
response
==
GTK_RESPONSE_CANCEL
)
{
gtk_widget_destroy
(
dialog
->
icon_filesel
);
}
dialog
->
icon_filesel
=
NULL
;
if
(
dialog
->
callback
)
dialog
->
callback
(
NULL
,
dialog
->
data
);
g_free
(
dialog
);
return
;
}
filename
=
gtk_file_chooser_get_filename
(
GTK_FILE_CHOOSER
(
dialog
->
icon_filesel
));
current_folder
=
gtk_file_chooser_get_current_folder
(
GTK_FILE_CHOOSER
(
dialog
->
icon_filesel
));
if
(
current_folder
!=
NULL
)
{
purple_prefs_set_path
(
PIDGIN_PREFS_ROOT
"/filelocations/last_icon_folder"
,
current_folder
);
g_free
(
current_folder
);
}
#else
/* FILECHOOSER */
static
void
icon_filesel_choose_cb
(
GtkWidget
*
w
,
struct
_icon_chooser
*
dialog
)
{
char
*
filename
,
*
current_folder
;
filename
=
g_strdup
(
gtk_file_selection_get_filename
(
GTK_FILE_SELECTION
(
dialog
->
icon_filesel
)));
/* If they typed in a directory, change there */
if
(
pidgin_check_if_dir
(
filename
,
GTK_FILE_SELECTION
(
dialog
->
icon_filesel
)))
{
g_free
(
filename
);
return
;
}
current_folder
=
g_path_get_dirname
(
filename
);
if
(
current_folder
!=
NULL
)
{
purple_prefs_set_path
(
PIDGIN_PREFS_ROOT
"/filelocations/last_icon_folder"
,
current_folder
);
g_free
(
current_folder
);
}
#endif
/* FILECHOOSER */
if
(
dialog
->
callback
)
dialog
->
callback
(
filename
,
dialog
->
data
);
gtk_widget_destroy
(
dialog
->
icon_filesel
);
g_free
(
filename
);
g_free
(
dialog
);
}
static
void
#if GTK_CHECK_VERSION(2,4,0)
/* FILECHOOSER */
icon_preview_change_cb
(
GtkFileChooser
*
widget
,
struct
_icon_chooser
*
dialog
)
#else
/* FILECHOOSER */
icon_preview_change_cb
(
GtkTreeSelection
*
sel
,
struct
_icon_chooser
*
dialog
)
#endif
/* FILECHOOSER */
{
GdkPixbuf
*
pixbuf
,
*
scale
;
int
height
,
width
;
char
*
basename
,
*
markup
,
*
size
;
struct
stat
st
;
char
*
filename
;
#if GTK_CHECK_VERSION(2,4,0)
/* FILECHOOSER */
filename
=
gtk_file_chooser_get_preview_filename
(
GTK_FILE_CHOOSER
(
dialog
->
icon_filesel
));
#else
/* FILECHOOSER */
filename
=
g_strdup
(
gtk_file_selection_get_filename
(
GTK_FILE_SELECTION
(
dialog
->
icon_filesel
)));
#endif
/* FILECHOOSER */
if
(
!
filename
||
g_stat
(
filename
,
&
st
)
||
!
(
pixbuf
=
gdk_pixbuf_new_from_file
(
filename
,
NULL
)))
{
gtk_image_set_from_pixbuf
(
GTK_IMAGE
(
dialog
->
icon_preview
),
NULL
);
gtk_label_set_markup
(
GTK_LABEL
(
dialog
->
icon_text
),
""
);
g_free
(
filename
);
return
;
}
width
=
gdk_pixbuf_get_width
(
pixbuf
);
height
=
gdk_pixbuf_get_height
(
pixbuf
);
basename
=
g_path_get_basename
(
filename
);
size
=
purple_str_size_to_units
(
st
.
st_size
);
markup
=
g_strdup_printf
(
_
(
"<b>File:</b> %s
\n
"
"<b>File size:</b> %s
\n
"
"<b>Image size:</b> %dx%d"
),
basename
,
size
,
width
,
height
);
scale
=
gdk_pixbuf_scale_simple
(
pixbuf
,
width
*
50
/
height
,
50
,
GDK_INTERP_BILINEAR
);
gtk_image_set_from_pixbuf
(
GTK_IMAGE
(
dialog
->
icon_preview
),
scale
);
gtk_label_set_markup
(
GTK_LABEL
(
dialog
->
icon_text
),
markup
);
g_object_unref
(
G_OBJECT
(
pixbuf
));
g_object_unref
(
G_OBJECT
(
scale
));
g_free
(
filename
);
g_free
(
basename
);
g_free
(
size
);
g_free
(
markup
);
}
GtkWidget
*
pidgin_buddy_icon_chooser_new
(
GtkWindow
*
parent
,
void
(
*
callback
)(
const
char
*
,
gpointer
),
gpointer
data
)
{
struct
_icon_chooser
*
dialog
=
g_new0
(
struct
_icon_chooser
,
1
);
#if GTK_CHECK_VERSION(2,4,0)
/* FILECHOOSER */
GtkWidget
*
vbox
;
#else
GtkWidget
*
hbox
;
GtkWidget
*
tv
;
GtkTreeSelection
*
sel
;
#endif
/* FILECHOOSER */
const
char
*
current_folder
;
dialog
->
callback
=
callback
;
dialog
->
data
=
data
;
if
(
dialog
->
icon_filesel
!=
NULL
)
{
gtk_window_present
(
GTK_WINDOW
(
dialog
->
icon_filesel
));
return
NULL
;
}
current_folder
=
purple_prefs_get_path
(
PIDGIN_PREFS_ROOT
"/filelocations/last_icon_folder"
);
#if GTK_CHECK_VERSION(2,4,0)
/* FILECHOOSER */
dialog
->
icon_filesel
=
gtk_file_chooser_dialog_new
(
_
(
"Buddy Icon"
),
parent
,
GTK_FILE_CHOOSER_ACTION_OPEN
,
GTK_STOCK_CANCEL
,
GTK_RESPONSE_CANCEL
,
GTK_STOCK_OPEN
,
GTK_RESPONSE_ACCEPT
,
NULL
);
gtk_dialog_set_default_response
(
GTK_DIALOG
(
dialog
->
icon_filesel
),
GTK_RESPONSE_ACCEPT
);
if
((
current_folder
!=
NULL
)
&&
(
*
current_folder
!=
'\0'
))
gtk_file_chooser_set_current_folder
(
GTK_FILE_CHOOSER
(
dialog
->
icon_filesel
),
current_folder
);
dialog
->
icon_preview
=
gtk_image_new
();
dialog
->
icon_text
=
gtk_label_new
(
NULL
);
vbox
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
gtk_widget_set_size_request
(
GTK_WIDGET
(
vbox
),
-1
,
50
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
GTK_WIDGET
(
dialog
->
icon_preview
),
TRUE
,
FALSE
,
0
);
gtk_box_pack_end
(
GTK_BOX
(
vbox
),
GTK_WIDGET
(
dialog
->
icon_text
),
FALSE
,
FALSE
,
0
);
gtk_widget_show_all
(
vbox
);
gtk_file_chooser_set_preview_widget
(
GTK_FILE_CHOOSER
(
dialog
->
icon_filesel
),
vbox
);
gtk_file_chooser_set_preview_widget_active
(
GTK_FILE_CHOOSER
(
dialog
->
icon_filesel
),
TRUE
);
gtk_file_chooser_set_use_preview_label
(
GTK_FILE_CHOOSER
(
dialog
->
icon_filesel
),
FALSE
);
g_signal_connect
(
G_OBJECT
(
dialog
->
icon_filesel
),
"update-preview"
,
G_CALLBACK
(
icon_preview_change_cb
),
dialog
);
g_signal_connect
(
G_OBJECT
(
dialog
->
icon_filesel
),
"response"
,
G_CALLBACK
(
icon_filesel_choose_cb
),
dialog
);
icon_preview_change_cb
(
NULL
,
dialog
);
#else
/* FILECHOOSER */
dialog
->
icon_filesel
=
gtk_file_selection_new
(
_
(
"Buddy Icon"
));
dialog
->
icon_preview
=
gtk_image_new
();
dialog
->
icon_text
=
gtk_label_new
(
NULL
);
if
((
current_folder
!=
NULL
)
&&
(
*
current_folder
!=
'\0'
))
gtk_file_selection_set_filename
(
GTK_FILE_SELECTION
(
dialog
->
icon_filesel
),
current_folder
);
gtk_widget_set_size_request
(
GTK_WIDGET
(
dialog
->
icon_preview
),
-1
,
50
);
hbox
=
gtk_hbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
gtk_box_pack_start
(
GTK_BOX
(
GTK_FILE_SELECTION
(
dialog
->
icon_filesel
)
->
main_vbox
),
hbox
,
FALSE
,
FALSE
,
0
);
gtk_box_pack_end
(
GTK_BOX
(
hbox
),
dialog
->
icon_preview
,
FALSE
,
FALSE
,
0
);
gtk_box_pack_end
(
GTK_BOX
(
hbox
),
dialog
->
icon_text
,
FALSE
,
FALSE
,
0
);
tv
=
GTK_FILE_SELECTION
(
dialog
->
icon_filesel
)
->
file_list
;
sel
=
gtk_tree_view_get_selection
(
GTK_TREE_VIEW
(
tv
));
g_signal_connect
(
G_OBJECT
(
sel
),
"changed"
,
G_CALLBACK
(
icon_preview_change_cb
),
dialog
);
g_signal_connect
(
G_OBJECT
(
GTK_FILE_SELECTION
(
dialog
->
icon_filesel
)
->
ok_button
),
"clicked"
,
G_CALLBACK
(
icon_filesel_choose_cb
),
dialog
);
g_signal_connect
(
G_OBJECT
(
GTK_FILE_SELECTION
(
dialog
->
icon_filesel
)
->
cancel_button
),
"clicked"
,
G_CALLBACK
(
icon_filesel_delete_cb
),
dialog
);
g_signal_connect
(
G_OBJECT
(
dialog
->
icon_filesel
),
"destroy"
,
G_CALLBACK
(
icon_filesel_delete_cb
),
dialog
);
#endif
/* FILECHOOSER */
return
dialog
->
icon_filesel
;
}
#if GTK_CHECK_VERSION(2,2,0)
static
gboolean
str_array_match
(
char
**
a
,
char
**
b
)
{
int
i
,
j
;
if
(
!
a
||
!
b
)
return
FALSE
;
for
(
i
=
0
;
a
[
i
]
!=
NULL
;
i
++
)
for
(
j
=
0
;
b
[
j
]
!=
NULL
;
j
++
)
if
(
!
g_ascii_strcasecmp
(
a
[
i
],
b
[
j
]))
return
TRUE
;
return
FALSE
;
}
#endif
gpointer
pidgin_convert_buddy_icon
(
PurplePlugin
*
plugin
,
const
char
*
path
,
size_t
*
len
)
{
PurplePluginProtocolInfo
*
prpl_info
;
#if GTK_CHECK_VERSION(2,2,0)
char
**
prpl_formats
;
int
width
,
height
;
char
**
pixbuf_formats
=
NULL
;
GdkPixbufFormat
*
format
;
GdkPixbuf
*
pixbuf
;
#if !GTK_CHECK_VERSION(2,4,0)
GdkPixbufLoader
*
loader
;
#endif
#endif
gchar
*
contents
;
gsize
length
;
prpl_info
=
PURPLE_PLUGIN_PROTOCOL_INFO
(
plugin
);
g_return_val_if_fail
(
prpl_info
->
icon_spec
.
format
!=
NULL
,
NULL
);
#if GTK_CHECK_VERSION(2,2,0)
#if GTK_CHECK_VERSION(2,4,0)
format
=
gdk_pixbuf_get_file_info
(
path
,
&
width
,
&
height
);
#else
loader
=
gdk_pixbuf_loader_new
();
if
(
g_file_get_contents
(
path
,
&
contents
,
&
length
,
NULL
))
{
gdk_pixbuf_loader_write
(
loader
,
contents
,
length
,
NULL
);
g_free
(
contents
);
}
gdk_pixbuf_loader_close
(
loader
,
NULL
);
pixbuf
=
gdk_pixbuf_loader_get_pixbuf
(
loader
);
width
=
gdk_pixbuf_get_width
(
pixbuf
);
height
=
gdk_pixbuf_get_height
(
pixbuf
);
format
=
gdk_pixbuf_loader_get_format
(
loader
);
g_object_unref
(
G_OBJECT
(
loader
));
#endif
if
(
format
==
NULL
)
return
NULL
;
pixbuf_formats
=
gdk_pixbuf_format_get_extensions
(
format
);
prpl_formats
=
g_strsplit
(
prpl_info
->
icon_spec
.
format
,
","
,
0
);
if
(
str_array_match
(
pixbuf_formats
,
prpl_formats
)
&&
/* This is an acceptable format AND */
(
!
(
prpl_info
->
icon_spec
.
scale_rules
&
PURPLE_ICON_SCALE_SEND
)
||
/* The prpl doesn't scale before it sends OR */
(
prpl_info
->
icon_spec
.
min_width
<=
width
&&
prpl_info
->
icon_spec
.
max_width
>=
width
&&
prpl_info
->
icon_spec
.
min_height
<=
height
&&
prpl_info
->
icon_spec
.
max_height
>=
height
)))
/* The icon is the correct size */
#endif
{
#if GTK_CHECK_VERSION(2,2,0)
g_strfreev
(
prpl_formats
);
g_strfreev
(
pixbuf_formats
);
#endif
/* We don't need to scale the image. */
contents
=
NULL
;
if
(
!
g_file_get_contents
(
path
,
&
contents
,
&
length
,
NULL
))
{
g_free
(
contents
);
#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0)
g_object_unref
(
G_OBJECT
(
pixbuf
));
#endif
return
NULL
;
}
}
#if GTK_CHECK_VERSION(2,2,0)
else
{
int
i
;
GError
*
error
=
NULL
;
GdkPixbuf
*
scale
;
gboolean
success
=
FALSE
;
char
*
filename
=
NULL
;
g_strfreev
(
pixbuf_formats
);
pixbuf
=
gdk_pixbuf_new_from_file
(
path
,
&
error
);
if
(
error
)
{
purple_debug_error
(
"buddyicon"
,
"Could not open icon for conversion: %s
\n
"
,
error
->
message
);
g_error_free
(
error
);
g_strfreev
(
prpl_formats
);
return
NULL
;
}
if
((
prpl_info
->
icon_spec
.
scale_rules
&
PURPLE_ICON_SCALE_SEND
)
&&
(
width
<
prpl_info
->
icon_spec
.
min_width
||
width
>
prpl_info
->
icon_spec
.
max_width
||
height
<
prpl_info
->
icon_spec
.
min_height
||
height
>
prpl_info
->
icon_spec
.
max_height
))
{
int
new_width
=
width
;
int
new_height
=
height
;
purple_buddy_icon_get_scale_size
(
&
prpl_info
->
icon_spec
,
&
new_width
,
&
new_height
);
scale
=
gdk_pixbuf_scale_simple
(
pixbuf
,
new_width
,
new_height
,
GDK_INTERP_HYPER
);
g_object_unref
(
G_OBJECT
(
pixbuf
));
pixbuf
=
scale
;
}
for
(
i
=
0
;
prpl_formats
[
i
];
i
++
)
{
FILE
*
fp
;
g_free
(
filename
);
fp
=
purple_mkstemp
(
&
filename
,
TRUE
);
if
(
!
fp
)
{
g_free
(
filename
);
return
NULL
;
}
fclose
(
fp
);
purple_debug_info
(
"buddyicon"
,
"Converting buddy icon to %s as %s
\n
"
,
prpl_formats
[
i
],
filename
);
/* The "compression" param wasn't supported until gdk-pixbuf 2.8.
* Using it in previous versions causes the save to fail (and an assert message). */
if
((
gdk_pixbuf_major_version
>
2
||
(
gdk_pixbuf_major_version
==
2
&&
gdk_pixbuf_minor_version
>=
8
))
&&
strcmp
(
prpl_formats
[
i
],
"png"
)
==
0
)
{
if
(
gdk_pixbuf_save
(
pixbuf
,
filename
,
prpl_formats
[
i
],
&
error
,
"compression"
,
"9"
,
NULL
))
{
success
=
TRUE
;
break
;
}
}
else
if
(
gdk_pixbuf_save
(
pixbuf
,
filename
,
prpl_formats
[
i
],
&
error
,
NULL
))
{
success
=
TRUE
;
break
;
}
/* The NULL checking is necessary due to this bug:
* http://bugzilla.gnome.org/show_bug.cgi?id=405539 */
purple_debug_warning
(
"buddyicon"
,
"Could not convert to %s: %s
\n
"
,
prpl_formats
[
i
],
(
error
&&
error
->
message
)
?
error
->
message
:
"Unknown error"
);
g_error_free
(
error
);
error
=
NULL
;
}
g_strfreev
(
prpl_formats
);
g_object_unref
(
G_OBJECT
(
pixbuf
));
if
(
!
success
)
{
purple_debug_error
(
"buddyicon"
,
"Could not convert icon to usable format.
\n
"
);
return
NULL
;
}
contents
=
NULL
;
if
(
!
g_file_get_contents
(
filename
,
&
contents
,
&
length
,
NULL
))
{
purple_debug_error
(
"buddyicon"
,
"Could not read '%s', which we just wrote to disk.
\n
"
,
filename
);
g_free
(
contents
);
g_free
(
filename
);
return
NULL
;
}
g_unlink
(
filename
);
g_free
(
filename
);
}
/* Check the image size */
/*
* TODO: If the file is too big, it would be cool if we checked if
* the prpl supported jpeg, and then we could convert to that
* and use a lower quality setting.
*/
if
((
prpl_info
->
icon_spec
.
max_filesize
!=
0
)
&&
(
length
>
prpl_info
->
icon_spec
.
max_filesize
))
{
gchar
*
tmp
;
tmp
=
g_strdup_printf
(
_
(
"The file '%s' is too large for %s. Please try a smaller image.
\n
"
),
path
,
plugin
->
info
->
name
);
purple_notify_error
(
NULL
,
_
(
"Icon Error"
),
_
(
"Could not set icon"
),
tmp
);
purple_debug_info
(
"buddyicon"
,
"'%s' was converted to an image which is %"
G_GSIZE_FORMAT
" bytes, but the maximum icon size for %s is %"
G_GSIZE_FORMAT
" bytes
\n
"
,
path
,
length
,
plugin
->
info
->
name
,
prpl_info
->
icon_spec
.
max_filesize
);
g_free
(
tmp
);
return
NULL
;
}
if
(
len
)
*
len
=
length
;
return
contents
;
#else
/*
* The chosen icon wasn't the right size, and we're using
* GTK+ 2.0 so we can't scale it.
*/
return
NULL
;
#endif
}
#if !GTK_CHECK_VERSION(2,6,0)
static
void
_gdk_file_scale_size_prepared_cb
(
GdkPixbufLoader
*
loader
,
int
width
,
int
height
,
gpointer
data
)
{
struct
{
gint
width
;
gint
height
;
gboolean
preserve_aspect_ratio
;
}
*
info
=
data
;
g_return_if_fail
(
width
>
0
&&
height
>
0
);
if
(
info
->
preserve_aspect_ratio
&&
(
info
->
width
>
0
||
info
->
height
>
0
))
{
if
(
info
->
width
<
0
)
{
width
=
width
*
(
double
)
info
->
height
/
(
double
)
height
;
height
=
info
->
height
;
}
else
if
(
info
->
height
<
0
)
{
height
=
height
*
(
double
)
info
->
width
/
(
double
)
width
;
width
=
info
->
width
;
}
else
if
((
double
)
height
*
(
double
)
info
->
width
>
(
double
)
width
*
(
double
)
info
->
height
)
{
width
=
0.5
+
(
double
)
width
*
(
double
)
info
->
height
/
(
double
)
height
;
height
=
info
->
height
;
}
else
{
height
=
0.5
+
(
double
)
height
*
(
double
)
info
->
width
/
(
double
)
width
;
width
=
info
->
width
;
}
}
else
{
if
(
info
->
width
>
0
)
width
=
info
->
width
;
if
(
info
->
height
>
0
)
height
=
info
->
height
;
}
#if GTK_CHECK_VERSION(2,2,0)
/* 2.0 users are going to have very strangely sized things */
gdk_pixbuf_loader_set_size
(
loader
,
width
,
height
);
#else
#warning nosnilmot could not be bothered to fix this properly for you
#warning ... good luck ... your images may end up strange sizes
#endif
}
GdkPixbuf
*
gdk_pixbuf_new_from_file_at_scale
(
const
char
*
filename
,
int
width
,
int
height
,
gboolean
preserve_aspect_ratio
,
GError
**
error
)
{
GdkPixbufLoader
*
loader
;
GdkPixbuf
*
pixbuf
;
guchar
buffer
[
4096
];
int
length
;
FILE
*
f
;
struct
{
gint
width
;
gint
height
;
gboolean
preserve_aspect_ratio
;
}
info
;
GdkPixbufAnimation
*
animation
;
GdkPixbufAnimationIter
*
iter
;
gboolean
has_frame
;
g_return_val_if_fail
(
filename
!=
NULL
,
NULL
);
g_return_val_if_fail
(
width
>
0
||
width
==
-1
,
NULL
);
g_return_val_if_fail
(
height
>
0
||
height
==
-1
,
NULL
);
f
=
g_fopen
(
filename
,
"rb"
);
if
(
!
f
)
{
gint
save_errno
=
errno
;
gchar
*
display_name
=
g_filename_to_utf8
(
filename
,
-1
,
NULL
,
NULL
,
NULL
);
g_set_error
(
error
,
G_FILE_ERROR
,
g_file_error_from_errno
(
save_errno
),
_
(
"Failed to open file '%s': %s"
),
display_name
?
display_name
:
"(unknown)"
,
g_strerror
(
save_errno
));
g_free
(
display_name
);
return
NULL
;
}
loader
=
gdk_pixbuf_loader_new
();
info
.
width
=
width
;
info
.
height
=
height
;
info
.
preserve_aspect_ratio
=
preserve_aspect_ratio
;
g_signal_connect
(
loader
,
"size-prepared"
,
G_CALLBACK
(
_gdk_file_scale_size_prepared_cb
),
&
info
);
has_frame
=
FALSE
;
while
(
!
has_frame
&&
!
feof
(
f
)
&&
!
ferror
(
f
))
{
length
=
fread
(
buffer
,
1
,
sizeof
(
buffer
),
f
);
if
(
length
>
0
)
if
(
!
gdk_pixbuf_loader_write
(
loader
,
buffer
,
length
,
error
))
{
gdk_pixbuf_loader_close
(
loader
,
NULL
);
fclose
(
f
);
g_object_unref
(
loader
);
return
NULL
;
}
animation
=
gdk_pixbuf_loader_get_animation
(
loader
);
if
(
animation
)
{
iter
=
gdk_pixbuf_animation_get_iter
(
animation
,
0
);
if
(
!
gdk_pixbuf_animation_iter_on_currently_loading_frame
(
iter
))
{
has_frame
=
TRUE
;
}
g_object_unref
(
iter
);
}
}
fclose
(
f
);
if
(
!
gdk_pixbuf_loader_close
(
loader
,
error
)
&&
!
has_frame
)
{
g_object_unref
(
loader
);
return
NULL
;
}
pixbuf
=
gdk_pixbuf_loader_get_pixbuf
(
loader
);
if
(
!
pixbuf
)
{
gchar
*
display_name
=
g_filename_to_utf8
(
filename
,
-1
,
NULL
,
NULL
,
NULL
);
g_object_unref
(
loader
);
g_set_error
(
error
,
GDK_PIXBUF_ERROR
,
GDK_PIXBUF_ERROR_FAILED
,
_
(
"Failed to load image '%s': reason not known, probably a corrupt image file"
),
display_name
?
display_name
:
"(unknown)"
);
g_free
(
display_name
);
return
NULL
;
}
g_object_ref
(
pixbuf
);
g_object_unref
(
loader
);
return
pixbuf
;
}
#endif
/* ! Gtk 2.6.0 */
void
pidgin_set_custom_buddy_icon
(
PurpleAccount
*
account
,
const
char
*
who
,
const
char
*
filename
)
{
PurpleBuddy
*
buddy
;
PurpleContact
*
contact
;
gpointer
data
=
NULL
;
size_t
len
=
0
;
buddy
=
purple_find_buddy
(
account
,
who
);
if
(
!
buddy
)
{
purple_debug_info
(
"custom-icon"
,
"You can only set custom icon for someone in your buddylist.
\n
"
);
return
;
}
contact
=
purple_buddy_get_contact
(
buddy
);
if
(
filename
)
{
const
char
*
prpl_id
=
purple_account_get_protocol_id
(
account
);
PurplePlugin
*
prpl
=
purple_find_prpl
(
prpl_id
);
data
=
pidgin_convert_buddy_icon
(
prpl
,
filename
,
&
len
);
/* We don't want to delete the old icon if the new one didn't load. */
if
(
data
==
NULL
)
return
;
}
purple_buddy_icons_set_custom_icon
(
contact
,
data
,
len
);
}
char
*
pidgin_make_pretty_arrows
(
const
char
*
str
)
{
char
*
ret
;
char
**
split
=
g_strsplit
(
str
,
"->"
,
-1
);
ret
=
g_strjoinv
(
"
\342\207\250
"
,
split
);
g_strfreev
(
split
);
split
=
g_strsplit
(
ret
,
"<-"
,
-1
);
g_free
(
ret
);
ret
=
g_strjoinv
(
"
\342\207\246
"
,
split
);
g_strfreev
(
split
);
return
ret
;
}
void
pidgin_set_urgent
(
GtkWindow
*
window
,
gboolean
urgent
)
{
#if GTK_CHECK_VERSION(2,8,0)
gtk_window_set_urgency_hint
(
window
,
urgent
);
#elif defined _WIN32
winpidgin_window_flash
(
window
,
urgent
);
#else
GdkWindow
*
gdkwin
;
XWMHints
*
hints
;
g_return_if_fail
(
window
!=
NULL
);
gdkwin
=
GTK_WIDGET
(
window
)
->
window
;
g_return_if_fail
(
gdkwin
!=
NULL
);
hints
=
XGetWMHints
(
GDK_WINDOW_XDISPLAY
(
gdkwin
),
GDK_WINDOW_XWINDOW
(
gdkwin
));
if
(
!
hints
)
hints
=
XAllocWMHints
();
if
(
urgent
)
hints
->
flags
|=
XUrgencyHint
;
else
hints
->
flags
&=
~
XUrgencyHint
;
XSetWMHints
(
GDK_WINDOW_XDISPLAY
(
gdkwin
),
GDK_WINDOW_XWINDOW
(
gdkwin
),
hints
);
XFree
(
hints
);
#endif
}
GSList
*
minidialogs
=
NULL
;
static
void
*
pidgin_utils_get_handle
()
{
static
int
handle
;
return
&
handle
;
}
static
void
connection_signed_off_cb
(
PurpleConnection
*
gc
)
{
GSList
*
list
;
for
(
list
=
minidialogs
;
list
;
list
=
list
->
next
)
{
if
(
g_object_get_data
(
G_OBJECT
(
list
->
data
),
"gc"
)
==
gc
)
{
gtk_widget_destroy
(
GTK_WIDGET
(
list
->
data
));
}
}
}
static
void
alert_killed_cb
(
GtkWidget
*
widget
)
{
minidialogs
=
g_slist_remove
(
minidialogs
,
widget
);
}
void
*
pidgin_make_mini_dialog
(
PurpleConnection
*
gc
,
const
char
*
icon_name
,
const
char
*
primary
,
const
char
*
secondary
,
void
*
user_data
,
...)
{
GtkWidget
*
vbox
;
GtkWidget
*
hbox
;
GtkWidget
*
hbox2
;
GtkWidget
*
label
;
GtkWidget
*
button
;
GtkWidget
*
img
=
NULL
;
GtkSizeGroup
*
sg
=
gtk_size_group_new
(
GTK_SIZE_GROUP_BOTH
);
char
label_text
[
2048
];
const
char
*
button_text
;
GCallback
callback
;
char
*
primary_esc
,
*
secondary_esc
=
NULL
;
va_list
args
;
static
gboolean
first_call
=
TRUE
;
img
=
gtk_image_new_from_stock
(
icon_name
,
gtk_icon_size_from_name
(
PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL
));
gtk_misc_set_alignment
(
GTK_MISC
(
img
),
0
,
0
);
vbox
=
gtk_vbox_new
(
FALSE
,
0
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
vbox
),
PIDGIN_HIG_BOX_SPACE
);
g_object_set_data
(
G_OBJECT
(
vbox
),
"gc"
,
gc
);
minidialogs
=
g_slist_prepend
(
minidialogs
,
vbox
);
g_signal_connect
(
G_OBJECT
(
vbox
),
"destroy"
,
G_CALLBACK
(
alert_killed_cb
),
NULL
);
if
(
first_call
)
{
first_call
=
FALSE
;
purple_signal_connect
(
purple_connections_get_handle
(),
"signed-off"
,
pidgin_utils_get_handle
(),
PURPLE_CALLBACK
(
connection_signed_off_cb
),
NULL
);
}
hbox
=
gtk_hbox_new
(
FALSE
,
0
);
gtk_container_add
(
GTK_CONTAINER
(
vbox
),
hbox
);
if
(
img
!=
NULL
)
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
img
,
FALSE
,
FALSE
,
0
);
primary_esc
=
g_markup_escape_text
(
primary
,
-1
);
if
(
secondary
)
secondary_esc
=
g_markup_escape_text
(
secondary
,
-1
);
g_snprintf
(
label_text
,
sizeof
(
label_text
),
"<span weight=
\"
bold
\"
size=
\"
smaller
\"
>%s</span>%s<span size=
\"
smaller
\"
>%s</span>"
,
primary_esc
,
secondary
?
"
\n
"
:
""
,
secondary_esc
?
secondary_esc
:
""
);
g_free
(
primary_esc
);
g_free
(
secondary_esc
);
label
=
gtk_label_new
(
NULL
);
gtk_widget_set_size_request
(
label
,
purple_prefs_get_int
(
PIDGIN_PREFS_ROOT
"/blist/width"
)
-25
,
-1
);
gtk_label_set_markup
(
GTK_LABEL
(
label
),
label_text
);
gtk_label_set_line_wrap
(
GTK_LABEL
(
label
),
TRUE
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
label
,
TRUE
,
TRUE
,
0
);
hbox2
=
gtk_hbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
hbox2
,
FALSE
,
FALSE
,
0
);
va_start
(
args
,
user_data
);
while
((
button_text
=
va_arg
(
args
,
char
*
)))
{
callback
=
va_arg
(
args
,
GCallback
);
button
=
gtk_button_new
();
if
(
callback
)
g_signal_connect_swapped
(
G_OBJECT
(
button
),
"clicked"
,
callback
,
user_data
);
g_signal_connect_swapped
(
G_OBJECT
(
button
),
"clicked"
,
G_CALLBACK
(
gtk_widget_destroy
),
vbox
);
hbox
=
gtk_hbox_new
(
FALSE
,
0
);
gtk_container_add
(
GTK_CONTAINER
(
button
),
hbox
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
hbox
),
3
);
g_snprintf
(
label_text
,
sizeof
(
label_text
),
"<span size=
\"
smaller
\"
>%s</span>"
,
button_text
);
label
=
gtk_label_new
(
NULL
);
gtk_label_set_markup_with_mnemonic
(
GTK_LABEL
(
label
),
label_text
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0.5
,
0.5
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
label
,
TRUE
,
TRUE
,
0
);
gtk_box_pack_end
(
GTK_BOX
(
hbox2
),
button
,
FALSE
,
FALSE
,
0
);
gtk_size_group_add_widget
(
sg
,
button
);
}
va_end
(
args
);
return
vbox
;
}
/*
* "This is so dead sexy."
* "Two thumbs up."
* "Best movie of the year."
*
* This is the function that handles CTRL+F searching in the buddy list.
* It finds the top-most buddy/group/chat/whatever containing the
* entered string.
*
* It's somewhat ineffecient, because we strip all the HTML from the
* "name" column of the buddy list (because the GtkTreeModel does not
* contain the screen name in a non-markedup format). But the alternative
* is to add an extra column to the GtkTreeModel. And this function is
* used rarely, so it shouldn't matter TOO much.
*/
gboolean
pidgin_tree_view_search_equal_func
(
GtkTreeModel
*
model
,
gint
column
,
const
gchar
*
key
,
GtkTreeIter
*
iter
,
gpointer
data
)
{
gchar
*
enteredstring
;
gchar
*
tmp
;
gchar
*
withmarkup
;
gchar
*
nomarkup
;
gchar
*
normalized
;
gboolean
result
;
size_t
i
;
size_t
len
;
PangoLogAttr
*
log_attrs
;
gchar
*
word
;
if
(
g_ascii_strcasecmp
(
key
,
"Global Thermonuclear War"
)
==
0
)
{
purple_notify_info
(
NULL
,
"WOPR"
,
"Wouldn't you prefer a nice game of chess?"
,
NULL
);
return
FALSE
;
}
gtk_tree_model_get
(
model
,
iter
,
column
,
&
withmarkup
,
-1
);
if
(
withmarkup
==
NULL
)
/* This is probably a separator */
return
TRUE
;
tmp
=
g_utf8_normalize
(
key
,
-1
,
G_NORMALIZE_DEFAULT
);
enteredstring
=
g_utf8_casefold
(
tmp
,
-1
);
g_free
(
tmp
);
nomarkup
=
purple_markup_strip_html
(
withmarkup
);
tmp
=
g_utf8_normalize
(
nomarkup
,
-1
,
G_NORMALIZE_DEFAULT
);
g_free
(
nomarkup
);
normalized
=
g_utf8_casefold
(
tmp
,
-1
);
g_free
(
tmp
);
if
(
purple_str_has_prefix
(
normalized
,
enteredstring
))
{
g_free
(
withmarkup
);
g_free
(
enteredstring
);
g_free
(
normalized
);
return
FALSE
;
}
/* Use Pango to separate by words. */
len
=
g_utf8_strlen
(
normalized
,
-1
);
log_attrs
=
g_new
(
PangoLogAttr
,
len
+
1
);
pango_get_log_attrs
(
normalized
,
strlen
(
normalized
),
-1
,
NULL
,
log_attrs
,
len
+
1
);
word
=
normalized
;
result
=
TRUE
;
for
(
i
=
0
;
i
<
(
len
-
1
)
;
i
++
)
{
if
(
log_attrs
[
i
].
is_word_start
&&
purple_str_has_prefix
(
word
,
enteredstring
))
{
result
=
FALSE
;
break
;
}
word
=
g_utf8_next_char
(
word
);
}
g_free
(
log_attrs
);
/* The non-Pango version. */
#if 0
word = normalized;
result = TRUE;
while (word[0] != '\0')
{
gunichar c = g_utf8_get_char(word);
if (!g_unichar_isalnum(c))
{
word = g_utf8_find_next_char(word, NULL);
if (purple_str_has_prefix(word, enteredstring))
{
result = FALSE;
break;
}
}
else
word = g_utf8_find_next_char(word, NULL);
}
#endif
g_free
(
withmarkup
);
g_free
(
enteredstring
);
g_free
(
normalized
);
return
result
;
}
gboolean
pidgin_gdk_pixbuf_is_opaque
(
GdkPixbuf
*
pixbuf
)
{
int
width
,
height
,
rowstride
,
i
;
unsigned
char
*
pixels
;
unsigned
char
*
row
;
if
(
!
gdk_pixbuf_get_has_alpha
(
pixbuf
))
return
TRUE
;
width
=
gdk_pixbuf_get_width
(
pixbuf
);
height
=
gdk_pixbuf_get_height
(
pixbuf
);
rowstride
=
gdk_pixbuf_get_rowstride
(
pixbuf
);
pixels
=
gdk_pixbuf_get_pixels
(
pixbuf
);
row
=
pixels
;
for
(
i
=
3
;
i
<
rowstride
;
i
+=
4
)
{
if
(
row
[
i
]
<
0xfe
)
return
FALSE
;
}
for
(
i
=
1
;
i
<
height
-
1
;
i
++
)
{
row
=
pixels
+
(
i
*
rowstride
);
if
(
row
[
3
]
<
0xfe
||
row
[
rowstride
-1
]
<
0xfe
)
{
return
FALSE
;
}
}
row
=
pixels
+
((
height
-1
)
*
rowstride
);
for
(
i
=
3
;
i
<
rowstride
;
i
+=
4
)
{
if
(
row
[
i
]
<
0xfe
)
return
FALSE
;
}
return
TRUE
;
}
void
pidgin_gdk_pixbuf_make_round
(
GdkPixbuf
*
pixbuf
)
{
int
width
,
height
,
rowstride
;
guchar
*
pixels
;
if
(
!
gdk_pixbuf_get_has_alpha
(
pixbuf
))
return
;
width
=
gdk_pixbuf_get_width
(
pixbuf
);
height
=
gdk_pixbuf_get_height
(
pixbuf
);
rowstride
=
gdk_pixbuf_get_rowstride
(
pixbuf
);
pixels
=
gdk_pixbuf_get_pixels
(
pixbuf
);
if
(
width
<
6
||
height
<
6
)
return
;
/* Top left */
pixels
[
3
]
=
0
;
pixels
[
7
]
=
0x80
;
pixels
[
11
]
=
0xC0
;
pixels
[
rowstride
+
3
]
=
0x80
;
pixels
[
rowstride
*
2
+
3
]
=
0xC0
;
/* Top right */
pixels
[
width
*
4
-
1
]
=
0
;
pixels
[
width
*
4
-
5
]
=
0x80
;
pixels
[
width
*
4
-
9
]
=
0xC0
;
pixels
[
rowstride
+
(
width
*
4
)
-
1
]
=
0x80
;
pixels
[(
2
*
rowstride
)
+
(
width
*
4
)
-
1
]
=
0xC0
;
/* Bottom left */
pixels
[(
height
-
1
)
*
rowstride
+
3
]
=
0
;
pixels
[(
height
-
1
)
*
rowstride
+
7
]
=
0x80
;
pixels
[(
height
-
1
)
*
rowstride
+
11
]
=
0xC0
;
pixels
[(
height
-
2
)
*
rowstride
+
3
]
=
0x80
;
pixels
[(
height
-
3
)
*
rowstride
+
3
]
=
0xC0
;
/* Bottom right */
pixels
[
height
*
rowstride
-
1
]
=
0
;
pixels
[(
height
-
1
)
*
rowstride
-
1
]
=
0x80
;
pixels
[(
height
-
2
)
*
rowstride
-
1
]
=
0xC0
;
pixels
[
height
*
rowstride
-
5
]
=
0x80
;
pixels
[
height
*
rowstride
-
9
]
=
0xC0
;
}
const
char
*
pidgin_get_dim_grey_string
(
GtkWidget
*
widget
)
{
static
char
dim_grey_string
[
8
]
=
""
;
GtkStyle
*
style
;
if
(
!
widget
)
return
"dim grey"
;
style
=
gtk_widget_get_style
(
widget
);
if
(
!
style
)
return
"dim grey"
;
snprintf
(
dim_grey_string
,
sizeof
(
dim_grey_string
),
"#%02x%02x%02x"
,
style
->
text_aa
[
GTK_STATE_NORMAL
].
red
>>
8
,
style
->
text_aa
[
GTK_STATE_NORMAL
].
green
>>
8
,
style
->
text_aa
[
GTK_STATE_NORMAL
].
blue
>>
8
);
return
dim_grey_string
;
}
#if !GTK_CHECK_VERSION(2,2,0)
GtkTreePath
*
gtk_tree_path_new_from_indices
(
gint
first_index
,
...)
{
int
arg
;
va_list
args
;
GtkTreePath
*
path
;
path
=
gtk_tree_path_new
();
va_start
(
args
,
first_index
);
arg
=
first_index
;
while
(
arg
!=
-1
)
{
gtk_tree_path_append_index
(
path
,
arg
);
arg
=
va_arg
(
args
,
gint
);
}
va_end
(
args
);
return
path
;
}
#endif
static
void
combo_box_changed_cb
(
GtkComboBox
*
combo_box
,
GtkEntry
*
entry
)
{
char
*
text
=
gtk_combo_box_get_active_text
(
combo_box
);
gtk_entry_set_text
(
entry
,
text
?
text
:
""
);
g_free
(
text
);
}
static
gboolean
entry_key_pressed_cb
(
GtkWidget
*
entry
,
GdkEventKey
*
key
,
GtkComboBox
*
combo
)
{
if
(
key
->
keyval
==
GDK_Down
||
key
->
keyval
==
GDK_Up
)
{
gtk_combo_box_popup
(
combo
);
return
TRUE
;
}
return
FALSE
;
}
GtkWidget
*
pidgin_text_combo_box_entry_new
(
const
char
*
default_item
,
GList
*
items
)
{
GtkComboBox
*
ret
=
NULL
;
GtkWidget
*
the_entry
=
NULL
;
ret
=
GTK_COMBO_BOX
(
gtk_combo_box_new_text
());
the_entry
=
gtk_entry_new
();
gtk_container_add
(
GTK_CONTAINER
(
ret
),
the_entry
);
if
(
default_item
)
gtk_entry_set_text
(
GTK_ENTRY
(
the_entry
),
default_item
);
for
(;
items
!=
NULL
;
items
=
items
->
next
)
{
char
*
text
=
items
->
data
;
if
(
text
&&
*
text
)
gtk_combo_box_append_text
(
ret
,
text
);
}
g_signal_connect
(
G_OBJECT
(
ret
),
"changed"
,
(
GCallback
)
combo_box_changed_cb
,
the_entry
);
g_signal_connect_after
(
G_OBJECT
(
the_entry
),
"key-press-event"
,
G_CALLBACK
(
entry_key_pressed_cb
),
ret
);
return
GTK_WIDGET
(
ret
);
}
const
char
*
pidgin_text_combo_box_entry_get_text
(
GtkWidget
*
widget
)
{
return
gtk_entry_get_text
(
GTK_ENTRY
(
GTK_BIN
((
widget
))
->
child
));
}
void
pidgin_text_combo_box_entry_set_text
(
GtkWidget
*
widget
,
const
char
*
text
)
{
gtk_entry_set_text
(
GTK_ENTRY
(
GTK_BIN
((
widget
))
->
child
),
(
text
));
}