pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Add user splits and account options to the IRCv3 protocol
20 months ago, Gary Kramlich
e393090804c1
Add user splits and account options to the IRCv3 protocol
Testing Done:
Opened the account editor and verified that all of the settings where there with the correct defaults.
Bugs closed: PIDGIN-17686
Reviewed at https://reviews.imfreedom.org/r/1875/
/*
* Pidgin - Internet Messenger
* Copyright (C) Pidgin Developers <devel@pidgin.im>
*
* 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, see <https://www.gnu.org/licenses/>.
*/
#include
<glib/gi18n-lib.h>
#include
"pidgin/pidginavatar.h"
struct
_PidginAvatar
{
GtkBox
parent
;
GtkWidget
*
icon
;
GdkPixbufAnimation
*
animation
;
gboolean
animate
;
PurpleBuddy
*
buddy
;
PurpleConversation
*
conversation
;
};
enum
{
PROP_0
,
PROP_ANIMATE
,
PROP_BUDDY
,
PROP_CONVERSATION
,
N_PROPERTIES
,
};
static
GParamSpec
*
properties
[
N_PROPERTIES
]
=
{
NULL
,
};
G_DEFINE_TYPE
(
PidginAvatar
,
pidgin_avatar
,
GTK_TYPE_BOX
)
/******************************************************************************
* Helpers
*****************************************************************************/
static
PurpleBuddy
*
pidgin_avatar_get_effective_buddy
(
PidginAvatar
*
avatar
)
{
PurpleBuddy
*
buddy
=
NULL
;
if
(
PURPLE_IS_BUDDY
(
avatar
->
buddy
))
{
buddy
=
PURPLE_BUDDY
(
avatar
->
buddy
);
}
else
if
(
PURPLE_IS_IM_CONVERSATION
(
avatar
->
conversation
))
{
PurpleAccount
*
account
=
NULL
;
const
gchar
*
name
=
NULL
;
account
=
purple_conversation_get_account
(
avatar
->
conversation
);
name
=
purple_conversation_get_name
(
avatar
->
conversation
);
buddy
=
purple_blist_find_buddy
(
account
,
name
);
}
return
buddy
;
}
static
GdkPixbufAnimation
*
pidgin_avatar_find_buddy_icon
(
PurpleBuddy
*
buddy
,
PurpleConversation
*
conversation
)
{
GdkPixbufAnimation
*
ret
=
NULL
;
GInputStream
*
stream
=
NULL
;
PurpleMetaContact
*
contact
=
NULL
;
g_return_val_if_fail
(
PURPLE_IS_BUDDY
(
buddy
),
NULL
);
/* First check if our user has set a custom icon for this buddy. */
contact
=
purple_buddy_get_contact
(
buddy
);
if
(
PURPLE_IS_META_CONTACT
(
contact
))
{
PurpleBlistNode
*
node
=
PURPLE_BLIST_NODE
(
contact
);
PurpleImage
*
custom_image
=
NULL
;
custom_image
=
purple_buddy_icons_node_find_custom_icon
(
node
);
if
(
PURPLE_IS_IMAGE
(
custom_image
))
{
gconstpointer
data
=
purple_image_get_data
(
custom_image
);
gsize
length
=
purple_image_get_data_size
(
custom_image
);
stream
=
g_memory_input_stream_new_from_data
(
data
,
(
gssize
)
length
,
NULL
);
}
}
/* If there is no custom icon, fall back to checking if the buddy has an
* icon set.
*/
if
(
!
G_IS_INPUT_STREAM
(
stream
))
{
PurpleBuddyIcon
*
icon
=
purple_buddy_get_icon
(
buddy
);
if
(
icon
!=
NULL
)
{
stream
=
purple_buddy_icon_get_stream
(
icon
);
}
}
/* Finally if we still don't have icon, we fallback to asking the
* conversation for one.
*/
if
(
!
G_IS_INPUT_STREAM
(
stream
)
&&
PURPLE_IS_IM_CONVERSATION
(
conversation
))
{
PurpleBuddyIcon
*
icon
=
purple_im_conversation_get_icon
(
PURPLE_IM_CONVERSATION
(
conversation
));
if
(
icon
!=
NULL
)
{
stream
=
purple_buddy_icon_get_stream
(
icon
);
}
}
if
(
G_IS_INPUT_STREAM
(
stream
))
{
ret
=
gdk_pixbuf_animation_new_from_stream
(
stream
,
NULL
,
NULL
);
g_clear_object
(
&
stream
);
}
return
ret
;
}
static
void
pidgin_avatar_update
(
PidginAvatar
*
avatar
)
{
PurpleBuddy
*
buddy
=
NULL
;
GdkPixbufAnimation
*
animation
=
NULL
;
GdkPixbuf
*
pixbuf
=
NULL
;
buddy
=
pidgin_avatar_get_effective_buddy
(
avatar
);
if
(
PURPLE_IS_BUDDY
(
buddy
))
{
animation
=
pidgin_avatar_find_buddy_icon
(
buddy
,
avatar
->
conversation
);
}
g_set_object
(
&
avatar
->
animation
,
animation
);
if
(
GDK_IS_PIXBUF_ANIMATION
(
avatar
->
animation
))
{
if
(
avatar
->
animate
&&
!
gdk_pixbuf_animation_is_static_image
(
avatar
->
animation
))
{
pixbuf
=
GDK_PIXBUF
(
avatar
->
animation
);
}
else
{
pixbuf
=
gdk_pixbuf_animation_get_static_image
(
avatar
->
animation
);
}
}
gtk_picture_set_pixbuf
(
GTK_PICTURE
(
avatar
->
icon
),
pixbuf
);
g_clear_object
(
&
animation
);
}
/******************************************************************************
* Actions
*****************************************************************************/
static
void
pidgin_avatar_save_response_cb
(
GtkNativeDialog
*
native
,
gint
response
,
gpointer
data
)
{
PidginAvatar
*
avatar
=
PIDGIN_AVATAR
(
data
);
PurpleBuddy
*
buddy
=
NULL
;
PurpleBuddyIcon
*
icon
=
NULL
;
if
(
response
!=
GTK_RESPONSE_ACCEPT
)
{
gtk_native_dialog_destroy
(
native
);
return
;
}
buddy
=
pidgin_avatar_get_effective_buddy
(
avatar
);
if
(
!
PURPLE_IS_BUDDY
(
buddy
))
{
gtk_native_dialog_destroy
(
native
);
return
;
}
icon
=
purple_buddy_get_icon
(
buddy
);
if
(
icon
!=
NULL
)
{
GtkFileChooser
*
chooser
=
GTK_FILE_CHOOSER
(
native
);
GFile
*
file
=
NULL
;
gchar
*
filename
=
NULL
;
file
=
gtk_file_chooser_get_file
(
chooser
);
filename
=
g_file_get_path
(
file
);
purple_buddy_icon_save_to_filename
(
icon
,
filename
,
NULL
);
g_free
(
filename
);
g_object_unref
(
file
);
}
gtk_native_dialog_destroy
(
native
);
}
static
void
pidgin_avatar_save_cb
(
GSimpleAction
*
action
,
GVariant
*
parameter
,
gpointer
data
)
{
PidginAvatar
*
avatar
=
PIDGIN_AVATAR
(
data
);
PurpleBuddy
*
buddy
=
NULL
;
PurpleAccount
*
account
=
NULL
;
GtkFileChooserNative
*
native
=
NULL
;
GtkFileChooser
*
chooser
=
NULL
;
GtkWindow
*
window
=
NULL
;
const
gchar
*
ext
=
NULL
,
*
name
=
NULL
;
gchar
*
filename
=
NULL
;
buddy
=
pidgin_avatar_get_effective_buddy
(
avatar
);
if
(
buddy
==
NULL
)
{
g_return_if_reached
();
}
ext
=
purple_buddy_icon_get_extension
(
purple_buddy_get_icon
(
buddy
));
account
=
purple_buddy_get_account
(
buddy
);
name
=
purple_buddy_get_name
(
buddy
);
filename
=
g_strdup_printf
(
"%s.%s"
,
purple_normalize
(
account
,
name
),
ext
);
window
=
GTK_WINDOW
(
gtk_widget_get_root
(
GTK_WIDGET
(
avatar
)));
native
=
gtk_file_chooser_native_new
(
_
(
"Save Avatar"
),
window
,
GTK_FILE_CHOOSER_ACTION_SAVE
,
_
(
"_Save"
),
_
(
"_Cancel"
));
g_signal_connect
(
G_OBJECT
(
native
),
"response"
,
G_CALLBACK
(
pidgin_avatar_save_response_cb
),
avatar
);
chooser
=
GTK_FILE_CHOOSER
(
native
);
gtk_file_chooser_set_current_name
(
chooser
,
filename
);
g_free
(
filename
);
gtk_native_dialog_show
(
GTK_NATIVE_DIALOG
(
native
));
}
static
void
pidgin_avatar_set_custom_response_cb
(
GtkNativeDialog
*
native
,
gint
response
,
gpointer
data
)
{
PidginAvatar
*
avatar
=
PIDGIN_AVATAR
(
data
);
PurpleBuddy
*
buddy
=
NULL
;
GtkFileChooser
*
chooser
=
GTK_FILE_CHOOSER
(
native
);
GFile
*
file
=
NULL
;
gchar
*
filename
=
NULL
;
if
(
response
!=
GTK_RESPONSE_ACCEPT
)
{
gtk_native_dialog_destroy
(
native
);
return
;
}
buddy
=
pidgin_avatar_get_effective_buddy
(
avatar
);
if
(
!
PURPLE_IS_BUDDY
(
buddy
))
{
gtk_native_dialog_destroy
(
native
);
return
;
}
file
=
gtk_file_chooser_get_file
(
chooser
);
filename
=
g_file_get_path
(
file
);
if
(
filename
!=
NULL
)
{
PurpleMetaContact
*
contact
=
purple_buddy_get_contact
(
buddy
);
PurpleBlistNode
*
node
=
PURPLE_BLIST_NODE
(
contact
);
purple_buddy_icons_node_set_custom_icon_from_file
(
node
,
filename
);
pidgin_avatar_update
(
avatar
);
}
g_free
(
filename
);
g_object_unref
(
file
);
gtk_native_dialog_destroy
(
native
);
}
static
void
pidgin_avatar_set_custom_cb
(
GSimpleAction
*
action
,
GVariant
*
parameter
,
gpointer
data
)
{
PidginAvatar
*
avatar
=
PIDGIN_AVATAR
(
data
);
GtkFileChooserNative
*
native
=
NULL
;
GtkWindow
*
window
=
NULL
;
window
=
GTK_WINDOW
(
gtk_widget_get_root
(
GTK_WIDGET
(
avatar
)));
native
=
gtk_file_chooser_native_new
(
_
(
"Set Custom Avatar"
),
window
,
GTK_FILE_CHOOSER_ACTION_OPEN
,
_
(
"_Set Custom"
),
_
(
"_Cancel"
));
g_signal_connect
(
G_OBJECT
(
native
),
"response"
,
G_CALLBACK
(
pidgin_avatar_set_custom_response_cb
),
avatar
);
gtk_native_dialog_show
(
GTK_NATIVE_DIALOG
(
native
));
}
static
void
pidgin_avatar_clear_custom_cb
(
GSimpleAction
*
action
,
GVariant
*
parameter
,
gpointer
data
)
{
PidginAvatar
*
avatar
=
PIDGIN_AVATAR
(
data
);
PurpleBuddy
*
buddy
=
NULL
;
buddy
=
pidgin_avatar_get_effective_buddy
(
avatar
);
if
(
PURPLE_IS_BUDDY
(
buddy
))
{
PurpleMetaContact
*
contact
=
purple_buddy_get_contact
(
buddy
);
PurpleBlistNode
*
node
=
PURPLE_BLIST_NODE
(
contact
);
purple_buddy_icons_node_set_custom_icon_from_file
(
node
,
NULL
);
pidgin_avatar_update
(
avatar
);
}
}
static
GActionEntry
actions
[]
=
{
{
.
name
=
"save-avatar"
,
.
activate
=
pidgin_avatar_save_cb
,
},
{
.
name
=
"set-custom-avatar"
,
.
activate
=
pidgin_avatar_set_custom_cb
,
},
{
.
name
=
"clear-custom-avatar"
,
.
activate
=
pidgin_avatar_clear_custom_cb
,
},
};
/******************************************************************************
* Callbacks
*****************************************************************************/
static
gboolean
pidgin_avatar_button_press_handler
(
G_GNUC_UNUSED
GtkGestureClick
*
event
,
G_GNUC_UNUSED
gint
n_press
,
gdouble
x
,
gdouble
y
,
gpointer
data
)
{
PidginAvatar
*
avatar
=
PIDGIN_AVATAR
(
data
);
GtkBuilder
*
builder
=
NULL
;
GtkWidget
*
menu
=
NULL
;
GMenuModel
*
model
=
NULL
;
builder
=
gtk_builder_new_from_resource
(
"/im/pidgin/Pidgin3/Avatar/menu.ui"
);
model
=
(
GMenuModel
*
)
gtk_builder_get_object
(
builder
,
"menu"
);
menu
=
gtk_popover_menu_new_from_model
(
model
);
gtk_widget_set_parent
(
menu
,
GTK_WIDGET
(
avatar
));
gtk_popover_set_pointing_to
(
GTK_POPOVER
(
menu
),
&
(
const
GdkRectangle
){(
int
)
x
,
(
int
)
y
,
0
,
0
});
g_clear_object
(
&
builder
);
gtk_popover_popup
(
GTK_POPOVER
(
menu
));
return
TRUE
;
}
/*
* This function is a callback for when properties change on the buddy we're
* tracking. It should not be reused for the conversation we're tracking
* because we have to disconnect old handlers and reuse of this function will
* cause issues if a buddy is changed but a conversation is not and vice versa.
*/
static
void
pidgin_avatar_buddy_icon_updated
(
GObject
*
obj
,
GParamSpec
*
pspec
,
gpointer
d
)
{
PidginAvatar
*
avatar
=
PIDGIN_AVATAR
(
d
);
pidgin_avatar_update
(
avatar
);
}
/*
* This function is a callback for when properties change on the conversation
* we're tracking. It should not be reused for the buddy we're tracking
* because we have to disconnect old handlers and reuse of this function will
* cause issues if a buddy is changed but a conversation is not and vice versa.
*/
static
void
pidgin_avatar_conversation_updated
(
GObject
*
obj
,
GParamSpec
*
pspec
,
gpointer
d
)
{
PidginAvatar
*
avatar
=
PIDGIN_AVATAR
(
d
);
pidgin_avatar_update
(
avatar
);
}
static
gboolean
pidgin_avatar_enter_notify_handler
(
G_GNUC_UNUSED
GtkEventControllerMotion
*
event
,
G_GNUC_UNUSED
gdouble
x
,
G_GNUC_UNUSED
gdouble
y
,
gpointer
data
)
{
PidginAvatar
*
avatar
=
PIDGIN_AVATAR
(
data
);
pidgin_avatar_set_animate
(
avatar
,
TRUE
);
return
FALSE
;
}
static
gboolean
pidgin_avatar_leave_notify_handler
(
G_GNUC_UNUSED
GtkEventControllerMotion
*
event
,
gpointer
data
)
{
PidginAvatar
*
avatar
=
PIDGIN_AVATAR
(
data
);
pidgin_avatar_set_animate
(
avatar
,
FALSE
);
return
FALSE
;
}
/******************************************************************************
* GObject Implementation
*****************************************************************************/
static
void
pidgin_avatar_get_property
(
GObject
*
obj
,
guint
param_id
,
GValue
*
value
,
GParamSpec
*
pspec
)
{
PidginAvatar
*
avatar
=
PIDGIN_AVATAR
(
obj
);
switch
(
param_id
)
{
case
PROP_ANIMATE
:
g_value_set_boolean
(
value
,
pidgin_avatar_get_animate
(
avatar
));
break
;
case
PROP_BUDDY
:
g_value_set_object
(
value
,
pidgin_avatar_get_buddy
(
avatar
));
break
;
case
PROP_CONVERSATION
:
g_value_set_object
(
value
,
pidgin_avatar_get_conversation
(
avatar
));
break
;
default
:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
obj
,
param_id
,
pspec
);
break
;
}
}
static
void
pidgin_avatar_set_property
(
GObject
*
obj
,
guint
param_id
,
const
GValue
*
value
,
GParamSpec
*
pspec
)
{
PidginAvatar
*
avatar
=
PIDGIN_AVATAR
(
obj
);
switch
(
param_id
)
{
case
PROP_ANIMATE
:
pidgin_avatar_set_animate
(
avatar
,
g_value_get_boolean
(
value
));
break
;
case
PROP_BUDDY
:
pidgin_avatar_set_buddy
(
avatar
,
g_value_get_object
(
value
));
break
;
case
PROP_CONVERSATION
:
pidgin_avatar_set_conversation
(
avatar
,
g_value_get_object
(
value
));
break
;
default
:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
obj
,
param_id
,
pspec
);
break
;
}
}
static
void
pidgin_avatar_dispose
(
GObject
*
obj
)
{
PidginAvatar
*
avatar
=
PIDGIN_AVATAR
(
obj
);
pidgin_avatar_set_buddy
(
avatar
,
NULL
);
pidgin_avatar_set_conversation
(
avatar
,
NULL
);
g_clear_object
(
&
avatar
->
animation
);
G_OBJECT_CLASS
(
pidgin_avatar_parent_class
)
->
dispose
(
obj
);
}
static
void
pidgin_avatar_init
(
PidginAvatar
*
avatar
)
{
GSimpleActionGroup
*
group
=
NULL
;
gtk_widget_init_template
(
GTK_WIDGET
(
avatar
));
#if GTK_CHECK_VERSION(4,8,0)
gtk_picture_set_content_fit
(
GTK_PICTURE
(
avatar
->
icon
),
GTK_CONTENT_FIT_SCALE_DOWN
);
#endif
/* Now setup our actions. */
group
=
g_simple_action_group_new
();
g_action_map_add_action_entries
(
G_ACTION_MAP
(
group
),
actions
,
G_N_ELEMENTS
(
actions
),
avatar
);
gtk_widget_insert_action_group
(
GTK_WIDGET
(
avatar
),
"avatar"
,
G_ACTION_GROUP
(
group
));
}
static
void
pidgin_avatar_class_init
(
PidginAvatarClass
*
klass
)
{
GObjectClass
*
obj_class
=
G_OBJECT_CLASS
(
klass
);
GtkWidgetClass
*
widget_class
=
GTK_WIDGET_CLASS
(
klass
);
obj_class
->
get_property
=
pidgin_avatar_get_property
;
obj_class
->
set_property
=
pidgin_avatar_set_property
;
obj_class
->
dispose
=
pidgin_avatar_dispose
;
/**
* PidginAvatar:animate:
*
* Whether or not an animated avatar should be animated.
*/
properties
[
PROP_ANIMATE
]
=
g_param_spec_boolean
(
"animate"
,
"animate"
,
"Whether or not to animate an animated avatar"
,
FALSE
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT
|
G_PARAM_STATIC_STRINGS
);
/**
* PidginAvatar:buddy:
*
* The buddy whose avatar will be displayed.
*/
properties
[
PROP_BUDDY
]
=
g_param_spec_object
(
"buddy"
,
"buddy"
,
"The buddy whose avatar to display"
,
PURPLE_TYPE_BUDDY
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT
|
G_PARAM_STATIC_STRINGS
);
/**
* PidginAvatar:conversation:
*
* The conversation which will be used to find the correct buddy.
*/
properties
[
PROP_CONVERSATION
]
=
g_param_spec_object
(
"conversation"
,
"conversation"
,
"The conversation used to find the correct buddy."
,
PURPLE_TYPE_CONVERSATION
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT
|
G_PARAM_STATIC_STRINGS
);
g_object_class_install_properties
(
obj_class
,
N_PROPERTIES
,
properties
);
gtk_widget_class_set_template_from_resource
(
widget_class
,
"/im/pidgin/Pidgin3/Avatar/avatar.ui"
);
gtk_widget_class_bind_template_child
(
widget_class
,
PidginAvatar
,
icon
);
gtk_widget_class_bind_template_callback
(
widget_class
,
pidgin_avatar_button_press_handler
);
gtk_widget_class_bind_template_callback
(
widget_class
,
pidgin_avatar_enter_notify_handler
);
gtk_widget_class_bind_template_callback
(
widget_class
,
pidgin_avatar_leave_notify_handler
);
}
/******************************************************************************
* API
*****************************************************************************/
GtkWidget
*
pidgin_avatar_new
(
void
)
{
return
GTK_WIDGET
(
g_object_new
(
PIDGIN_TYPE_AVATAR
,
NULL
));
}
void
pidgin_avatar_set_animate
(
PidginAvatar
*
avatar
,
gboolean
animate
)
{
g_return_if_fail
(
PIDGIN_IS_AVATAR
(
avatar
));
avatar
->
animate
=
animate
;
if
(
GDK_IS_PIXBUF_ANIMATION
(
avatar
->
animation
))
{
if
(
avatar
->
animate
&&
!
gdk_pixbuf_animation_is_static_image
(
avatar
->
animation
))
{
gtk_picture_set_pixbuf
(
GTK_PICTURE
(
avatar
->
icon
),
GDK_PIXBUF
(
avatar
->
animation
));
}
else
{
GdkPixbuf
*
frame
=
NULL
;
frame
=
gdk_pixbuf_animation_get_static_image
(
avatar
->
animation
);
gtk_picture_set_pixbuf
(
GTK_PICTURE
(
avatar
->
icon
),
frame
);
}
}
}
gboolean
pidgin_avatar_get_animate
(
PidginAvatar
*
avatar
)
{
g_return_val_if_fail
(
PIDGIN_IS_AVATAR
(
avatar
),
FALSE
);
return
avatar
->
animate
;
}
void
pidgin_avatar_set_buddy
(
PidginAvatar
*
avatar
,
PurpleBuddy
*
buddy
)
{
g_return_if_fail
(
PIDGIN_IS_AVATAR
(
avatar
));
/* Remove our old signal handler. */
if
(
PURPLE_IS_BUDDY
(
avatar
->
buddy
))
{
g_signal_handlers_disconnect_by_func
(
avatar
->
buddy
,
pidgin_avatar_buddy_icon_updated
,
avatar
);
}
if
(
g_set_object
(
&
avatar
->
buddy
,
buddy
))
{
pidgin_avatar_update
(
avatar
);
g_object_notify_by_pspec
(
G_OBJECT
(
avatar
),
properties
[
PROP_BUDDY
]);
}
/* Add the notify signal so we can update when the icon changes. */
if
(
PURPLE_IS_BUDDY
(
avatar
->
buddy
))
{
g_signal_connect
(
G_OBJECT
(
avatar
->
buddy
),
"notify::icon"
,
G_CALLBACK
(
pidgin_avatar_buddy_icon_updated
),
avatar
);
}
}
PurpleBuddy
*
pidgin_avatar_get_buddy
(
PidginAvatar
*
avatar
)
{
g_return_val_if_fail
(
PIDGIN_IS_AVATAR
(
avatar
),
NULL
);
return
avatar
->
buddy
;
}
void
pidgin_avatar_set_conversation
(
PidginAvatar
*
avatar
,
PurpleConversation
*
conversation
)
{
g_return_if_fail
(
PIDGIN_IS_AVATAR
(
avatar
));
/* Remove our old signal handler. */
if
(
PURPLE_IS_CONVERSATION
(
avatar
->
conversation
))
{
g_signal_handlers_disconnect_by_func
(
avatar
->
conversation
,
pidgin_avatar_conversation_updated
,
avatar
);
}
if
(
g_set_object
(
&
avatar
->
conversation
,
conversation
))
{
g_object_notify_by_pspec
(
G_OBJECT
(
avatar
),
properties
[
PROP_CONVERSATION
]);
}
/* Add the notify signal so we can update when the icon changes. */
if
(
PURPLE_IS_CONVERSATION
(
avatar
->
conversation
))
{
g_signal_connect
(
G_OBJECT
(
avatar
->
conversation
),
"notify"
,
G_CALLBACK
(
pidgin_avatar_conversation_updated
),
avatar
);
}
}
PurpleConversation
*
pidgin_avatar_get_conversation
(
PidginAvatar
*
avatar
)
{
g_return_val_if_fail
(
PIDGIN_IS_AVATAR
(
avatar
),
NULL
);
return
avatar
->
conversation
;
}