pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
merge of '77693555855fe9cd3215414f79964dba346cc5fa'
gaim
2008-11-12, Richard Laager
1966704b3e42
merge of '77693555855fe9cd3215414f79964dba346cc5fa'
and '19a87e98e5857ad0289f2c760d460f7f1dbbb42d'
/**
* @file gtkutils.c GTK+ utility functions
* @ingroup gtkui
*
* gaim
*
* Gaim 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include
"internal.h"
#include
"gtkgaim.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
"gaimstock.h"
#include
"gtkthemes.h"
#include
"gtkutils.h"
static
guint
accels_save_timer
=
0
;
static
gboolean
url_clicked_idle_cb
(
gpointer
data
)
{
gaim_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
)
gaim_imgstore_get
,
(
GtkIMHtmlGetImageDataFunc
)
gaim_imgstore_get_data
,
(
GtkIMHtmlGetImageSizeFunc
)
gaim_imgstore_get_size
,
(
GtkIMHtmlGetImageFilenameFunc
)
gaim_imgstore_get_filename
,
gaim_imgstore_ref
,
gaim_imgstore_unref
,
};
void
gaim_setup_imhtml
(
GtkWidget
*
imhtml
)
{
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
);
gaim_gtkthemes_smiley_themeize
(
imhtml
);
gtk_imhtml_set_funcs
(
GTK_IMHTML
(
imhtml
),
&
gtkimhtml_cbs
);
}
GtkWidget
*
gaim_gtk_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
&&
gaim_prefs_get_bool
(
"/gaim/gtk/conversations/spellcheck"
))
gaim_gtk_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"
);
}
gaim_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
gaim_gtk_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
gaim_gtk_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
gaim_gtk_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
gaim_gtk_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
);
}
void
gaim_separator
(
GtkWidget
*
menu
)
{
GtkWidget
*
menuitem
;
menuitem
=
gtk_separator_menu_item_new
();
gtk_widget_show
(
menuitem
);
gtk_menu_shell_append
(
GTK_MENU_SHELL
(
menu
),
menuitem
);
}
GtkWidget
*
gaim_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);
*/
gaim_set_accessible_label
(
menuitem
,
label
);
return
menuitem
;
}
GtkWidget
*
gaim_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
*
gaim_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_MENU
);
gtk_box_pack_start
(
GTK_BOX
(
bbox
),
image
,
FALSE
,
FALSE
,
0
);
gtk_widget_show_all
(
bbox
);
return
button
;
}
GtkWidget
*
gaim_pixbuf_button_from_stock
(
const
char
*
text
,
const
char
*
icon
,
GaimButtonOrientation
style
)
{
GtkWidget
*
button
,
*
image
,
*
label
,
*
bbox
,
*
ibox
,
*
lbox
=
NULL
;
button
=
gtk_button_new
();
if
(
style
==
GAIM_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
);
gaim_set_accessible_label
(
button
,
label
);
}
gtk_widget_show_all
(
bbox
);
return
button
;
}
GtkWidget
*
gaim_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_MENU
);
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
*
gaim_gtk_make_frame
(
GtkWidget
*
parent
,
const
char
*
title
)
{
GtkWidget
*
vbox
,
*
label
,
*
hbox
;
char
*
labeltitle
;
vbox
=
gtk_vbox_new
(
FALSE
,
GAIM_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
);
gaim_set_accessible_label
(
vbox
,
label
);
hbox
=
gtk_hbox_new
(
FALSE
,
GAIM_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
,
GAIM_HIG_BOX_SPACE
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
vbox
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
vbox
);
return
vbox
;
}
static
void
protocol_menu_cb
(
GtkWidget
*
optmenu
,
GCallback
cb
)
{
GtkWidget
*
menu
;
GtkWidget
*
item
;
const
char
*
protocol
;
gpointer
user_data
;
menu
=
gtk_option_menu_get_menu
(
GTK_OPTION_MENU
(
optmenu
));
item
=
gtk_menu_get_active
(
GTK_MENU
(
menu
));
protocol
=
g_object_get_data
(
G_OBJECT
(
item
),
"protocol"
);
user_data
=
(
g_object_get_data
(
G_OBJECT
(
optmenu
),
"user_data"
));
if
(
cb
!=
NULL
)
((
void
(
*
)(
GtkWidget
*
,
const
char
*
,
gpointer
))
cb
)(
item
,
protocol
,
user_data
);
}
GtkWidget
*
gaim_gtk_protocol_option_menu_new
(
const
char
*
id
,
GCallback
cb
,
gpointer
user_data
)
{
GaimPluginProtocolInfo
*
prpl_info
;
GaimPlugin
*
plugin
;
GtkWidget
*
hbox
;
GtkWidget
*
label
;
GtkWidget
*
optmenu
;
GtkWidget
*
menu
;
GtkWidget
*
item
;
GtkWidget
*
image
;
GdkPixbuf
*
pixbuf
;
GdkPixbuf
*
scale
;
GList
*
p
;
GtkSizeGroup
*
sg
;
char
*
filename
;
const
char
*
proto_name
;
char
buf
[
256
];
int
i
,
selected_index
=
-1
;
optmenu
=
gtk_option_menu_new
();
gtk_widget_show
(
optmenu
);
g_object_set_data
(
G_OBJECT
(
optmenu
),
"user_data"
,
user_data
);
menu
=
gtk_menu_new
();
gtk_widget_show
(
menu
);
sg
=
gtk_size_group_new
(
GTK_SIZE_GROUP_HORIZONTAL
);
for
(
p
=
gaim_plugins_get_protocols
(),
i
=
0
;
p
!=
NULL
;
p
=
p
->
next
,
i
++
)
{
plugin
=
(
GaimPlugin
*
)
p
->
data
;
prpl_info
=
GAIM_PLUGIN_PROTOCOL_INFO
(
plugin
);
/* Create the item. */
item
=
gtk_menu_item_new
();
/* Create the hbox. */
hbox
=
gtk_hbox_new
(
FALSE
,
4
);
gtk_container_add
(
GTK_CONTAINER
(
item
),
hbox
);
gtk_widget_show
(
hbox
);
/* Load the image. */
proto_name
=
prpl_info
->
list_icon
(
NULL
,
NULL
);
g_snprintf
(
buf
,
sizeof
(
buf
),
"%s.png"
,
proto_name
);
filename
=
g_build_filename
(
DATADIR
,
"pixmaps"
,
"gaim"
,
"status"
,
"default"
,
buf
,
NULL
);
pixbuf
=
gdk_pixbuf_new_from_file
(
filename
,
NULL
);
g_free
(
filename
);
if
(
pixbuf
!=
NULL
)
{
/* Scale and insert the image */
scale
=
gdk_pixbuf_scale_simple
(
pixbuf
,
16
,
16
,
GDK_INTERP_BILINEAR
);
image
=
gtk_image_new_from_pixbuf
(
scale
);
g_object_unref
(
G_OBJECT
(
pixbuf
));
g_object_unref
(
G_OBJECT
(
scale
));
}
else
image
=
gtk_image_new
();
gtk_size_group_add_widget
(
sg
,
image
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
image
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
image
);
/* Create the label. */
label
=
gtk_label_new
(
plugin
->
info
->
name
);
gtk_label_set_justify
(
GTK_LABEL
(
label
),
GTK_JUSTIFY_LEFT
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0.5
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
label
,
TRUE
,
TRUE
,
0
);
gtk_widget_show
(
label
);
g_object_set_data
(
G_OBJECT
(
item
),
"protocol"
,
plugin
->
info
->
id
);
gtk_menu_shell_append
(
GTK_MENU_SHELL
(
menu
),
item
);
gtk_widget_show
(
item
);
gaim_set_accessible_label
(
item
,
label
);
if
(
id
!=
NULL
&&
!
strcmp
(
plugin
->
info
->
id
,
id
))
selected_index
=
i
;
}
gtk_option_menu_set_menu
(
GTK_OPTION_MENU
(
optmenu
),
menu
);
if
(
selected_index
!=
-1
)
gtk_option_menu_set_history
(
GTK_OPTION_MENU
(
optmenu
),
selected_index
);
g_signal_connect
(
G_OBJECT
(
optmenu
),
"changed"
,
G_CALLBACK
(
protocol_menu_cb
),
cb
);
g_object_unref
(
sg
);
return
optmenu
;
}
GaimAccount
*
gaim_gtk_account_option_menu_get_selected
(
GtkWidget
*
optmenu
)
{
GtkWidget
*
menu
=
gtk_option_menu_get_menu
(
GTK_OPTION_MENU
(
optmenu
));
GtkWidget
*
item
=
gtk_menu_get_active
(
GTK_MENU
(
menu
));
return
g_object_get_data
(
G_OBJECT
(
item
),
"account"
);
}
static
void
account_menu_cb
(
GtkWidget
*
optmenu
,
GCallback
cb
)
{
GtkWidget
*
menu
;
GtkWidget
*
item
;
GaimAccount
*
account
;
gpointer
user_data
;
menu
=
gtk_option_menu_get_menu
(
GTK_OPTION_MENU
(
optmenu
));
item
=
gtk_menu_get_active
(
GTK_MENU
(
menu
));
account
=
g_object_get_data
(
G_OBJECT
(
item
),
"account"
);
user_data
=
g_object_get_data
(
G_OBJECT
(
optmenu
),
"user_data"
);
if
(
cb
!=
NULL
)
((
void
(
*
)(
GtkWidget
*
,
GaimAccount
*
,
gpointer
))
cb
)(
item
,
account
,
user_data
);
}
static
void
create_account_menu
(
GtkWidget
*
optmenu
,
GaimAccount
*
default_account
,
GaimFilterAccountFunc
filter_func
,
gboolean
show_all
)
{
GaimAccount
*
account
;
GtkWidget
*
menu
;
GtkWidget
*
item
;
GtkWidget
*
image
;
GtkWidget
*
hbox
;
GtkWidget
*
label
;
GdkPixbuf
*
pixbuf
;
GdkPixbuf
*
scale
;
GList
*
list
;
GList
*
p
;
GtkSizeGroup
*
sg
;
char
*
filename
;
const
char
*
proto_name
;
char
buf
[
256
];
int
i
,
selected_index
=
-1
;
if
(
show_all
)
list
=
gaim_accounts_get_all
();
else
list
=
gaim_connections_get_all
();
menu
=
gtk_menu_new
();
gtk_widget_show
(
menu
);
sg
=
gtk_size_group_new
(
GTK_SIZE_GROUP_HORIZONTAL
);
for
(
p
=
list
,
i
=
0
;
p
!=
NULL
;
p
=
p
->
next
,
i
++
)
{
GaimPluginProtocolInfo
*
prpl_info
=
NULL
;
GaimPlugin
*
plugin
;
if
(
show_all
)
account
=
(
GaimAccount
*
)
p
->
data
;
else
{
GaimConnection
*
gc
=
(
GaimConnection
*
)
p
->
data
;
account
=
gaim_connection_get_account
(
gc
);
}
if
(
filter_func
&&
!
filter_func
(
account
))
{
i
--
;
continue
;
}
plugin
=
gaim_find_prpl
(
gaim_account_get_protocol_id
(
account
));
if
(
plugin
!=
NULL
)
prpl_info
=
GAIM_PLUGIN_PROTOCOL_INFO
(
plugin
);
/* Create the item. */
item
=
gtk_menu_item_new
();
/* Create the hbox. */
hbox
=
gtk_hbox_new
(
FALSE
,
4
);
gtk_container_add
(
GTK_CONTAINER
(
item
),
hbox
);
gtk_widget_show
(
hbox
);
/* Load the image. */
if
(
prpl_info
!=
NULL
)
{
proto_name
=
prpl_info
->
list_icon
(
account
,
NULL
);
g_snprintf
(
buf
,
sizeof
(
buf
),
"%s.png"
,
proto_name
);
filename
=
g_build_filename
(
DATADIR
,
"pixmaps"
,
"gaim"
,
"status"
,
"default"
,
buf
,
NULL
);
pixbuf
=
gdk_pixbuf_new_from_file
(
filename
,
NULL
);
g_free
(
filename
);
if
(
pixbuf
!=
NULL
)
{
/* Scale and insert the image */
scale
=
gdk_pixbuf_scale_simple
(
pixbuf
,
16
,
16
,
GDK_INTERP_BILINEAR
);
if
(
gaim_account_is_disconnected
(
account
)
&&
show_all
&&
gaim_connections_get_all
())
gdk_pixbuf_saturate_and_pixelate
(
scale
,
scale
,
0.0
,
FALSE
);
image
=
gtk_image_new_from_pixbuf
(
scale
);
g_object_unref
(
G_OBJECT
(
pixbuf
));
g_object_unref
(
G_OBJECT
(
scale
));
}
else
image
=
gtk_image_new
();
}
else
image
=
gtk_image_new
();
gtk_size_group_add_widget
(
sg
,
image
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
image
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
image
);
if
(
gaim_account_get_alias
(
account
))
{
g_snprintf
(
buf
,
sizeof
(
buf
),
"%s (%s) (%s)"
,
gaim_account_get_username
(
account
),
gaim_account_get_alias
(
account
),
gaim_account_get_protocol_name
(
account
));
}
else
{
g_snprintf
(
buf
,
sizeof
(
buf
),
"%s (%s)"
,
gaim_account_get_username
(
account
),
gaim_account_get_protocol_name
(
account
));
}
/* Create the label. */
label
=
gtk_label_new
(
buf
);
gtk_label_set_justify
(
GTK_LABEL
(
label
),
GTK_JUSTIFY_LEFT
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0.5
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
label
,
TRUE
,
TRUE
,
0
);
gtk_widget_show
(
label
);
g_object_set_data
(
G_OBJECT
(
item
),
"account"
,
account
);
gtk_menu_shell_append
(
GTK_MENU_SHELL
(
menu
),
item
);
gtk_widget_show
(
item
);
gaim_set_accessible_label
(
item
,
label
);
if
(
default_account
!=
NULL
&&
account
==
default_account
)
selected_index
=
i
;
}
g_object_unref
(
sg
);
gtk_option_menu_set_menu
(
GTK_OPTION_MENU
(
optmenu
),
menu
);
/* Set the place we should be at. */
if
(
selected_index
!=
-1
)
gtk_option_menu_set_history
(
GTK_OPTION_MENU
(
optmenu
),
selected_index
);
}
static
void
regenerate_account_menu
(
GtkWidget
*
optmenu
)
{
GtkWidget
*
menu
;
GtkWidget
*
item
;
gboolean
show_all
;
GaimAccount
*
account
;
GaimFilterAccountFunc
filter_func
;
menu
=
gtk_option_menu_get_menu
(
GTK_OPTION_MENU
(
optmenu
));
item
=
gtk_menu_get_active
(
GTK_MENU
(
menu
));
account
=
g_object_get_data
(
G_OBJECT
(
item
),
"account"
);
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"
);
gtk_option_menu_remove_menu
(
GTK_OPTION_MENU
(
optmenu
));
create_account_menu
(
optmenu
,
account
,
filter_func
,
show_all
);
}
static
void
account_menu_sign_on_off_cb
(
GaimConnection
*
gc
,
GtkWidget
*
optmenu
)
{
regenerate_account_menu
(
optmenu
);
}
static
void
account_menu_added_removed_cb
(
GaimAccount
*
account
,
GtkWidget
*
optmenu
)
{
regenerate_account_menu
(
optmenu
);
}
static
gboolean
account_menu_destroyed_cb
(
GtkWidget
*
optmenu
,
GdkEvent
*
event
,
void
*
user_data
)
{
gaim_signals_disconnect_by_handle
(
optmenu
);
return
FALSE
;
}
void
gaim_gtk_account_option_menu_set_selected
(
GtkWidget
*
optmenu
,
GaimAccount
*
account
)
{
GtkWidget
*
menu
;
GtkWidget
*
item
;
gboolean
show_all
;
GaimAccount
*
curaccount
;
GaimFilterAccountFunc
filter_func
;
menu
=
gtk_option_menu_get_menu
(
GTK_OPTION_MENU
(
optmenu
));
item
=
gtk_menu_get_active
(
GTK_MENU
(
menu
));
curaccount
=
g_object_get_data
(
G_OBJECT
(
item
),
"account"
);
if
(
account
==
curaccount
)
return
;
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"
);
gtk_option_menu_remove_menu
(
GTK_OPTION_MENU
(
optmenu
));
create_account_menu
(
optmenu
,
account
,
filter_func
,
show_all
);
}
GtkWidget
*
gaim_gtk_account_option_menu_new
(
GaimAccount
*
default_account
,
gboolean
show_all
,
GCallback
cb
,
GaimFilterAccountFunc
filter_func
,
gpointer
user_data
)
{
GtkWidget
*
optmenu
;
/* Create the option menu */
optmenu
=
gtk_option_menu_new
();
gtk_widget_show
(
optmenu
);
g_signal_connect
(
G_OBJECT
(
optmenu
),
"destroy"
,
G_CALLBACK
(
account_menu_destroyed_cb
),
NULL
);
/* Register the gaim sign on/off event callbacks. */
gaim_signal_connect
(
gaim_connections_get_handle
(),
"signed-on"
,
optmenu
,
GAIM_CALLBACK
(
account_menu_sign_on_off_cb
),
optmenu
);
gaim_signal_connect
(
gaim_connections_get_handle
(),
"signed-off"
,
optmenu
,
GAIM_CALLBACK
(
account_menu_sign_on_off_cb
),
optmenu
);
gaim_signal_connect
(
gaim_accounts_get_handle
(),
"account-added"
,
optmenu
,
GAIM_CALLBACK
(
account_menu_added_removed_cb
),
optmenu
);
gaim_signal_connect
(
gaim_accounts_get_handle
(),
"account-removed"
,
optmenu
,
GAIM_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
);
/* Create and set the actual menu. */
create_account_menu
(
optmenu
,
default_account
,
filter_func
,
show_all
);
/* And now the last callback. */
g_signal_connect
(
G_OBJECT
(
optmenu
),
"changed"
,
G_CALLBACK
(
account_menu_cb
),
cb
);
return
optmenu
;
}
gboolean
gaim_gtk_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
gaim_gtk_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
)
{
gaim_debug_warning
(
"gtkspell"
,
"Failed to setup GtkSpell: %s
\n
"
,
error
->
message
);
g_error_free
(
error
);
}
#endif
/* USE_GTKSPELL */
}
void
gaim_gtk_save_accels_cb
(
GtkAccelGroup
*
accel_group
,
guint
arg1
,
GdkModifierType
arg2
,
GClosure
*
arg3
,
gpointer
data
)
{
gaim_debug
(
GAIM_DEBUG_MISC
,
"accels"
,
"accel changed, scheduling save.
\n
"
);
if
(
!
accels_save_timer
)
accels_save_timer
=
g_timeout_add
(
5000
,
gaim_gtk_save_accels
,
NULL
);
}
gboolean
gaim_gtk_save_accels
(
gpointer
data
)
{
char
*
filename
=
NULL
;
filename
=
g_build_filename
(
gaim_user_dir
(),
G_DIR_SEPARATOR_S
,
"accels"
,
NULL
);
gaim_debug
(
GAIM_DEBUG_MISC
,
"accels"
,
"saving accels to %s
\n
"
,
filename
);
gtk_accel_map_save
(
filename
);
g_free
(
filename
);
accels_save_timer
=
0
;
return
FALSE
;
}
void
gaim_gtk_load_accels
()
{
char
*
filename
=
NULL
;
filename
=
g_build_filename
(
gaim_user_dir
(),
G_DIR_SEPARATOR_S
,
"accels"
,
NULL
);
gtk_accel_map_load
(
filename
);
g_free
(
filename
);
}
gboolean
gaim_gtk_parse_x_im_contact
(
const
char
*
msg
,
gboolean
all_accounts
,
GaimAccount
**
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
;
GaimAccount
*
account
=
NULL
;
GList
*
l
;
const
char
*
protoname
;
if
(
all_accounts
)
list
=
gaim_accounts_get_all
();
else
list
=
gaim_connections_get_all
();
for
(
l
=
list
;
l
!=
NULL
;
l
=
l
->
next
)
{
GaimConnection
*
gc
;
GaimPluginProtocolInfo
*
prpl_info
=
NULL
;
GaimPlugin
*
plugin
;
if
(
all_accounts
)
{
account
=
(
GaimAccount
*
)
l
->
data
;
plugin
=
gaim_plugins_find_with_id
(
gaim_account_get_protocol_id
(
account
));
if
(
plugin
==
NULL
)
{
account
=
NULL
;
continue
;
}
prpl_info
=
GAIM_PLUGIN_PROTOCOL_INFO
(
plugin
);
}
else
{
gc
=
(
GaimConnection
*
)
l
->
data
;
account
=
gaim_connection_get_account
(
gc
);
prpl_info
=
GAIM_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
)
{
GaimConnection
*
gc
;
GaimPluginProtocolInfo
*
prpl_info
=
NULL
;
GaimPlugin
*
plugin
;
if
(
all_accounts
)
{
account
=
(
GaimAccount
*
)
l
->
data
;
plugin
=
gaim_plugins_find_with_id
(
gaim_account_get_protocol_id
(
account
));
if
(
plugin
==
NULL
)
{
account
=
NULL
;
continue
;
}
prpl_info
=
GAIM_PLUGIN_PROTOCOL_INFO
(
plugin
);
}
else
{
gc
=
(
GaimConnection
*
)
l
->
data
;
account
=
gaim_connection_get_account
(
gc
);
prpl_info
=
GAIM_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
gaim_set_accessible_label
(
GtkWidget
*
w
,
GtkWidget
*
l
)
{
AtkObject
*
acc
,
*
label
;
AtkObject
*
rel_obj
[
1
];
AtkRelationSet
*
set
;
AtkRelation
*
relation
;
const
gchar
*
label_text
;
const
gchar
*
existing_name
;
acc
=
gtk_widget_get_accessible
(
w
);
label
=
gtk_widget_get_accessible
(
l
);
/* 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
);
}
/* 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
);
}
#if GTK_CHECK_VERSION(2,2,0)
static
void
gaim_gtk_menu_position_func
(
GtkMenu
*
menu
,
gint
*
x
,
gint
*
y
,
gboolean
*
push_in
,
gpointer
data
)
{
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
gaim_gtk_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
;
#if GTK_CHECK_VERSION(2,2,0)
gaim_gtk_menu_position_func
(
menu
,
x
,
y
,
push_in
,
data
);
#endif
}
enum
{
DND_FILE_TRANSFER
,
DND_IM_IMAGE
,
DND_BUDDY_ICON
};
typedef
struct
{
char
*
filename
;
GaimAccount
*
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
;
GaimConversation
*
conv
;
GaimGtkConversation
*
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
));
gaim_notify_error
(
NULL
,
NULL
,
_
(
"Failed to load image"
),
str
);
g_free
(
str
);
return
;
}
gaim_gtk_set_custom_buddy_icon
(
data
->
account
,
data
->
who
,
data
->
filename
);
break
;
case
DND_FILE_TRANSFER
:
serv_send_file
(
gaim_account_get_connection
(
data
->
account
),
data
->
who
,
data
->
filename
);
break
;
case
DND_IM_IMAGE
:
conv
=
gaim_conversation_new
(
GAIM_CONV_TYPE_IM
,
data
->
account
,
data
->
who
);
gtkconv
=
GAIM_GTK_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
);
gaim_notify_error
(
NULL
,
NULL
,
_
(
"Failed to load image"
),
str
);
g_error_free
(
err
);
g_free
(
str
);
return
;
}
id
=
gaim_imgstore_add
(
filedata
,
size
,
data
->
filename
);
g_free
(
filedata
);
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
);
gaim_imgstore_unref
(
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
gaim_dnd_file_manage
(
GtkSelectionData
*
sd
,
GaimAccount
*
account
,
const
char
*
who
)
{
GList
*
tmp
;
GdkPixbuf
*
pb
;
GList
*
files
=
gaim_uri_list_extract_filenames
((
const
gchar
*
)
sd
->
data
);
GaimConnection
*
gc
=
gaim_account_get_connection
(
account
);
GaimPluginProtocolInfo
*
prpl_info
=
NULL
;
gboolean
file_send_ok
=
FALSE
;
#ifndef _WIN32
GaimDesktopItem
*
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
;
str
=
g_strdup_printf
(
_
(
"Cannot send folder %s."
),
basename
);
gaim_notify_error
(
NULL
,
NULL
,
str
,
_
(
"Gaim cannot transfer a folder. You will need to send the files within individually"
));
g_free
(
str
);
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
=
GAIM_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
)
gaim_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
,
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
))
gaim_request_yes_no
(
NULL
,
NULL
,
_
(
"You have dragged an image"
),
_
(
"Would you like to set it as the buddy icon for this user?"
),
0
,
data
,
(
GCallback
)
dnd_set_icon_ok_cb
,
(
GCallback
)
dnd_set_icon_cancel_cb
);
else
gaim_request_choice
(
NULL
,
NULL
,
_
(
"You have dragged an image"
),
ft
?
_
(
"You can send this image as a file transfer or "
"embed it into this message, 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
,
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
(
gaim_str_has_suffix
(
basename
,
".desktop"
)
&&
(
item
=
gaim_desktop_item_new_from_file
(
filename
)))
{
GaimDesktopItemType
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
=
gaim_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
=
gaim_desktop_item_get_string
(
item
,
key
);
#endif
if
(
!
itemname
)
itemname
=
gaim_desktop_item_get_string
(
item
,
"Name"
);
dtype
=
gaim_desktop_item_get_entry_type
(
item
);
switch
(
dtype
)
{
GaimConversation
*
conv
;
GaimGtkConversation
*
gtkconv
;
case
GAIM_DESKTOP_ITEM_TYPE_LINK
:
conv
=
gaim_conversation_new
(
GAIM_CONV_TYPE_IM
,
account
,
who
);
gtkconv
=
GAIM_GTK_CONVERSATION
(
conv
);
gtk_imhtml_insert_link
(
GTK_IMHTML
(
gtkconv
->
entry
),
gtk_text_buffer_get_insert
(
GTK_IMHTML
(
gtkconv
->
entry
)
->
text_buffer
),
gaim_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 */
gaim_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
;
}
gaim_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
gaim_gtk_buddy_icon_get_scale_size
(
GdkPixbuf
*
buf
,
GaimBuddyIconSpec
*
spec
,
GaimIconScaleRules
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
;
gaim_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
*
gaim_gtk_create_prpl_icon
(
GaimAccount
*
account
,
double
scale_factor
)
{
GaimPlugin
*
prpl
;
GaimPluginProtocolInfo
*
prpl_info
;
const
char
*
protoname
=
NULL
;
char
buf
[
256
];
/* TODO: We should use a define for max file length */
char
*
filename
=
NULL
;
GdkPixbuf
*
pixbuf
,
*
scaled
;
g_return_val_if_fail
(
account
!=
NULL
,
NULL
);
prpl
=
gaim_find_prpl
(
gaim_account_get_protocol_id
(
account
));
if
(
prpl
==
NULL
)
return
NULL
;
prpl_info
=
GAIM_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
*/
g_snprintf
(
buf
,
sizeof
(
buf
),
"%s.png"
,
protoname
);
filename
=
g_build_filename
(
DATADIR
,
"pixmaps"
,
"gaim"
,
"status"
,
"default"
,
buf
,
NULL
);
pixbuf
=
gdk_pixbuf_new_from_file
(
filename
,
NULL
);
g_free
(
filename
);
scaled
=
gdk_pixbuf_scale_simple
(
pixbuf
,
32
*
scale_factor
,
32
*
scale_factor
,
GDK_INTERP_BILINEAR
);
g_object_unref
(
pixbuf
);
return
scaled
;
}
static
GdkPixbuf
*
overlay_status_onto_icon
(
GdkPixbuf
*
pixbuf
,
GaimStatusPrimitive
primitive
)
{
const
char
*
type_name
;
char
basename
[
256
];
char
*
filename
;
GdkPixbuf
*
emblem
;
type_name
=
gaim_primitive_get_id_from_type
(
primitive
);
g_snprintf
(
basename
,
sizeof
(
basename
),
"%s.png"
,
type_name
);
filename
=
g_build_filename
(
DATADIR
,
"pixmaps"
,
"gaim"
,
"status"
,
"default"
,
basename
,
NULL
);
emblem
=
gdk_pixbuf_new_from_file
(
filename
,
NULL
);
g_free
(
filename
);
if
(
emblem
!=
NULL
)
{
int
width
,
height
,
emblem_width
,
emblem_height
;
int
new_emblem_width
,
new_emblem_height
;
width
=
gdk_pixbuf_get_width
(
pixbuf
);
height
=
gdk_pixbuf_get_height
(
pixbuf
);
emblem_width
=
gdk_pixbuf_get_width
(
emblem
);
emblem_height
=
gdk_pixbuf_get_height
(
emblem
);
/*
* Figure out how big to make the emblem. Normally the emblem
* will have half the width of the pixbuf. But we don't make
* an emblem any smaller than 10 pixels because it becomes
* unrecognizable, unless the width of the pixbuf is less than
* 10 pixels, in which case we make the emblem width the same
* as the pixbuf width.
*/
new_emblem_width
=
MAX
(
width
/
2
,
MIN
(
width
,
10
));
new_emblem_height
=
MAX
(
height
/
2
,
MIN
(
height
,
10
));
/* Overlay emblem onto the bottom right corner of pixbuf */
gdk_pixbuf_composite
(
emblem
,
pixbuf
,
width
-
new_emblem_width
,
height
-
new_emblem_height
,
new_emblem_width
,
new_emblem_height
,
width
-
new_emblem_width
,
height
-
new_emblem_height
,
(
double
)
new_emblem_width
/
(
double
)
emblem_width
,
(
double
)
new_emblem_height
/
(
double
)
emblem_height
,
GDK_INTERP_BILINEAR
,
255
);
g_object_unref
(
emblem
);
}
return
pixbuf
;
}
GdkPixbuf
*
gaim_gtk_create_prpl_icon_with_status
(
GaimAccount
*
account
,
GaimStatusType
*
status_type
,
double
scale_factor
)
{
GdkPixbuf
*
pixbuf
;
pixbuf
=
gaim_gtk_create_prpl_icon
(
account
,
scale_factor
);
if
(
pixbuf
==
NULL
)
return
NULL
;
/*
* TODO: Let the prpl pick the emblem on a per status basis,
* and only use the primitive as a fallback?
*/
return
overlay_status_onto_icon
(
pixbuf
,
gaim_status_type_get_primitive
(
status_type
));
}
GdkPixbuf
*
gaim_gtk_create_gaim_icon_with_status
(
GaimStatusPrimitive
primitive
,
double
scale_factor
)
{
gchar
*
filename
;
GdkPixbuf
*
orig
,
*
pixbuf
;
filename
=
g_build_filename
(
DATADIR
,
"pixmaps"
,
"gaim.png"
,
NULL
);
orig
=
gdk_pixbuf_new_from_file
(
filename
,
NULL
);
g_free
(
filename
);
if
(
orig
==
NULL
)
return
NULL
;
pixbuf
=
gdk_pixbuf_scale_simple
(
orig
,
32
*
scale_factor
,
32
*
scale_factor
,
GDK_INTERP_BILINEAR
);
g_object_unref
(
G_OBJECT
(
orig
));
return
overlay_status_onto_icon
(
pixbuf
,
primitive
);
}
static
void
menu_action_cb
(
GtkMenuItem
*
item
,
gpointer
object
)
{
gpointer
data
;
void
(
*
callback
)(
gpointer
,
gpointer
);
callback
=
g_object_get_data
(
G_OBJECT
(
item
),
"gaimcallback"
);
data
=
g_object_get_data
(
G_OBJECT
(
item
),
"gaimcallbackdata"
);
if
(
callback
)
callback
(
object
,
data
);
}
void
gaim_gtk_append_menu_action
(
GtkWidget
*
menu
,
GaimMenuAction
*
act
,
gpointer
object
)
{
if
(
act
==
NULL
)
{
gaim_separator
(
menu
);
}
else
{
GtkWidget
*
menuitem
;
if
(
act
->
children
==
NULL
)
{
menuitem
=
gtk_menu_item_new_with_mnemonic
(
act
->
label
);
if
(
act
->
callback
!=
NULL
)
{
g_object_set_data
(
G_OBJECT
(
menuitem
),
"gaimcallback"
,
act
->
callback
);
g_object_set_data
(
G_OBJECT
(
menuitem
),
"gaimcallbackdata"
,
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
)
{
GaimMenuAction
*
act
=
(
GaimMenuAction
*
)
l
->
data
;
gaim_gtk_append_menu_action
(
submenu
,
act
,
object
);
}
g_list_free
(
act
->
children
);
act
->
children
=
NULL
;
}
gaim_menu_action_free
(
act
);
}
}
#if GTK_CHECK_VERSION(2,3,0)
# define NEW_STYLE_COMPLETION
#endif
#ifndef NEW_STYLE_COMPLETION
typedef
struct
{
GCompletion
*
completion
;
gboolean
completion_started
;
gboolean
all
;
}
GaimGtkCompletionData
;
#endif
#ifndef NEW_STYLE_COMPLETION
static
gboolean
completion_entry_event
(
GtkEditable
*
entry
,
GdkEventKey
*
event
,
GaimGtkCompletionData
*
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
,
GaimGtkCompletionData
*
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
&&
gaim_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
&&
gaim_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
,
gpointer
*
user_data
)
{
GValue
val
;
GtkWidget
*
optmenu
=
user_data
[
1
];
GaimAccount
*
account
;
val
.
g_type
=
0
;
gtk_tree_model_get_value
(
model
,
iter
,
1
,
&
val
);
gtk_entry_set_text
(
GTK_ENTRY
(
user_data
[
0
]),
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
)
{
GList
*
items
;
guint
index
=
0
;
gaim_gtk_account_option_menu_set_selected
(
optmenu
,
account
);
items
=
GTK_MENU_SHELL
(
gtk_option_menu_get_menu
(
GTK_OPTION_MENU
(
optmenu
)))
->
children
;
do
{
if
(
account
==
g_object_get_data
(
G_OBJECT
(
items
->
data
),
"account"
))
{
/* Set the account in the GUI. */
gtk_option_menu_set_history
(
GTK_OPTION_MENU
(
optmenu
),
index
);
return
TRUE
;
}
index
++
;
}
while
((
items
=
items
->
next
)
!=
NULL
);
}
return
TRUE
;
}
static
void
add_screenname_autocomplete_entry
(
GtkListStore
*
store
,
const
char
*
buddy_alias
,
const
char
*
contact_alias
,
const
GaimAccount
*
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
(
GaimLogSet
*
set
,
gpointer
value
,
gpointer
**
set_hash_data
)
{
/* 1. Don't show buddies because we will have gotten them already.
* 2. Only show those with non-NULL accounts that are currently connected.
* 3. The boxes that use this autocomplete code handle only IMs. */
if
(
!
set
->
buddy
&&
(
GPOINTER_TO_INT
(
set_hash_data
[
1
])
||
(
set
->
account
!=
NULL
&&
gaim_account_is_connected
(
set
->
account
)))
&&
set
->
type
==
GAIM_LOG_IM
)
{
#ifdef NEW_STYLE_COMPLETION
add_screenname_autocomplete_entry
((
GtkListStore
*
)
set_hash_data
[
0
],
NULL
,
NULL
,
set
->
account
,
set
->
name
);
#else
GList
**
items
=
((
GList
**
)
set_hash_data
[
0
]);
/* Steal the name for the GCompletion. */
*
items
=
g_list_append
(
*
items
,
set
->
name
);
set
->
name
=
set
->
normalized_name
=
NULL
;
#endif
/* NEW_STYLE_COMPLETION */
}
}
#ifdef NEW_STYLE_COMPLETION
static
void
add_completion_list
(
GtkListStore
*
store
)
{
GaimBlistNode
*
gnode
,
*
cnode
,
*
bnode
;
gboolean
all
=
GPOINTER_TO_INT
(
g_object_get_data
(
G_OBJECT
(
store
),
"screenname-all"
));
GHashTable
*
sets
;
gpointer
set_hash_data
[]
=
{
store
,
GINT_TO_POINTER
(
all
)};
gtk_list_store_clear
(
store
);
for
(
gnode
=
gaim_get_blist
()
->
root
;
gnode
!=
NULL
;
gnode
=
gnode
->
next
)
{
if
(
!
GAIM_BLIST_NODE_IS_GROUP
(
gnode
))
continue
;
for
(
cnode
=
gnode
->
child
;
cnode
!=
NULL
;
cnode
=
cnode
->
next
)
{
if
(
!
GAIM_BLIST_NODE_IS_CONTACT
(
cnode
))
continue
;
for
(
bnode
=
cnode
->
child
;
bnode
!=
NULL
;
bnode
=
bnode
->
next
)
{
GaimBuddy
*
buddy
=
(
GaimBuddy
*
)
bnode
;
if
(
!
all
&&
!
gaim_account_is_connected
(
buddy
->
account
))
continue
;
add_screenname_autocomplete_entry
(
store
,
((
GaimContact
*
)
cnode
)
->
alias
,
gaim_buddy_get_contact_alias
(
buddy
),
buddy
->
account
,
buddy
->
name
);
}
}
}
sets
=
gaim_log_get_log_sets
();
g_hash_table_foreach
(
sets
,
(
GHFunc
)
get_log_set_name
,
&
set_hash_data
);
g_hash_table_destroy
(
sets
);
}
#else
static
void
add_completion_list
(
GaimGtkCompletionData
*
data
)
{
GaimBlistNode
*
gnode
,
*
cnode
,
*
bnode
;
GCompletion
*
completion
;
GList
*
item
=
g_list_append
(
NULL
,
NULL
);
GHashTable
*
sets
;
gpointer
set_hash_data
[
2
];
completion
=
data
->
completion
;
g_list_foreach
(
completion
->
items
,
(
GFunc
)
g_free
,
NULL
);
g_completion_clear_items
(
completion
);
for
(
gnode
=
gaim_get_blist
()
->
root
;
gnode
!=
NULL
;
gnode
=
gnode
->
next
)
{
if
(
!
GAIM_BLIST_NODE_IS_GROUP
(
gnode
))
continue
;
for
(
cnode
=
gnode
->
child
;
cnode
!=
NULL
;
cnode
=
cnode
->
next
)
{
if
(
!
GAIM_BLIST_NODE_IS_CONTACT
(
cnode
))
continue
;
for
(
bnode
=
cnode
->
child
;
bnode
!=
NULL
;
bnode
=
bnode
->
next
)
{
GaimBuddy
*
buddy
=
(
GaimBuddy
*
)
bnode
;
if
(
!
data
->
all
&&
!
gaim_account_is_connected
(
buddy
->
account
))
continue
;
item
->
data
=
g_strdup
(
buddy
->
name
);
g_completion_add_items
(
data
->
completion
,
item
);
}
}
}
g_list_free
(
item
);
sets
=
gaim_log_get_log_sets
();
item
=
NULL
;
set_hash_data
[
0
]
=
&
item
;
set_hash_data
[
1
]
=
GINT_TO_POINTER
(
data
->
all
);
g_hash_table_foreach
(
sets
,
(
GHFunc
)
get_log_set_name
,
&
set_hash_data
);
g_hash_table_destroy
(
sets
);
g_completion_add_items
(
data
->
completion
,
item
);
g_list_free
(
item
);
}
#endif
static
void
screenname_autocomplete_destroyed_cb
(
GtkWidget
*
widget
,
gpointer
data
)
{
gaim_signals_disconnect_by_handle
(
widget
);
}
static
void
repopulate_autocomplete
(
gpointer
something
,
gpointer
data
)
{
add_completion_list
(
data
);
}
void
gaim_gtk_setup_screenname_autocomplete
(
GtkWidget
*
entry
,
GtkWidget
*
accountopt
,
gboolean
all
)
{
gpointer
cb_data
=
NULL
;
#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
=
gtk_list_store_new
(
5
,
G_TYPE_STRING
,
G_TYPE_STRING
,
G_TYPE_STRING
,
G_TYPE_STRING
,
G_TYPE_POINTER
);
GtkEntryCompletion
*
completion
;
gpointer
*
data
;
g_object_set_data
(
G_OBJECT
(
store
),
"screenname-all"
,
GINT_TO_POINTER
(
all
));
add_completion_list
(
store
);
cb_data
=
store
;
/* 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
);
data
=
g_new0
(
gpointer
,
2
);
data
[
0
]
=
entry
;
data
[
1
]
=
accountopt
;
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 */
GaimGtkCompletionData
*
data
;
data
=
g_new0
(
GaimGtkCompletionData
,
1
);
data
->
completion
=
g_completion_new
(
NULL
);
data
->
all
=
all
;
g_completion_set_compare
(
data
->
completion
,
g_ascii_strncasecmp
);
add_completion_list
(
data
);
cb_data
=
data
;
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 */
if
(
!
all
)
{
gaim_signal_connect
(
gaim_connections_get_handle
(),
"signed-on"
,
entry
,
GAIM_CALLBACK
(
repopulate_autocomplete
),
cb_data
);
gaim_signal_connect
(
gaim_connections_get_handle
(),
"signed-off"
,
entry
,
GAIM_CALLBACK
(
repopulate_autocomplete
),
cb_data
);
}
gaim_signal_connect
(
gaim_accounts_get_handle
(),
"account-added"
,
entry
,
GAIM_CALLBACK
(
repopulate_autocomplete
),
cb_data
);
gaim_signal_connect
(
gaim_accounts_get_handle
(),
"account-removed"
,
entry
,
GAIM_CALLBACK
(
repopulate_autocomplete
),
cb_data
);
g_signal_connect
(
G_OBJECT
(
entry
),
"destroy"
,
G_CALLBACK
(
screenname_autocomplete_destroyed_cb
),
NULL
);
}
void
gaim_gtk_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
gaim_gtk_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
)
{
gaim_prefs_set_path
(
"/gaim/gtk/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
(
gaim_gtk_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
)
{
gaim_prefs_set_path
(
"/gaim/gtk/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
))
{
g_free
(
filename
);
return
;
}
pixbuf
=
gdk_pixbuf_new_from_file
(
filename
,
NULL
);
if
(
!
pixbuf
)
{
gtk_image_set_from_pixbuf
(
GTK_IMAGE
(
dialog
->
icon_preview
),
NULL
);
gtk_label_set_markup
(
GTK_LABEL
(
dialog
->
icon_text
),
""
);
#if GTK_CHECK_VERSION(2,4,0)
/* FILECHOOSER */
gtk_file_chooser_set_preview_widget_active
(
GTK_FILE_CHOOSER
(
dialog
->
icon_filesel
),
FALSE
);
#endif
/* FILECHOOSER */
g_free
(
filename
);
return
;
}
width
=
gdk_pixbuf_get_width
(
pixbuf
);
height
=
gdk_pixbuf_get_height
(
pixbuf
);
basename
=
g_path_get_basename
(
filename
);
size
=
gaim_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
);
#if GTK_CHECK_VERSION(2,4,0)
/* FILECHOOSER */
gtk_file_chooser_set_preview_widget_active
(
GTK_FILE_CHOOSER
(
dialog
->
icon_filesel
),
TRUE
);
#endif
/* FILECHOOSER */
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
*
gaim_gtk_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
*
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
=
gaim_prefs_get_path
(
"/gaim/gtk/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
);
gtk_widget_set_size_request
(
GTK_WIDGET
(
dialog
->
icon_preview
),
-1
,
50
);
gtk_file_chooser_set_preview_widget
(
GTK_FILE_CHOOSER
(
dialog
->
icon_filesel
),
GTK_WIDGET
(
dialog
->
icon_preview
));
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
,
GAIM_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
char
*
gaim_gtk_convert_buddy_icon
(
GaimPlugin
*
plugin
,
const
char
*
path
)
{
GaimPluginProtocolInfo
*
prpl_info
;
#if GTK_CHECK_VERSION(2,2,0)
char
**
prpl_formats
;
int
width
,
height
;
char
**
pixbuf_formats
=
NULL
;
struct
stat
st
;
GdkPixbufFormat
*
format
;
GdkPixbuf
*
pixbuf
;
#if !GTK_CHECK_VERSION(2,4,0)
GdkPixbufLoader
*
loader
;
#endif
#endif
gchar
*
contents
;
gsize
length
;
const
char
*
dirname
;
char
*
random
;
char
*
filename
;
prpl_info
=
GAIM_PLUGIN_PROTOCOL_INFO
(
plugin
);
g_return_val_if_fail
(
prpl_info
->
icon_spec
.
format
!=
NULL
,
NULL
);
dirname
=
gaim_buddy_icons_get_cache_dir
();
if
(
!
g_file_test
(
dirname
,
G_FILE_TEST_IS_DIR
))
{
gaim_debug_info
(
"buddyicon"
,
"Creating icon cache directory.
\n
"
);
if
(
g_mkdir
(
dirname
,
S_IRUSR
|
S_IWUSR
|
S_IXUSR
)
<
0
)
{
gaim_debug_error
(
"buddyicon"
,
"Unable to create directory %s: %s
\n
"
,
dirname
,
strerror
(
errno
));
return
NULL
;
}
}
random
=
g_strdup_printf
(
"%x"
,
g_random_int
());
filename
=
g_build_filename
(
dirname
,
random
,
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
&
GAIM_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
{
FILE
*
image
;
#if GTK_CHECK_VERSION(2,2,0)
g_strfreev
(
prpl_formats
);
g_strfreev
(
pixbuf_formats
);
#endif
/* We don't need to scale the image, so copy it to the cache folder verbatim */
contents
=
NULL
;
if
(
!
g_file_get_contents
(
path
,
&
contents
,
&
length
,
NULL
)
||
(
image
=
g_fopen
(
filename
,
"wb"
))
==
NULL
)
{
g_free
(
random
);
g_free
(
filename
);
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
(
fwrite
(
contents
,
1
,
length
,
image
)
!=
length
)
{
fclose
(
image
);
g_unlink
(
filename
);
g_free
(
random
);
g_free
(
filename
);
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
;
}
fclose
(
image
);
g_free
(
contents
);
#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0)
g_object_unref
(
G_OBJECT
(
pixbuf
));
#endif
}
#if GTK_CHECK_VERSION(2,2,0)
else
{
int
i
;
GError
*
error
=
NULL
;
GdkPixbuf
*
scale
;
g_strfreev
(
pixbuf_formats
);
pixbuf
=
gdk_pixbuf_new_from_file
(
path
,
&
error
);
if
(
error
)
{
gaim_debug_error
(
"buddyicon"
,
"Could not open icon for conversion: %s
\n
"
,
error
->
message
);
g_error_free
(
error
);
g_free
(
random
);
g_free
(
filename
);
g_strfreev
(
prpl_formats
);
return
NULL
;
}
if
((
prpl_info
->
icon_spec
.
scale_rules
&
GAIM_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
;
gaim_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
++
)
{
gaim_debug_info
(
"buddyicon"
,
"Converting buddy icon to %s as %s
\n
"
,
prpl_formats
[
i
],
filename
);
if
(
strcmp
(
prpl_formats
[
i
],
"png"
)
==
0
)
{
if
(
gdk_pixbuf_save
(
pixbuf
,
filename
,
prpl_formats
[
i
],
&
error
,
"compression"
,
"9"
,
NULL
))
/* Success! */
break
;
}
else
if
(
gdk_pixbuf_save
(
pixbuf
,
filename
,
prpl_formats
[
i
],
&
error
,
NULL
))
{
/* Success! */
break
;
}
gaim_debug_warning
(
"buddyicon"
,
"Could not convert to %s: %s
\n
"
,
prpl_formats
[
i
],
error
->
message
);
g_error_free
(
error
);
error
=
NULL
;
}
g_strfreev
(
prpl_formats
);
g_object_unref
(
G_OBJECT
(
pixbuf
));
if
(
error
)
{
gaim_debug_error
(
"buddyicon"
,
"Could not convert icon to usable format: %s
\n
"
,
error
->
message
);
g_error_free
(
error
);
g_free
(
random
);
g_free
(
filename
);
return
NULL
;
}
}
if
(
g_stat
(
filename
,
&
st
)
!=
0
)
{
gaim_debug_error
(
"buddyicon"
,
"Could not stat '%s', which we just wrote to disk: %s
\n
"
,
filename
,
strerror
(
errno
));
g_free
(
random
);
g_free
(
filename
);
return
NULL
;
}
/* Check the file 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
)
&&
(
st
.
st_size
>
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
);
gaim_notify_error
(
NULL
,
_
(
"Icon Error"
),
_
(
"Could not set icon"
),
tmp
);
gaim_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
,
st
.
st_size
,
plugin
->
info
->
name
,
prpl_info
->
icon_spec
.
max_filesize
);
g_free
(
tmp
);
g_free
(
random
);
g_free
(
filename
);
return
NULL
;
}
g_free
(
filename
);
return
random
;
#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
gaim_gtk_set_custom_buddy_icon
(
GaimAccount
*
account
,
const
char
*
who
,
const
char
*
filename
)
{
GaimConversation
*
conv
;
GaimBuddy
*
buddy
;
GaimBlistNode
*
node
;
char
*
path
=
NULL
;
buddy
=
gaim_find_buddy
(
account
,
who
);
if
(
!
buddy
)
{
gaim_debug_info
(
"custom-icon"
,
"You can only set custom icon for someone in your buddylist.
\n
"
);
return
;
}
node
=
(
GaimBlistNode
*
)
gaim_buddy_get_contact
(
buddy
);
path
=
(
char
*
)
gaim_blist_node_get_string
(
node
,
"custom_buddy_icon"
);
if
(
path
)
{
struct
stat
st
;
if
(
g_stat
(
path
,
&
st
)
==
0
)
g_unlink
(
path
);
path
=
NULL
;
}
if
(
filename
)
{
char
*
newfile
;
newfile
=
gaim_gtk_convert_buddy_icon
(
gaim_find_prpl
(
gaim_account_get_protocol_id
(
account
)),
filename
);
path
=
gaim_buddy_icons_get_full_path
(
newfile
);
g_free
(
newfile
);
}
gaim_blist_node_set_string
(
node
,
"custom_buddy_icon"
,
path
);
g_free
(
path
);
/* Update the conversation */
conv
=
gaim_find_conversation_with_account
(
GAIM_CONV_TYPE_IM
,
who
,
account
);
if
(
conv
)
gaim_conversation_update
(
conv
,
GAIM_CONV_UPDATE_ICON
);
/* Update the buddylist */
if
(
buddy
)
gaim_blist_update_buddy_icon
(
buddy
);
}
char
*
gaim_gtk_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
gaim_gtk_set_urgent
(
GtkWindow
*
window
,
gboolean
urgent
)
{
#if GTK_CHECK_VERSION(2,8,0)
gtk_window_set_urgency_hint
(
window
,
urgent
);
#elif defined _WIN32
gtkwgaim_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
*
gaim_gtk_utils_get_handle
()
{
static
int
handle
;
return
&
handle
;
}
static
void
connection_signed_off_cb
(
GaimConnection
*
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
*
gaim_gtk_make_mini_dialog
(
GaimConnection
*
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
;
va_list
args
;
static
gboolean
first_call
=
TRUE
;
img
=
gtk_image_new_from_stock
(
icon_name
,
GTK_ICON_SIZE_BUTTON
);
gtk_misc_set_alignment
(
GTK_MISC
(
img
),
0
,
0
);
vbox
=
gtk_vbox_new
(
FALSE
,
0
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
vbox
),
GAIM_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
;
gaim_signal_connect
(
gaim_connections_get_handle
(),
"signed-off"
,
gaim_gtk_utils_get_handle
(),
GAIM_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
?
secondary_esc
:
""
);
g_free
(
primary_esc
);
label
=
gtk_label_new
(
NULL
);
gtk_widget_set_size_request
(
label
,
gaim_prefs_get_int
(
"/gaim/gtk/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
,
GAIM_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
gaim_gtk_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
(
strcasecmp
(
key
,
"Global Thermonuclear War"
)
==
0
)
{
gaim_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
=
gaim_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
(
gaim_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
&&
gaim_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 (gaim_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
;
}
#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