pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Use Meson summary() function.
2021-07-27, Elliott Sales de Andrade
cb640ea0f315
Use Meson summary() function.
Now that we require at least 0.52, we can use Meson's builtin summary printing to display the results of configuration.
Testing Done:
Configured with defaults, and with pixmaps disabled to trigger the warning: https://asciinema.org/a/mV2oxOoVCJNdmrPwgqqUJ3mkU?t=17
Reviewed at https://reviews.imfreedom.org/r/848/
/*
* 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
"gntlog.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
(
PurpleContact
*
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
null
);
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
null
);
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
null
);
/* 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
blist_node_compare_log
(
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_CONTACT
(
node
))
{
PurpleBlistNode
*
nd
;
for
(
nd
=
purple_blist_node_get_first_child
(
node
);
nd
;
nd
=
purple_blist_node_get_sibling_next
(
nd
))
{
if
(
default_can_add_node
(
nd
))
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
*
nd
;
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
(
nd
=
purple_blist_node_get_first_child
(
node
);
nd
;
nd
=
purple_blist_node_get_sibling_next
(
nd
))
{
if
(
default_can_add_node
(
nd
))
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_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_CONTACT
(
node
))
{
PurpleBuddy
*
pr
=
purple_contact_get_priority_buddy
((
PurpleContact
*
)
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
))
{
PurpleChat
*
chat
=
(
PurpleChat
*
)
node
;
PurpleAccount
*
account
=
purple_chat_get_account
(
chat
);
g_string_append_printf
(
str
,
_
(
"Account: %s (%s)"
),
purple_account_get_username
(
account
),
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
)
{
if
(
node
->
signed_timer
)
{
g_source_remove
(
node
->
signed_timer
);
}
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_CONTACT
(
node
))
node
=
PURPLE_BLIST_NODE
(
purple_contact_get_priority_buddy
(
PURPLE_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_CONTACT
(
node
))
{
node
=
PURPLE_BLIST_NODE
(
purple_contact_get_priority_buddy
(
PURPLE_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
(
PurpleBuddyList
*
list
,
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_CONTACT
(
node
))
{
add_contact
((
PurpleContact
*
)
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_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
(
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
(
void
*
data
,
PurpleRequestFields
*
allfields
)
{
const
char
*
username
=
purple_request_fields_get_string
(
allfields
,
"screenname"
);
const
char
*
alias
=
purple_request_fields_get_string
(
allfields
,
"alias"
);
const
char
*
group
=
purple_request_fields_get_string
(
allfields
,
"group"
);
const
char
*
invite
=
purple_request_fields_get_string
(
allfields
,
"invite"
);
PurpleAccount
*
account
=
purple_request_fields_get_account
(
allfields
,
"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
(
PurpleBuddyList
*
list
,
PurpleAccount
*
account
,
const
char
*
username
,
const
char
*
grp
,
const
char
*
alias
)
{
PurpleRequestFields
*
fields
=
purple_request_fields_new
();
PurpleRequestFieldGroup
*
group
=
purple_request_field_group_new
(
NULL
);
PurpleRequestField
*
field
;
purple_request_fields_add_group
(
fields
,
group
);
field
=
purple_request_field_string_new
(
"screenname"
,
_
(
"Username"
),
username
,
FALSE
);
purple_request_field_group_add_field
(
group
,
field
);
field
=
purple_request_field_string_new
(
"alias"
,
_
(
"Alias (optional)"
),
alias
,
FALSE
);
purple_request_field_group_add_field
(
group
,
field
);
field
=
purple_request_field_string_new
(
"invite"
,
_
(
"Invite message (optional)"
),
NULL
,
FALSE
);
purple_request_field_group_add_field
(
group
,
field
);
field
=
purple_request_field_string_new
(
"group"
,
_
(
"Add in group"
),
grp
,
FALSE
);
purple_request_field_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
(
field
,
FALSE
);
if
(
account
)
purple_request_field_account_set_value
(
field
,
account
);
purple_request_field_group_add_field
(
group
,
field
);
purple_request_fields
(
NULL
,
_
(
"Add Buddy"
),
NULL
,
_
(
"Please enter buddy information."
),
fields
,
_
(
"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
(
void
*
data
,
PurpleRequestFields
*
allfields
)
{
PurpleAccount
*
account
;
const
char
*
alias
,
*
name
,
*
group
;
PurpleChat
*
chat
;
PurpleGroup
*
grp
;
GHashTable
*
hash
=
NULL
;
PurpleConnection
*
gc
;
gboolean
autojoin
;
PurpleProtocol
*
protocol
;
account
=
purple_request_fields_get_account
(
allfields
,
"account"
);
name
=
purple_request_fields_get_string
(
allfields
,
"name"
);
alias
=
purple_request_fields_get_string
(
allfields
,
"alias"
);
group
=
purple_request_fields_get_string
(
allfields
,
"group"
);
autojoin
=
purple_request_fields_get_bool
(
allfields
,
"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
(
PurpleBuddyList
*
list
,
PurpleAccount
*
account
,
PurpleGroup
*
grp
,
const
char
*
alias
,
const
char
*
name
)
{
PurpleRequestFields
*
fields
=
purple_request_fields_new
();
PurpleRequestFieldGroup
*
group
=
purple_request_field_group_new
(
NULL
);
PurpleRequestField
*
field
;
purple_request_fields_add_group
(
fields
,
group
);
field
=
purple_request_field_account_new
(
"account"
,
_
(
"Account"
),
NULL
);
purple_request_field_account_set_show_all
(
field
,
FALSE
);
if
(
account
)
purple_request_field_account_set_value
(
field
,
account
);
purple_request_field_group_add_field
(
group
,
field
);
field
=
purple_request_field_string_new
(
"name"
,
_
(
"Name"
),
name
,
FALSE
);
purple_request_field_group_add_field
(
group
,
field
);
field
=
purple_request_field_string_new
(
"alias"
,
_
(
"Alias"
),
alias
,
FALSE
);
purple_request_field_group_add_field
(
group
,
field
);
field
=
purple_request_field_string_new
(
"group"
,
_
(
"Group"
),
grp
?
purple_group_get_name
(
grp
)
:
NULL
,
FALSE
);
purple_request_field_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_field_group_add_field
(
group
,
field
);
purple_request_fields
(
NULL
,
_
(
"Add Chat"
),
NULL
,
_
(
"You can edit more information from the context menu later."
),
fields
,
_
(
"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
);
if
(
ggblist
->
new_group_timeout
)
g_source_remove
(
ggblist
->
new_group_timeout
);
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_CONTACT
(
node
))
node
=
PURPLE_BLIST_NODE
(
purple_contact_get_priority_buddy
(
PURPLE_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
(
PurpleContact
*
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
;
PurpleContact
*
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_contact_get_priority_buddy
(
contact
))
{
blist_update_row_flags
(
ggblist
,
(
PurpleBlistNode
*
)
contact
);
}
}
static
void
selection_activate
(
GntWidget
*
widget
,
FinchBuddyList
*
ggblist
)
{
GntTree
*
tree
=
GNT_TREE
(
ggblist
->
tree
);
PurpleBlistNode
*
node
=
gnt_tree_get_selection_data
(
tree
);
if
(
!
node
)
return
;
if
(
PURPLE_IS_CONTACT
(
node
))
node
=
PURPLE_BLIST_NODE
(
purple_contact_get_priority_buddy
(
PURPLE_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
,
PurpleCallback
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
,
PurpleRequestFields
*
allfields
)
{
GList
*
groups
,
*
fields
;
for
(
groups
=
purple_request_fields_get_groups
(
allfields
);
groups
;
groups
=
groups
->
next
)
{
fields
=
purple_request_field_group_get_fields
(
groups
->
data
);
for
(;
fields
;
fields
=
fields
->
next
)
{
PurpleRequestField
*
field
=
fields
->
data
;
const
char
*
id
;
char
*
val
;
id
=
purple_request_field_get_id
(
field
);
if
(
purple_request_field_get_field_type
(
field
)
==
PURPLE_REQUEST_FIELD_INTEGER
)
val
=
g_strdup_printf
(
"%d"
,
purple_request_field_int_get_value
(
field
));
else
val
=
g_strdup
(
purple_request_field_string_get_value
(
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 */
}
}
}
}
static
void
chat_components_edit
(
PurpleBlistNode
*
selected
,
PurpleChat
*
chat
)
{
PurpleRequestFields
*
fields
=
purple_request_fields_new
();
PurpleRequestFieldGroup
*
group
=
purple_request_field_group_new
(
NULL
);
PurpleRequestField
*
field
;
GList
*
parts
,
*
iter
;
PurpleProtocol
*
protocol
;
PurpleProtocolChatEntry
*
pce
;
PurpleConnection
*
gc
;
purple_request_fields_add_group
(
fields
,
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
(
field
,
TRUE
);
}
if
(
pce
->
required
)
purple_request_field_set_required
(
field
,
TRUE
);
purple_request_field_group_add_field
(
group
,
field
);
g_free
(
pce
);
}
g_list_free
(
parts
);
purple_request_fields
(
NULL
,
_
(
"Edit Chat"
),
NULL
,
_
(
"Please Update the necessary fields."
),
fields
,
_
(
"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"
),
(
PurpleCallback
)
chat_components_edit
,
chat
);
}
static
void
finch_add_buddy
(
PurpleBlistNode
*
selected
,
PurpleGroup
*
grp
)
{
purple_blist_request_add_buddy
(
NULL
,
NULL
,
grp
?
purple_group_get_name
(
grp
)
:
NULL
,
NULL
);
}
static
void
finch_add_group
(
PurpleBlistNode
*
selected
,
PurpleGroup
*
grp
)
{
purple_blist_request_add_group
();
}
static
void
finch_add_chat
(
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"
),
PURPLE_CALLBACK
(
finch_add_buddy
),
group
);
add_custom_action
(
menu
,
_
(
"Add Chat"
),
PURPLE_CALLBACK
(
finch_add_chat
),
group
);
add_custom_action
(
menu
,
_
(
"Add Group"
),
PURPLE_CALLBACK
(
finch_add_group
),
group
);
}
gpointer
finch_retrieve_user_info
(
PurpleConnection
*
conn
,
const
char
*
name
)
{
PurpleNotifyUserInfo
*
info
=
purple_notify_user_info_new
();
gpointer
uihandle
;
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
);
purple_serv_get_info
(
conn
,
name
);
return
uihandle
;
}
static
void
finch_blist_get_buddy_info_cb
(
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
(
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_block_buddy
(
GntMenuItem
*
item
,
gpointer
buddy
)
{
gboolean
block
=
gnt_menuitem_check_get_checked
(
GNT_MENU_ITEM_CHECK
(
item
));
PurpleAccount
*
account
=
purple_buddy_get_account
(
buddy
);
const
char
*
name
=
purple_buddy_get_name
(
buddy
);
block
?
purple_account_privacy_deny
(
account
,
name
)
:
purple_account_privacy_allow
(
account
,
name
);
}
static
void
toggle_show_offline
(
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
)
{
PurpleAccount
*
account
;
gboolean
permitted
;
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"
),
PURPLE_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"
),
PURPLE_CALLBACK
(
finch_blist_menu_send_file_cb
),
buddy
);
}
}
account
=
purple_buddy_get_account
(
buddy
);
permitted
=
purple_account_privacy_check
(
account
,
purple_buddy_get_name
(
buddy
));
item
=
gnt_menuitem_check_new
(
_
(
"Blocked"
));
gnt_menuitem_check_set_checked
(
GNT_MENU_ITEM_CHECK
(
item
),
!
permitted
);
gnt_menuitem_set_callback
(
item
,
toggle_block_buddy
,
buddy
);
gnt_menu_add_item
(
menu
,
item
);
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
(
PurpleContact
*
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_CONTACT
(
node
))
{
PurpleContact
*
contact
=
(
PurpleContact
*
)
node
;
PurpleBuddy
*
buddy
=
purple_contact_get_priority_buddy
(
contact
);
purple_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
(
PurpleBlistNode
*
selected
,
PurpleBlistNode
*
node
)
{
const
char
*
name
=
NULL
;
char
*
prompt
;
const
char
*
text
;
if
(
PURPLE_IS_CONTACT
(
node
))
name
=
purple_contact_get_alias
((
PurpleContact
*
)
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
);
}
static
void
showlog_cb
(
PurpleBlistNode
*
sel
,
PurpleBlistNode
*
node
)
{
PurpleLogType
type
;
PurpleAccount
*
account
;
char
*
name
=
NULL
;
if
(
PURPLE_IS_BUDDY
(
node
))
{
PurpleBuddy
*
b
=
(
PurpleBuddy
*
)
node
;
type
=
PURPLE_LOG_IM
;
name
=
g_strdup
(
purple_buddy_get_name
(
b
));
account
=
purple_buddy_get_account
(
b
);
}
else
if
(
PURPLE_IS_CHAT
(
node
))
{
PurpleChat
*
c
=
(
PurpleChat
*
)
node
;
PurpleProtocol
*
protocol
=
NULL
;
type
=
PURPLE_LOG_CHAT
;
account
=
purple_chat_get_account
(
c
);
protocol
=
purple_account_get_protocol
(
account
);
if
(
protocol
)
{
name
=
purple_protocol_chat_get_name
(
PURPLE_PROTOCOL_CHAT
(
protocol
),
purple_chat_get_components
(
c
));
}
}
else
if
(
PURPLE_IS_CONTACT
(
node
))
{
finch_log_show_contact
((
PurpleContact
*
)
node
);
return
;
}
else
{
/* This callback should not have been registered for a node
* that doesn't match the type of one of the blocks above. */
g_return_if_reached
();
}
if
(
name
&&
account
)
{
finch_log_show
(
type
,
name
,
account
);
g_free
(
name
);
}
}
/* 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_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_CONTACT
(
node
))
{
remove_contact
((
PurpleContact
*
)
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
(
PurpleBlistNode
*
selected
,
PurpleBlistNode
*
node
)
{
PurpleAccount
*
account
=
NULL
;
char
*
primary
;
const
char
*
name
,
*
sec
=
NULL
;
if
(
PURPLE_IS_CONTACT
(
node
))
{
PurpleContact
*
c
=
(
PurpleContact
*
)
node
;
name
=
purple_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_CONTACT
(
node
))
update_buddy_display
(
purple_contact_get_priority_buddy
(
PURPLE_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
;
PurpleContact
*
tc
=
NULL
;
if
(
PURPLE_IS_GROUP
(
target
))
tg
=
(
PurpleGroup
*
)
target
;
else
if
(
PURPLE_IS_BUDDY
(
target
))
{
tc
=
(
PurpleContact
*
)
purple_blist_node_get_parent
(
target
);
tg
=
(
PurpleGroup
*
)
purple_blist_node_get_parent
((
PurpleBlistNode
*
)
tc
);
}
else
if
(
PURPLE_IS_CONTACT
(
target
))
{
tc
=
(
PurpleContact
*
)
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_CONTACT
(
node
))
{
update_buddy_display
(
purple_contact_get_priority_buddy
((
PurpleContact
*
)
node
),
ggblist
);
if
(
PURPLE_BLIST_NODE
(
tg
)
==
target
)
{
/* The target is a group, just add the contact to the group. */
purple_blist_add_contact
((
PurpleContact
*
)
node
,
tg
,
NULL
);
}
else
if
(
tc
)
{
/* The target is either a buddy, or a contact. Merge with that contact. */
purple_contact_merge
((
PurpleContact
*
)
node
,
(
PurpleBlistNode
*
)
tc
);
}
else
{
/* The target is a chat. Add the contact to the group after this chat. */
purple_blist_add_contact
((
PurpleContact
*
)
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_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
(
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_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_CONTACT
(
node
))
{
ggblist
->
cnode
=
PURPLE_BLIST_NODE
(
purple_contact_get_priority_buddy
(
PURPLE_CONTACT
(
node
)));
create_buddy_menu
(
GNT_MENU
(
context
),
(
PurpleBuddy
*
)
ggblist
->
cnode
);
title
=
g_strdup
(
purple_contact_get_alias
((
PurpleContact
*
)
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"
),
PURPLE_CALLBACK
(
finch_blist_rename_node_cb
),
node
);
add_custom_action
(
GNT_MENU
(
context
),
_
(
"Remove"
),
PURPLE_CALLBACK
(
finch_blist_remove_node_cb
),
node
);
if
(
ggblist
->
tagged
&&
(
PURPLE_IS_CONTACT
(
node
)
||
PURPLE_IS_GROUP
(
node
)))
{
add_custom_action
(
GNT_MENU
(
context
),
_
(
"Place tagged"
),
PURPLE_CALLBACK
(
finch_blist_place_tagged
),
node
);
}
if
(
PURPLE_IS_BUDDY
(
node
)
||
PURPLE_IS_CONTACT
(
node
))
{
add_custom_action
(
GNT_MENU
(
context
),
_
(
"Toggle Tag"
),
PURPLE_CALLBACK
(
finch_blist_toggle_tag_buddy
),
node
);
}
if
(
!
PURPLE_IS_GROUP
(
node
))
{
add_custom_action
(
GNT_MENU
(
context
),
_
(
"View Log"
),
PURPLE_CALLBACK
(
showlog_cb
),
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
)
{
PurpleProtocol
*
protocol
;
PurpleAccount
*
account
;
PurpleNotifyUserInfo
*
user_info
;
PurplePresence
*
presence
;
const
char
*
alias
=
purple_buddy_get_alias
(
buddy
);
char
*
tmp
,
*
strip
;
user_info
=
purple_notify_user_info_new
();
account
=
purple_buddy_get_account
(
buddy
);
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_account_get_username
(
account
),
purple_account_get_protocol_name
(
account
));
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Account"
),
tmp
);
g_free
(
tmp
);
protocol
=
purple_account_get_protocol
(
account
);
if
(
protocol
)
{
purple_protocol_client_tooltip_text
(
PURPLE_PROTOCOL_CLIENT
(
protocol
),
buddy
,
user_info
,
full
);
}
if
(
purple_prefs_get_bool
(
"/finch/blist/idletime"
))
{
PurplePresence
*
pre
=
purple_buddy_get_presence
(
buddy
);
if
(
purple_presence_is_idle
(
pre
))
{
time_t
idle
=
purple_presence_get_idle_time
(
pre
);
if
(
idle
>
0
)
{
char
*
st
=
purple_str_seconds_to_string
(
time
(
NULL
)
-
idle
);
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 retreiving 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
,
(
GSourceFunc
)
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
(
GntWidget
*
widget
,
gpointer
old
,
gpointer
current
,
FinchBuddyList
*
ggblist
)
{
remove_peripherals
(
ggblist
);
draw_tooltip
(
ggblist
);
}
static
gboolean
context_menu
(
GntWidget
*
widget
,
FinchBuddyList
*
ggblist
)
{
draw_context_menu
(
ggblist
);
return
TRUE
;
}
static
gboolean
key_pressed
(
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
)
{
PurpleContact
*
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_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
,
PurpleStatus
*
old
,
PurpleStatus
*
now
,
FinchBuddyList
*
ggblist
)
{
update_buddy_display
(
buddy
,
ggblist
);
}
static
void
buddy_idle_changed
(
PurpleBuddy
*
buddy
,
int
old
,
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
,
int
wi
,
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
(
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
(
GntWidget
*
window
,
gpointer
null
)
{
purple_signals_disconnect_by_handle
(
finch_blist_get_handle
());
if
(
ggblist
->
typing
)
g_source_remove
(
ggblist
->
typing
);
remove_peripherals
(
ggblist
);
if
(
ggblist
->
tagged
)
g_list_free
(
ggblist
->
tagged
);
if
(
ggblist
->
new_group_timeout
)
g_source_remove
(
ggblist
->
new_group_timeout
);
if
(
ggblist
->
new_group
)
g_list_free
(
ggblist
->
new_group
);
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
);
}
else
if
(
purple_strequal
(
purple_prefs_get_string
(
PREF_ROOT
"/sort_type"
),
"log"
))
{
gnt_tree_set_compare_func
(
GNT_TREE
(
ggblist
->
tree
),
(
GCompareFunc
)
blist_node_compare_log
);
}
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
(
const
char
*
name
,
PurplePrefType
type
,
gconstpointer
val
,
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
()
{
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
(
gpointer
null
)
{
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
);
if
(
ggblist
->
typing
)
g_source_remove
(
ggblist
->
typing
);
ggblist
->
typing
=
0
;
return
FALSE
;
}
static
void
status_selection_changed
(
GntComboBox
*
box
,
StatusBoxItem
*
old
,
StatusBoxItem
*
now
,
gpointer
null
)
{
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
,
(
GSourceFunc
)
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
(
GntEntry
*
entry
,
const
char
*
text
,
gpointer
null
)
{
if
((
text
[
0
]
==
27
||
(
text
[
0
]
==
'\t'
&&
text
[
1
]
==
'\0'
))
&&
ggblist
->
typing
==
0
)
return
FALSE
;
if
(
ggblist
->
typing
)
g_source_remove
(
ggblist
->
typing
);
ggblist
->
typing
=
0
;
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
,
(
GSourceFunc
)
remove_typing_cb
,
NULL
);
return
FALSE
;
}
static
void
savedstatus_changed
(
PurpleSavedStatus
*
now
,
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_CONTACT
(
n1
))
{
s1
=
purple_contact_get_alias
((
PurpleContact
*
)
n1
);
s2
=
purple_contact_get_alias
((
PurpleContact
*
)
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_CONTACT
(
n1
))
n1
=
PURPLE_BLIST_NODE
(
purple_contact_get_priority_buddy
(
PURPLE_CONTACT
(
n1
)));
if
(
PURPLE_IS_CONTACT
(
n2
))
n2
=
PURPLE_BLIST_NODE
(
purple_contact_get_priority_buddy
(
PURPLE_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
int
get_contact_log_size
(
PurpleBlistNode
*
c
)
{
int
log
=
0
;
PurpleBlistNode
*
node
;
for
(
node
=
purple_blist_node_get_first_child
(
c
);
node
;
node
=
purple_blist_node_get_sibling_next
(
node
))
{
PurpleBuddy
*
b
=
(
PurpleBuddy
*
)
node
;
log
+=
purple_log_get_total_size
(
PURPLE_LOG_IM
,
purple_buddy_get_name
(
b
),
purple_buddy_get_account
(
b
));
}
return
log
;
}
static
int
blist_node_compare_log
(
PurpleBlistNode
*
n1
,
PurpleBlistNode
*
n2
)
{
int
ret
;
PurpleBuddy
*
b1
,
*
b2
;
if
(
G_OBJECT_TYPE
(
n1
)
!=
G_OBJECT_TYPE
(
n2
))
return
blist_node_compare_position
(
n1
,
n2
);
if
(
PURPLE_IS_BUDDY
(
n1
))
{
b1
=
(
PurpleBuddy
*
)
n1
;
b2
=
(
PurpleBuddy
*
)
n2
;
ret
=
purple_log_get_total_size
(
PURPLE_LOG_IM
,
purple_buddy_get_name
(
b2
),
purple_buddy_get_account
(
b2
))
-
purple_log_get_total_size
(
PURPLE_LOG_IM
,
purple_buddy_get_name
(
b1
),
purple_buddy_get_account
(
b1
));
if
(
ret
!=
0
)
return
ret
;
}
else
if
(
PURPLE_IS_CONTACT
(
n1
))
{
ret
=
get_contact_log_size
(
n2
)
-
get_contact_log_size
(
n1
);
if
(
ret
!=
0
)
return
ret
;
}
else
{
return
blist_node_compare_position
(
n1
,
n2
);
}
ret
=
blist_node_compare_text
(
n1
,
n2
);
return
ret
;
}
static
void
plugin_action
(
GntMenuItem
*
item
,
gpointer
data
)
{
PurplePluginAction
*
action
=
data
;
if
(
action
&&
action
->
callback
)
action
->
callback
(
action
);
}
static
void
build_plugin_actions
(
GntMenuItem
*
item
,
PurplePlugin
*
plugin
)
{
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
);
}
}
}
static
void
protocol_action
(
GntMenuItem
*
item
,
gpointer
data
)
{
PurpleProtocolAction
*
action
=
data
;
if
(
action
&&
action
->
callback
)
action
->
callback
(
action
);
}
static
void
build_protocol_actions
(
GntMenuItem
*
item
,
PurpleProtocol
*
protocol
,
PurpleConnection
*
gc
)
{
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
);
}
}
}
static
gboolean
buddy_recent_signed_on_off
(
gpointer
data
)
{
PurpleBlistNode
*
node
=
data
;
FinchBlistNode
*
fnode
=
g_object_get_data
(
G_OBJECT
(
node
),
UI_DATA
);
g_source_remove
(
fnode
->
signed_timer
);
fnode
->
signed_timer
=
0
;
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_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
;
}
if
(
fnode
->
signed_timer
)
{
g_source_remove
(
fnode
->
signed_timer
);
}
g_object_ref
(
node
);
fnode
->
signed_timer
=
g_timeout_add_seconds
(
6
,
(
GSourceFunc
)
buddy_recent_signed_on_off
,
data
);
update_node_display
(
node
,
ggblist
);
if
(
purple_blist_node_get_parent
(
node
)
&&
PURPLE_IS_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
,
gpointer
null
)
{
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
));
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
);
}
}
static
void
reconstruct_plugins_menu_cb
(
GObject
*
plugin_manager
,
GPluginPlugin
*
plugin
,
gpointer
data
)
{
reconstruct_plugins_menu
();
}
static
void
reconstruct_accounts_menu
(
void
)
{
GntWidget
*
sub
;
GntMenuItem
*
acc
,
*
item
;
GList
*
iter
;
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
));
for
(
iter
=
purple_accounts_get_all_active
();
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
);
if
(
PURPLE_PROTOCOL_IMPLEMENTS
(
protocol
,
CLIENT
,
get_actions
))
{
item
=
gnt_menuitem_new
(
purple_account_get_username
(
account
));
gnt_menu_add_item
(
GNT_MENU
(
sub
),
item
);
build_protocol_actions
(
item
,
protocol
,
gc
);
}
}
}
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
,
gpointer
null
)
{
g_idle_add
(
auto_join_chats
,
gc
);
return
TRUE
;
}
static
void
toggle_pref_cb
(
GntMenuItem
*
item
,
gpointer
n
)
{
purple_prefs_set_bool
(
n
,
!
purple_prefs_get_bool
(
n
));
}
static
void
sort_blist_change_cb
(
GntMenuItem
*
item
,
gpointer
n
)
{
purple_prefs_set_string
(
PREF_ROOT
"/sort_type"
,
n
);
}
static
void
block_select_cb
(
gpointer
data
,
PurpleRequestFields
*
fields
)
{
PurpleAccount
*
account
=
purple_request_fields_get_account
(
fields
,
"account"
);
const
char
*
name
=
purple_request_fields_get_string
(
fields
,
"screenname"
);
if
(
account
&&
name
&&
*
name
!=
'\0'
)
{
if
(
GPOINTER_TO_INT
(
purple_request_fields_get_choice
(
fields
,
"block"
))
==
1
)
{
purple_account_privacy_deny
(
account
,
name
);
}
else
{
purple_account_privacy_allow
(
account
,
name
);
}
}
}
static
void
block_select
(
GntMenuItem
*
item
,
gpointer
n
)
{
PurpleRequestFields
*
fields
;
PurpleRequestFieldGroup
*
group
;
PurpleRequestField
*
field
;
fields
=
purple_request_fields_new
();
group
=
purple_request_field_group_new
(
NULL
);
purple_request_fields_add_group
(
fields
,
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_field_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_field_group_add_field
(
group
,
field
);
field
=
purple_request_field_choice_new
(
"block"
,
_
(
"Block/Unblock"
),
GINT_TO_POINTER
(
1
));
purple_request_field_choice_add
(
field
,
_
(
"Block"
),
GINT_TO_POINTER
(
1
));
purple_request_field_choice_add
(
field
,
_
(
"Unblock"
),
GINT_TO_POINTER
(
2
));
purple_request_field_group_add_field
(
group
,
field
);
purple_request_fields
(
purple_blist_get_default
(),
_
(
"Block/Unblock"
),
NULL
,
_
(
"Please enter the username or alias of the person "
"you would like to Block/Unblock."
),
fields
,
_
(
"OK"
),
G_CALLBACK
(
block_select_cb
),
_
(
"Cancel"
),
NULL
,
NULL
,
NULL
);
}
/* send_im_select* -- Xerox */
static
void
send_im_select_cb
(
gpointer
data
,
PurpleRequestFields
*
fields
)
{
PurpleAccount
*
account
;
const
char
*
username
;
PurpleConversation
*
im
;
account
=
purple_request_fields_get_account
(
fields
,
"account"
);
username
=
purple_request_fields_get_string
(
fields
,
"screenname"
);
im
=
purple_im_conversation_new
(
account
,
username
);
purple_conversation_present
(
im
);
}
static
void
send_im_select
(
GntMenuItem
*
item
,
gpointer
n
)
{
PurpleRequestFields
*
fields
;
PurpleRequestFieldGroup
*
group
;
PurpleRequestField
*
field
;
fields
=
purple_request_fields_new
();
group
=
purple_request_field_group_new
(
NULL
);
purple_request_fields_add_group
(
fields
,
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_field_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_field_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."
),
fields
,
_
(
"OK"
),
G_CALLBACK
(
send_im_select_cb
),
_
(
"Cancel"
),
NULL
,
NULL
,
NULL
);
}
static
void
join_chat_select_cb
(
gpointer
data
,
PurpleRequestFields
*
fields
)
{
PurpleAccount
*
account
;
const
char
*
name
;
PurpleConnection
*
gc
;
PurpleConversationManager
*
manager
;
PurpleChat
*
chat
;
GHashTable
*
hash
=
NULL
;
PurpleConversation
*
conv
;
account
=
purple_request_fields_get_account
(
fields
,
"account"
);
name
=
purple_request_fields_get_string
(
fields
,
"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
(
GntMenuItem
*
item
,
gpointer
n
)
{
PurpleRequestFields
*
fields
;
PurpleRequestFieldGroup
*
group
;
PurpleRequestField
*
field
;
fields
=
purple_request_fields_new
();
group
=
purple_request_field_group_new
(
NULL
);
purple_request_fields_add_group
(
fields
,
group
);
field
=
purple_request_field_string_new
(
"chat"
,
_
(
"Channel"
),
NULL
,
FALSE
);
purple_request_field_set_required
(
field
,
TRUE
);
purple_request_field_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_field_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."
),
fields
,
_
(
"Join"
),
G_CALLBACK
(
join_chat_select_cb
),
_
(
"Cancel"
),
NULL
,
NULL
,
NULL
);
}
static
void
view_log_select_cb
(
gpointer
data
,
PurpleRequestFields
*
fields
)
{
PurpleAccount
*
account
;
const
char
*
name
;
PurpleBuddy
*
buddy
;
PurpleContact
*
contact
;
account
=
purple_request_fields_get_account
(
fields
,
"account"
);
name
=
purple_request_fields_get_string
(
fields
,
"screenname"
);
buddy
=
purple_blist_find_buddy
(
account
,
name
);
if
(
buddy
)
{
contact
=
purple_buddy_get_contact
(
buddy
);
}
else
{
contact
=
NULL
;
}
if
(
contact
)
{
finch_log_show_contact
(
contact
);
}
else
{
finch_log_show
(
PURPLE_LOG_IM
,
name
,
account
);
}
}
static
void
view_log_cb
(
GntMenuItem
*
item
,
gpointer
n
)
{
PurpleRequestFields
*
fields
;
PurpleRequestFieldGroup
*
group
;
PurpleRequestField
*
field
;
fields
=
purple_request_fields_new
();
group
=
purple_request_field_group_new
(
NULL
);
purple_request_fields_add_group
(
fields
,
group
);
field
=
purple_request_field_string_new
(
"screenname"
,
_
(
"Name"
),
NULL
,
FALSE
);
purple_request_field_set_type_hint
(
field
,
"screenname-all"
);
purple_request_field_set_required
(
field
,
TRUE
);
purple_request_field_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_accounts_get_all
()
!=
NULL
&&
purple_accounts_get_all
()
->
next
!=
NULL
));
purple_request_field_set_required
(
field
,
TRUE
);
purple_request_field_group_add_field
(
group
,
field
);
purple_request_field_account_set_show_all
(
field
,
TRUE
);
purple_request_fields
(
purple_blist_get_default
(),
_
(
"View Log"
),
NULL
,
_
(
"Please enter the username or alias of the person "
"whose log you would like to view."
),
fields
,
_
(
"OK"
),
G_CALLBACK
(
view_log_select_cb
),
_
(
"Cancel"
),
NULL
,
NULL
,
NULL
);
}
static
void
view_all_logs_cb
(
GntMenuItem
*
item
,
gpointer
n
)
{
finch_log_show
(
PURPLE_LOG_IM
,
NULL
,
NULL
);
}
static
void
menu_add_buddy_cb
(
GntMenuItem
*
item
,
gpointer
null
)
{
purple_blist_request_add_buddy
(
NULL
,
NULL
,
NULL
,
NULL
);
}
static
void
menu_add_chat_cb
(
GntMenuItem
*
item
,
gpointer
null
)
{
purple_blist_request_add_chat
(
NULL
,
NULL
,
NULL
,
NULL
);
}
static
void
menu_add_group_cb
(
GntMenuItem
*
item
,
gpointer
null
)
{
purple_blist_request_add_group
();
}
static
void
menu_group_set_cb
(
GntMenuItem
*
item
,
gpointer
null
)
{
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
(
_
(
"Block/Unblock..."
));
gnt_menuitem_set_id
(
GNT_MENU_ITEM
(
item
),
"block-unblock"
);
gnt_menu_add_item
(
GNT_MENU
(
sub
),
item
);
gnt_menuitem_set_callback
(
GNT_MENU_ITEM
(
item
),
block_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
(
_
(
"View Log..."
));
gnt_menuitem_set_id
(
GNT_MENU_ITEM
(
item
),
"view-log"
);
gnt_menu_add_item
(
GNT_MENU
(
sub
),
item
);
gnt_menuitem_set_callback
(
GNT_MENU_ITEM
(
item
),
view_log_cb
,
NULL
);
item
=
gnt_menuitem_new
(
_
(
"View All Logs"
));
gnt_menuitem_set_id
(
GNT_MENU_ITEM
(
item
),
"view-all-logs"
);
gnt_menu_add_item
(
GNT_MENU
(
sub
),
item
);
gnt_menuitem_set_callback
(
GNT_MENU_ITEM
(
item
),
view_all_logs_cb
,
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
()
{
blist_show
(
purple_blist_get_default
());
}
static
void
group_collapsed
(
GntWidget
*
widget
,
PurpleBlistNode
*
node
,
gboolean
collapsed
,
gpointer
null
)
{
if
(
PURPLE_IS_GROUP
(
node
))
purple_blist_node_set_bool
(
node
,
"collapsed"
,
collapsed
);
}
static
void
blist_show
(
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
(),
PURPLE_CALLBACK
(
reconstruct_accounts_menu
),
NULL
);
purple_signal_connect
(
purple_connections_get_handle
(),
"signed-off"
,
finch_blist_get_handle
(),
PURPLE_CALLBACK
(
reconstruct_accounts_menu
),
NULL
);
purple_signal_connect
(
purple_accounts_get_handle
(),
"account-actions-changed"
,
finch_blist_get_handle
(),
PURPLE_CALLBACK
(
reconstruct_accounts_menu
),
NULL
);
purple_signal_connect
(
purple_blist_get_handle
(),
"buddy-status-changed"
,
finch_blist_get_handle
(),
PURPLE_CALLBACK
(
buddy_status_changed
),
ggblist
);
purple_signal_connect
(
purple_blist_get_handle
(),
"buddy-idle-changed"
,
finch_blist_get_handle
(),
PURPLE_CALLBACK
(
buddy_idle_changed
),
ggblist
);
plugin_manager
=
gplugin_manager_get_instance
();
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
(),
PURPLE_CALLBACK
(
buddy_signed_on_off
),
ggblist
);
purple_signal_connect
(
purple_blist_get_handle
(),
"buddy-signed-off"
,
finch_blist_get_handle
(),
PURPLE_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
(),
PURPLE_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
()
{
}
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
;
}