qulogic/pidgin
Clone
Summary
Browse
Changes
Graph
Make sure all of the license headers for IRCv3 are GPLv2
default
4 months ago, Gary Kramlich
31e8c7c92e2f
Make sure all of the license headers for IRCv3 are GPLv2
Testing Done:
Ran `licensecheck` from debian in the following way.
```
$ licensecheck *.[ch] | cut -d: -f 2 | sort | uniq -c
27 GNU General Public License v2.0 or later
```
Reviewed at https://reviews.imfreedom.org/r/2913/
/*
* finch
*
* Finch is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#include NCURSES_HEADER
#include
<string.h>
#include
<glib/gi18n-lib.h>
#include
<gplugin.h>
#include
<purple.h>
#include
<gnt.h>
#include
"gntblist.h"
#include
"gntconv.h"
#include
"gntmenuutil.h"
#include
"gntstatus.h"
#define PREF_ROOT "/finch/blist"
#define TYPING_TIMEOUT_S 4
#define UI_DATA "ui-finch"
#define SHOW_EMPTY_GROUP_TIMEOUT 60
struct
_FinchBuddyList
{
PurpleBuddyList
parent
;
GntWidget
*
window
;
GntWidget
*
tree
;
GntWidget
*
tooltip
;
PurpleBlistNode
*
tnode
;
/* Who is the tooltip being displayed for? */
GList
*
tagged
;
/* A list of tagged blistnodes */
GntWidget
*
context
;
PurpleBlistNode
*
cnode
;
/* XXX: I am KISSing */
GntWidget
*
status
;
/* Dropdown with the statuses */
GntWidget
*
statustext
;
/* Status message */
int
typing
;
GntWidget
*
menu
;
/* These are the menuitems that get regenerated */
GntMenuItem
*
accounts
;
GntMenuItem
*
plugins
;
GntMenuItem
*
grouping
;
/* When a new group is manually added, it is empty, but we still want to show it
* for a while (SHOW_EMPTY_GROUP_TIMEOUT seconds) even if 'show empty groups' is
* not selected.
*/
GList
*
new_group
;
guint
new_group_timeout
;
FinchBlistManager
*
manager
;
};
typedef
struct
{
gpointer
row
;
/* the row in the GntTree */
guint
signed_timer
;
/* used when 'recently' signed on/off */
}
FinchBlistNode
;
typedef
enum
{
STATUS_PRIMITIVE
=
0
,
STATUS_SAVED_POPULAR
,
STATUS_SAVED_ALL
,
STATUS_SAVED_NEW
}
StatusType
;
typedef
struct
{
StatusType
type
;
union
{
PurpleStatusPrimitive
prim
;
PurpleSavedStatus
*
saved
;
}
u
;
}
StatusBoxItem
;
static
FinchBuddyList
*
ggblist
;
static
void
add_buddy
(
PurpleBuddy
*
buddy
,
FinchBuddyList
*
ggblist
);
static
void
add_contact
(
PurpleMetaContact
*
contact
,
FinchBuddyList
*
ggblist
);
static
void
add_group
(
PurpleGroup
*
group
,
FinchBuddyList
*
ggblist
);
static
void
add_chat
(
PurpleChat
*
chat
,
FinchBuddyList
*
ggblist
);
static
void
add_node
(
PurpleBlistNode
*
node
,
FinchBuddyList
*
ggblist
);
static
void
node_update
(
PurpleBuddyList
*
list
,
PurpleBlistNode
*
node
);
static
void
draw_tooltip
(
FinchBuddyList
*
ggblist
);
static
void
tooltip_for_buddy
(
PurpleBuddy
*
buddy
,
GString
*
str
,
gboolean
full
);
static
gboolean
remove_typing_cb
(
gpointer
data
);
static
void
remove_peripherals
(
FinchBuddyList
*
ggblist
);
static
const
char
*
get_display_name
(
PurpleBlistNode
*
node
);
static
void
savedstatus_changed
(
PurpleSavedStatus
*
now
,
PurpleSavedStatus
*
old
);
static
void
blist_show
(
PurpleBuddyList
*
list
);
static
void
update_node_display
(
PurpleBlistNode
*
buddy
,
FinchBuddyList
*
ggblist
);
static
void
update_buddy_display
(
PurpleBuddy
*
buddy
,
FinchBuddyList
*
ggblist
);
static
gboolean
account_autojoin_cb
(
PurpleConnection
*
pc
,
gpointer
data
);
static
void
finch_request_add_buddy
(
PurpleBuddyList
*
list
,
PurpleAccount
*
account
,
const
char
*
username
,
const
char
*
grp
,
const
char
*
alias
);
static
void
menu_group_set_cb
(
GntMenuItem
*
item
,
gpointer
data
);
/* Sort functions */
static
int
blist_node_compare_position
(
PurpleBlistNode
*
n1
,
PurpleBlistNode
*
n2
);
static
int
blist_node_compare_text
(
PurpleBlistNode
*
n1
,
PurpleBlistNode
*
n2
);
static
int
blist_node_compare_status
(
PurpleBlistNode
*
n1
,
PurpleBlistNode
*
n2
);
static
int
color_available
;
static
int
color_away
;
static
int
color_offline
;
static
int
color_idle
;
/*
* Buddy List Manager functions.
*/
static
gboolean
default_can_add_node
(
PurpleBlistNode
*
node
)
{
gboolean
offline
=
purple_prefs_get_bool
(
PREF_ROOT
"/showoffline"
);
if
(
PURPLE_IS_BUDDY
(
node
))
{
PurpleBuddy
*
buddy
=
(
PurpleBuddy
*
)
node
;
FinchBlistNode
*
fnode
=
g_object_get_data
(
G_OBJECT
(
node
),
UI_DATA
);
if
(
!
purple_buddy_get_contact
(
buddy
))
return
FALSE
;
/* When a new buddy is added and show-offline is set */
if
(
PURPLE_BUDDY_IS_ONLINE
(
buddy
))
return
TRUE
;
/* The buddy is online */
if
(
!
purple_account_is_connected
(
purple_buddy_get_account
(
buddy
)))
return
FALSE
;
/* The account is disconnected. Do not show */
if
(
offline
)
return
TRUE
;
/* We want to see offline buddies too */
if
(
fnode
&&
fnode
->
signed_timer
)
return
TRUE
;
/* Show if the buddy just signed off */
if
(
purple_blist_node_get_bool
(
node
,
"show_offline"
))
return
TRUE
;
}
else
if
(
PURPLE_IS_META_CONTACT
(
node
))
{
PurpleBlistNode
*
child
;
for
(
child
=
purple_blist_node_get_first_child
(
node
);
child
;
child
=
purple_blist_node_get_sibling_next
(
child
))
{
if
(
default_can_add_node
(
child
))
{
return
TRUE
;
}
}
}
else
if
(
PURPLE_IS_CHAT
(
node
))
{
PurpleChat
*
chat
=
(
PurpleChat
*
)
node
;
if
(
purple_account_is_connected
(
purple_chat_get_account
(
chat
)))
return
TRUE
;
/* Show whenever the account is online */
}
else
if
(
PURPLE_IS_GROUP
(
node
))
{
PurpleBlistNode
*
child
;
gboolean
empty
=
purple_prefs_get_bool
(
PREF_ROOT
"/emptygroups"
);
if
(
empty
)
return
TRUE
;
/* If we want to see empty groups, we can show any group */
for
(
child
=
purple_blist_node_get_first_child
(
node
);
child
;
child
=
purple_blist_node_get_sibling_next
(
child
))
{
if
(
default_can_add_node
(
child
))
{
return
TRUE
;
}
}
if
(
ggblist
&&
ggblist
->
new_group
&&
g_list_find
(
ggblist
->
new_group
,
node
))
return
TRUE
;
}
return
FALSE
;
}
static
gpointer
default_find_parent
(
PurpleBlistNode
*
node
)
{
gpointer
ret
=
NULL
;
if
(
PURPLE_IS_BUDDY
(
node
)
||
PURPLE_IS_META_CONTACT
(
node
)
||
PURPLE_IS_CHAT
(
node
))
ret
=
purple_blist_node_get_parent
(
node
);
if
(
ret
)
add_node
(
ret
,
ggblist
);
return
ret
;
}
static
gboolean
default_create_tooltip
(
gpointer
selected_row
,
GString
**
body
,
char
**
tool_title
)
{
GString
*
str
;
PurpleBlistNode
*
node
=
selected_row
;
int
lastseen
=
0
;
char
*
title
;
str
=
g_string_new
(
""
);
if
(
PURPLE_IS_META_CONTACT
(
node
))
{
PurpleBuddy
*
pr
=
purple_meta_contact_get_priority_buddy
((
PurpleMetaContact
*
)
node
);
gboolean
offline
=
!
PURPLE_BUDDY_IS_ONLINE
(
pr
);
gboolean
showoffline
=
purple_prefs_get_bool
(
PREF_ROOT
"/showoffline"
);
const
char
*
name
=
purple_buddy_get_name
(
pr
);
title
=
g_strdup
(
name
);
tooltip_for_buddy
(
pr
,
str
,
TRUE
);
for
(
node
=
purple_blist_node_get_first_child
(
node
);
node
;
node
=
purple_blist_node_get_sibling_next
(
node
))
{
PurpleBuddy
*
buddy
=
(
PurpleBuddy
*
)
node
;
if
(
offline
)
{
int
value
=
purple_blist_node_get_int
(
node
,
"last_seen"
);
if
(
value
>
lastseen
)
lastseen
=
value
;
}
if
(
node
==
(
PurpleBlistNode
*
)
pr
)
continue
;
if
(
!
purple_account_is_connected
(
purple_buddy_get_account
(
buddy
)))
continue
;
if
(
!
showoffline
&&
!
PURPLE_BUDDY_IS_ONLINE
(
buddy
))
continue
;
str
=
g_string_append
(
str
,
"
\n
----------
\n
"
);
tooltip_for_buddy
(
buddy
,
str
,
FALSE
);
}
}
else
if
(
PURPLE_IS_BUDDY
(
node
))
{
PurpleBuddy
*
buddy
=
(
PurpleBuddy
*
)
node
;
tooltip_for_buddy
(
buddy
,
str
,
TRUE
);
title
=
g_strdup
(
purple_buddy_get_name
(
buddy
));
if
(
!
PURPLE_BUDDY_IS_ONLINE
((
PurpleBuddy
*
)
node
))
lastseen
=
purple_blist_node_get_int
(
node
,
"last_seen"
);
}
else
if
(
PURPLE_IS_GROUP
(
node
))
{
PurpleGroup
*
group
=
(
PurpleGroup
*
)
node
;
g_string_append_printf
(
str
,
_
(
"Online: %d
\n
Total: %d"
),
purple_counting_node_get_online_count
(
PURPLE_COUNTING_NODE
(
group
)),
purple_counting_node_get_current_size
(
PURPLE_COUNTING_NODE
(
group
)));
title
=
g_strdup
(
purple_group_get_name
(
group
));
}
else
if
(
PURPLE_IS_CHAT
(
node
))
{
PurpleAccount
*
account
=
NULL
;
PurpleContactInfo
*
info
=
NULL
;
PurpleChat
*
chat
=
NULL
;
chat
=
PURPLE_CHAT
(
node
);
account
=
purple_chat_get_account
(
chat
);
info
=
PURPLE_CONTACT_INFO
(
account
);
g_string_append_printf
(
str
,
_
(
"Account: %s (%s)"
),
purple_contact_info_get_username
(
info
),
purple_account_get_protocol_name
(
account
));
title
=
g_strdup
(
purple_chat_get_name
(
chat
));
}
else
{
g_string_free
(
str
,
TRUE
);
return
FALSE
;
}
if
(
lastseen
>
0
)
{
char
*
tmp
=
purple_str_seconds_to_string
(
time
(
NULL
)
-
lastseen
);
g_string_append_printf
(
str
,
_
(
"
\n
Last Seen: %s ago"
),
tmp
);
g_free
(
tmp
);
}
if
(
tool_title
)
*
tool_title
=
title
;
else
g_free
(
title
);
if
(
body
)
*
body
=
str
;
else
g_string_free
(
str
,
TRUE
);
return
TRUE
;
}
static
FinchBlistManager
default_manager
=
{
"default"
,
N_
(
"Default"
),
NULL
,
NULL
,
default_can_add_node
,
default_find_parent
,
default_create_tooltip
,
{
NULL
,
NULL
,
NULL
,
NULL
}
};
static
GList
*
managers
;
static
void
finch_blist_node_free
(
FinchBlistNode
*
node
)
{
g_clear_handle_id
(
&
node
->
signed_timer
,
g_source_remove
);
g_free
(
node
);
}
static
FinchBlistNode
*
create_finch_blist_node
(
PurpleBlistNode
*
node
,
gpointer
row
)
{
FinchBlistNode
*
fnode
=
g_object_get_data
(
G_OBJECT
(
node
),
UI_DATA
);
if
(
!
fnode
)
{
fnode
=
g_new0
(
FinchBlistNode
,
1
);
fnode
->
signed_timer
=
0
;
g_object_set_data_full
(
G_OBJECT
(
node
),
UI_DATA
,
fnode
,
(
GDestroyNotify
)
finch_blist_node_free
);
}
fnode
->
row
=
row
;
return
fnode
;
}
static
int
get_display_color
(
PurpleBlistNode
*
node
)
{
PurpleBuddy
*
buddy
;
int
color
=
0
;
if
(
PURPLE_IS_META_CONTACT
(
node
))
node
=
PURPLE_BLIST_NODE
(
purple_meta_contact_get_priority_buddy
(
PURPLE_META_CONTACT
(
node
)));
if
(
!
PURPLE_IS_BUDDY
(
node
))
return
0
;
buddy
=
(
PurpleBuddy
*
)
node
;
if
(
purple_presence_is_idle
(
purple_buddy_get_presence
(
buddy
)))
{
color
=
color_idle
;
}
else
if
(
purple_presence_is_available
(
purple_buddy_get_presence
(
buddy
)))
{
color
=
color_available
;
}
else
if
(
purple_presence_is_online
(
purple_buddy_get_presence
(
buddy
))
&&
!
purple_presence_is_available
(
purple_buddy_get_presence
(
buddy
)))
{
color
=
color_away
;
}
else
if
(
!
purple_presence_is_online
(
purple_buddy_get_presence
(
buddy
)))
{
color
=
color_offline
;
}
return
color
;
}
static
GntTextFormatFlags
get_blist_node_flag
(
FinchBuddyList
*
ggblist
,
PurpleBlistNode
*
node
)
{
GntTextFormatFlags
flag
=
0
;
FinchBlistNode
*
fnode
=
g_object_get_data
(
G_OBJECT
(
node
),
UI_DATA
);
if
(
ggblist
->
tagged
&&
g_list_find
(
ggblist
->
tagged
,
node
))
flag
|=
GNT_TEXT_FLAG_BOLD
;
if
(
fnode
&&
fnode
->
signed_timer
)
flag
|=
GNT_TEXT_FLAG_BLINK
;
else
if
(
PURPLE_IS_META_CONTACT
(
node
))
{
node
=
PURPLE_BLIST_NODE
(
purple_meta_contact_get_priority_buddy
(
PURPLE_META_CONTACT
(
node
)));
fnode
=
g_object_get_data
(
G_OBJECT
(
node
),
UI_DATA
);
if
(
fnode
&&
fnode
->
signed_timer
)
flag
|=
GNT_TEXT_FLAG_BLINK
;
}
return
flag
;
}
static
void
blist_update_row_flags
(
FinchBuddyList
*
ggblist
,
PurpleBlistNode
*
node
)
{
gnt_tree_set_row_flags
(
GNT_TREE
(
ggblist
->
tree
),
node
,
get_blist_node_flag
(
ggblist
,
node
));
gnt_tree_set_row_color
(
GNT_TREE
(
ggblist
->
tree
),
node
,
get_display_color
(
node
));
}
static
void
new_node
(
G_GNUC_UNUSED
PurpleBuddyList
*
list
,
G_GNUC_UNUSED
PurpleBlistNode
*
node
)
{
}
static
void
add_node
(
PurpleBlistNode
*
node
,
FinchBuddyList
*
ggblist
)
{
if
(
g_object_get_data
(
G_OBJECT
(
node
),
UI_DATA
))
{
return
;
}
if
(
!
ggblist
->
manager
->
can_add_node
(
node
))
{
return
;
}
if
(
PURPLE_IS_BUDDY
(
node
))
{
add_buddy
((
PurpleBuddy
*
)
node
,
ggblist
);
}
else
if
(
PURPLE_IS_META_CONTACT
(
node
))
{
add_contact
((
PurpleMetaContact
*
)
node
,
ggblist
);
}
else
if
(
PURPLE_IS_GROUP
(
node
))
{
add_group
((
PurpleGroup
*
)
node
,
ggblist
);
}
else
if
(
PURPLE_IS_CHAT
(
node
))
{
add_chat
((
PurpleChat
*
)
node
,
ggblist
);
}
draw_tooltip
(
ggblist
);
}
void
finch_blist_manager_add_node
(
PurpleBlistNode
*
node
)
{
add_node
(
node
,
ggblist
);
}
static
void
remove_tooltip
(
FinchBuddyList
*
ggblist
)
{
gnt_widget_destroy
(
ggblist
->
tooltip
);
ggblist
->
tooltip
=
NULL
;
ggblist
->
tnode
=
NULL
;
}
static
void
node_remove
(
PurpleBuddyList
*
list
,
PurpleBlistNode
*
node
)
{
FinchBuddyList
*
ggblist
=
FINCH_BUDDY_LIST
(
list
);
PurpleBlistNode
*
parent
;
if
(
ggblist
==
NULL
||
g_object_get_data
(
G_OBJECT
(
node
),
UI_DATA
)
==
NULL
)
return
;
if
(
PURPLE_IS_GROUP
(
node
)
&&
ggblist
->
new_group
)
{
ggblist
->
new_group
=
g_list_remove
(
ggblist
->
new_group
,
node
);
}
gnt_tree_remove
(
GNT_TREE
(
ggblist
->
tree
),
node
);
if
(
ggblist
->
tagged
)
ggblist
->
tagged
=
g_list_remove
(
ggblist
->
tagged
,
node
);
parent
=
purple_blist_node_get_parent
(
node
);
for
(
node
=
purple_blist_node_get_first_child
(
node
);
node
;
node
=
purple_blist_node_get_sibling_next
(
node
))
node_remove
(
list
,
node
);
if
(
parent
)
{
if
(
!
ggblist
->
manager
->
can_add_node
(
parent
))
node_remove
(
list
,
parent
);
else
node_update
(
list
,
parent
);
}
draw_tooltip
(
ggblist
);
}
static
void
node_update
(
PurpleBuddyList
*
list
,
PurpleBlistNode
*
node
)
{
FinchBuddyList
*
ggblist
;
g_return_if_fail
(
FINCH_IS_BUDDY_LIST
(
list
));
/* It really looks like this should never happen ... but it does.
This will at least emit a warning to the log when it
happens, so maybe someone will figure it out. */
g_return_if_fail
(
node
!=
NULL
);
ggblist
=
FINCH_BUDDY_LIST
(
list
);
if
(
ggblist
->
window
==
NULL
)
{
return
;
}
if
(
g_object_get_data
(
G_OBJECT
(
node
),
UI_DATA
)
!=
NULL
)
{
gnt_tree_change_text
(
GNT_TREE
(
ggblist
->
tree
),
node
,
0
,
get_display_name
(
node
));
gnt_tree_sort_row
(
GNT_TREE
(
ggblist
->
tree
),
node
);
blist_update_row_flags
(
ggblist
,
node
);
if
(
gnt_tree_get_parent_key
(
GNT_TREE
(
ggblist
->
tree
),
node
)
!=
ggblist
->
manager
->
find_parent
(
node
))
{
node_remove
(
list
,
node
);
}
}
if
(
PURPLE_IS_BUDDY
(
node
))
{
PurpleBuddy
*
buddy
=
(
PurpleBuddy
*
)
node
;
add_node
((
PurpleBlistNode
*
)
buddy
,
FINCH_BUDDY_LIST
(
list
));
node_update
(
list
,
purple_blist_node_get_parent
(
node
));
}
else
if
(
PURPLE_IS_CHAT
(
node
))
{
add_node
(
node
,
FINCH_BUDDY_LIST
(
list
));
}
else
if
(
PURPLE_IS_META_CONTACT
(
node
))
{
if
(
g_object_get_data
(
G_OBJECT
(
node
),
UI_DATA
)
==
NULL
)
{
/* The core seems to expect the UI to add the buddies. */
for
(
node
=
purple_blist_node_get_first_child
(
node
);
node
;
node
=
purple_blist_node_get_sibling_next
(
node
))
add_node
(
node
,
FINCH_BUDDY_LIST
(
list
));
}
}
else
if
(
PURPLE_IS_GROUP
(
node
))
{
if
(
!
ggblist
->
manager
->
can_add_node
(
node
))
node_remove
(
list
,
node
);
else
add_node
(
node
,
FINCH_BUDDY_LIST
(
list
));
}
if
(
ggblist
->
tnode
==
node
)
{
draw_tooltip
(
ggblist
);
}
}
static
gboolean
remove_new_empty_group
(
G_GNUC_UNUSED
gpointer
data
)
{
PurpleBuddyList
*
list
;
FinchBuddyList
*
ggblist
;
list
=
purple_blist_get_default
();
g_return_val_if_fail
(
list
,
FALSE
);
ggblist
=
FINCH_BUDDY_LIST
(
list
);
ggblist
->
new_group_timeout
=
0
;
while
(
ggblist
->
new_group
)
{
PurpleBlistNode
*
group
=
ggblist
->
new_group
->
data
;
ggblist
->
new_group
=
g_list_delete_link
(
ggblist
->
new_group
,
ggblist
->
new_group
);
node_update
(
list
,
group
);
}
return
FALSE
;
}
static
void
add_buddy_cb
(
G_GNUC_UNUSED
gpointer
data
,
PurpleRequestPage
*
page
)
{
const
char
*
username
=
purple_request_page_get_string
(
page
,
"screenname"
);
const
char
*
alias
=
purple_request_page_get_string
(
page
,
"alias"
);
const
char
*
group
=
purple_request_page_get_string
(
page
,
"group"
);
const
char
*
invite
=
purple_request_page_get_string
(
page
,
"invite"
);
PurpleAccount
*
account
=
purple_request_page_get_account
(
page
,
"account"
);
const
char
*
error
=
NULL
;
PurpleGroup
*
grp
;
PurpleBuddy
*
buddy
;
if
(
!
username
)
error
=
_
(
"You must provide a username for the buddy."
);
else
if
(
!
group
)
error
=
_
(
"You must provide a group."
);
else
if
(
!
account
)
error
=
_
(
"You must select an account."
);
else
if
(
!
purple_account_is_connected
(
account
))
error
=
_
(
"The selected account is not online."
);
if
(
error
)
{
finch_request_add_buddy
(
purple_blist_get_default
(),
account
,
username
,
group
,
alias
);
purple_notify_error
(
NULL
,
_
(
"Error"
),
_
(
"Error adding buddy"
),
error
,
purple_request_cpar_from_account
(
account
));
return
;
}
grp
=
purple_blist_find_group
(
group
);
if
(
!
grp
)
{
grp
=
purple_group_new
(
group
);
purple_blist_add_group
(
grp
,
NULL
);
}
/* XXX: Ask to merge if there's already a buddy with the same alias in the same group (#4553) */
if
((
buddy
=
purple_blist_find_buddy_in_group
(
account
,
username
,
grp
))
==
NULL
)
{
buddy
=
purple_buddy_new
(
account
,
username
,
alias
);
purple_blist_add_buddy
(
buddy
,
NULL
,
grp
,
NULL
);
}
purple_account_add_buddy
(
account
,
buddy
,
invite
);
}
static
void
finch_request_add_buddy
(
G_GNUC_UNUSED
PurpleBuddyList
*
list
,
PurpleAccount
*
account
,
const
char
*
username
,
const
char
*
grp
,
const
char
*
alias
)
{
PurpleRequestPage
*
page
=
purple_request_page_new
();
PurpleRequestGroup
*
group
=
purple_request_group_new
(
NULL
);
PurpleRequestField
*
field
;
purple_request_page_add_group
(
page
,
group
);
field
=
purple_request_field_string_new
(
"screenname"
,
_
(
"Username"
),
username
,
FALSE
);
purple_request_group_add_field
(
group
,
field
);
field
=
purple_request_field_string_new
(
"alias"
,
_
(
"Alias (optional)"
),
alias
,
FALSE
);
purple_request_group_add_field
(
group
,
field
);
field
=
purple_request_field_string_new
(
"invite"
,
_
(
"Invite message (optional)"
),
NULL
,
FALSE
);
purple_request_group_add_field
(
group
,
field
);
field
=
purple_request_field_string_new
(
"group"
,
_
(
"Add in group"
),
grp
,
FALSE
);
purple_request_group_add_field
(
group
,
field
);
purple_request_field_set_type_hint
(
field
,
"group"
);
field
=
purple_request_field_account_new
(
"account"
,
_
(
"Account"
),
NULL
);
purple_request_field_account_set_show_all
(
PURPLE_REQUEST_FIELD_ACCOUNT
(
field
),
FALSE
);
if
(
account
)
{
purple_request_field_account_set_value
(
PURPLE_REQUEST_FIELD_ACCOUNT
(
field
),
account
);
}
purple_request_group_add_field
(
group
,
field
);
purple_request_fields
(
NULL
,
_
(
"Add Buddy"
),
NULL
,
_
(
"Please enter buddy information."
),
page
,
_
(
"Add"
),
G_CALLBACK
(
add_buddy_cb
),
_
(
"Cancel"
),
NULL
,
purple_request_cpar_from_account
(
account
),
NULL
);
}
static
void
join_chat
(
PurpleChat
*
chat
)
{
PurpleAccount
*
account
=
purple_chat_get_account
(
chat
);
PurpleConversationManager
*
manager
;
const
char
*
name
;
PurpleConversation
*
conv
;
name
=
purple_chat_get_name_only
(
chat
);
manager
=
purple_conversation_manager_get_default
();
conv
=
purple_conversation_manager_find_chat
(
manager
,
account
,
name
);
if
(
!
conv
||
purple_chat_conversation_has_left
(
PURPLE_CHAT_CONVERSATION
(
conv
)))
{
purple_serv_join_chat
(
purple_account_get_connection
(
account
),
purple_chat_get_components
(
chat
));
}
else
if
(
conv
)
{
purple_conversation_present
(
conv
);
}
}
static
void
add_chat_cb
(
G_GNUC_UNUSED
gpointer
data
,
PurpleRequestPage
*
page
)
{
PurpleAccount
*
account
;
const
char
*
alias
,
*
name
,
*
group
;
PurpleChat
*
chat
;
PurpleGroup
*
grp
;
GHashTable
*
hash
=
NULL
;
PurpleConnection
*
gc
;
gboolean
autojoin
;
PurpleProtocol
*
protocol
;
account
=
purple_request_page_get_account
(
page
,
"account"
);
name
=
purple_request_page_get_string
(
page
,
"name"
);
alias
=
purple_request_page_get_string
(
page
,
"alias"
);
group
=
purple_request_page_get_string
(
page
,
"group"
);
autojoin
=
purple_request_page_get_bool
(
page
,
"autojoin"
);
if
(
!
purple_account_is_connected
(
account
)
||
!
name
||
!*
name
)
return
;
if
(
!
group
||
!*
group
)
group
=
_
(
"Chats"
);
gc
=
purple_account_get_connection
(
account
);
protocol
=
purple_connection_get_protocol
(
gc
);
hash
=
purple_protocol_chat_info_defaults
(
PURPLE_PROTOCOL_CHAT
(
protocol
),
gc
,
name
);
chat
=
purple_chat_new
(
account
,
name
,
hash
);
if
(
chat
!=
NULL
)
{
if
((
grp
=
purple_blist_find_group
(
group
))
==
NULL
)
{
grp
=
purple_group_new
(
group
);
purple_blist_add_group
(
grp
,
NULL
);
}
purple_blist_add_chat
(
chat
,
grp
,
NULL
);
purple_chat_set_alias
(
chat
,
alias
);
purple_blist_node_set_bool
((
PurpleBlistNode
*
)
chat
,
"gnt-autojoin"
,
autojoin
);
if
(
autojoin
)
{
join_chat
(
chat
);
}
}
}
static
void
finch_request_add_chat
(
G_GNUC_UNUSED
PurpleBuddyList
*
list
,
PurpleAccount
*
account
,
PurpleGroup
*
grp
,
const
char
*
alias
,
const
char
*
name
)
{
PurpleRequestPage
*
page
=
purple_request_page_new
();
PurpleRequestGroup
*
group
=
purple_request_group_new
(
NULL
);
PurpleRequestField
*
field
;
purple_request_page_add_group
(
page
,
group
);
field
=
purple_request_field_account_new
(
"account"
,
_
(
"Account"
),
NULL
);
purple_request_field_account_set_show_all
(
PURPLE_REQUEST_FIELD_ACCOUNT
(
field
),
FALSE
);
if
(
account
)
{
purple_request_field_account_set_value
(
PURPLE_REQUEST_FIELD_ACCOUNT
(
field
),
account
);
}
purple_request_group_add_field
(
group
,
field
);
field
=
purple_request_field_string_new
(
"name"
,
_
(
"Name"
),
name
,
FALSE
);
purple_request_group_add_field
(
group
,
field
);
field
=
purple_request_field_string_new
(
"alias"
,
_
(
"Alias"
),
alias
,
FALSE
);
purple_request_group_add_field
(
group
,
field
);
field
=
purple_request_field_string_new
(
"group"
,
_
(
"Group"
),
grp
?
purple_group_get_name
(
grp
)
:
NULL
,
FALSE
);
purple_request_group_add_field
(
group
,
field
);
purple_request_field_set_type_hint
(
field
,
"group"
);
field
=
purple_request_field_bool_new
(
"autojoin"
,
_
(
"Auto-join"
),
FALSE
);
purple_request_group_add_field
(
group
,
field
);
purple_request_fields
(
NULL
,
_
(
"Add Chat"
),
NULL
,
_
(
"You can edit more information from the context menu later."
),
page
,
_
(
"Add"
),
G_CALLBACK
(
add_chat_cb
),
_
(
"Cancel"
),
NULL
,
NULL
,
NULL
);
}
static
void
add_group_cb
(
FinchBuddyList
*
ggblist
,
const
char
*
group
)
{
PurpleGroup
*
grp
;
if
(
!
group
||
!*
group
)
{
purple_notify_error
(
NULL
,
_
(
"Error"
),
_
(
"Error adding group"
),
_
(
"You must give a name for the group to add."
),
NULL
);
g_object_unref
(
ggblist
);
return
;
}
grp
=
purple_blist_find_group
(
group
);
if
(
!
grp
)
{
grp
=
purple_group_new
(
group
);
purple_blist_add_group
(
grp
,
NULL
);
}
/* Treat the group as a new group even if it had existed before. This should
* make things easier to add buddies to empty groups (new or old) without having
* to turn on 'show empty groups' setting */
ggblist
->
new_group
=
g_list_prepend
(
ggblist
->
new_group
,
grp
);
g_clear_handle_id
(
&
ggblist
->
new_group_timeout
,
g_source_remove
);
ggblist
->
new_group_timeout
=
g_timeout_add_seconds
(
SHOW_EMPTY_GROUP_TIMEOUT
,
remove_new_empty_group
,
NULL
);
/* Select the group */
if
(
ggblist
->
tree
)
{
FinchBlistNode
*
fnode
=
g_object_get_data
(
G_OBJECT
(
grp
),
UI_DATA
);
if
(
!
fnode
)
add_node
((
PurpleBlistNode
*
)
grp
,
ggblist
);
gnt_tree_set_selected
(
GNT_TREE
(
ggblist
->
tree
),
grp
);
}
g_object_unref
(
ggblist
);
}
static
void
finch_request_add_group
(
PurpleBuddyList
*
list
)
{
purple_request_input
(
NULL
,
_
(
"Add Group"
),
NULL
,
_
(
"Enter the name of the group"
),
NULL
,
FALSE
,
FALSE
,
NULL
,
_
(
"Add"
),
G_CALLBACK
(
add_group_cb
),
_
(
"Cancel"
),
G_CALLBACK
(
g_object_unref
),
NULL
,
g_object_ref
(
list
));
}
static
gpointer
finch_blist_get_handle
(
void
)
{
static
int
handle
;
return
&
handle
;
}
static
void
add_group
(
PurpleGroup
*
group
,
FinchBuddyList
*
ggblist
)
{
gpointer
parent
;
PurpleBlistNode
*
node
=
(
PurpleBlistNode
*
)
group
;
if
(
g_object_get_data
(
G_OBJECT
(
node
),
UI_DATA
))
{
return
;
}
parent
=
ggblist
->
manager
->
find_parent
((
PurpleBlistNode
*
)
group
);
create_finch_blist_node
(
node
,
gnt_tree_add_row_after
(
GNT_TREE
(
ggblist
->
tree
),
group
,
gnt_tree_create_row
(
GNT_TREE
(
ggblist
->
tree
),
get_display_name
(
node
)),
parent
,
NULL
));
gnt_tree_set_expanded
(
GNT_TREE
(
ggblist
->
tree
),
node
,
!
purple_blist_node_get_bool
(
node
,
"collapsed"
));
}
static
const
char
*
get_display_name
(
PurpleBlistNode
*
node
)
{
static
char
text
[
2096
];
char
status
[
8
]
=
" "
;
const
char
*
name
=
NULL
;
if
(
PURPLE_IS_META_CONTACT
(
node
))
node
=
PURPLE_BLIST_NODE
(
purple_meta_contact_get_priority_buddy
(
PURPLE_META_CONTACT
(
node
)));
/* XXX: this can return NULL?! */
if
(
node
==
NULL
)
return
NULL
;
if
(
PURPLE_IS_BUDDY
(
node
))
{
PurpleBuddy
*
buddy
=
(
PurpleBuddy
*
)
node
;
PurpleStatusPrimitive
prim
;
PurplePresence
*
presence
;
PurpleStatus
*
now
;
gboolean
ascii
=
gnt_ascii_only
();
presence
=
purple_buddy_get_presence
(
buddy
);
if
(
purple_presence_is_status_primitive_active
(
presence
,
PURPLE_STATUS_MOBILE
))
strncpy
(
status
,
ascii
?
":"
:
"☎"
,
sizeof
(
status
)
-
1
);
else
{
now
=
purple_presence_get_active_status
(
presence
);
prim
=
purple_status_type_get_primitive
(
purple_status_get_status_type
(
now
));
switch
(
prim
)
{
case
PURPLE_STATUS_OFFLINE
:
strncpy
(
status
,
ascii
?
"x"
:
"⊗"
,
sizeof
(
status
)
-
1
);
break
;
case
PURPLE_STATUS_AVAILABLE
:
strncpy
(
status
,
ascii
?
"o"
:
"â—¯"
,
sizeof
(
status
)
-
1
);
break
;
default
:
strncpy
(
status
,
ascii
?
"."
:
"⊖"
,
sizeof
(
status
)
-
1
);
break
;
}
}
name
=
purple_buddy_get_alias
(
buddy
);
}
else
if
(
PURPLE_IS_CHAT
(
node
))
{
PurpleChat
*
chat
=
(
PurpleChat
*
)
node
;
name
=
purple_chat_get_name
(
chat
);
strncpy
(
status
,
"~"
,
sizeof
(
status
)
-
1
);
}
else
if
(
PURPLE_IS_GROUP
(
node
))
return
purple_group_get_name
((
PurpleGroup
*
)
node
);
g_snprintf
(
text
,
sizeof
(
text
)
-
1
,
"%s %s"
,
status
,
name
);
return
text
;
}
static
void
add_chat
(
PurpleChat
*
chat
,
FinchBuddyList
*
ggblist
)
{
gpointer
parent
;
PurpleBlistNode
*
node
=
(
PurpleBlistNode
*
)
chat
;
if
(
g_object_get_data
(
G_OBJECT
(
node
),
UI_DATA
))
{
return
;
}
if
(
!
purple_account_is_connected
(
purple_chat_get_account
(
chat
)))
{
return
;
}
parent
=
ggblist
->
manager
->
find_parent
((
PurpleBlistNode
*
)
chat
);
create_finch_blist_node
(
node
,
gnt_tree_add_row_after
(
GNT_TREE
(
ggblist
->
tree
),
chat
,
gnt_tree_create_row
(
GNT_TREE
(
ggblist
->
tree
),
get_display_name
(
node
)),
parent
,
NULL
));
}
static
void
add_contact
(
PurpleMetaContact
*
contact
,
FinchBuddyList
*
ggblist
)
{
gpointer
parent
;
PurpleBlistNode
*
node
=
(
PurpleBlistNode
*
)
contact
;
const
char
*
name
;
if
(
g_object_get_data
(
G_OBJECT
(
node
),
UI_DATA
))
{
return
;
}
name
=
get_display_name
(
node
);
if
(
name
==
NULL
)
return
;
parent
=
ggblist
->
manager
->
find_parent
((
PurpleBlistNode
*
)
contact
);
create_finch_blist_node
(
node
,
gnt_tree_add_row_after
(
GNT_TREE
(
ggblist
->
tree
),
contact
,
gnt_tree_create_row
(
GNT_TREE
(
ggblist
->
tree
),
name
),
parent
,
NULL
));
gnt_tree_set_expanded
(
GNT_TREE
(
ggblist
->
tree
),
contact
,
FALSE
);
}
static
void
add_buddy
(
PurpleBuddy
*
buddy
,
FinchBuddyList
*
ggblist
)
{
gpointer
parent
;
PurpleBlistNode
*
node
=
(
PurpleBlistNode
*
)
buddy
;
PurpleMetaContact
*
contact
;
if
(
g_object_get_data
(
G_OBJECT
(
node
),
UI_DATA
))
{
return
;
}
contact
=
purple_buddy_get_contact
(
buddy
);
parent
=
ggblist
->
manager
->
find_parent
((
PurpleBlistNode
*
)
buddy
);
create_finch_blist_node
(
node
,
gnt_tree_add_row_after
(
GNT_TREE
(
ggblist
->
tree
),
buddy
,
gnt_tree_create_row
(
GNT_TREE
(
ggblist
->
tree
),
get_display_name
(
node
)),
parent
,
NULL
));
blist_update_row_flags
(
ggblist
,
(
PurpleBlistNode
*
)
buddy
);
if
(
buddy
==
purple_meta_contact_get_priority_buddy
(
contact
))
{
blist_update_row_flags
(
ggblist
,
(
PurpleBlistNode
*
)
contact
);
}
}
static
void
selection_activate
(
G_GNUC_UNUSED
GntWidget
*
widget
,
FinchBuddyList
*
ggblist
)
{
GntTree
*
tree
=
GNT_TREE
(
ggblist
->
tree
);
PurpleBlistNode
*
node
=
gnt_tree_get_selection_data
(
tree
);
if
(
!
node
)
return
;
if
(
PURPLE_IS_META_CONTACT
(
node
))
node
=
PURPLE_BLIST_NODE
(
purple_meta_contact_get_priority_buddy
(
PURPLE_META_CONTACT
(
node
)));
if
(
PURPLE_IS_BUDDY
(
node
))
{
PurpleBuddy
*
buddy
=
(
PurpleBuddy
*
)
node
;
PurpleConversation
*
im
;
PurpleConversationManager
*
manager
;
manager
=
purple_conversation_manager_get_default
();
im
=
purple_conversation_manager_find_im
(
manager
,
purple_buddy_get_account
(
buddy
),
purple_buddy_get_name
(
buddy
));
if
(
!
PURPLE_IS_IM_CONVERSATION
(
im
))
{
im
=
purple_im_conversation_new
(
purple_buddy_get_account
(
buddy
),
purple_buddy_get_name
(
buddy
));
}
else
{
FinchConv
*
ggconv
=
FINCH_CONV
(
im
);
gnt_window_present
(
ggconv
->
window
);
}
finch_conversation_set_active
(
im
);
}
else
if
(
PURPLE_IS_CHAT
(
node
))
{
join_chat
((
PurpleChat
*
)
node
);
}
}
static
void
append_proto_menu
(
GntMenu
*
menu
,
PurpleConnection
*
gc
,
PurpleBlistNode
*
node
)
{
GList
*
list
;
PurpleProtocol
*
protocol
=
purple_connection_get_protocol
(
gc
);
if
(
!
PURPLE_PROTOCOL_IMPLEMENTS
(
protocol
,
CLIENT
,
blist_node_menu
))
{
return
;
}
for
(
list
=
purple_protocol_client_blist_node_menu
(
PURPLE_PROTOCOL_CLIENT
(
protocol
),
node
);
list
;
list
=
g_list_delete_link
(
list
,
list
))
{
PurpleActionMenu
*
act
=
(
PurpleActionMenu
*
)
list
->
data
;
if
(
!
act
)
continue
;
purple_action_menu_set_data
(
act
,
node
);
finch_append_menu_action
(
menu
,
act
,
node
);
}
}
static
void
add_custom_action
(
GntMenu
*
menu
,
const
char
*
label
,
GCallback
callback
,
gpointer
data
)
{
PurpleActionMenu
*
action
=
purple_action_menu_new
(
label
,
callback
,
data
,
NULL
);
finch_append_menu_action
(
menu
,
action
,
NULL
);
}
static
void
chat_components_edit_ok
(
PurpleChat
*
chat
,
PurpleRequestPage
*
page
)
{
guint
n_groups
;
n_groups
=
g_list_model_get_n_items
(
G_LIST_MODEL
(
page
));
for
(
guint
group_index
=
0
;
group_index
<
n_groups
;
group_index
++
)
{
GListModel
*
group
=
NULL
;
guint
n_fields
=
0
;
group
=
g_list_model_get_item
(
G_LIST_MODEL
(
page
),
group_index
);
n_fields
=
g_list_model_get_n_items
(
group
);
for
(
guint
field_index
=
0
;
field_index
<
n_fields
;
field_index
++
)
{
PurpleRequestField
*
field
=
NULL
;
const
char
*
id
;
char
*
val
;
field
=
g_list_model_get_item
(
group
,
field_index
);
id
=
purple_request_field_get_id
(
field
);
if
(
PURPLE_IS_REQUEST_FIELD_INT
(
field
))
{
PurpleRequestFieldInt
*
ifield
=
PURPLE_REQUEST_FIELD_INT
(
field
);
val
=
g_strdup_printf
(
"%d"
,
purple_request_field_int_get_value
(
ifield
));
}
else
{
val
=
g_strdup
(
purple_request_field_string_get_value
(
PURPLE_REQUEST_FIELD_STRING
(
field
)));
}
if
(
!
val
)
{
g_hash_table_remove
(
purple_chat_get_components
(
chat
),
id
);
}
else
{
g_hash_table_replace
(
purple_chat_get_components
(
chat
),
g_strdup
(
id
),
val
);
/* val should not be free'd */
}
g_object_unref
(
field
);
}
g_object_unref
(
group
);
}
}
static
void
chat_components_edit
(
G_GNUC_UNUSED
PurpleBlistNode
*
selected
,
PurpleChat
*
chat
)
{
PurpleRequestPage
*
page
=
purple_request_page_new
();
PurpleRequestGroup
*
group
=
purple_request_group_new
(
NULL
);
PurpleRequestField
*
field
;
GList
*
parts
,
*
iter
;
PurpleProtocol
*
protocol
;
PurpleProtocolChatEntry
*
pce
;
PurpleConnection
*
gc
;
purple_request_page_add_group
(
page
,
group
);
gc
=
purple_account_get_connection
(
purple_chat_get_account
(
chat
));
protocol
=
purple_connection_get_protocol
(
gc
);
parts
=
purple_protocol_chat_info
(
PURPLE_PROTOCOL_CHAT
(
protocol
),
gc
);
for
(
iter
=
parts
;
iter
;
iter
=
iter
->
next
)
{
pce
=
iter
->
data
;
if
(
pce
->
is_int
)
{
int
val
;
const
char
*
str
=
g_hash_table_lookup
(
purple_chat_get_components
(
chat
),
pce
->
identifier
);
if
(
!
str
||
sscanf
(
str
,
"%d"
,
&
val
)
!=
1
)
val
=
pce
->
min
;
field
=
purple_request_field_int_new
(
pce
->
identifier
,
pce
->
label
,
val
,
INT_MIN
,
INT_MAX
);
}
else
{
field
=
purple_request_field_string_new
(
pce
->
identifier
,
pce
->
label
,
g_hash_table_lookup
(
purple_chat_get_components
(
chat
),
pce
->
identifier
),
FALSE
);
if
(
pce
->
secret
)
{
purple_request_field_string_set_masked
(
PURPLE_REQUEST_FIELD_STRING
(
field
),
TRUE
);
}
}
if
(
pce
->
required
)
purple_request_field_set_required
(
field
,
TRUE
);
purple_request_group_add_field
(
group
,
field
);
g_free
(
pce
);
}
g_list_free
(
parts
);
purple_request_fields
(
NULL
,
_
(
"Edit Chat"
),
NULL
,
_
(
"Please Update the necessary fields."
),
page
,
_
(
"Edit"
),
G_CALLBACK
(
chat_components_edit_ok
),
_
(
"Cancel"
),
NULL
,
NULL
,
chat
);
}
static
void
autojoin_toggled
(
GntMenuItem
*
item
,
gpointer
data
)
{
PurpleActionMenu
*
action
=
data
;
purple_blist_node_set_bool
(
purple_action_menu_get_data
(
action
),
"gnt-autojoin"
,
gnt_menuitem_check_get_checked
(
GNT_MENU_ITEM_CHECK
(
item
)));
}
static
void
create_chat_menu
(
GntMenu
*
menu
,
PurpleChat
*
chat
)
{
PurpleActionMenu
*
action
=
purple_action_menu_new
(
_
(
"Auto-join"
),
NULL
,
chat
,
NULL
);
GntMenuItem
*
check
=
gnt_menuitem_check_new
(
purple_action_menu_get_label
(
action
));
gnt_menuitem_check_set_checked
(
GNT_MENU_ITEM_CHECK
(
check
),
purple_blist_node_get_bool
((
PurpleBlistNode
*
)
chat
,
"gnt-autojoin"
));
gnt_menu_add_item
(
menu
,
check
);
gnt_menuitem_set_callback
(
check
,
autojoin_toggled
,
action
);
g_signal_connect_swapped
(
G_OBJECT
(
menu
),
"destroy"
,
G_CALLBACK
(
purple_action_menu_free
),
action
);
/* Protocol actions */
append_proto_menu
(
menu
,
purple_account_get_connection
(
purple_chat_get_account
(
chat
)),
(
PurpleBlistNode
*
)
chat
);
add_custom_action
(
menu
,
_
(
"Edit Settings"
),
(
GCallback
)
chat_components_edit
,
chat
);
}
static
void
finch_add_buddy
(
G_GNUC_UNUSED
PurpleBlistNode
*
selected
,
PurpleGroup
*
grp
)
{
purple_blist_request_add_buddy
(
NULL
,
NULL
,
grp
?
purple_group_get_name
(
grp
)
:
NULL
,
NULL
);
}
static
void
finch_add_group
(
G_GNUC_UNUSED
PurpleBlistNode
*
selected
,
G_GNUC_UNUSED
PurpleGroup
*
grp
)
{
purple_blist_request_add_group
();
}
static
void
finch_add_chat
(
G_GNUC_UNUSED
PurpleBlistNode
*
selected
,
PurpleGroup
*
grp
)
{
purple_blist_request_add_chat
(
NULL
,
grp
,
NULL
,
NULL
);
}
static
void
create_group_menu
(
GntMenu
*
menu
,
PurpleGroup
*
group
)
{
add_custom_action
(
menu
,
_
(
"Add Buddy"
),
G_CALLBACK
(
finch_add_buddy
),
group
);
add_custom_action
(
menu
,
_
(
"Add Chat"
),
G_CALLBACK
(
finch_add_chat
),
group
);
add_custom_action
(
menu
,
_
(
"Add Group"
),
G_CALLBACK
(
finch_add_group
),
group
);
}
gpointer
finch_retrieve_user_info
(
PurpleConnection
*
conn
,
const
char
*
name
)
{
PurpleProtocol
*
protocol
=
NULL
;
PurpleNotifyUserInfo
*
info
=
NULL
;
gpointer
uihandle
;
protocol
=
purple_connection_get_protocol
(
conn
);
if
(
!
PURPLE_IS_PROTOCOL_SERVER
(
protocol
))
{
return
NULL
;
}
purple_protocol_server_get_info
(
PURPLE_PROTOCOL_SERVER
(
protocol
),
conn
,
name
);
info
=
purple_notify_user_info_new
();
purple_notify_user_info_add_pair_plaintext
(
info
,
_
(
"Information"
),
_
(
"Retrieving..."
));
uihandle
=
purple_notify_userinfo
(
conn
,
name
,
info
,
NULL
,
NULL
);
purple_notify_user_info_destroy
(
info
);
return
uihandle
;
}
static
void
finch_blist_get_buddy_info_cb
(
G_GNUC_UNUSED
PurpleBlistNode
*
selected
,
PurpleBuddy
*
buddy
)
{
finch_retrieve_user_info
(
purple_account_get_connection
(
purple_buddy_get_account
(
buddy
)),
purple_buddy_get_name
(
buddy
));
}
static
void
finch_blist_menu_send_file_cb
(
G_GNUC_UNUSED
PurpleBlistNode
*
selected
,
PurpleBuddy
*
buddy
)
{
purple_serv_send_file
(
purple_account_get_connection
(
purple_buddy_get_account
(
buddy
)),
purple_buddy_get_name
(
buddy
),
NULL
);
}
static
void
toggle_show_offline
(
G_GNUC_UNUSED
GntMenuItem
*
item
,
gpointer
buddy
)
{
purple_blist_node_set_bool
(
buddy
,
"show_offline"
,
!
purple_blist_node_get_bool
(
buddy
,
"show_offline"
));
if
(
!
ggblist
->
manager
->
can_add_node
(
buddy
))
node_remove
(
purple_blist_get_default
(),
buddy
);
else
node_update
(
purple_blist_get_default
(),
buddy
);
}
static
void
create_buddy_menu
(
GntMenu
*
menu
,
PurpleBuddy
*
buddy
)
{
GntMenuItem
*
item
;
PurpleProtocol
*
protocol
;
PurpleConnection
*
gc
=
purple_account_get_connection
(
purple_buddy_get_account
(
buddy
));
protocol
=
purple_connection_get_protocol
(
gc
);
if
(
protocol
&&
PURPLE_PROTOCOL_IMPLEMENTS
(
protocol
,
SERVER
,
get_info
))
{
add_custom_action
(
menu
,
_
(
"Get Info"
),
G_CALLBACK
(
finch_blist_get_buddy_info_cb
),
buddy
);
}
if
(
PURPLE_IS_PROTOCOL_XFER
(
protocol
))
{
if
(
purple_protocol_xfer_can_receive
(
PURPLE_PROTOCOL_XFER
(
protocol
),
gc
,
purple_buddy_get_name
(
buddy
))
)
{
add_custom_action
(
menu
,
_
(
"Send File"
),
G_CALLBACK
(
finch_blist_menu_send_file_cb
),
buddy
);
}
}
item
=
gnt_menuitem_check_new
(
_
(
"Show when offline"
));
gnt_menuitem_check_set_checked
(
GNT_MENU_ITEM_CHECK
(
item
),
purple_blist_node_get_bool
((
PurpleBlistNode
*
)
buddy
,
"show_offline"
));
gnt_menuitem_set_callback
(
item
,
toggle_show_offline
,
buddy
);
gnt_menu_add_item
(
menu
,
item
);
/* Protocol actions */
append_proto_menu
(
menu
,
purple_account_get_connection
(
purple_buddy_get_account
(
buddy
)),
(
PurpleBlistNode
*
)
buddy
);
}
static
void
append_extended_menu
(
GntMenu
*
menu
,
PurpleBlistNode
*
node
)
{
GList
*
iter
;
for
(
iter
=
purple_blist_node_get_extended_menu
(
node
);
iter
;
iter
=
g_list_delete_link
(
iter
,
iter
))
{
finch_append_menu_action
(
menu
,
iter
->
data
,
node
);
}
}
/* Xerox'd from gtkdialogs.c:purple_gtkdialogs_remove_contact_cb */
static
void
remove_contact
(
PurpleMetaContact
*
contact
)
{
PurpleBlistNode
*
bnode
,
*
cnode
;
PurpleGroup
*
group
;
cnode
=
(
PurpleBlistNode
*
)
contact
;
group
=
(
PurpleGroup
*
)
purple_blist_node_get_parent
(
cnode
);
for
(
bnode
=
purple_blist_node_get_first_child
(
cnode
);
bnode
;
bnode
=
purple_blist_node_get_sibling_next
(
bnode
))
{
PurpleBuddy
*
buddy
=
(
PurpleBuddy
*
)
bnode
;
PurpleAccount
*
account
=
purple_buddy_get_account
(
buddy
);
if
(
purple_account_is_connected
(
account
))
purple_account_remove_buddy
(
account
,
buddy
,
group
);
}
purple_blist_remove_contact
(
contact
);
}
static
void
rename_blist_node
(
PurpleBlistNode
*
node
,
const
char
*
newname
)
{
const
char
*
name
=
newname
;
if
(
name
&&
!*
name
)
name
=
NULL
;
if
(
PURPLE_IS_META_CONTACT
(
node
))
{
PurpleMetaContact
*
contact
=
(
PurpleMetaContact
*
)
node
;
PurpleBuddy
*
buddy
=
purple_meta_contact_get_priority_buddy
(
contact
);
purple_meta_contact_set_alias
(
contact
,
name
);
purple_buddy_set_local_alias
(
buddy
,
name
);
purple_serv_alias_buddy
(
buddy
);
}
else
if
(
PURPLE_IS_BUDDY
(
node
))
{
purple_buddy_set_local_alias
((
PurpleBuddy
*
)
node
,
name
);
purple_serv_alias_buddy
((
PurpleBuddy
*
)
node
);
}
else
if
(
PURPLE_IS_CHAT
(
node
))
purple_chat_set_alias
((
PurpleChat
*
)
node
,
name
);
else
if
(
PURPLE_IS_GROUP
(
node
)
&&
(
name
!=
NULL
))
purple_group_set_name
((
PurpleGroup
*
)
node
,
name
);
else
g_return_if_reached
();
}
static
void
finch_blist_rename_node_cb
(
G_GNUC_UNUSED
PurpleBlistNode
*
selected
,
PurpleBlistNode
*
node
)
{
const
char
*
name
=
NULL
;
char
*
prompt
;
const
char
*
text
;
if
(
PURPLE_IS_META_CONTACT
(
node
))
name
=
purple_meta_contact_get_alias
((
PurpleMetaContact
*
)
node
);
else
if
(
PURPLE_IS_BUDDY
(
node
))
name
=
purple_buddy_get_contact_alias
((
PurpleBuddy
*
)
node
);
else
if
(
PURPLE_IS_CHAT
(
node
))
name
=
purple_chat_get_name
((
PurpleChat
*
)
node
);
else
if
(
PURPLE_IS_GROUP
(
node
))
name
=
purple_group_get_name
((
PurpleGroup
*
)
node
);
else
g_return_if_reached
();
prompt
=
g_strdup_printf
(
_
(
"Please enter the new name for %s"
),
name
);
text
=
PURPLE_IS_GROUP
(
node
)
?
_
(
"Rename"
)
:
_
(
"Set Alias"
);
purple_request_input
(
node
,
text
,
prompt
,
_
(
"Enter empty string to reset the name."
),
name
,
FALSE
,
FALSE
,
NULL
,
text
,
G_CALLBACK
(
rename_blist_node
),
_
(
"Cancel"
),
NULL
,
NULL
,
node
);
g_free
(
prompt
);
}
/* Xeroxed from gtkdialogs.c:purple_gtkdialogs_remove_group_cb*/
static
void
remove_group
(
PurpleGroup
*
group
)
{
PurpleBlistNode
*
cnode
,
*
bnode
;
cnode
=
purple_blist_node_get_first_child
(((
PurpleBlistNode
*
)
group
));
while
(
cnode
)
{
if
(
PURPLE_IS_META_CONTACT
(
cnode
))
{
bnode
=
purple_blist_node_get_first_child
(
cnode
);
cnode
=
purple_blist_node_get_sibling_next
(
cnode
);
while
(
bnode
)
{
PurpleBuddy
*
buddy
;
if
(
PURPLE_IS_BUDDY
(
bnode
))
{
PurpleAccount
*
account
;
buddy
=
(
PurpleBuddy
*
)
bnode
;
bnode
=
purple_blist_node_get_sibling_next
(
bnode
);
account
=
purple_buddy_get_account
(
buddy
);
if
(
purple_account_is_connected
(
account
))
{
purple_account_remove_buddy
(
account
,
buddy
,
group
);
purple_blist_remove_buddy
(
buddy
);
}
}
else
{
bnode
=
purple_blist_node_get_sibling_next
(
bnode
);
}
}
}
else
if
(
PURPLE_IS_CHAT
(
cnode
))
{
PurpleChat
*
chat
=
(
PurpleChat
*
)
cnode
;
cnode
=
purple_blist_node_get_sibling_next
(
cnode
);
if
(
purple_account_is_connected
(
purple_chat_get_account
(
chat
)))
purple_blist_remove_chat
(
chat
);
}
else
{
cnode
=
purple_blist_node_get_sibling_next
(
cnode
);
}
}
purple_blist_remove_group
(
group
);
}
static
void
finch_blist_remove_node
(
PurpleBlistNode
*
node
)
{
if
(
PURPLE_IS_META_CONTACT
(
node
))
{
remove_contact
((
PurpleMetaContact
*
)
node
);
}
else
if
(
PURPLE_IS_BUDDY
(
node
))
{
PurpleBuddy
*
buddy
=
(
PurpleBuddy
*
)
node
;
PurpleGroup
*
group
=
purple_buddy_get_group
(
buddy
);
purple_account_remove_buddy
(
purple_buddy_get_account
(
buddy
),
buddy
,
group
);
purple_blist_remove_buddy
(
buddy
);
}
else
if
(
PURPLE_IS_CHAT
(
node
))
{
purple_blist_remove_chat
((
PurpleChat
*
)
node
);
}
else
if
(
PURPLE_IS_GROUP
(
node
))
{
remove_group
((
PurpleGroup
*
)
node
);
}
}
static
void
finch_blist_remove_node_cb
(
G_GNUC_UNUSED
PurpleBlistNode
*
selected
,
PurpleBlistNode
*
node
)
{
PurpleAccount
*
account
=
NULL
;
char
*
primary
;
const
char
*
name
,
*
sec
=
NULL
;
if
(
PURPLE_IS_META_CONTACT
(
node
))
{
PurpleMetaContact
*
c
=
(
PurpleMetaContact
*
)
node
;
name
=
purple_meta_contact_get_alias
(
c
);
if
(
purple_counting_node_get_total_size
(
PURPLE_COUNTING_NODE
(
c
))
>
1
)
sec
=
_
(
"Removing this contact will also remove all the buddies in the contact"
);
}
else
if
(
PURPLE_IS_BUDDY
(
node
))
{
name
=
purple_buddy_get_name
((
PurpleBuddy
*
)
node
);
account
=
purple_buddy_get_account
((
PurpleBuddy
*
)
node
);
}
else
if
(
PURPLE_IS_CHAT
(
node
))
{
name
=
purple_chat_get_name
((
PurpleChat
*
)
node
);
}
else
if
(
PURPLE_IS_GROUP
(
node
))
{
name
=
purple_group_get_name
((
PurpleGroup
*
)
node
);
sec
=
_
(
"Removing this group will also remove all the buddies in the group"
);
}
else
return
;
primary
=
g_strdup_printf
(
_
(
"Are you sure you want to remove %s?"
),
name
);
/* XXX: anything to do with the returned ui-handle? */
purple_request_action
(
node
,
_
(
"Confirm Remove"
),
primary
,
sec
,
1
,
purple_request_cpar_from_account
(
account
),
node
,
2
,
_
(
"Remove"
),
finch_blist_remove_node
,
_
(
"Cancel"
),
NULL
);
g_free
(
primary
);
}
static
void
finch_blist_toggle_tag_buddy
(
PurpleBlistNode
*
node
)
{
GList
*
iter
;
if
(
node
==
NULL
)
return
;
if
(
ggblist
->
tagged
&&
(
iter
=
g_list_find
(
ggblist
->
tagged
,
node
))
!=
NULL
)
{
ggblist
->
tagged
=
g_list_delete_link
(
ggblist
->
tagged
,
iter
);
}
else
{
ggblist
->
tagged
=
g_list_prepend
(
ggblist
->
tagged
,
node
);
}
if
(
PURPLE_IS_META_CONTACT
(
node
))
update_buddy_display
(
purple_meta_contact_get_priority_buddy
(
PURPLE_META_CONTACT
(
node
)),
ggblist
);
else
if
(
PURPLE_IS_BUDDY
(
node
))
update_buddy_display
((
PurpleBuddy
*
)
node
,
ggblist
);
else
update_node_display
(
node
,
ggblist
);
}
static
void
finch_blist_place_tagged
(
PurpleBlistNode
*
target
)
{
PurpleGroup
*
tg
=
NULL
;
PurpleMetaContact
*
tc
=
NULL
;
if
(
PURPLE_IS_GROUP
(
target
))
tg
=
(
PurpleGroup
*
)
target
;
else
if
(
PURPLE_IS_BUDDY
(
target
))
{
tc
=
(
PurpleMetaContact
*
)
purple_blist_node_get_parent
(
target
);
tg
=
(
PurpleGroup
*
)
purple_blist_node_get_parent
((
PurpleBlistNode
*
)
tc
);
}
else
if
(
PURPLE_IS_META_CONTACT
(
target
))
{
tc
=
(
PurpleMetaContact
*
)
target
;
tg
=
(
PurpleGroup
*
)
purple_blist_node_get_parent
(
target
);
}
else
if
(
PURPLE_IS_CHAT
(
target
))
{
tg
=
(
PurpleGroup
*
)
purple_blist_node_get_parent
(
target
);
}
else
{
return
;
}
if
(
ggblist
->
tagged
)
{
GList
*
list
=
ggblist
->
tagged
;
ggblist
->
tagged
=
NULL
;
while
(
list
)
{
PurpleBlistNode
*
node
=
list
->
data
;
list
=
g_list_delete_link
(
list
,
list
);
if
(
PURPLE_IS_GROUP
(
node
))
{
update_node_display
(
node
,
ggblist
);
/* Add the group after the current group */
purple_blist_add_group
((
PurpleGroup
*
)
node
,
(
PurpleBlistNode
*
)
tg
);
}
else
if
(
PURPLE_IS_META_CONTACT
(
node
))
{
update_buddy_display
(
purple_meta_contact_get_priority_buddy
((
PurpleMetaContact
*
)
node
),
ggblist
);
if
(
PURPLE_BLIST_NODE
(
tg
)
==
target
)
{
/* The target is a group, just add the contact to the group. */
purple_blist_add_contact
((
PurpleMetaContact
*
)
node
,
tg
,
NULL
);
}
else
if
(
tc
)
{
/* The target is either a buddy, or a contact. Merge with that contact. */
purple_meta_contact_merge
((
PurpleMetaContact
*
)
node
,
(
PurpleBlistNode
*
)
tc
);
}
else
{
/* The target is a chat. Add the contact to the group after this chat. */
purple_blist_add_contact
((
PurpleMetaContact
*
)
node
,
NULL
,
target
);
}
}
else
if
(
PURPLE_IS_BUDDY
(
node
))
{
update_buddy_display
((
PurpleBuddy
*
)
node
,
ggblist
);
if
(
PURPLE_BLIST_NODE
(
tg
)
==
target
)
{
/* The target is a group. Add this buddy in a new contact under this group. */
purple_blist_add_buddy
((
PurpleBuddy
*
)
node
,
NULL
,
tg
,
NULL
);
}
else
if
(
PURPLE_IS_META_CONTACT
(
target
))
{
/* Add to the contact. */
purple_blist_add_buddy
((
PurpleBuddy
*
)
node
,
tc
,
NULL
,
NULL
);
}
else
if
(
PURPLE_IS_BUDDY
(
target
))
{
/* Add to the contact after the selected buddy. */
purple_blist_add_buddy
((
PurpleBuddy
*
)
node
,
NULL
,
NULL
,
target
);
}
else
if
(
PURPLE_IS_CHAT
(
target
))
{
/* Add to the selected chat's group. */
purple_blist_add_buddy
((
PurpleBuddy
*
)
node
,
NULL
,
tg
,
NULL
);
}
}
else
if
(
PURPLE_IS_CHAT
(
node
))
{
update_node_display
(
node
,
ggblist
);
if
(
PURPLE_BLIST_NODE
(
tg
)
==
target
)
purple_blist_add_chat
((
PurpleChat
*
)
node
,
tg
,
NULL
);
else
purple_blist_add_chat
((
PurpleChat
*
)
node
,
NULL
,
target
);
}
}
}
}
static
void
context_menu_destroyed
(
G_GNUC_UNUSED
GntWidget
*
widget
,
FinchBuddyList
*
ggblist
)
{
ggblist
->
context
=
NULL
;
}
static
void
draw_context_menu
(
FinchBuddyList
*
ggblist
)
{
PurpleBlistNode
*
node
=
NULL
;
GntWidget
*
context
=
NULL
;
GntTree
*
tree
=
NULL
;
int
x
,
y
,
top
,
width
;
char
*
title
=
NULL
;
if
(
ggblist
->
context
)
return
;
tree
=
GNT_TREE
(
ggblist
->
tree
);
node
=
gnt_tree_get_selection_data
(
tree
);
if
(
node
&&
!
(
PURPLE_IS_BUDDY
(
node
)
||
PURPLE_IS_META_CONTACT
(
node
)
||
PURPLE_IS_GROUP
(
node
)
||
PURPLE_IS_CHAT
(
node
)))
return
;
if
(
ggblist
->
tooltip
)
remove_tooltip
(
ggblist
);
ggblist
->
cnode
=
node
;
ggblist
->
context
=
context
=
gnt_menu_new
(
GNT_MENU_POPUP
);
g_signal_connect
(
G_OBJECT
(
context
),
"destroy"
,
G_CALLBACK
(
context_menu_destroyed
),
ggblist
);
g_signal_connect
(
G_OBJECT
(
context
),
"hide"
,
G_CALLBACK
(
gnt_widget_destroy
),
NULL
);
if
(
!
node
)
{
create_group_menu
(
GNT_MENU
(
context
),
NULL
);
title
=
g_strdup
(
_
(
"Buddy List"
));
}
else
if
(
PURPLE_IS_META_CONTACT
(
node
))
{
ggblist
->
cnode
=
PURPLE_BLIST_NODE
(
purple_meta_contact_get_priority_buddy
(
PURPLE_META_CONTACT
(
node
)));
create_buddy_menu
(
GNT_MENU
(
context
),
(
PurpleBuddy
*
)
ggblist
->
cnode
);
title
=
g_strdup
(
purple_meta_contact_get_alias
((
PurpleMetaContact
*
)
node
));
}
else
if
(
PURPLE_IS_BUDDY
(
node
))
{
PurpleBuddy
*
buddy
=
(
PurpleBuddy
*
)
node
;
create_buddy_menu
(
GNT_MENU
(
context
),
buddy
);
title
=
g_strdup
(
purple_buddy_get_name
(
buddy
));
}
else
if
(
PURPLE_IS_CHAT
(
node
))
{
PurpleChat
*
chat
=
(
PurpleChat
*
)
node
;
create_chat_menu
(
GNT_MENU
(
context
),
chat
);
title
=
g_strdup
(
purple_chat_get_name
(
chat
));
}
else
if
(
PURPLE_IS_GROUP
(
node
))
{
PurpleGroup
*
group
=
(
PurpleGroup
*
)
node
;
create_group_menu
(
GNT_MENU
(
context
),
group
);
title
=
g_strdup
(
purple_group_get_name
(
group
));
}
append_extended_menu
(
GNT_MENU
(
context
),
node
);
/* These are common for everything */
if
(
node
)
{
add_custom_action
(
GNT_MENU
(
context
),
PURPLE_IS_GROUP
(
node
)
?
_
(
"Rename"
)
:
_
(
"Alias"
),
G_CALLBACK
(
finch_blist_rename_node_cb
),
node
);
add_custom_action
(
GNT_MENU
(
context
),
_
(
"Remove"
),
G_CALLBACK
(
finch_blist_remove_node_cb
),
node
);
if
(
ggblist
->
tagged
&&
(
PURPLE_IS_META_CONTACT
(
node
)
||
PURPLE_IS_GROUP
(
node
)))
{
add_custom_action
(
GNT_MENU
(
context
),
_
(
"Place tagged"
),
G_CALLBACK
(
finch_blist_place_tagged
),
node
);
}
if
(
PURPLE_IS_BUDDY
(
node
)
||
PURPLE_IS_META_CONTACT
(
node
))
{
add_custom_action
(
GNT_MENU
(
context
),
_
(
"Toggle Tag"
),
G_CALLBACK
(
finch_blist_toggle_tag_buddy
),
node
);
}
}
/* Set the position for the popup */
gnt_widget_get_position
(
GNT_WIDGET
(
tree
),
&
x
,
&
y
);
gnt_widget_get_size
(
GNT_WIDGET
(
tree
),
&
width
,
NULL
);
top
=
gnt_tree_get_selection_visible_line
(
tree
);
x
+=
width
;
y
+=
top
-
1
;
gnt_widget_set_position
(
context
,
x
,
y
);
gnt_screen_menu_show
(
GNT_MENU
(
context
));
g_free
(
title
);
}
static
void
tooltip_for_buddy
(
PurpleBuddy
*
buddy
,
GString
*
str
,
gboolean
full
)
{
PurpleAccount
*
account
=
NULL
;
PurpleContactInfo
*
info
=
NULL
;
PurpleNotifyUserInfo
*
user_info
;
PurplePresence
*
presence
=
NULL
;
const
char
*
alias
=
purple_buddy_get_alias
(
buddy
);
char
*
tmp
,
*
strip
;
user_info
=
purple_notify_user_info_new
();
account
=
purple_buddy_get_account
(
buddy
);
info
=
PURPLE_CONTACT_INFO
(
account
);
presence
=
purple_buddy_get_presence
(
buddy
);
if
(
!
full
||
g_utf8_collate
(
purple_buddy_get_name
(
buddy
),
alias
))
{
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Nickname"
),
alias
);
}
tmp
=
g_strdup_printf
(
"%s (%s)"
,
purple_contact_info_get_username
(
info
),
purple_account_get_protocol_name
(
account
));
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Account"
),
tmp
);
g_free
(
tmp
);
if
(
purple_prefs_get_bool
(
"/finch/blist/idletime"
))
{
PurplePresence
*
pre
=
purple_buddy_get_presence
(
buddy
);
if
(
purple_presence_is_idle
(
pre
))
{
GDateTime
*
idle
=
purple_presence_get_idle_time
(
pre
);
if
(
idle
!=
NULL
)
{
GDateTime
*
now
=
NULL
;
GTimeSpan
since
=
0
;
char
*
st
=
NULL
;
now
=
g_date_time_new_now_local
();
since
=
g_date_time_difference
(
now
,
idle
);
g_date_time_unref
(
now
);
st
=
purple_str_seconds_to_string
(
since
/
G_TIME_SPAN_SECOND
);
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Idle"
),
st
);
g_free
(
st
);
}
}
}
tmp
=
purple_notify_user_info_get_text_with_newline
(
user_info
,
"<BR>"
);
purple_notify_user_info_destroy
(
user_info
);
strip
=
purple_markup_strip_html
(
tmp
);
g_string_append
(
str
,
strip
);
if
(
purple_presence_is_status_primitive_active
(
presence
,
PURPLE_STATUS_MOBILE
))
{
g_string_append
(
str
,
"
\n
"
);
g_string_append
(
str
,
_
(
"On Mobile"
));
}
g_free
(
strip
);
g_free
(
tmp
);
}
static
GString
*
make_sure_text_fits
(
GString
*
string
)
{
int
maxw
=
getmaxx
(
stdscr
)
-
3
;
char
*
str
=
gnt_util_onscreen_fit_string
(
string
->
str
,
maxw
);
string
=
g_string_assign
(
string
,
str
);
g_free
(
str
);
return
string
;
}
static
gboolean
draw_tooltip_real
(
FinchBuddyList
*
ggblist
)
{
PurpleBlistNode
*
node
;
int
x
,
y
,
top
,
width
,
w
,
h
;
GString
*
str
=
NULL
;
GntTree
*
tree
;
GntWidget
*
widget
,
*
box
,
*
tv
;
char
*
title
=
NULL
;
widget
=
ggblist
->
tree
;
tree
=
GNT_TREE
(
widget
);
if
(
!
gnt_widget_has_focus
(
ggblist
->
tree
)
||
(
ggblist
->
context
&&
gnt_widget_get_visible
(
ggblist
->
context
)))
return
FALSE
;
if
(
ggblist
->
tooltip
)
{
/* XXX: Once we can properly redraw on expose events, this can be removed at the end
* to avoid the blinking*/
remove_tooltip
(
ggblist
);
}
node
=
gnt_tree_get_selection_data
(
tree
);
if
(
!
node
)
return
FALSE
;
if
(
!
ggblist
->
manager
->
create_tooltip
(
node
,
&
str
,
&
title
))
return
FALSE
;
gnt_widget_get_position
(
widget
,
&
x
,
&
y
);
gnt_widget_get_size
(
widget
,
&
width
,
NULL
);
top
=
gnt_tree_get_selection_visible_line
(
tree
);
x
+=
width
;
y
+=
top
-
1
;
box
=
gnt_box_new
(
FALSE
,
FALSE
);
gnt_box_set_toplevel
(
GNT_BOX
(
box
),
TRUE
);
gnt_widget_set_has_shadow
(
box
,
FALSE
);
gnt_box_set_title
(
GNT_BOX
(
box
),
title
);
str
=
make_sure_text_fits
(
str
);
gnt_util_get_text_bound
(
str
->
str
,
&
w
,
&
h
);
h
=
MAX
(
1
,
h
);
tv
=
gnt_text_view_new
();
gnt_widget_set_size
(
tv
,
w
+
1
,
h
);
gnt_text_view_set_flag
(
GNT_TEXT_VIEW
(
tv
),
GNT_TEXT_VIEW_NO_SCROLL
);
gnt_box_add_widget
(
GNT_BOX
(
box
),
tv
);
if
(
x
+
w
>=
getmaxx
(
stdscr
))
x
-=
w
+
width
+
2
;
gnt_widget_set_position
(
box
,
x
,
y
);
gnt_widget_set_take_focus
(
box
,
FALSE
);
gnt_widget_set_transient
(
box
,
TRUE
);
gnt_widget_draw
(
box
);
gnt_text_view_append_text_with_flags
(
GNT_TEXT_VIEW
(
tv
),
str
->
str
,
GNT_TEXT_FLAG_NORMAL
);
gnt_text_view_scroll
(
GNT_TEXT_VIEW
(
tv
),
0
);
g_free
(
title
);
g_string_free
(
str
,
TRUE
);
ggblist
->
tooltip
=
box
;
ggblist
->
tnode
=
node
;
gnt_widget_set_name
(
ggblist
->
tooltip
,
"tooltip"
);
return
FALSE
;
}
static
void
draw_tooltip
(
FinchBuddyList
*
ggblist
)
{
/* When an account has signed off, it removes one buddy at a time.
* Drawing the tooltip after removing each buddy is expensive. On
* top of that, if the selected buddy belongs to the disconnected
* account, then retrieving the tooltip for that causes crash. So
* let's make sure we wait for all the buddies to be removed first.*/
int
id
=
g_timeout_add
(
0
,
G_SOURCE_FUNC
(
draw_tooltip_real
),
ggblist
);
g_object_set_data_full
(
G_OBJECT
(
ggblist
->
window
),
"draw_tooltip_calback"
,
GINT_TO_POINTER
(
id
),
(
GDestroyNotify
)
g_source_remove
);
}
static
void
selection_changed
(
G_GNUC_UNUSED
GntWidget
*
widget
,
G_GNUC_UNUSED
gpointer
old
,
G_GNUC_UNUSED
gpointer
current
,
FinchBuddyList
*
ggblist
)
{
remove_peripherals
(
ggblist
);
draw_tooltip
(
ggblist
);
}
static
gboolean
context_menu
(
G_GNUC_UNUSED
GntWidget
*
widget
,
FinchBuddyList
*
ggblist
)
{
draw_context_menu
(
ggblist
);
return
TRUE
;
}
static
gboolean
key_pressed
(
G_GNUC_UNUSED
GntWidget
*
widget
,
const
char
*
text
,
FinchBuddyList
*
ggblist
)
{
if
(
text
[
0
]
==
27
&&
text
[
1
]
==
0
)
{
/* Escape was pressed */
if
(
gnt_tree_is_searching
(
GNT_TREE
(
ggblist
->
tree
)))
gnt_bindable_perform_action_named
(
GNT_BINDABLE
(
ggblist
->
tree
),
"end-search"
,
NULL
);
remove_peripherals
(
ggblist
);
}
else
if
(
purple_strequal
(
text
,
GNT_KEY_INS
))
{
PurpleBlistNode
*
node
=
gnt_tree_get_selection_data
(
GNT_TREE
(
ggblist
->
tree
));
purple_blist_request_add_buddy
(
NULL
,
NULL
,
node
&&
PURPLE_IS_GROUP
(
node
)
?
purple_group_get_name
(
PURPLE_GROUP
(
node
))
:
NULL
,
NULL
);
}
else
if
(
!
gnt_tree_is_searching
(
GNT_TREE
(
ggblist
->
tree
)))
{
if
(
purple_strequal
(
text
,
"t"
))
{
finch_blist_toggle_tag_buddy
(
gnt_tree_get_selection_data
(
GNT_TREE
(
ggblist
->
tree
)));
gnt_bindable_perform_action_named
(
GNT_BINDABLE
(
ggblist
->
tree
),
"move-down"
,
NULL
);
}
else
if
(
purple_strequal
(
text
,
"a"
))
{
finch_blist_place_tagged
(
gnt_tree_get_selection_data
(
GNT_TREE
(
ggblist
->
tree
)));
}
else
return
FALSE
;
}
else
return
FALSE
;
return
TRUE
;
}
static
void
update_node_display
(
PurpleBlistNode
*
node
,
FinchBuddyList
*
ggblist
)
{
GntTextFormatFlags
flag
=
get_blist_node_flag
(
ggblist
,
node
);
gnt_tree_set_row_flags
(
GNT_TREE
(
ggblist
->
tree
),
node
,
flag
);
}
static
void
update_buddy_display
(
PurpleBuddy
*
buddy
,
FinchBuddyList
*
ggblist
)
{
PurpleMetaContact
*
contact
;
contact
=
purple_buddy_get_contact
(
buddy
);
gnt_tree_change_text
(
GNT_TREE
(
ggblist
->
tree
),
buddy
,
0
,
get_display_name
((
PurpleBlistNode
*
)
buddy
));
gnt_tree_change_text
(
GNT_TREE
(
ggblist
->
tree
),
contact
,
0
,
get_display_name
((
PurpleBlistNode
*
)
contact
));
blist_update_row_flags
(
ggblist
,
(
PurpleBlistNode
*
)
buddy
);
if
(
buddy
==
purple_meta_contact_get_priority_buddy
(
contact
))
blist_update_row_flags
(
ggblist
,
(
PurpleBlistNode
*
)
contact
);
if
(
ggblist
->
tnode
==
(
PurpleBlistNode
*
)
buddy
)
{
draw_tooltip
(
ggblist
);
}
}
static
void
buddy_status_changed
(
PurpleBuddy
*
buddy
,
G_GNUC_UNUSED
PurpleStatus
*
old
,
G_GNUC_UNUSED
PurpleStatus
*
now
,
FinchBuddyList
*
ggblist
)
{
update_buddy_display
(
buddy
,
ggblist
);
}
static
void
buddy_idle_changed
(
PurpleBuddy
*
buddy
,
G_GNUC_UNUSED
int
old
,
G_GNUC_UNUSED
int
new
,
FinchBuddyList
*
ggblist
)
{
update_buddy_display
(
buddy
,
ggblist
);
}
static
void
remove_peripherals
(
FinchBuddyList
*
ggblist
)
{
if
(
ggblist
->
tooltip
)
remove_tooltip
(
ggblist
);
else
if
(
ggblist
->
context
)
gnt_widget_destroy
(
ggblist
->
context
);
}
static
void
size_changed_cb
(
GntWidget
*
w
,
G_GNUC_UNUSED
int
wi
,
G_GNUC_UNUSED
int
h
)
{
int
width
,
height
;
gnt_widget_get_size
(
w
,
&
width
,
&
height
);
purple_prefs_set_int
(
PREF_ROOT
"/size/width"
,
width
);
purple_prefs_set_int
(
PREF_ROOT
"/size/height"
,
height
);
}
static
void
save_position_cb
(
G_GNUC_UNUSED
GntWidget
*
w
,
int
x
,
int
y
)
{
purple_prefs_set_int
(
PREF_ROOT
"/position/x"
,
x
);
purple_prefs_set_int
(
PREF_ROOT
"/position/y"
,
y
);
}
static
void
reset_blist_window
(
G_GNUC_UNUSED
GntWidget
*
window
,
G_GNUC_UNUSED
gpointer
data
)
{
purple_signals_disconnect_by_handle
(
finch_blist_get_handle
());
g_clear_handle_id
(
&
ggblist
->
typing
,
g_source_remove
);
remove_peripherals
(
ggblist
);
g_clear_list
(
&
ggblist
->
tagged
,
NULL
);
g_clear_handle_id
(
&
ggblist
->
new_group_timeout
,
g_source_remove
);
g_clear_list
(
&
ggblist
->
new_group
,
NULL
);
ggblist
=
NULL
;
}
static
void
populate_buddylist
(
void
)
{
PurpleBlistNode
*
node
;
PurpleBuddyList
*
list
;
if
(
ggblist
->
manager
->
init
)
ggblist
->
manager
->
init
();
if
(
purple_strequal
(
purple_prefs_get_string
(
PREF_ROOT
"/sort_type"
),
"text"
))
{
gnt_tree_set_compare_func
(
GNT_TREE
(
ggblist
->
tree
),
(
GCompareFunc
)
blist_node_compare_text
);
}
else
if
(
purple_strequal
(
purple_prefs_get_string
(
PREF_ROOT
"/sort_type"
),
"status"
))
{
gnt_tree_set_compare_func
(
GNT_TREE
(
ggblist
->
tree
),
(
GCompareFunc
)
blist_node_compare_status
);
}
list
=
purple_blist_get_default
();
node
=
purple_blist_get_root
(
list
);
while
(
node
)
{
node_update
(
list
,
node
);
node
=
purple_blist_node_next
(
node
,
FALSE
);
}
}
static
void
destroy_status_list
(
GList
*
list
)
{
g_list_free_full
(
list
,
g_free
);
}
static
void
populate_status_dropdown
(
void
)
{
int
i
;
GList
*
iter
;
GList
*
items
=
NULL
;
StatusBoxItem
*
item
=
NULL
;
/* First the primitives */
PurpleStatusPrimitive
prims
[]
=
{
PURPLE_STATUS_AVAILABLE
,
PURPLE_STATUS_AWAY
,
PURPLE_STATUS_INVISIBLE
,
PURPLE_STATUS_OFFLINE
,
PURPLE_STATUS_UNSET
};
gnt_combo_box_remove_all
(
GNT_COMBO_BOX
(
ggblist
->
status
));
for
(
i
=
0
;
prims
[
i
]
!=
PURPLE_STATUS_UNSET
;
i
++
)
{
item
=
g_new0
(
StatusBoxItem
,
1
);
item
->
type
=
STATUS_PRIMITIVE
;
item
->
u
.
prim
=
prims
[
i
];
items
=
g_list_prepend
(
items
,
item
);
gnt_combo_box_add_data
(
GNT_COMBO_BOX
(
ggblist
->
status
),
item
,
purple_primitive_get_name_from_type
(
prims
[
i
]));
}
/* Now the popular statuses */
for
(
iter
=
purple_savedstatuses_get_popular
(
6
);
iter
;
iter
=
g_list_delete_link
(
iter
,
iter
))
{
item
=
g_new0
(
StatusBoxItem
,
1
);
item
->
type
=
STATUS_SAVED_POPULAR
;
item
->
u
.
saved
=
iter
->
data
;
items
=
g_list_prepend
(
items
,
item
);
gnt_combo_box_add_data
(
GNT_COMBO_BOX
(
ggblist
->
status
),
item
,
purple_savedstatus_get_title
(
iter
->
data
));
}
/* New savedstatus */
item
=
g_new0
(
StatusBoxItem
,
1
);
item
->
type
=
STATUS_SAVED_NEW
;
items
=
g_list_prepend
(
items
,
item
);
gnt_combo_box_add_data
(
GNT_COMBO_BOX
(
ggblist
->
status
),
item
,
_
(
"New..."
));
/* More savedstatuses */
item
=
g_new0
(
StatusBoxItem
,
1
);
item
->
type
=
STATUS_SAVED_ALL
;
items
=
g_list_prepend
(
items
,
item
);
gnt_combo_box_add_data
(
GNT_COMBO_BOX
(
ggblist
->
status
),
item
,
_
(
"Saved..."
));
/* The keys for the combobox are created here, and never used
* anywhere else. So make sure the keys are freed when the widget
* is destroyed. */
g_object_set_data_full
(
G_OBJECT
(
ggblist
->
status
),
"list of statuses"
,
items
,
(
GDestroyNotify
)
destroy_status_list
);
}
static
void
redraw_blist
(
G_GNUC_UNUSED
const
char
*
name
,
G_GNUC_UNUSED
PurplePrefType
type
,
G_GNUC_UNUSED
gconstpointer
val
,
G_GNUC_UNUSED
gpointer
data
)
{
PurpleBlistNode
*
sel
;
FinchBlistManager
*
manager
;
if
(
ggblist
==
NULL
)
return
;
manager
=
finch_blist_manager_find
(
purple_prefs_get_string
(
PREF_ROOT
"/grouping"
));
if
(
manager
==
NULL
)
manager
=
&
default_manager
;
if
(
ggblist
->
manager
!=
manager
)
{
if
(
ggblist
->
manager
->
uninit
)
ggblist
->
manager
->
uninit
();
ggblist
->
manager
=
manager
;
if
(
manager
->
can_add_node
==
NULL
)
manager
->
can_add_node
=
default_can_add_node
;
if
(
manager
->
find_parent
==
NULL
)
manager
->
find_parent
=
default_find_parent
;
if
(
manager
->
create_tooltip
==
NULL
)
manager
->
create_tooltip
=
default_create_tooltip
;
}
if
(
ggblist
->
window
==
NULL
)
return
;
sel
=
gnt_tree_get_selection_data
(
GNT_TREE
(
ggblist
->
tree
));
gnt_tree_remove_all
(
GNT_TREE
(
ggblist
->
tree
));
populate_buddylist
();
gnt_tree_set_selected
(
GNT_TREE
(
ggblist
->
tree
),
sel
);
draw_tooltip
(
ggblist
);
}
void
finch_blist_init
(
void
)
{
color_available
=
gnt_style_get_color
(
NULL
,
"color-available"
);
if
(
!
color_available
)
color_available
=
gnt_color_add_pair
(
COLOR_GREEN
,
-1
);
color_away
=
gnt_style_get_color
(
NULL
,
"color-away"
);
if
(
!
color_away
)
color_away
=
gnt_color_add_pair
(
COLOR_BLUE
,
-1
);
color_idle
=
gnt_style_get_color
(
NULL
,
"color-idle"
);
if
(
!
color_idle
)
color_idle
=
gnt_color_add_pair
(
COLOR_CYAN
,
-1
);
color_offline
=
gnt_style_get_color
(
NULL
,
"color-offline"
);
if
(
!
color_offline
)
color_offline
=
gnt_color_add_pair
(
COLOR_RED
,
-1
);
purple_prefs_add_none
(
PREF_ROOT
);
purple_prefs_add_none
(
PREF_ROOT
"/size"
);
purple_prefs_add_int
(
PREF_ROOT
"/size/width"
,
20
);
purple_prefs_add_int
(
PREF_ROOT
"/size/height"
,
17
);
purple_prefs_add_none
(
PREF_ROOT
"/position"
);
purple_prefs_add_int
(
PREF_ROOT
"/position/x"
,
0
);
purple_prefs_add_int
(
PREF_ROOT
"/position/y"
,
0
);
purple_prefs_add_bool
(
PREF_ROOT
"/idletime"
,
TRUE
);
purple_prefs_add_bool
(
PREF_ROOT
"/showoffline"
,
FALSE
);
purple_prefs_add_bool
(
PREF_ROOT
"/emptygroups"
,
FALSE
);
purple_prefs_add_string
(
PREF_ROOT
"/sort_type"
,
"text"
);
purple_prefs_add_string
(
PREF_ROOT
"/grouping"
,
"default"
);
purple_prefs_connect_callback
(
finch_blist_get_handle
(),
PREF_ROOT
"/emptygroups"
,
redraw_blist
,
NULL
);
purple_prefs_connect_callback
(
finch_blist_get_handle
(),
PREF_ROOT
"/showoffline"
,
redraw_blist
,
NULL
);
purple_prefs_connect_callback
(
finch_blist_get_handle
(),
PREF_ROOT
"/sort_type"
,
redraw_blist
,
NULL
);
purple_prefs_connect_callback
(
finch_blist_get_handle
(),
PREF_ROOT
"/grouping"
,
redraw_blist
,
NULL
);
purple_signal_connect_priority
(
purple_connections_get_handle
(),
"autojoin"
,
purple_blist_get_handle
(),
G_CALLBACK
(
account_autojoin_cb
),
NULL
,
PURPLE_SIGNAL_PRIORITY_HIGHEST
);
finch_blist_install_manager
(
&
default_manager
);
}
static
gboolean
remove_typing_cb
(
G_GNUC_UNUSED
gpointer
data
)
{
PurpleSavedStatus
*
current
;
const
char
*
message
,
*
newmessage
;
char
*
escnewmessage
;
PurpleStatusPrimitive
prim
,
newprim
;
StatusBoxItem
*
item
;
current
=
purple_savedstatus_get_current
();
message
=
purple_savedstatus_get_message
(
current
);
prim
=
purple_savedstatus_get_primitive_type
(
current
);
newmessage
=
gnt_entry_get_text
(
GNT_ENTRY
(
ggblist
->
statustext
));
item
=
gnt_combo_box_get_selected_data
(
GNT_COMBO_BOX
(
ggblist
->
status
));
escnewmessage
=
newmessage
?
g_markup_escape_text
(
newmessage
,
-1
)
:
NULL
;
switch
(
item
->
type
)
{
case
STATUS_PRIMITIVE
:
newprim
=
item
->
u
.
prim
;
break
;
case
STATUS_SAVED_POPULAR
:
newprim
=
purple_savedstatus_get_primitive_type
(
item
->
u
.
saved
);
break
;
default
:
goto
end
;
/* 'New' or 'Saved' is selected, but this should never happen. */
}
if
(
newprim
!=
prim
||
((
message
&&
!
escnewmessage
)
||
(
!
message
&&
escnewmessage
)
||
(
message
&&
escnewmessage
&&
g_utf8_collate
(
message
,
escnewmessage
)
!=
0
)))
{
PurpleSavedStatus
*
status
=
purple_savedstatus_find_transient_by_type_and_message
(
newprim
,
escnewmessage
);
/* Holy Crap! That's a LAWNG function name */
if
(
status
==
NULL
)
{
status
=
purple_savedstatus_new
(
NULL
,
newprim
);
purple_savedstatus_set_message
(
status
,
escnewmessage
);
}
purple_savedstatus_activate
(
status
);
}
gnt_box_give_focus_to_child
(
GNT_BOX
(
ggblist
->
window
),
ggblist
->
tree
);
end
:
g_free
(
escnewmessage
);
g_clear_handle_id
(
&
ggblist
->
typing
,
g_source_remove
);
return
FALSE
;
}
static
void
status_selection_changed
(
G_GNUC_UNUSED
GntComboBox
*
box
,
G_GNUC_UNUSED
StatusBoxItem
*
old
,
StatusBoxItem
*
now
,
G_GNUC_UNUSED
gpointer
data
)
{
gnt_entry_set_text
(
GNT_ENTRY
(
ggblist
->
statustext
),
NULL
);
if
(
now
->
type
==
STATUS_SAVED_POPULAR
)
{
/* Set the status immediately */
purple_savedstatus_activate
(
now
->
u
.
saved
);
}
else
if
(
now
->
type
==
STATUS_PRIMITIVE
)
{
/* Move the focus to the entry box */
/* XXX: Make sure the selected status can have a message */
gnt_box_move_focus
(
GNT_BOX
(
ggblist
->
window
),
1
);
ggblist
->
typing
=
g_timeout_add_seconds
(
TYPING_TIMEOUT_S
,
G_SOURCE_FUNC
(
remove_typing_cb
),
NULL
);
}
else
if
(
now
->
type
==
STATUS_SAVED_ALL
)
{
/* Restore the selection to reflect current status. */
savedstatus_changed
(
purple_savedstatus_get_current
(),
NULL
);
gnt_box_give_focus_to_child
(
GNT_BOX
(
ggblist
->
window
),
ggblist
->
tree
);
finch_savedstatus_show_all
();
}
else
if
(
now
->
type
==
STATUS_SAVED_NEW
)
{
savedstatus_changed
(
purple_savedstatus_get_current
(),
NULL
);
gnt_box_give_focus_to_child
(
GNT_BOX
(
ggblist
->
window
),
ggblist
->
tree
);
finch_savedstatus_edit
(
NULL
);
}
else
g_return_if_reached
();
}
static
gboolean
status_text_changed
(
G_GNUC_UNUSED
GntEntry
*
entry
,
const
char
*
text
,
G_GNUC_UNUSED
gpointer
data
)
{
if
((
text
[
0
]
==
27
||
(
text
[
0
]
==
'\t'
&&
text
[
1
]
==
'\0'
))
&&
ggblist
->
typing
==
0
)
return
FALSE
;
g_clear_handle_id
(
&
ggblist
->
typing
,
g_source_remove
);
if
(
text
[
0
]
==
'\r'
&&
text
[
1
]
==
0
)
{
/* Set the status only after you press 'Enter' */
remove_typing_cb
(
NULL
);
return
TRUE
;
}
ggblist
->
typing
=
g_timeout_add_seconds
(
TYPING_TIMEOUT_S
,
G_SOURCE_FUNC
(
remove_typing_cb
),
NULL
);
return
FALSE
;
}
static
void
savedstatus_changed
(
PurpleSavedStatus
*
now
,
G_GNUC_UNUSED
PurpleSavedStatus
*
old
)
{
GList
*
list
;
PurpleStatusPrimitive
prim
;
const
char
*
message
;
gboolean
found
=
FALSE
,
saved
=
TRUE
;
if
(
!
ggblist
)
return
;
/* Block the signals we don't want to emit */
g_signal_handlers_block_matched
(
ggblist
->
status
,
G_SIGNAL_MATCH_FUNC
,
0
,
0
,
NULL
,
status_selection_changed
,
NULL
);
g_signal_handlers_block_matched
(
ggblist
->
statustext
,
G_SIGNAL_MATCH_FUNC
,
0
,
0
,
NULL
,
status_text_changed
,
NULL
);
prim
=
purple_savedstatus_get_primitive_type
(
now
);
message
=
purple_savedstatus_get_message
(
now
);
/* Rebuild the status dropdown */
populate_status_dropdown
();
while
(
!
found
)
{
list
=
g_object_get_data
(
G_OBJECT
(
ggblist
->
status
),
"list of statuses"
);
for
(;
list
;
list
=
list
->
next
)
{
StatusBoxItem
*
item
=
list
->
data
;
if
((
saved
&&
item
->
type
!=
STATUS_PRIMITIVE
&&
item
->
u
.
saved
==
now
)
||
(
!
saved
&&
item
->
type
==
STATUS_PRIMITIVE
&&
item
->
u
.
prim
==
prim
))
{
char
*
mess
=
purple_unescape_html
(
message
);
gnt_combo_box_set_selected
(
GNT_COMBO_BOX
(
ggblist
->
status
),
item
);
gnt_entry_set_text
(
GNT_ENTRY
(
ggblist
->
statustext
),
mess
);
gnt_widget_draw
(
ggblist
->
status
);
g_free
(
mess
);
found
=
TRUE
;
break
;
}
}
if
(
!
saved
)
break
;
saved
=
FALSE
;
}
g_signal_handlers_unblock_matched
(
ggblist
->
status
,
G_SIGNAL_MATCH_FUNC
,
0
,
0
,
NULL
,
status_selection_changed
,
NULL
);
g_signal_handlers_unblock_matched
(
ggblist
->
statustext
,
G_SIGNAL_MATCH_FUNC
,
0
,
0
,
NULL
,
status_text_changed
,
NULL
);
}
static
int
blist_node_compare_position
(
PurpleBlistNode
*
n1
,
PurpleBlistNode
*
n2
)
{
while
((
n1
=
purple_blist_node_get_sibling_prev
(
n1
))
!=
NULL
)
if
(
n1
==
n2
)
return
1
;
return
-1
;
}
static
int
blist_node_compare_text
(
PurpleBlistNode
*
n1
,
PurpleBlistNode
*
n2
)
{
const
char
*
s1
,
*
s2
;
char
*
us1
,
*
us2
;
int
ret
;
if
(
G_OBJECT_TYPE
(
n1
)
!=
G_OBJECT_TYPE
(
n2
))
return
blist_node_compare_position
(
n1
,
n2
);
if
(
PURPLE_IS_CHAT
(
n1
))
{
s1
=
purple_chat_get_name
((
PurpleChat
*
)
n1
);
s2
=
purple_chat_get_name
((
PurpleChat
*
)
n2
);
}
else
if
(
PURPLE_IS_BUDDY
(
n1
))
{
return
purple_buddy_presence_compare
(
PURPLE_BUDDY_PRESENCE
(
purple_buddy_get_presence
(
PURPLE_BUDDY
(
n1
))),
PURPLE_BUDDY_PRESENCE
(
purple_buddy_get_presence
(
PURPLE_BUDDY
(
n2
))));
}
else
if
(
PURPLE_IS_META_CONTACT
(
n1
))
{
s1
=
purple_meta_contact_get_alias
((
PurpleMetaContact
*
)
n1
);
s2
=
purple_meta_contact_get_alias
((
PurpleMetaContact
*
)
n2
);
}
else
{
return
blist_node_compare_position
(
n1
,
n2
);
}
us1
=
g_utf8_strup
(
s1
,
-1
);
us2
=
g_utf8_strup
(
s2
,
-1
);
ret
=
g_utf8_collate
(
us1
,
us2
);
g_free
(
us1
);
g_free
(
us2
);
return
ret
;
}
static
int
blist_node_compare_status
(
PurpleBlistNode
*
n1
,
PurpleBlistNode
*
n2
)
{
int
ret
;
if
(
G_OBJECT_TYPE
(
n1
)
!=
G_OBJECT_TYPE
(
n2
))
return
blist_node_compare_position
(
n1
,
n2
);
if
(
PURPLE_IS_META_CONTACT
(
n1
))
n1
=
PURPLE_BLIST_NODE
(
purple_meta_contact_get_priority_buddy
(
PURPLE_META_CONTACT
(
n1
)));
if
(
PURPLE_IS_META_CONTACT
(
n2
))
n2
=
PURPLE_BLIST_NODE
(
purple_meta_contact_get_priority_buddy
(
PURPLE_META_CONTACT
(
n2
)));
if
(
PURPLE_IS_BUDDY
(
n1
)
&&
PURPLE_IS_BUDDY
(
n2
))
{
ret
=
purple_buddy_presence_compare
(
PURPLE_BUDDY_PRESENCE
(
purple_buddy_get_presence
(
PURPLE_BUDDY
(
n1
))),
PURPLE_BUDDY_PRESENCE
(
purple_buddy_get_presence
(
PURPLE_BUDDY
(
n2
))));
if
(
ret
!=
0
)
return
ret
;
}
else
{
return
blist_node_compare_position
(
n1
,
n2
);
}
/* Sort alphabetically if presence is not comparable */
ret
=
blist_node_compare_text
(
n1
,
n2
);
return
ret
;
}
static
void
plugin_action
(
G_GNUC_UNUSED
GntMenuItem
*
item
,
G_GNUC_UNUSED
gpointer
data
)
{
/* TODO: Convert to GAction/GMenu. */
#if 0
PurplePluginAction *action = data;
if (action && action->callback)
action->callback(action);
#endif
}
static
void
build_plugin_actions
(
G_GNUC_UNUSED
GntMenuItem
*
item
,
G_GNUC_UNUSED
PurplePlugin
*
plugin
)
{
/* TODO: port to GAction/GMenu. */
#if 0
GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP);
PurplePluginActionsCb actions_cb;
GList *actions;
GntMenuItem *menuitem;
actions_cb =
purple_plugin_info_get_actions_cb(purple_plugin_get_info(plugin));
gnt_menuitem_set_submenu(item, GNT_MENU(sub));
for (actions = actions_cb(plugin); actions;
actions = g_list_delete_link(actions, actions)) {
if (actions->data) {
PurplePluginAction *action = actions->data;
action->plugin = plugin;
menuitem = gnt_menuitem_new(action->label);
gnt_menu_add_item(GNT_MENU(sub), menuitem);
gnt_menuitem_set_callback(menuitem, plugin_action, action);
g_object_set_data_full(G_OBJECT(menuitem), "plugin_action",
action, (GDestroyNotify)purple_plugin_action_free);
}
}
#endif
}
static
void
protocol_action
(
G_GNUC_UNUSED
GntMenuItem
*
item
,
gpointer
data
)
{
PurpleProtocolAction
*
action
=
data
;
if
(
action
&&
action
->
callback
)
action
->
callback
(
action
);
}
static
void
build_protocol_actions
(
G_GNUC_UNUSED
GntMenuItem
*
item
,
G_GNUC_UNUSED
PurpleProtocol
*
protocol
,
G_GNUC_UNUSED
PurpleConnection
*
gc
)
{
/* TODO: port to PurpleProtocolActions. */
#if 0
GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP);
GList *actions;
GntMenuItem *menuitem;
gnt_menuitem_set_submenu(item, GNT_MENU(sub));
for (actions = purple_protocol_client_get_actions(PURPLE_PROTOCOL_CLIENT(protocol), gc);
actions; actions = g_list_delete_link(actions, actions)) {
if (actions->data) {
PurpleProtocolAction *action = actions->data;
action->connection = gc;
menuitem = gnt_menuitem_new(action->label);
gnt_menu_add_item(GNT_MENU(sub), menuitem);
gnt_menuitem_set_callback(menuitem, protocol_action, action);
g_object_set_data_full(G_OBJECT(menuitem), "protocol_action",
action, (GDestroyNotify)purple_protocol_action_free);
}
}
#endif
}
static
gboolean
buddy_recent_signed_on_off
(
gpointer
data
)
{
PurpleBlistNode
*
node
=
data
;
FinchBlistNode
*
fnode
=
g_object_get_data
(
G_OBJECT
(
node
),
UI_DATA
);
g_clear_handle_id
(
&
fnode
->
signed_timer
,
g_source_remove
);
if
(
!
ggblist
->
manager
->
can_add_node
(
node
))
{
node_remove
(
purple_blist_get_default
(),
node
);
}
else
{
update_node_display
(
node
,
ggblist
);
if
(
purple_blist_node_get_parent
(
node
)
&&
PURPLE_IS_META_CONTACT
(
purple_blist_node_get_parent
(
node
)))
update_node_display
(
purple_blist_node_get_parent
(
node
),
ggblist
);
}
g_object_unref
(
node
);
return
FALSE
;
}
static
gboolean
buddy_signed_on_off_cb
(
gpointer
data
)
{
PurpleBlistNode
*
node
=
data
;
FinchBlistNode
*
fnode
=
g_object_get_data
(
G_OBJECT
(
node
),
UI_DATA
);
if
(
!
ggblist
||
!
fnode
)
{
return
FALSE
;
}
g_clear_handle_id
(
&
fnode
->
signed_timer
,
g_source_remove
);
g_object_ref
(
node
);
fnode
->
signed_timer
=
g_timeout_add_seconds
(
6
,
G_SOURCE_FUNC
(
buddy_recent_signed_on_off
),
data
);
update_node_display
(
node
,
ggblist
);
if
(
purple_blist_node_get_parent
(
node
)
&&
PURPLE_IS_META_CONTACT
(
purple_blist_node_get_parent
(
node
)))
update_node_display
(
purple_blist_node_get_parent
(
node
),
ggblist
);
return
FALSE
;
}
static
void
buddy_signed_on_off
(
PurpleBuddy
*
buddy
,
G_GNUC_UNUSED
gpointer
data
)
{
g_idle_add
(
buddy_signed_on_off_cb
,
buddy
);
}
static
void
reconstruct_plugins_menu
(
void
)
{
GntWidget
*
sub
;
GntMenuItem
*
plg
;
GList
*
iter
;
if
(
!
ggblist
)
return
;
if
(
ggblist
->
plugins
==
NULL
)
ggblist
->
plugins
=
gnt_menuitem_new
(
_
(
"Plugins"
));
plg
=
ggblist
->
plugins
;
sub
=
gnt_menu_new
(
GNT_MENU_POPUP
);
gnt_menuitem_set_submenu
(
plg
,
GNT_MENU
(
sub
));
/* TODO: port to GAction/GMenu. */
#if 0
for (iter = purple_plugins_get_loaded(); iter; iter = iter->next) {
PurplePlugin *plugin = iter->data;
PurplePluginInfo *info = purple_plugin_get_info(plugin);
GntMenuItem *item;
if (!purple_plugin_info_get_actions_cb(info))
continue;
item = gnt_menuitem_new(_(gplugin_plugin_info_get_name(
GPLUGIN_PLUGIN_INFO(info))));
gnt_menu_add_item(GNT_MENU(sub), item);
build_plugin_actions(item, plugin);
}
#endif
}
static
void
reconstruct_plugins_menu_cb
(
G_GNUC_UNUSED
GObject
*
plugin_manager
,
G_GNUC_UNUSED
GPluginPlugin
*
plugin
,
G_GNUC_UNUSED
gpointer
data
)
{
reconstruct_plugins_menu
();
}
static
void
reconstruct_accounts_menu
(
void
)
{
#if 0
PurpleAccountManager *manager = NULL;
#endif
GntWidget
*
sub
;
GntMenuItem
*
acc
;
#if 0
GntMenuItem *item;
GList *iter;
#endif
if
(
!
ggblist
)
return
;
if
(
ggblist
->
accounts
==
NULL
)
ggblist
->
accounts
=
gnt_menuitem_new
(
_
(
"Accounts"
));
acc
=
ggblist
->
accounts
;
sub
=
gnt_menu_new
(
GNT_MENU_POPUP
);
gnt_menuitem_set_submenu
(
acc
,
GNT_MENU
(
sub
));
#if 0
manager = purple_account_manager_get_default();
iter = purple_account_manager_get_enabled(manager);
for (; iter; iter = g_list_delete_link(iter, iter)) {
PurpleAccount *account = iter->data;
PurpleConnection *gc = purple_account_get_connection(account);
PurpleProtocol *protocol;
if (!gc || !PURPLE_CONNECTION_IS_CONNECTED(gc))
continue;
protocol = purple_connection_get_protocol(gc);
/* TODO: port to PurpleProtocolActions */
if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_actions)) {
PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
item = gnt_menuitem_new(purple_contact_info_get_username(info));
gnt_menu_add_item(GNT_MENU(sub), item);
build_protocol_actions(item, protocol, gc);
}
}
#endif
}
static
void
reconstruct_grouping_menu
(
void
)
{
GList
*
iter
;
GntWidget
*
subsub
;
if
(
!
ggblist
||
!
ggblist
->
grouping
)
return
;
subsub
=
gnt_menu_new
(
GNT_MENU_POPUP
);
gnt_menuitem_set_submenu
(
ggblist
->
grouping
,
GNT_MENU
(
subsub
));
for
(
iter
=
managers
;
iter
;
iter
=
iter
->
next
)
{
char
menuid
[
128
];
FinchBlistManager
*
manager
=
iter
->
data
;
GntMenuItem
*
item
=
gnt_menuitem_new
(
_
(
manager
->
name
));
g_snprintf
(
menuid
,
sizeof
(
menuid
),
"grouping-%s"
,
manager
->
id
);
gnt_menuitem_set_id
(
GNT_MENU_ITEM
(
item
),
menuid
);
gnt_menu_add_item
(
GNT_MENU
(
subsub
),
item
);
g_object_set_data_full
(
G_OBJECT
(
item
),
"grouping-id"
,
g_strdup
(
manager
->
id
),
g_free
);
gnt_menuitem_set_callback
(
item
,
menu_group_set_cb
,
NULL
);
}
}
static
gboolean
auto_join_chats
(
gpointer
data
)
{
PurpleBlistNode
*
node
;
PurpleConnection
*
pc
=
data
;
PurpleAccount
*
account
=
purple_connection_get_account
(
pc
);
for
(
node
=
purple_blist_get_default_root
();
node
;
node
=
purple_blist_node_next
(
node
,
FALSE
))
{
if
(
PURPLE_IS_CHAT
(
node
))
{
PurpleChat
*
chat
=
(
PurpleChat
*
)
node
;
if
(
purple_chat_get_account
(
chat
)
==
account
&&
purple_blist_node_get_bool
(
node
,
"gnt-autojoin"
))
purple_serv_join_chat
(
purple_account_get_connection
(
account
),
purple_chat_get_components
(
chat
));
}
}
return
FALSE
;
}
static
gboolean
account_autojoin_cb
(
PurpleConnection
*
gc
,
G_GNUC_UNUSED
gpointer
data
)
{
g_idle_add
(
auto_join_chats
,
gc
);
return
TRUE
;
}
static
void
toggle_pref_cb
(
G_GNUC_UNUSED
GntMenuItem
*
item
,
gpointer
n
)
{
purple_prefs_set_bool
(
n
,
!
purple_prefs_get_bool
(
n
));
}
static
void
sort_blist_change_cb
(
G_GNUC_UNUSED
GntMenuItem
*
item
,
gpointer
n
)
{
purple_prefs_set_string
(
PREF_ROOT
"/sort_type"
,
n
);
}
/* send_im_select* -- Xerox */
static
void
send_im_select_cb
(
G_GNUC_UNUSED
gpointer
data
,
PurpleRequestPage
*
page
)
{
PurpleAccount
*
account
;
const
char
*
username
;
PurpleConversation
*
im
;
account
=
purple_request_page_get_account
(
page
,
"account"
);
username
=
purple_request_page_get_string
(
page
,
"screenname"
);
im
=
purple_im_conversation_new
(
account
,
username
);
purple_conversation_present
(
im
);
}
static
void
send_im_select
(
G_GNUC_UNUSED
GntMenuItem
*
item
,
G_GNUC_UNUSED
gpointer
n
)
{
PurpleRequestPage
*
page
;
PurpleRequestGroup
*
group
;
PurpleRequestField
*
field
;
page
=
purple_request_page_new
();
group
=
purple_request_group_new
(
NULL
);
purple_request_page_add_group
(
page
,
group
);
field
=
purple_request_field_string_new
(
"screenname"
,
_
(
"Name"
),
NULL
,
FALSE
);
purple_request_field_set_type_hint
(
field
,
"screenname"
);
purple_request_field_set_required
(
field
,
TRUE
);
purple_request_group_add_field
(
group
,
field
);
field
=
purple_request_field_account_new
(
"account"
,
_
(
"Account"
),
NULL
);
purple_request_field_set_type_hint
(
field
,
"account"
);
purple_request_field_set_visible
(
field
,
(
purple_connections_get_all
()
!=
NULL
&&
purple_connections_get_all
()
->
next
!=
NULL
));
purple_request_field_set_required
(
field
,
TRUE
);
purple_request_group_add_field
(
group
,
field
);
purple_request_fields
(
purple_blist_get_default
(),
_
(
"New Instant Message"
),
NULL
,
_
(
"Please enter the username or alias of the person "
"you would like to IM."
),
page
,
_
(
"OK"
),
G_CALLBACK
(
send_im_select_cb
),
_
(
"Cancel"
),
NULL
,
NULL
,
NULL
);
}
static
void
join_chat_select_cb
(
G_GNUC_UNUSED
gpointer
data
,
PurpleRequestPage
*
page
)
{
PurpleAccount
*
account
;
const
char
*
name
;
PurpleConnection
*
gc
;
PurpleConversationManager
*
manager
;
PurpleChat
*
chat
;
GHashTable
*
hash
=
NULL
;
PurpleConversation
*
conv
;
account
=
purple_request_page_get_account
(
page
,
"account"
);
name
=
purple_request_page_get_string
(
page
,
"chat"
);
if
(
!
purple_account_is_connected
(
account
))
return
;
gc
=
purple_account_get_connection
(
account
);
manager
=
purple_conversation_manager_get_default
();
/* Create a new conversation now. This will give focus to the new window.
* But it's necessary to pretend that we left the chat, because otherwise
* a new conversation window will pop up when we finally join the chat. */
conv
=
purple_conversation_manager_find_chat
(
manager
,
account
,
name
);
if
(
!
PURPLE_IS_CHAT_CONVERSATION
(
conv
))
{
conv
=
purple_chat_conversation_new
(
account
,
name
);
purple_chat_conversation_leave
(
PURPLE_CHAT_CONVERSATION
(
conv
));
}
else
{
purple_conversation_present
(
conv
);
}
chat
=
purple_blist_find_chat
(
account
,
name
);
if
(
chat
==
NULL
)
{
PurpleProtocol
*
protocol
=
purple_connection_get_protocol
(
gc
);
hash
=
purple_protocol_chat_info_defaults
(
PURPLE_PROTOCOL_CHAT
(
protocol
),
gc
,
name
);
}
else
{
hash
=
purple_chat_get_components
(
chat
);
}
purple_serv_join_chat
(
gc
,
hash
);
if
(
chat
==
NULL
&&
hash
!=
NULL
)
g_hash_table_destroy
(
hash
);
}
static
void
join_chat_select
(
G_GNUC_UNUSED
GntMenuItem
*
item
,
G_GNUC_UNUSED
gpointer
n
)
{
PurpleRequestPage
*
page
;
PurpleRequestGroup
*
group
;
PurpleRequestField
*
field
;
page
=
purple_request_page_new
();
group
=
purple_request_group_new
(
NULL
);
purple_request_page_add_group
(
page
,
group
);
field
=
purple_request_field_string_new
(
"chat"
,
_
(
"Channel"
),
NULL
,
FALSE
);
purple_request_field_set_required
(
field
,
TRUE
);
purple_request_group_add_field
(
group
,
field
);
field
=
purple_request_field_account_new
(
"account"
,
_
(
"Account"
),
NULL
);
purple_request_field_set_type_hint
(
field
,
"account"
);
purple_request_field_set_visible
(
field
,
(
purple_connections_get_all
()
!=
NULL
&&
purple_connections_get_all
()
->
next
!=
NULL
));
purple_request_field_set_required
(
field
,
TRUE
);
purple_request_group_add_field
(
group
,
field
);
purple_request_fields
(
purple_blist_get_default
(),
_
(
"Join a Chat"
),
NULL
,
_
(
"Please enter the name of the chat you want to join."
),
page
,
_
(
"Join"
),
G_CALLBACK
(
join_chat_select_cb
),
_
(
"Cancel"
),
NULL
,
NULL
,
NULL
);
}
static
void
menu_add_buddy_cb
(
G_GNUC_UNUSED
GntMenuItem
*
item
,
G_GNUC_UNUSED
gpointer
data
)
{
purple_blist_request_add_buddy
(
NULL
,
NULL
,
NULL
,
NULL
);
}
static
void
menu_add_chat_cb
(
G_GNUC_UNUSED
GntMenuItem
*
item
,
G_GNUC_UNUSED
gpointer
data
)
{
purple_blist_request_add_chat
(
NULL
,
NULL
,
NULL
,
NULL
);
}
static
void
menu_add_group_cb
(
G_GNUC_UNUSED
GntMenuItem
*
item
,
G_GNUC_UNUSED
gpointer
data
)
{
purple_blist_request_add_group
();
}
static
void
menu_group_set_cb
(
GntMenuItem
*
item
,
G_GNUC_UNUSED
gpointer
data
)
{
const
char
*
id
=
g_object_get_data
(
G_OBJECT
(
item
),
"grouping-id"
);
purple_prefs_set_string
(
PREF_ROOT
"/grouping"
,
id
);
}
static
void
create_menu
(
void
)
{
GntWidget
*
menu
,
*
sub
,
*
subsub
;
GntMenuItem
*
item
;
GntWindow
*
window
;
if
(
!
ggblist
)
return
;
window
=
GNT_WINDOW
(
ggblist
->
window
);
ggblist
->
menu
=
menu
=
gnt_menu_new
(
GNT_MENU_TOPLEVEL
);
gnt_window_set_menu
(
window
,
GNT_MENU
(
menu
));
item
=
gnt_menuitem_new
(
_
(
"Options"
));
gnt_menu_add_item
(
GNT_MENU
(
menu
),
item
);
sub
=
gnt_menu_new
(
GNT_MENU_POPUP
);
gnt_menuitem_set_submenu
(
item
,
GNT_MENU
(
sub
));
item
=
gnt_menuitem_new
(
_
(
"Send IM..."
));
gnt_menuitem_set_id
(
GNT_MENU_ITEM
(
item
),
"send-im"
);
gnt_menu_add_item
(
GNT_MENU
(
sub
),
item
);
gnt_menuitem_set_callback
(
GNT_MENU_ITEM
(
item
),
send_im_select
,
NULL
);
item
=
gnt_menuitem_new
(
_
(
"Join Chat..."
));
gnt_menuitem_set_id
(
GNT_MENU_ITEM
(
item
),
"join-chat"
);
gnt_menu_add_item
(
GNT_MENU
(
sub
),
item
);
gnt_menuitem_set_callback
(
GNT_MENU_ITEM
(
item
),
join_chat_select
,
NULL
);
item
=
gnt_menuitem_new
(
_
(
"Show"
));
gnt_menu_add_item
(
GNT_MENU
(
sub
),
item
);
subsub
=
gnt_menu_new
(
GNT_MENU_POPUP
);
gnt_menuitem_set_submenu
(
item
,
GNT_MENU
(
subsub
));
item
=
gnt_menuitem_check_new
(
_
(
"Empty groups"
));
gnt_menuitem_set_id
(
GNT_MENU_ITEM
(
item
),
"show-empty-groups"
);
gnt_menuitem_check_set_checked
(
GNT_MENU_ITEM_CHECK
(
item
),
purple_prefs_get_bool
(
PREF_ROOT
"/emptygroups"
));
gnt_menu_add_item
(
GNT_MENU
(
subsub
),
item
);
gnt_menuitem_set_callback
(
GNT_MENU_ITEM
(
item
),
toggle_pref_cb
,
PREF_ROOT
"/emptygroups"
);
item
=
gnt_menuitem_check_new
(
_
(
"Offline buddies"
));
gnt_menuitem_set_id
(
GNT_MENU_ITEM
(
item
),
"show-offline-buddies"
);
gnt_menuitem_check_set_checked
(
GNT_MENU_ITEM_CHECK
(
item
),
purple_prefs_get_bool
(
PREF_ROOT
"/showoffline"
));
gnt_menu_add_item
(
GNT_MENU
(
subsub
),
item
);
gnt_menuitem_set_callback
(
GNT_MENU_ITEM
(
item
),
toggle_pref_cb
,
PREF_ROOT
"/showoffline"
);
item
=
gnt_menuitem_new
(
_
(
"Sort"
));
gnt_menu_add_item
(
GNT_MENU
(
sub
),
item
);
subsub
=
gnt_menu_new
(
GNT_MENU_POPUP
);
gnt_menuitem_set_submenu
(
item
,
GNT_MENU
(
subsub
));
item
=
gnt_menuitem_new
(
_
(
"By Status"
));
gnt_menuitem_set_id
(
GNT_MENU_ITEM
(
item
),
"sort-status"
);
gnt_menu_add_item
(
GNT_MENU
(
subsub
),
item
);
gnt_menuitem_set_callback
(
GNT_MENU_ITEM
(
item
),
sort_blist_change_cb
,
"status"
);
item
=
gnt_menuitem_new
(
_
(
"Alphabetically"
));
gnt_menuitem_set_id
(
GNT_MENU_ITEM
(
item
),
"sort-alpha"
);
gnt_menu_add_item
(
GNT_MENU
(
subsub
),
item
);
gnt_menuitem_set_callback
(
GNT_MENU_ITEM
(
item
),
sort_blist_change_cb
,
"text"
);
item
=
gnt_menuitem_new
(
_
(
"By Log Size"
));
gnt_menuitem_set_id
(
GNT_MENU_ITEM
(
item
),
"sort-log"
);
gnt_menu_add_item
(
GNT_MENU
(
subsub
),
item
);
gnt_menuitem_set_callback
(
GNT_MENU_ITEM
(
item
),
sort_blist_change_cb
,
"log"
);
item
=
gnt_menuitem_new
(
_
(
"Add"
));
gnt_menu_add_item
(
GNT_MENU
(
sub
),
item
);
subsub
=
gnt_menu_new
(
GNT_MENU_POPUP
);
gnt_menuitem_set_submenu
(
item
,
GNT_MENU
(
subsub
));
item
=
gnt_menuitem_new
(
_
(
"Buddy"
));
gnt_menuitem_set_id
(
GNT_MENU_ITEM
(
item
),
"add-buddy"
);
gnt_menu_add_item
(
GNT_MENU
(
subsub
),
item
);
gnt_menuitem_set_callback
(
item
,
menu_add_buddy_cb
,
NULL
);
item
=
gnt_menuitem_new
(
_
(
"Chat"
));
gnt_menuitem_set_id
(
GNT_MENU_ITEM
(
item
),
"add-chat"
);
gnt_menu_add_item
(
GNT_MENU
(
subsub
),
item
);
gnt_menuitem_set_callback
(
item
,
menu_add_chat_cb
,
NULL
);
item
=
gnt_menuitem_new
(
_
(
"Group"
));
gnt_menuitem_set_id
(
GNT_MENU_ITEM
(
item
),
"add-group"
);
gnt_menu_add_item
(
GNT_MENU
(
subsub
),
item
);
gnt_menuitem_set_callback
(
item
,
menu_add_group_cb
,
NULL
);
ggblist
->
grouping
=
item
=
gnt_menuitem_new
(
_
(
"Grouping"
));
gnt_menu_add_item
(
GNT_MENU
(
sub
),
item
);
reconstruct_grouping_menu
();
reconstruct_accounts_menu
();
gnt_menu_add_item
(
GNT_MENU
(
menu
),
ggblist
->
accounts
);
reconstruct_plugins_menu
();
gnt_menu_add_item
(
GNT_MENU
(
menu
),
ggblist
->
plugins
);
}
void
finch_blist_show
(
void
)
{
blist_show
(
purple_blist_get_default
());
}
static
void
group_collapsed
(
G_GNUC_UNUSED
GntWidget
*
widget
,
PurpleBlistNode
*
node
,
gboolean
collapsed
,
G_GNUC_UNUSED
gpointer
data
)
{
if
(
PURPLE_IS_GROUP
(
node
))
purple_blist_node_set_bool
(
node
,
"collapsed"
,
collapsed
);
}
static
void
blist_show
(
G_GNUC_UNUSED
PurpleBuddyList
*
list
)
{
GPluginManager
*
plugin_manager
=
NULL
;
if
(
ggblist
->
window
)
{
gnt_window_present
(
ggblist
->
window
);
return
;
}
ggblist
->
window
=
gnt_vwindow_new
(
FALSE
);
gnt_widget_set_name
(
ggblist
->
window
,
"buddylist"
);
gnt_box_set_toplevel
(
GNT_BOX
(
ggblist
->
window
),
TRUE
);
gnt_box_set_title
(
GNT_BOX
(
ggblist
->
window
),
_
(
"Buddy List"
));
gnt_box_set_pad
(
GNT_BOX
(
ggblist
->
window
),
0
);
ggblist
->
tree
=
gnt_tree_new
();
gnt_widget_set_has_border
(
ggblist
->
tree
,
FALSE
);
gnt_widget_set_size
(
ggblist
->
tree
,
purple_prefs_get_int
(
PREF_ROOT
"/size/width"
),
purple_prefs_get_int
(
PREF_ROOT
"/size/height"
));
gnt_widget_set_position
(
ggblist
->
window
,
purple_prefs_get_int
(
PREF_ROOT
"/position/x"
),
purple_prefs_get_int
(
PREF_ROOT
"/position/y"
));
gnt_box_add_widget
(
GNT_BOX
(
ggblist
->
window
),
ggblist
->
tree
);
ggblist
->
status
=
gnt_combo_box_new
();
gnt_box_add_widget
(
GNT_BOX
(
ggblist
->
window
),
ggblist
->
status
);
ggblist
->
statustext
=
gnt_entry_new
(
NULL
);
gnt_box_add_widget
(
GNT_BOX
(
ggblist
->
window
),
ggblist
->
statustext
);
gnt_widget_show
(
ggblist
->
window
);
purple_signal_connect
(
purple_connections_get_handle
(),
"signed-on"
,
finch_blist_get_handle
(),
G_CALLBACK
(
reconstruct_accounts_menu
),
NULL
);
purple_signal_connect
(
purple_connections_get_handle
(),
"signed-off"
,
finch_blist_get_handle
(),
G_CALLBACK
(
reconstruct_accounts_menu
),
NULL
);
purple_signal_connect
(
purple_accounts_get_handle
(),
"account-actions-changed"
,
finch_blist_get_handle
(),
G_CALLBACK
(
reconstruct_accounts_menu
),
NULL
);
purple_signal_connect
(
purple_blist_get_handle
(),
"buddy-status-changed"
,
finch_blist_get_handle
(),
G_CALLBACK
(
buddy_status_changed
),
ggblist
);
purple_signal_connect
(
purple_blist_get_handle
(),
"buddy-idle-changed"
,
finch_blist_get_handle
(),
G_CALLBACK
(
buddy_idle_changed
),
ggblist
);
plugin_manager
=
gplugin_manager_get_default
();
g_signal_connect_object
(
plugin_manager
,
"loaded-plugin"
,
G_CALLBACK
(
reconstruct_plugins_menu_cb
),
ggblist
,
0
);
g_signal_connect_object
(
plugin_manager
,
"unloaded-plugin"
,
G_CALLBACK
(
reconstruct_plugins_menu_cb
),
ggblist
,
0
);
purple_signal_connect
(
purple_blist_get_handle
(),
"buddy-signed-on"
,
finch_blist_get_handle
(),
G_CALLBACK
(
buddy_signed_on_off
),
ggblist
);
purple_signal_connect
(
purple_blist_get_handle
(),
"buddy-signed-off"
,
finch_blist_get_handle
(),
G_CALLBACK
(
buddy_signed_on_off
),
ggblist
);
g_signal_connect
(
G_OBJECT
(
ggblist
->
tree
),
"selection_changed"
,
G_CALLBACK
(
selection_changed
),
ggblist
);
g_signal_connect
(
G_OBJECT
(
ggblist
->
tree
),
"key_pressed"
,
G_CALLBACK
(
key_pressed
),
ggblist
);
g_signal_connect
(
G_OBJECT
(
ggblist
->
tree
),
"context-menu"
,
G_CALLBACK
(
context_menu
),
ggblist
);
g_signal_connect
(
G_OBJECT
(
ggblist
->
tree
),
"collapse-toggled"
,
G_CALLBACK
(
group_collapsed
),
NULL
);
g_signal_connect
(
G_OBJECT
(
ggblist
->
tree
),
"activate"
,
G_CALLBACK
(
selection_activate
),
ggblist
);
g_signal_connect_data
(
G_OBJECT
(
ggblist
->
tree
),
"gained-focus"
,
G_CALLBACK
(
draw_tooltip
),
ggblist
,
0
,
G_CONNECT_AFTER
|
G_CONNECT_SWAPPED
);
g_signal_connect_data
(
G_OBJECT
(
ggblist
->
tree
),
"lost-focus"
,
G_CALLBACK
(
remove_peripherals
),
ggblist
,
0
,
G_CONNECT_AFTER
|
G_CONNECT_SWAPPED
);
g_signal_connect_data
(
G_OBJECT
(
ggblist
->
window
),
"workspace-hidden"
,
G_CALLBACK
(
remove_peripherals
),
ggblist
,
0
,
G_CONNECT_AFTER
|
G_CONNECT_SWAPPED
);
g_signal_connect
(
G_OBJECT
(
ggblist
->
tree
),
"size_changed"
,
G_CALLBACK
(
size_changed_cb
),
NULL
);
g_signal_connect
(
G_OBJECT
(
ggblist
->
window
),
"position_set"
,
G_CALLBACK
(
save_position_cb
),
NULL
);
g_signal_connect
(
G_OBJECT
(
ggblist
->
window
),
"destroy"
,
G_CALLBACK
(
reset_blist_window
),
NULL
);
/* Status signals */
purple_signal_connect
(
purple_savedstatuses_get_handle
(),
"savedstatus-changed"
,
finch_blist_get_handle
(),
G_CALLBACK
(
savedstatus_changed
),
NULL
);
g_signal_connect
(
G_OBJECT
(
ggblist
->
status
),
"selection_changed"
,
G_CALLBACK
(
status_selection_changed
),
NULL
);
g_signal_connect
(
G_OBJECT
(
ggblist
->
statustext
),
"key_pressed"
,
G_CALLBACK
(
status_text_changed
),
NULL
);
create_menu
();
populate_buddylist
();
savedstatus_changed
(
purple_savedstatus_get_current
(),
NULL
);
}
void
finch_blist_uninit
(
void
)
{
}
gboolean
finch_blist_get_position
(
int
*
x
,
int
*
y
)
{
if
(
!
ggblist
||
!
ggblist
->
window
)
return
FALSE
;
gnt_widget_get_position
(
ggblist
->
window
,
x
,
y
);
return
TRUE
;
}
void
finch_blist_set_position
(
int
x
,
int
y
)
{
gnt_widget_set_position
(
ggblist
->
window
,
x
,
y
);
}
gboolean
finch_blist_get_size
(
int
*
width
,
int
*
height
)
{
if
(
!
ggblist
||
!
ggblist
->
window
)
return
FALSE
;
gnt_widget_get_size
(
ggblist
->
window
,
width
,
height
);
return
TRUE
;
}
void
finch_blist_set_size
(
int
width
,
int
height
)
{
gnt_widget_set_size
(
ggblist
->
window
,
width
,
height
);
}
void
finch_blist_install_manager
(
const
FinchBlistManager
*
manager
)
{
if
(
!
g_list_find
(
managers
,
manager
))
{
managers
=
g_list_append
(
managers
,
(
gpointer
)
manager
);
reconstruct_grouping_menu
();
if
(
purple_strequal
(
manager
->
id
,
purple_prefs_get_string
(
PREF_ROOT
"/grouping"
)))
purple_prefs_trigger_callback
(
PREF_ROOT
"/grouping"
);
}
}
void
finch_blist_uninstall_manager
(
const
FinchBlistManager
*
manager
)
{
if
(
g_list_find
(
managers
,
manager
))
{
managers
=
g_list_remove
(
managers
,
manager
);
reconstruct_grouping_menu
();
if
(
purple_strequal
(
manager
->
id
,
purple_prefs_get_string
(
PREF_ROOT
"/grouping"
)))
purple_prefs_trigger_callback
(
PREF_ROOT
"/grouping"
);
}
}
FinchBlistManager
*
finch_blist_manager_find
(
const
char
*
id
)
{
GList
*
iter
=
managers
;
if
(
!
id
)
return
NULL
;
for
(;
iter
;
iter
=
iter
->
next
)
{
FinchBlistManager
*
m
=
iter
->
data
;
if
(
purple_strequal
(
id
,
m
->
id
))
return
m
;
}
return
NULL
;
}
GntTree
*
finch_blist_get_tree
(
void
)
{
return
ggblist
?
GNT_TREE
(
ggblist
->
tree
)
:
NULL
;
}
/**************************************************************************
* GObject code
**************************************************************************/
G_DEFINE_TYPE
(
FinchBuddyList
,
finch_buddy_list
,
PURPLE_TYPE_BUDDY_LIST
)
static
void
finch_buddy_list_init
(
FinchBuddyList
*
self
)
{
if
(
!
ggblist
)
{
/* The first buddy list object becomes the default. */
ggblist
=
self
;
}
self
->
manager
=
finch_blist_manager_find
(
purple_prefs_get_string
(
PREF_ROOT
"/grouping"
));
if
(
!
self
->
manager
)
{
self
->
manager
=
&
default_manager
;
}
}
static
void
finch_buddy_list_finalize
(
GObject
*
obj
)
{
FinchBuddyList
*
ggblist
=
FINCH_BUDDY_LIST
(
obj
);
gnt_widget_destroy
(
ggblist
->
window
);
G_OBJECT_CLASS
(
finch_buddy_list_parent_class
)
->
finalize
(
obj
);
}
static
void
finch_buddy_list_class_init
(
FinchBuddyListClass
*
klass
)
{
GObjectClass
*
obj_class
=
G_OBJECT_CLASS
(
klass
);
PurpleBuddyListClass
*
purple_blist_class
;
obj_class
->
finalize
=
finch_buddy_list_finalize
;
purple_blist_class
=
PURPLE_BUDDY_LIST_CLASS
(
klass
);
purple_blist_class
->
new_node
=
new_node
;
purple_blist_class
->
show
=
blist_show
;
purple_blist_class
->
update
=
node_update
;
purple_blist_class
->
remove
=
node_remove
;
purple_blist_class
->
request_add_buddy
=
finch_request_add_buddy
;
purple_blist_class
->
request_add_chat
=
finch_request_add_chat
;
purple_blist_class
->
request_add_group
=
finch_request_add_group
;
}
/**************************************************************************
* GBoxed code
**************************************************************************/
static
FinchBlistManager
*
finch_blist_manager_copy
(
FinchBlistManager
*
manager
)
{
FinchBlistManager
*
manager_new
;
g_return_val_if_fail
(
manager
!=
NULL
,
NULL
);
manager_new
=
g_new
(
FinchBlistManager
,
1
);
*
manager_new
=
*
manager
;
return
manager_new
;
}
static
void
finch_blist_manager_free
(
FinchBlistManager
*
manager
)
{
g_return_if_fail
(
manager
!=
NULL
);
g_free
(
manager
);
}
GType
finch_blist_manager_get_type
(
void
)
{
static
GType
type
=
0
;
if
(
type
==
0
)
{
type
=
g_boxed_type_register_static
(
"FinchBlistManager"
,
(
GBoxedCopyFunc
)
finch_blist_manager_copy
,
(
GBoxedFreeFunc
)
finch_blist_manager_free
);
}
return
type
;
}