soc.2009.vulture: f58e7edd: Show buddy icons in buddy list.
gdick at soc.pidgin.im
gdick at soc.pidgin.im
Fri Jul 31 16:15:52 EDT 2009
-----------------------------------------------------------------
Revision: f58e7eddac05e1d15a5cde153ceee0aa395732e4
Ancestor: 0f75fbf629264d6f7c592c1a43b8ca8666aa2182
Author: gdick at soc.pidgin.im
Date: 2009-07-31T20:09:15
Branch: im.pidgin.soc.2009.vulture
URL: http://d.pidgin.im/viewmtn/revision/info/f58e7eddac05e1d15a5cde153ceee0aa395732e4
Modified files:
vulture/purpleblist.c vulture/purpleblist.h
vulture/purplemain.c vulture/purplemain.h
vulture/purplequeue.c vulture/purplequeue.h
vulture/vulturebicon.h vulture/vultureblist.c
vulture/vultureblist.h
ChangeLog:
Show buddy icons in buddy list.
-------------- next part --------------
============================================================
--- vulture/purpleblist.c a65035b950969f1d2881f18ce4c1f8baa21ffaa7
+++ vulture/purpleblist.c 346020b1270353d12bc8e3b11f7c7be4fece456e
@@ -78,6 +78,7 @@ void PurpleBlistUpdateNode(PurpleBuddyLi
lpvbn->bExpanded = FALSE;
lpvbn->szStatusText = NULL;
lpvbn->iStatusIcon = 0;
+ lpvbn->ui.bIconCacheValid = FALSE;
InitializeCriticalSection(&lpvbn->cs);
}
@@ -361,8 +362,9 @@ void PurpleBuddyStatusChanged(PurpleBudd
if(lpvbnContact->bExpanded)
{
VULTURE_BLIST_NODE *lpvbn = ((PurpleBlistNode*)lpbuddy)->ui_data;
+
VultureBListNodeAddRef(lpvbn);
- VulturePostUIMessage(VUIMSG_UPDATEBLISTNODE, ((PurpleBlistNode*)lpbuddy)->ui_data);
+ VulturePostUIMessage(VUIMSG_UPDATEBLISTNODE, lpvbn);
}
VultureBListNodeAddRef(lpvbnContact);
@@ -691,3 +693,38 @@ static int GetStatusIconIndex(PurpleBudd
return SICON_AVAILABLE;
}
+
+
+/**
+ * Called in response to buddy-icon-changed signal.
+ *
+ * @param lpbuddy Buddy.
+ */
+void PurpleBuddyIconChanged(PurpleBuddy *lpbuddy)
+{
+ VULTURE_BLIST_NODE *lpvbnContact = ((PurpleBlistNode*)lpbuddy)->parent ? ((PurpleBlistNode*)lpbuddy)->parent->ui_data : NULL;
+
+ if(lpvbnContact)
+ {
+ if(lpvbnContact->bExpanded)
+ {
+ VULTURE_BLIST_NODE *lpvbn = ((PurpleBlistNode*)lpbuddy)->ui_data;
+
+ VultureBListNodeAddRef(lpvbn);
+ VulturePostUIMessage(VUIMSG_INVALIDATEICONCACHE, lpvbn);
+
+ VultureBListNodeAddRef(lpvbn);
+ VulturePostUIMessage(VUIMSG_UPDATEBLISTNODE, lpvbn);
+ }
+
+ VultureBListNodeAddRef(lpvbnContact);
+ VulturePostUIMessage(VUIMSG_INVALIDATEICONCACHE, lpvbnContact);
+
+ VultureBListNodeAddRef(lpvbnContact);
+ VulturePostUIMessage(VUIMSG_UPDATEBLISTNODE, lpvbnContact);
+ }
+
+ /* We don't need to update any conversations showing our icon: this
+ * will be done in response to the conversation-updated signal.
+ */
+}
============================================================
--- vulture/purpleblist.h b53c5677c75086f107a4a1adac187b4581447694
+++ vulture/purpleblist.h 7c4a22afbf7ad86eaa68607aa86214e5ae53b79a
@@ -43,6 +43,7 @@ void PurpleCommonMakeMenu(HMENU hmenu, P
void PurpleBlistAliasNode(PurpleBlistNode *lpblistnode, LPCTSTR szAlias);
void PurpleDeleteBlistNode(PurpleBlistNode *lpblistnode);
void PurpleCommonMakeMenu(HMENU hmenu, PurpleBlistNode *lpblistnode);
+void PurpleBuddyIconChanged(PurpleBuddy *lpbuddy);
static INLINE void VultureBListNodeAddRef(VULTURE_BLIST_NODE *lpvblnode) { InterlockedIncrement(&lpvblnode->lRefCount); }
============================================================
--- vulture/purplemain.c 8afc7b182175d5011dab06bb294fa70dd6d333ab
+++ vulture/purplemain.c 6823e999af2741fae0e621fbd5b3b1cca3877023
@@ -211,6 +211,7 @@ static void InitUI(void)
purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-changed", GINT_TO_POINTER(VSH_STATUS), PURPLE_CALLBACK(PurpleStatusChanged), NULL);
purple_signal_connect(purple_conversations_get_handle(), "conversation-updated", GINT_TO_POINTER(VSH_CONV), PURPLE_CALLBACK(PurpleConvChanged), NULL);
purple_signal_connect(purple_blist_get_handle(), "buddy-status-changed", GINT_TO_POINTER(VSH_BLIST), PURPLE_CALLBACK(PurpleBuddyStatusChanged), NULL);
+ purple_signal_connect(purple_blist_get_handle(), "buddy-icon-changed", GINT_TO_POINTER(VSH_BLIST), PURPLE_CALLBACK(PurpleBuddyIconChanged), NULL);
purple_signal_connect(purple_connections_get_handle(), "signed-on", GINT_TO_POINTER(VSH_BLIST), PURPLE_CALLBACK(PurpleAccountSignedOn), NULL);
/* Create and load libpurple's buddy-list. */
============================================================
--- vulture/purplemain.h e06ec0b2e136014244b565f6509f56e59fb18617
+++ vulture/purplemain.h e12b252d7f0db0b03520757baec24498284e11b4
@@ -88,6 +88,9 @@ enum ENUM_VULTURE_UI_MESSAGES
/* (VULTURE_CHAT_REMOVE_USERS*) */
VUIMSG_CHATREMOVEUSERS,
+
+ /* (VULTURE_BLIST_NODE*) Node whose cache should be invalidated. */
+ VUIMSG_INVALIDATEICONCACHE,
};
/* HandlEs for reigistering signal handlERs. */
============================================================
--- vulture/purplequeue.c 19a07741826bd13526233187fa34d3acf7ff281a
+++ vulture/purplequeue.c eed2f2a320fff388c7928c6ec9b52d519a5892cc
@@ -398,6 +398,16 @@ static void DispatchPurpleCall(PURPLE_CA
break;
+ case PC_GETBLISTNODEICON:
+ {
+ VULTURE_GET_BLIST_NODE_ICON *lpvgblnicon = lppurplecall->lpvParam;
+ lpvgblnicon->hbmIcon = lpvgblnicon->lpvblistnode ?
+ PurpleGetBlistNodeIcon(lpvgblnicon->lpvblistnode->lpblistnode, -1, -1) :
+ NULL;
+ }
+
+ break;
+
case PC_QUIT:
purple_core_quit();
g_main_loop_quit(g_lpgmainloop);
============================================================
--- vulture/purplequeue.h 323b68d1531a12509450d4dc972706a06c2c7bcc
+++ vulture/purplequeue.h c4de7e75926aed165aa95580586e8847d83daa59
@@ -109,6 +109,9 @@ enum PURPLE_CALL_ID
/* (VULTURE_BLIST_NODE*) */
PC_UPDATEBLISTCHILDREN,
+
+ /* (VULTURE_GET_BLIST_NODE_ICON*) */
+ PC_GETBLISTNODEICON,
};
============================================================
--- vulture/vulturebicon.h e725d73b16aedf7eaa9ea29678401ab86a831715
+++ vulture/vulturebicon.h 8009f6df0f48fb39bdd6a2cfc9b584f0cf5b6932
@@ -37,5 +37,11 @@ typedef struct _VULTURE_GET_IM_BUDDY_ICO
HBITMAP hbmIcon;
} VULTURE_GET_IM_BUDDY_ICON;
+typedef struct _VULTURE_GET_BLIST_NODE_ICON
+{
+ VULTURE_BLIST_NODE *lpvblistnode;
+ HBITMAP hbmIcon;
+} VULTURE_GET_BLIST_NODE_ICON;
+
#endif
============================================================
--- vulture/vultureblist.c bf8b0c2b28339f9e533dd8c82a9d30b83a62e244
+++ vulture/vultureblist.c 2bbc69fe7564c2520d1185cfc889ba09a6ca09ef
@@ -39,6 +39,7 @@
#include "vultureconv.h"
#include "purpleconv.h"
#include "vulturedlg.h"
+#include "vulturebicon.h"
typedef struct _STATUSDLGDATA
@@ -62,6 +63,8 @@ static void DrawBListNodeExtra(LPNMTVCUS
static void RemoveNodeRequest(HWND hwndBuddies, VULTURE_BLIST_NODE *lpvblistnode);
static void UpdateBListNode(HWND hwndBlistTree, VULTURE_BLIST_NODE *lpvbn);
static void DrawBListNodeExtra(LPNMTVCUSTOMDRAW lpnmtvcdraw);
+static HBITMAP GetBListNodeIcon(VULTURE_BLIST_NODE *lpvblistnode);
+static void InvalidateBListNodeIconCache(VULTURE_BLIST_NODE *lpvblistnode);
#define BLIST_MARGIN 6
@@ -318,6 +321,18 @@ static LRESULT CALLBACK MainWndProc(HWND
hwndForward = ((VULTURE_CHAT_REMOVE_USERS*)lParam)->lpvconvChat->hwndConv;
break;
+ case VUIMSG_INVALIDATEICONCACHE:
+ {
+ VULTURE_BLIST_NODE *lpvblistnode = (VULTURE_BLIST_NODE*)lParam;
+
+ InvalidateBListNodeIconCache(lpvblistnode);
+
+ /* Release the reference for this call. */
+ VultureBListNodeRelease(lpvblistnode);
+ }
+
+ break;
+
case VUIMSG_QUIT:
DestroyWindow(hwnd);
break;
@@ -1060,6 +1075,8 @@ static BOOL RunCommonMenuCmd(HWND hwndBu
{
VULTURE_BLIST_NODE_STRING_PAIR vblnstringpairNewIcon;
+ InvalidateBListNodeIconCache(lpvblistnode);
+
vblnstringpairNewIcon.lpvblistnode = lpvblistnode;
vblnstringpairNewIcon.sz = szFilename;
@@ -1073,6 +1090,8 @@ static BOOL RunCommonMenuCmd(HWND hwndBu
{
VULTURE_BLIST_NODE_STRING_PAIR vblnstringpairNewIcon;
+ InvalidateBListNodeIconCache(lpvblistnode);
+
vblnstringpairNewIcon.lpvblistnode = lpvblistnode;
vblnstringpairNewIcon.sz = NULL;
@@ -1164,51 +1183,60 @@ static void UpdateBListNode(HWND hwndBli
*/
static void UpdateBListNode(HWND hwndBlistTree, VULTURE_BLIST_NODE *lpvbn)
{
- EnterCriticalSection(&lpvbn->cs);
+ TVITEMEX tvitemex;
+
+ if(lpvbn->hti)
{
- TVITEMEX tvitemex;
+ HTREEITEM htiParent = TreeView_GetParent(hwndBlistTree, lpvbn->hti);
- if(lpvbn->hti)
+ /* If the parent doesn't match, we need
+ * to recreate.
+ */
+ EnterCriticalSection(&lpvbn->cs);
{
- HTREEITEM htiParent = TreeView_GetParent(hwndBlistTree, lpvbn->hti);
-
- /* If the parent doesn't match, we need
- * to recreate.
- */
if((lpvbn->lpvbnParent && lpvbn->lpvbnParent->hti != htiParent) ||
(!lpvbn->lpvbnParent && htiParent))
{
RemoveBListNode(hwndBlistTree, lpvbn);
}
}
+ LeaveCriticalSection(&lpvbn->cs);
+ }
- /* New node? */
- if(!lpvbn->hti)
- {
- TVINSERTSTRUCT tvis;
+ /* New node? */
+ if(!lpvbn->hti)
+ {
+ TVINSERTSTRUCT tvis;
- /* We cache this in the tree-view. */
- VultureBListNodeAddRef(lpvbn);
+ /* We cache this in the tree-view. */
+ VultureBListNodeAddRef(lpvbn);
+ EnterCriticalSection(&lpvbn->cs);
+ {
tvis.hParent = lpvbn->lpvbnParent ? lpvbn->lpvbnParent->hti : TVI_ROOT;
tvis.hInsertAfter = TVI_SORT;
tvis.itemex.mask = TVIF_PARAM;
tvis.itemex.lParam = (LPARAM)lpvbn;
+ }
+ LeaveCriticalSection(&lpvbn->cs);
- lpvbn->hti = TreeView_InsertItem(hwndBlistTree, &tvis);
- }
+ lpvbn->hti = TreeView_InsertItem(hwndBlistTree, &tvis);
+ }
- /* Set text and height. */
+ /* Set height. */
+ EnterCriticalSection(&lpvbn->cs);
+ {
tvitemex.mask = TVIF_HANDLE | TVIF_INTEGRAL;
tvitemex.hItem = lpvbn->hti;
tvitemex.iIntegral =
((lpvbn->nodetype == PURPLE_BLIST_CONTACT_NODE && lpvbn->bExpanded) || lpvbn->nodetype == PURPLE_BLIST_GROUP_NODE) ?
1 :
2;
- TreeView_SetItem(hwndBlistTree, &tvitemex);
}
LeaveCriticalSection(&lpvbn->cs);
+
+ TreeView_SetItem(hwndBlistTree, &tvitemex);
}
@@ -1222,6 +1250,7 @@ static void DrawBListNodeExtra(LPNMTVCUS
RECT rcText, rcClient;
COLORREF crOldFG, crOldBG;
VULTURE_BLIST_NODE *lpvblistnode = (VULTURE_BLIST_NODE*)lpnmtvcdraw->nmcd.lItemlParam;
+ HBITMAP hbmIcon;
GetClientRect(lpnmtvcdraw->nmcd.hdr.hwndFrom, &rcClient);
@@ -1235,9 +1264,12 @@ static void DrawBListNodeExtra(LPNMTVCUS
crOldBG = SetBkColor(lpnmtvcdraw->nmcd.hdc, lpnmtvcdraw->clrTextBk);
crOldFG = SetTextColor(lpnmtvcdraw->nmcd.hdc, lpnmtvcdraw->clrText);
+ /* This call must be made outside the CS. */
+ hbmIcon = GetBListNodeIcon(lpvblistnode);
+
EnterCriticalSection(&lpvblistnode->cs);
{
- /* Draw icon. */
+ /* Draw status icon. */
if(lpvblistnode->nodetype != PURPLE_BLIST_GROUP_NODE)
{
int xIcon = rcText.left;
@@ -1270,6 +1302,53 @@ static void DrawBListNodeExtra(LPNMTVCUS
ImageList_Draw(g_himlStatusIcons, iIndex, lpnmtvcdraw->nmcd.hdc, xIcon, yIcon, ILD_NORMAL);
}
+ /* Draw main icon. */
+ if(hbmIcon)
+ {
+ BITMAP bitmap;
+ HDC hdcMem;
+ HBITMAP hbmOld;
+ int iEdge = rcText.bottom - rcText.top;
+ int xScaled, yScaled, cxScaled, cyScaled;
+ int iOldStretchMode;
+
+ GetObject(hbmIcon, sizeof(bitmap), &bitmap);
+
+ /* Adjust text rectangle at the right-hand edge. */
+ rcText.right -= iEdge + CX_BLISTNODEINTSPACER;
+
+ /* Calculate dimensions. */
+ if(bitmap.bmWidth <= iEdge && bitmap.bmHeight <= iEdge)
+ {
+ /* We fit entirely in the room available. */
+ cxScaled = bitmap.bmWidth;
+ cyScaled = bitmap.bmHeight;
+ }
+ else if(bitmap.bmWidth > bitmap.bmHeight)
+ {
+ /* Scale to fit width. */
+ cxScaled = iEdge;
+ cyScaled = MulDiv(bitmap.bmHeight, iEdge, bitmap.bmWidth);
+ }
+ else
+ {
+ /* Scale to fit height. */
+ cxScaled = MulDiv(bitmap.bmWidth, iEdge, bitmap.bmHeight);
+ cyScaled = iEdge;
+ }
+
+ xScaled = rcText.right + CX_BLISTNODEINTSPACER + (iEdge - cxScaled) / 2;
+ yScaled = rcText.top + (iEdge - cyScaled) / 2;
+
+ hdcMem = CreateCompatibleDC(lpnmtvcdraw->nmcd.hdc);
+ hbmOld = SelectObject(hdcMem, hbmIcon);
+ iOldStretchMode = SetStretchBltMode(lpnmtvcdraw->nmcd.hdc, COLORONCOLOR);
+ StretchBlt(lpnmtvcdraw->nmcd.hdc, xScaled, yScaled, cxScaled, cyScaled, hdcMem, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY);
+ SetStretchBltMode(lpnmtvcdraw->nmcd.hdc, iOldStretchMode);
+ SelectObject(hdcMem, hbmOld);
+ DeleteDC(hdcMem);
+ }
+
if(lpvblistnode->szStatusText &&
((lpvblistnode->nodetype == PURPLE_BLIST_CONTACT_NODE && !lpvblistnode->bExpanded) ||
lpvblistnode->nodetype == PURPLE_BLIST_BUDDY_NODE))
@@ -1300,3 +1379,42 @@ static void DrawBListNodeExtra(LPNMTVCUS
SetTextColor(lpnmtvcdraw->nmcd.hdc, crOldFG);
SetBkColor(lpnmtvcdraw->nmcd.hdc, crOldBG);
}
+
+
+/**
+ * Retrieves the icon to display for a buddy-list node. This may make a call to
+ * the core, so beware of deadlocks.
+ *
+ * @param lpvblistnode Buddy-list node.
+ */
+static HBITMAP GetBListNodeIcon(VULTURE_BLIST_NODE *lpvblistnode)
+{
+ if(!lpvblistnode->ui.bIconCacheValid)
+ {
+ VULTURE_GET_BLIST_NODE_ICON vgblnicon;
+
+ vgblnicon.lpvblistnode = lpvblistnode;
+ VultureSingleSyncPurpleCall(PC_GETBLISTNODEICON, &vgblnicon);
+ lpvblistnode->ui.hbmIconCache = vgblnicon.hbmIcon;
+ lpvblistnode->ui.bIconCacheValid = TRUE;
+ }
+
+ return lpvblistnode->ui.hbmIconCache;
+}
+
+
+/**
+ * Invalidates the icon cache for a buddy-list node, freeing the cached bitmap.
+ *
+ * @param lpvblistnode Buddy-list node.
+ */
+static void InvalidateBListNodeIconCache(VULTURE_BLIST_NODE *lpvblistnode)
+{
+ if(lpvblistnode->ui.bIconCacheValid)
+ {
+ if(lpvblistnode->ui.hbmIconCache)
+ DeleteObject(lpvblistnode->ui.hbmIconCache);
+
+ lpvblistnode->ui.bIconCacheValid = FALSE;
+ }
+}
============================================================
--- vulture/vultureblist.h fbed41f6358e7ee4666133e5a6c21a3d1d38c9c5
+++ vulture/vultureblist.h 3efbb54c59c0255137701386c423d0a0aa23cc68
@@ -41,6 +41,13 @@ typedef struct _VULTURE_BLIST_NODE
LONG lRefCount;
CRITICAL_SECTION cs;
+ /* Things only the UI is allowed to touch after creation. */
+ struct
+ {
+ HBITMAP hbmIconCache;
+ BOOL bIconCacheValid;
+ } ui;
+
/* For contacts and buddies. */
int iStatusIcon;
More information about the Commits
mailing list