soc.2009.vulture: 322f66ad: Custom buddy icons.

gdick at soc.pidgin.im gdick at soc.pidgin.im
Fri Jul 24 18:30:55 EDT 2009


-----------------------------------------------------------------
Revision: 322f66ad3510a5a862b1e7e50d638810668b55c0
Ancestor: 126c471fce73c7f92c794e2be3f72073041fb04a
Author: gdick at soc.pidgin.im
Date: 2009-07-24T17:07:42
Branch: im.pidgin.soc.2009.vulture
URL: http://d.pidgin.im/viewmtn/revision/info/322f66ad3510a5a862b1e7e50d638810668b55c0

Modified files:
        vulture/purplebicon.c vulture/purplebicon.h
        vulture/purpleblist.c vulture/purpleblist.h
        vulture/purplemain.h vulture/purplequeue.c
        vulture/purplequeue.h vulture/resource.h
        vulture/vulture-res.rc vulture/vulture.c vulture/vulture.h
        vulture/vultureblist.c vulture/vultureblist.h

ChangeLog: 

Custom buddy icons.

-------------- next part --------------
============================================================
--- vulture/purplebicon.c	200d6c7d2537e25ae244a92d148869164a7657aa
+++ vulture/purplebicon.c	7c1d66b962c1c38a8c0bfcbc257b22a58eee7bcd
@@ -30,7 +30,10 @@
 #include "purplebicon.h"
 
 
+static HBITMAP GetBuddyIcon(gconstpointer lpvBuddyIconData, size_t cbBuddyIconData, int cxMax, int cyMax);
 
+
+
 /**
  * Retrieves the buddy icon for a buddy. It is scaled, with its aspect ratio
  * maintained, so as not to exceed the specified dimensions, unless either of
@@ -42,13 +45,10 @@
  *
  * @return Bitmap handle, or NULL on error.
  */
-HBITMAP PurpleGetBuddyIcon(PurpleBuddy *lpbuddy, int cxMax, int cyMax)
+static HBITMAP GetBuddyIcon(gconstpointer lpvBuddyIconData, size_t cbBuddyIconData, int cxMax, int cyMax)
 {
 	GdkPixbuf *lppixbuf;
 	GdkPixbufLoader *lppbloader;
-	PurpleBuddyIcon *lpbuddyicon;
-	gconstpointer lpvBuddyIconData;
-	size_t cbBuddyIconData;
 	guchar *lpucPixbufPixels;
 	int cbPixbufRowstride, cbDIBRowstride;
 	int cx, cy, cxScaled, cyScaled;
@@ -60,16 +60,6 @@ HBITMAP PurpleGetBuddyIcon(PurpleBuddy *
 	int iChannels, iBitsPerSample;
 	GError *lpgerror = NULL;
 
-	if(!lpbuddy)
-		return NULL;
-
-	lpbuddyicon = purple_buddy_icons_find(lpbuddy->account, lpbuddy->name);
-
-	if(!lpbuddyicon)
-		return NULL;
-
-	lpvBuddyIconData = purple_buddy_icon_get_data(lpbuddyicon, &cbBuddyIconData);
-
 	lppbloader = gdk_pixbuf_loader_new();
 	if(!gdk_pixbuf_loader_write(lppbloader, lpvBuddyIconData, cbBuddyIconData, &lpgerror))
 	gdk_pixbuf_loader_close(lppbloader, NULL);
@@ -195,8 +185,93 @@ HBITMAP PurpleGetIMBuddyIcon(PurpleConve
  */
 HBITMAP PurpleGetIMBuddyIcon(PurpleConversation *lpconv, int cxMax, int cyMax)
 {
+	PurpleBuddy *lpbuddy;
+	PurpleContact *lpcontact;
+	PurpleStoredImage *lpstoredimg;
+	gconstpointer lpvBuddyIconData = NULL;
+	size_t cbBuddyIconData;
+	PurpleBuddyIcon *lpbuddyicon = NULL;
+	HBITMAP hbitmap = NULL;
+
 	if(!lpconv || lpconv->type != PURPLE_CONV_TYPE_IM)
 		return NULL;
 
-	return PurpleGetBuddyIcon(purple_find_buddy(lpconv->account, lpconv->name), cxMax, cyMax);
+	/* First we attempt to load the custom icon for the buddy's contact. */
+	if((lpbuddy = purple_find_buddy(lpconv->account, purple_conversation_get_name(lpconv))) &&
+		(lpcontact = purple_buddy_get_contact(lpbuddy)) &&
+		(lpstoredimg = purple_buddy_icons_node_find_custom_icon((PurpleBlistNode*)lpcontact)))
+	{
+		lpvBuddyIconData = purple_imgstore_get_data(lpstoredimg);
+		cbBuddyIconData = purple_imgstore_get_size(lpstoredimg);
+	}
+
+	if(!lpvBuddyIconData &&
+		(lpbuddyicon = purple_conv_im_get_icon(PURPLE_CONV_IM(lpconv))))
+	{
+		lpvBuddyIconData = purple_buddy_icon_get_data(lpbuddyicon, &cbBuddyIconData);
+	}
+
+
+	if(lpvBuddyIconData)
+		hbitmap = GetBuddyIcon(lpvBuddyIconData, cbBuddyIconData, cxMax, cyMax);
+
+	/* These are safe even when their arguments are NULL. */
+	purple_imgstore_unref(lpstoredimg);
+	purple_buddy_icon_unref(lpbuddyicon);
+
+	return hbitmap;
 }
+
+
+/**
+ * Retrieves the buddy icon to be shown for a buddy-list node. It is scaled,
+ * with its aspect ratio maintained, so as not to exceed the specified
+ * dimensions, unless either of these is non-positive, in which case no scaling
+ * is performed.
+ *
+ * @param	lpconv	IM conversation.
+ * @param	cxMax	Maximum width.
+ * @param	cyMax	Maximum height.
+ *
+ * @return Bitmap handle, or NULL on error.
+ */
+HBITMAP PurpleGetBlistNodeIcon(PurpleBlistNode *lpblistnode, int cxMax, int cyMax)
+{
+	PurpleStoredImage *lpstoredimg;
+	gconstpointer lpvBuddyIconData = NULL;
+	size_t cbBuddyIconData;
+	PurpleBuddy *lpbuddy = NULL;
+	HBITMAP hbitmap = NULL;
+	PurpleBuddyIcon *lpbuddyicon = NULL;
+
+	if(!lpblistnode)
+		return NULL;
+
+	/* First we attempt to load a custom icon for the node. */
+	if((lpstoredimg = purple_buddy_icons_node_find_custom_icon(lpblistnode)))
+	{
+		lpvBuddyIconData = purple_imgstore_get_data(lpstoredimg);
+		cbBuddyIconData = purple_imgstore_get_size(lpstoredimg);
+	}
+
+	if(!lpvBuddyIconData)
+	{
+		/* If that failed, fall back to a buddy if we can. */
+		if(PURPLE_BLIST_NODE_IS_BUDDY(lpblistnode))
+			lpbuddy = (PurpleBuddy*)lpblistnode;
+		else if(PURPLE_BLIST_NODE_IS_CONTACT(lpblistnode))
+			lpbuddy = purple_contact_get_priority_buddy((PurpleContact*)lpblistnode);
+
+		if(lpbuddy && (lpbuddyicon = purple_buddy_icons_find(lpbuddy->account, lpbuddy->name)))
+			lpvBuddyIconData = purple_buddy_icon_get_data(lpbuddyicon, &cbBuddyIconData);
+	}
+
+	if(lpvBuddyIconData)
+		hbitmap = GetBuddyIcon(lpvBuddyIconData, cbBuddyIconData, cxMax, cyMax);
+
+	/* These are safe even when their arguments are NULL. */
+	purple_imgstore_unref(lpstoredimg);
+	purple_buddy_icon_unref(lpbuddyicon);
+
+	return hbitmap;
+}
============================================================
--- vulture/purplebicon.h	61d1e4646d4b768a00c5eb237cbaa032c5cd21d6
+++ vulture/purplebicon.h	ae61a69f5ec73d830e00c73ac9e1ae909da47d33
@@ -29,8 +29,8 @@
 #include "purple.h"
 
 
-HBITMAP PurpleGetBuddyIcon(PurpleBuddy *lpbuddy, int cxMax, int cyMax);
 HBITMAP PurpleGetIMBuddyIcon(PurpleConversation *lpconv, int cxMax, int cyMax);
+HBITMAP PurpleGetBlistNodeIcon(PurpleBlistNode *lpblistnode, int cxMax, int cyMax);
 
 
 #endif
============================================================
--- vulture/purpleblist.c	b516a5fb34e9b61b21ee2ac59442445af5d1d233
+++ vulture/purpleblist.c	e4c727b8e4e0477433b216f436822375d9d25371
@@ -437,6 +437,19 @@ void PurpleMakeChatMenu(HMENU hmenu, Pur
 
 
 /**
+ * Sets up common context menu items/
+ *
+ * @param[in,out]	hmenu		Menu.
+ * @param		lpblistnode	Buddy-list node.
+ */
+void PurpleCommonMakeMenu(HMENU hmenu, PurpleBlistNode *lpblistnode)
+{
+	if(!PURPLE_BLIST_NODE_IS_BUDDY(lpblistnode))
+		EnableMenuItem(hmenu, IDM_BLIST_CONTEXT_REMOVEICON, purple_buddy_icons_node_has_custom_icon(lpblistnode) ? MF_ENABLED : MF_GRAYED);
+}
+
+
+/**
  * Sets the alias or name of a buddy-list node, as appropriate for the type.
  *
  * @param	lpblistnode	Buddy-list node.
============================================================
--- vulture/purpleblist.h	a70828ebc277bb5291ce8a37f144db6f62bf2941
+++ vulture/purpleblist.h	b53c5677c75086f107a4a1adac187b4581447694
@@ -42,6 +42,7 @@ void PurpleDeleteBlistNode(PurpleBlistNo
 void PurpleMakeChatMenu(HMENU hmenu, PurpleBlistNode *lpblistnode, GList **lplpglistVMA);
 void PurpleBlistAliasNode(PurpleBlistNode *lpblistnode, LPCTSTR szAlias);
 void PurpleDeleteBlistNode(PurpleBlistNode *lpblistnode);
+void PurpleCommonMakeMenu(HMENU hmenu, PurpleBlistNode *lpblistnode);
 
 
 static INLINE void VultureBListNodeAddRef(VULTURE_BLIST_NODE *lpvblnode) { InterlockedIncrement(&lpvblnode->lRefCount); }
============================================================
--- vulture/purplemain.h	f2d1ae8bd5cbd694895307d697e1e9bcaac6825e
+++ vulture/purplemain.h	e06ec0b2e136014244b565f6509f56e59fb18617
@@ -45,6 +45,7 @@ typedef struct _VULTURE_MAKE_CONTEXT_MEN
 	HMENU			hmenu;
 	VULTURE_BLIST_NODE	*lpvblistnode;
 	GList			**lplpglistVMA;
+	BOOL			bExtraItems;
 } VULTURE_MAKE_CONTEXT_MENU;
 
 
============================================================
--- vulture/purplequeue.c	f233b9684865dcadab89c0135eb76d10cd5d8e31
+++ vulture/purplequeue.c	fbb3b5ffe704c67af911d81a046e582c64afc5b5
@@ -279,10 +279,15 @@ static void DispatchPurpleCall(PURPLE_CA
 			
 			if(lpvmcm->lpvblistnode->lpblistnode)
 			{
-				if(PURPLE_BLIST_NODE_IS_BUDDY(lpvmcm->lpvblistnode->lpblistnode) || PURPLE_BLIST_NODE_IS_CONTACT(lpvmcm->lpvblistnode->lpblistnode))
-					PurpleMakeBuddyMenu(lpvmcm->hmenu, EFFECTIVE_BUDDY(lpvmcm->lpvblistnode->lpblistnode), lpvmcm->lplpglistVMA);
-				else if(PURPLE_BLIST_NODE_IS_CHAT(lpvmcm->lpvblistnode->lpblistnode))
-					PurpleMakeChatMenu(lpvmcm->hmenu, lpvmcm->lpvblistnode->lpblistnode, lpvmcm->lplpglistVMA);
+				if(lpvmcm->bExtraItems)
+				{
+					if(PURPLE_BLIST_NODE_IS_BUDDY(lpvmcm->lpvblistnode->lpblistnode) || PURPLE_BLIST_NODE_IS_CONTACT(lpvmcm->lpvblistnode->lpblistnode))
+						PurpleMakeBuddyMenu(lpvmcm->hmenu, EFFECTIVE_BUDDY(lpvmcm->lpvblistnode->lpblistnode), lpvmcm->lplpglistVMA);
+					else if(PURPLE_BLIST_NODE_IS_CHAT(lpvmcm->lpvblistnode->lpblistnode))
+						PurpleMakeChatMenu(lpvmcm->hmenu, lpvmcm->lpvblistnode->lpblistnode, lpvmcm->lplpglistVMA);
+				}
+
+				PurpleCommonMakeMenu(lpvmcm->hmenu, lpvmcm->lpvblistnode->lpblistnode);
 			}
 		}
 
@@ -347,6 +352,29 @@ static void DispatchPurpleCall(PURPLE_CA
 
 		break;
 
+	case PC_SETCUSTOMICON:
+		{
+			VULTURE_BLIST_NODE_STRING_PAIR *lpvblnstringpair = lppurplecall->lpvParam;
+
+			if(lpvblnstringpair->lpvblistnode->lpblistnode)
+			{
+				if(lpvblnstringpair->sz)
+				{
+					gchar *szFilename = VultureTCHARToUTF8(lpvblnstringpair->sz);
+
+					purple_buddy_icons_node_set_custom_icon_from_file(lpvblnstringpair->lpvblistnode->lpblistnode, szFilename);
+
+					g_free(szFilename);
+				}
+				else
+				{
+					purple_buddy_icons_node_set_custom_icon(lpvblnstringpair->lpvblistnode->lpblistnode, NULL, 0);
+				}
+			}
+		}
+
+		break;
+
 	case PC_QUIT:
 		purple_core_quit();
 		g_main_loop_quit(g_lpgmainloop);
============================================================
--- vulture/purplequeue.h	1b419a9f8e73dc3f4fd4b4a5cc176df3be224f96
+++ vulture/purplequeue.h	39ebf80110057edb2e4e69c0701690c9f2a45903
@@ -103,6 +103,9 @@ enum PURPLE_CALL_ID
 
 	/* (VULTURE_BLIST_NODE_GET_BOOL*) */
 	PC_BLISTNODEHASCHILDREN,
+
+	/* (VULTURE_BLIST_NODE_STRING_PAIR*) */
+	PC_SETCUSTOMICON,
 };
 
 
============================================================
--- vulture/resource.h	71fba6866b61e5ef3518586cb0f28c4e6ac96f2a
+++ vulture/resource.h	669e6a63c1cf0159b89a21101ca82ecf35f96b66
@@ -70,3 +70,5 @@
 #define IDS_ERROR_RICHEDIT                      6
 #define IDS_QUERY_DELGROUP                      7
 #define IDS_QUERY_DELCONTACT                    8
+#define IDS_BUDDYICON_FILTER                    9
+#define IDS_BUDDYICON_TITLE                     10
============================================================
--- vulture/vulture-res.rc	c2410c425b80bc856886f306c742534fb14f0f68
+++ vulture/vulture-res.rc	2e43bbe3fd1f175873a471b01ed720ef7ea845e6
@@ -55,10 +55,13 @@ IDM_CONV MENU
 	MENUITEM "&Block", IDM_BLIST_CONTEXT_BLOCK, MFT_STRING, MFS_GRAYED \
 	MENUITEM "Show when &offline", IDM_BLIST_CONTEXT_SHOWOFFLINE, MFT_STRING
 
+#define CONTEXT_CUSTOMICON \
+	MENUITEM "Set custom &icon...", IDM_BLIST_CONTEXT_CUSTOMICON, MFT_STRING \
+	MENUITEM "Remo&ve custom icon", IDM_BLIST_CONTEXT_REMOVEICON, MFT_STRING
+
 #define CONTACT_TAIL \
 	MENUITEM "", 0, MFT_SEPARATOR \
-	MENUITEM "Set custom &icon...", IDM_BLIST_CONTEXT_CUSTOMICON, MFT_STRING, MFS_GRAYED \
-	MENUITEM "Remo&ve custom icon", IDM_BLIST_CONTEXT_REMOVEICON, MFT_STRING, MFS_GRAYED \
+	CONTEXT_CUSTOMICON \
 	CONTEXT_ALIAS_REMOVE \
 	MENUITEM "", 0, MFT_SEPARATOR \
 	MENUITEM "Sho&w individual buddies", IDM_BLIST_CONTEXT_SHOWBUDDIES, MFT_STRING, MFS_GRAYED
@@ -91,6 +94,8 @@ IDM_BLIST_CONTEXT MENUEX
 		CONTEXT_VIEWLOG
 		MENUITEM "", 0, MFT_SEPARATOR
 		CONTEXT_ALIAS_REMOVE
+		CONTEXT_CUSTOMICON
+		MENUITEM "", 0, MFT_SEPARATOR
 		MENUITEM "&Properties", IDM_BLIST_CONTEXT_PROPERTIES, MFT_STRING, MFS_GRAYED
 	}
 }
@@ -202,7 +207,6 @@ STRINGTABLE
 //
 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
 STRINGTABLE
-
 {
     IDS_ERROR_BLIST               "Couldn't initialise buddy list."
     IDS_ERROR_PURPLEINIT          "Couldn't initialise libpurple."
@@ -212,6 +216,8 @@ STRINGTABLE
     IDS_ERROR_RICHEDIT            "Couldn't load RichEdit."
     IDS_QUERY_DELGROUP            "Deleting this group will also delete everything contained in it. Do you wish to continue?"
     IDS_QUERY_DELCONTACT          "Deleting this contact will also delete all buddies associated with it. Do you wish to continue?"
+    IDS_BUDDYICON_FILTER          "Image Files\t*.bmp;*.png;*.gif;*.jpg\tAll Files (*.*)\t*.*\t"
+    IDS_BUDDYICON_TITLE           "Choose Custom Icon"
 }
 
 
============================================================
--- vulture/vulture.c	84fb7c964b69dc4d3ad5f178e7158b36bd65fb11
+++ vulture/vulture.c	7afb980eef536b50f2edb07386d4fe9b4cef6f1f
@@ -278,3 +278,78 @@ int VultureGetMenuPosFromID(HMENU hmenu,
 
 	return -1;
 }
+
+
+/**
+ * Displays the Open common dialogue box.
+ *
+ * @param	hwnd			Parent window.
+ * @param[out]	szFileNameReturn	Filename is returned here. May be
+ *					clobbered even if cancelled.
+ * @param	cchFileNameReturn	Length of filename buffer.
+ * @param	szTitle			Title of dialogue.
+ * @param	szFilter		Type filter string.
+ * @param	szDefExt		Default extension, without the dot.
+ * @param	szInitFileName		Initial filename.
+ * @param	iFlags			Additional flags for the comdlg32
+ *					library call. See its documentation.
+ *
+ * @return Nonzero if the user selects a file and OKs; zero if cancelled or on
+ * error.
+ */
+int VultureCommDlgOpen(HWND hwnd, LPTSTR szFileNameReturn, UINT cchFileNameReturn, LPCTSTR szTitle, LPCTSTR szFilter, LPCTSTR szDefExt, LPCTSTR szInitFilename, int iFlags)
+{
+	OPENFILENAME ofn;
+
+	_tcsncpy(szFileNameReturn, szInitFilename ? szInitFilename : TEXT(""), cchFileNameReturn - 1);
+	szFileNameReturn[cchFileNameReturn - 1] = TEXT('\0');
+
+	ofn.Flags = iFlags;
+	ofn.hwndOwner = hwnd;
+	ofn.lpstrFilter = szFilter;
+	ofn.nFilterIndex = 1;
+	ofn.lpstrDefExt = szDefExt;
+	ofn.lpstrFile = szFileNameReturn;
+	ofn.nMaxFile = cchFileNameReturn;
+	ofn.lpfnHook = NULL;
+	ofn.lpstrCustomFilter = NULL;
+	ofn.lpstrFileTitle = NULL;
+	ofn.lpstrInitialDir = NULL;
+	ofn.lpTemplateName = NULL;
+	ofn.lpstrTitle = szTitle;
+	ofn.lStructSize = sizeof(ofn);
+
+	return GetOpenFileName(&ofn);
+}
+
+
+/**
+ * Loads a string from the string table and replaces tabs with NULs. This gets
+ * around a limitation of the resource compiler.
+ *
+ * Parameters:
+ *   @param		unStringID	ID of string resource.
+ *   @param[out]	szFilter	Buffer in which to return string.
+ *   @param		cchFilter	Size of buffer in characters, including
+ *					space for terminators (both of them!).
+ */
+void VultureLoadAndFormatFilterString(USHORT unStringID, LPTSTR szFilter, UINT cchFilter)
+{
+	TCHAR *szInFilter = szFilter;
+
+	/* Get filter string from string table. The string is terminated with
+	 * *two* NULs.
+	 */
+	LoadString(g_hInstance, unStringID, szFilter, cchFilter-1);
+	szFilter[cchFilter - 1] = TEXT('\0');
+
+	/* Replace tabs with NULs. The resource format doesn't doesn't support
+	 * NULs embedded in strings, so we have to do it.
+	 */
+	do
+	{
+		if(*szInFilter == TEXT('\t'))
+			*szInFilter = TEXT('\0');
+	}
+	while(*(++szInFilter));
+}
============================================================
--- vulture/vulture.h	88e53c7056d2a2f393c398d3855264c70e2df1a9
+++ vulture/vulture.h	6cf7933aa57a6475608fad587677b2cd289e38cf
@@ -63,6 +63,8 @@ int VultureGetMenuPosFromID(HMENU hmenu,
 LPTSTR VultureAmpersandify(LPCTSTR szUnderscored);
 gint VultureCompareTCHARStrings(gconstpointer lpvStr1, gconstpointer lpvStr2, gpointer lpvUnused);
 int VultureGetMenuPosFromID(HMENU hmenu, UINT uiID);
+int VultureCommDlgOpen(HWND hwnd, LPTSTR szFileNameReturn, UINT cchFileNameReturn, LPCTSTR szTitle, LPCTSTR szFilter, LPCTSTR szDefExt, LPCTSTR szInitFilename, int iFlags);
+void VultureLoadAndFormatFilterString(USHORT unStringID, LPTSTR szFilter, UINT cchFilter);
 
 
 
============================================================
--- vulture/vultureblist.c	def4faff462d708a6cddf8a610974a4dfbb21809
+++ vulture/vultureblist.c	e4f1ec40843f6d2462a429673608e34ffe9883e6
@@ -606,7 +606,7 @@ static INT_PTR CALLBACK BuddyListDlgProc
 							HMENU hmenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDM_BLIST_CONTEXT));
 							HMENU hmenuSubmenu = NULL;
 							GList *lpglistVMA = NULL;
-							BOOL bExtraItems;
+							VULTURE_MAKE_CONTEXT_MENU vmcm;
 
 							/* Really select this node. */
 							TreeView_SelectItem(lpnmhdr->hwndFrom, tvitem.hItem);
@@ -618,7 +618,7 @@ static INT_PTR CALLBACK BuddyListDlgProc
 							VultureBListNodeAddRef(lpvblistnode);
 
 							/* Assume we need to ask the core for extra items. */
-							bExtraItems = TRUE;
+							vmcm.bExtraItems = TRUE;
 
 							/* Reading lpvblistnode->nodetype is atomic and so
 							 * we don't need our critical section.
@@ -633,7 +633,7 @@ static INT_PTR CALLBACK BuddyListDlgProc
 								if(TreeView_GetChild(lpnmhdr->hwndFrom, tvitem.hItem))
 								{
 									hmenuSubmenu = GetSubMenu(hmenu, CMI_CONTACT_BASIC);
-									bExtraItems = FALSE;
+									vmcm.bExtraItems = FALSE;
 								}
 								else
 									hmenuSubmenu = GetSubMenu(hmenu, CMI_CONTACT_COMPOSITE);
@@ -645,21 +645,16 @@ static INT_PTR CALLBACK BuddyListDlgProc
 								break;
 
 							default:
-								bExtraItems = FALSE;
+								vmcm.bExtraItems = FALSE;
 								break;
 							}
 
-							if(bExtraItems)
-							{
-								VULTURE_MAKE_CONTEXT_MENU vmcm;
+							vmcm.hmenu = hmenuSubmenu;
+							vmcm.lpvblistnode = lpvblistnode;
+							vmcm.lplpglistVMA = &lpglistVMA;
 
-								vmcm.hmenu = hmenuSubmenu;
-								vmcm.lpvblistnode = lpvblistnode;
-								vmcm.lplpglistVMA = &lpglistVMA;
+							VultureSingleSyncPurpleCall(PC_MAKECONTEXTMENU, &vmcm);
 
-								VultureSingleSyncPurpleCall(PC_MAKECONTEXTMENU, &vmcm);
-							}
-
 							if(hmenuSubmenu)
 							{
 								POINT ptMouse;
@@ -999,6 +994,38 @@ static BOOL RunCommonMenuCmd(HWND hwndBu
 		RemoveNodeRequest(hwndBuddies, lpvblistnode);
 		return TRUE;
 
+	case IDM_BLIST_CONTEXT_CUSTOMICON:
+		{
+			TCHAR szFilename[MAX_PATH];
+			TCHAR szFilter[256];
+			TCHAR szTitle[256];
+
+			VultureLoadAndFormatFilterString(IDS_BUDDYICON_FILTER, szFilter, NUM_ELEMENTS(szFilter));
+			LoadString(g_hInstance, IDS_BUDDYICON_TITLE, szTitle, NUM_ELEMENTS(szTitle));
+
+			if(VultureCommDlgOpen(g_hwndMain, szFilename, NUM_ELEMENTS(szFilename), szTitle, szFilter, TEXT("png"), NULL, OFN_FILEMUSTEXIST | OFN_HIDEREADONLY))
+			{
+				VULTURE_BLIST_NODE_STRING_PAIR vblnstringpairNewIcon;
+
+				vblnstringpairNewIcon.lpvblistnode = lpvblistnode;
+				vblnstringpairNewIcon.sz = szFilename;
+
+				VultureSingleSyncPurpleCall(PC_SETCUSTOMICON, &vblnstringpairNewIcon);
+			}
+		}
+
+		return TRUE;
+
+	case IDM_BLIST_CONTEXT_REMOVEICON:
+		{
+			VULTURE_BLIST_NODE_STRING_PAIR vblnstringpairNewIcon;
+
+			vblnstringpairNewIcon.lpvblistnode = lpvblistnode;
+			vblnstringpairNewIcon.sz = NULL;
+
+			VultureSingleSyncPurpleCall(PC_SETCUSTOMICON, &vblnstringpairNewIcon);
+		}
+
 	default:
 		/* Not a static command that we recongise; might be a dynamic
 		 * command.
============================================================
--- vulture/vultureblist.h	87f6739800c61a41b79503397408ffe6fa037fac
+++ vulture/vultureblist.h	2b2ca0a4da9b9fd73b04ec96c87f0a9755710c58
@@ -53,7 +53,13 @@ typedef struct _VULTURE_BLIST_NODE_GET_B
 	BOOL			bReturn;
 } VULTURE_BLIST_NODE_GET_BOOL;
 
+typedef struct _VULTURE_BLIST_NODE_STRING_PAIR
+{
+	VULTURE_BLIST_NODE	*lpvblistnode;
+	LPTSTR			sz;
+} VULTURE_BLIST_NODE_STRING_PAIR;
 
+
 extern HWND g_hwndMain;
 GList *g_lpglistConvContainers;
 


More information about the Commits mailing list