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