soc.2009.vulture: 8f058922: Query libpurple and the prpl for extra b...

gdick at soc.pidgin.im gdick at soc.pidgin.im
Fri Jul 17 15:35:37 EDT 2009


-----------------------------------------------------------------
Revision: 8f05892209824acd762d87f1e49366ca3345fd9b
Ancestor: ff449a695fe56c628800212fcfd9de7f41f75f08
Author: gdick at soc.pidgin.im
Date: 2009-07-17T19:28:28
Branch: im.pidgin.soc.2009.vulture
URL: http://d.pidgin.im/viewmtn/revision/info/8f05892209824acd762d87f1e49366ca3345fd9b

Modified files:
        vulture/purpleblist.c vulture/purpleblist.h
        vulture/purplemain.c 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

ChangeLog: 

Query libpurple and the prpl for extra buddy menu items.

-------------- next part --------------
============================================================
--- vulture/purpleblist.c	803071d56717d4032ab124a3292d0be7b0bdbd7f
+++ vulture/purpleblist.c	3bd807fd44abe6dc95ad476ea8cb5fa4ee24bb01
@@ -28,6 +28,7 @@
 #include "purpleblist.h"
 #include "vultureblist.h"
 #include "purplemain.h"
+#include "resource.h"
 
 
 
@@ -294,3 +295,46 @@ void PurpleBuddyStatusChanged(PurpleBudd
 	if(lpconv)
 		VulturePostUIMessage(VUIMSG_UPDATEIMSTATUSTEXT, lpconv->ui_data);
 }
+
+
+/**
+ * Builds the context menu for a buddy-list node.
+ *
+ * @param[in,out]	hmenu		Basic menu loaded from the resources,
+ *					which will be augmented.
+ * @param		lpblistnode	Buddy-list node.
+ * @param[out]		lplpglistVMA	Used to return a list populated with
+ *					pointers to item-data for the menu
+ *					items that we add, which the caller
+ *					should g_free (and then g_list_free)
+ *					once it's done with the menu, but which
+ *					otherwise it probably doesn't care
+ *					about.
+ */
+void PurpleMakeBuddyMenu(HMENU hmenu, PurpleBlistNode *lpblistnode, GList **lplpglistVMA)
+{
+	GList *lpglistPMA;
+	PurplePluginProtocolInfo *lpprplinfo;
+	PurpleConnection *lpconnection = ((PurpleBuddy*)lpblistnode)->account->gc;
+	UINT uiNextID = IDM_DYNAMIC_FIRST;
+
+	*lplpglistVMA = NULL;
+
+	if(!lpblistnode)
+		return;
+
+	if(lpconnection &&
+		(lpprplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(lpconnection->prpl)) &&
+		lpprplinfo->blist_node_menu)
+	{
+		/* Insert at the bottom of the first section. */
+		lpglistPMA = lpprplinfo->blist_node_menu(lpblistnode);
+		PurpleInsertDynamicMenu(hmenu, VultureGetMenuPosFromID(hmenu, IDM_BLIST_CONTEXT_VIEWLOG) + 1, &uiNextID, lpglistPMA, lplpglistVMA, lpblistnode);
+		g_list_free(lpglistPMA);
+	}
+
+	/* Insert at the top of the second section. */
+	lpglistPMA = purple_blist_node_get_extended_menu(lpblistnode);
+	PurpleInsertDynamicMenu(hmenu, VultureGetMenuPosFromID(hmenu, IDM_BLIST_CONTEXT_BLOCK), &uiNextID, lpglistPMA, lplpglistVMA, lpblistnode);
+	g_list_free(lpglistPMA);
+}
============================================================
--- vulture/purpleblist.h	8fda2a675d558096e29191f6e55eaa1ac51b7ad4
+++ vulture/purpleblist.h	ed5a2b70215e322cb6fc30a9b2323d2bce6f83d7
@@ -50,6 +50,7 @@ void PurpleBuddyStatusChanged(PurpleBudd
 void PurpleBListNodeActivated(VULTURE_BLIST_NODE *lpvbn);
 LPTSTR PurpleBuddyGetStatusText(PurpleBuddy *lpbuddy);
 void PurpleBuddyStatusChanged(PurpleBuddy *lpbuddy, PurpleStatus *lpstatusOld, PurpleStatus *lpstatusNew);
+void PurpleMakeBuddyMenu(HMENU hmenu, PurpleBlistNode *lpblistnode, GList **lplpglistVMA);
 
 
 static INLINE void VultureBListNodeAddRef(VULTURE_BLIST_NODE *lpvblnode) { InterlockedIncrement(&lpvblnode->lRefCount); }
============================================================
--- vulture/purplemain.c	c030ffed4a929cb36cb2c180c861e7ced380e993
+++ vulture/purplemain.c	0c2f14781a7dcb8dc9c856f76e842b31372c156c
@@ -225,3 +225,89 @@ static void Quitting(void)
 	/* The core is on its way out, so tell the UI to destroy itself. */
 	VulturePostUIMessage(VUIMSG_QUIT, NULL);
 }
+
+
+/**
+ * Inserts items from a list of PurpleMenuActions into a Win32 menu. Inspired
+ * by pidgin_append_menu_action.
+ *
+ * @param[in,out]	hmenu		Menu to add to.
+ * @param		iIndex		Index of item before which to insert
+ *					our stuff.
+ * @param[in,out]	lpuiNextID	First ID of new menu item. We increment
+ *					this each time we use it, so that it's
+ *					ready for the next call once we've
+ *					finished.
+ * @param		lpglistPMA	List of PurpleMenuAction structures
+ *					describing the new menu items.
+ * @param[in,out]	lplpglistVMA	Our own item-data will be added to this
+ *					list, which should be g_freed once the
+ *					menu has been destroyed.
+ */
+void PurpleInsertDynamicMenu(HMENU hmenu, int iIndex, UINT *lpuiNextID, GList *lpglistPMA, GList **lplpglistVMA, gpointer lpvObject)
+{
+	GList *lpglistRover;
+
+	for(lpglistRover = lpglistPMA; lpglistRover; lpglistRover = lpglistRover->next)
+	{
+		PurpleMenuAction *lppma = lpglistRover->data;
+		MENUITEMINFO mii;
+
+		mii.cbSize = sizeof(mii);
+		mii.dwTypeData = NULL;
+		
+		if(lppma)
+		{
+			LPTSTR szNameTemp = VultureUTF8ToTCHAR(lppma->label);
+
+			mii.fMask = MIIM_FTYPE | MIIM_STRING;
+			mii.fType = MFT_STRING;
+			mii.dwTypeData = VultureAmpersandify(szNameTemp);
+
+			g_free(szNameTemp);
+
+			if(lppma->children)
+			{
+				/* We're a submenu. Recur. */
+
+				mii.fMask |= MIIM_SUBMENU;
+				mii.hSubMenu = CreateMenu();
+				PurpleInsertDynamicMenu(mii.hSubMenu, 0, lpuiNextID, lppma->children, lplpglistVMA, lpvObject);
+
+				g_list_free(lppma->children);
+			}
+			else
+			{
+				/* Normal menu item. */
+
+				VULTURE_MENU_ACTION *lpvma = g_new(VULTURE_MENU_ACTION, 1);
+
+				lpvma->lpfnCallback = lppma->callback;
+				lpvma->lpvData = lppma->data;
+				lpvma->lpvObject = lpvObject;
+
+				mii.fMask |= MIIM_ID | MIIM_DATA;
+				mii.wID = (*lpuiNextID)++;
+				mii.dwItemData = (ULONG_PTR)lpvma;
+
+				/* Add the item-data to the list for ease of
+				 * freeing.
+				 */
+				*lplpglistVMA = g_list_prepend(*lplpglistVMA, lpvma);
+			}
+
+			g_free(lppma);
+		}
+		else
+		{
+			/* Separator. */
+			mii.fMask = MIIM_FTYPE;
+			mii.fType = MFT_SEPARATOR;
+		}
+
+		InsertMenuItem(hmenu, iIndex, TRUE, &mii);
+
+		if(mii.dwTypeData)
+			ProcHeapFree(mii.dwTypeData);
+	}
+}
============================================================
--- vulture/purplemain.h	781eddcde1d88f0b2a1dbd17f177d6fd344bbb2e
+++ vulture/purplemain.h	f2d1ae8bd5cbd694895307d697e1e9bcaac6825e
@@ -30,8 +30,24 @@
 
 #include "vulture.h"
 #include "vultureblist.h"
+#include "purpleblist.h"
 
 
+typedef struct _VULTURE_MENU_ACTION
+{
+	void		(*lpfnCallback)(gpointer lpvObject, gpointer lpvData);
+	gpointer	lpvObject;
+	gpointer	lpvData;
+} VULTURE_MENU_ACTION;
+
+typedef struct _VULTURE_MAKE_CONTEXT_MENU
+{
+	HMENU			hmenu;
+	VULTURE_BLIST_NODE	*lpvblistnode;
+	GList			**lplpglistVMA;
+} VULTURE_MAKE_CONTEXT_MENU;
+
+
 /* The significance of lpvParam is given for each message. */
 enum ENUM_VULTURE_UI_MESSAGES
 {
@@ -83,6 +99,7 @@ void VultureShutDownLibpurple(void);
 
 void VultureInitLibpurple(HANDLE *lphthread);
 void VultureShutDownLibpurple(void);
+void PurpleInsertDynamicMenu(HMENU hmenu, int iIndex, UINT *lpuiNextID, GList *lpglistPMA, GList **lplpglistVMA, gpointer lpvObject);
 
 extern GMainLoop *g_lpgmainloop;
 
============================================================
--- vulture/purplequeue.c	c8d03c413912a1e71932bf8917306339fd2da624
+++ vulture/purplequeue.c	6e81e1a076615cab44d89789b58161abca0e605b
@@ -268,6 +268,14 @@ static void DispatchPurpleCall(PURPLE_CA
 		
 		break;
 
+	case PC_MAKEBUDDYMENU:
+		{
+			VULTURE_MAKE_CONTEXT_MENU *lpvmcm = lppurplecall->lpvParam;
+			PurpleMakeBuddyMenu(lpvmcm->hmenu, lpvmcm->lpvblistnode->lpblistnode, lpvmcm->lplpglistVMA);
+		}
+
+		break;
+
 	case PC_QUIT:
 		purple_core_quit();
 		g_main_loop_quit(g_lpgmainloop);
============================================================
--- vulture/purplequeue.h	db8cec4ed6eddc3e65d57a5b2c77b3f16f65d1f8
+++ vulture/purplequeue.h	1eb3b67668548d96665910278a93d44e5963fdac
@@ -82,6 +82,9 @@ enum PURPLE_CALL_ID
 
 	/* (VULTURE_GET_IM_BUDDY_ICON*) */
 	PC_GETIMBUDDYICON,
+
+	/* (VULTURE_MAKE_CONTEXT_MENU*) */
+	PC_MAKEBUDDYMENU,
 };
 
 
============================================================
--- vulture/resource.h	73f206b82322d3979b81de5f6758091262a5a5d5
+++ vulture/resource.h	184fcd45fbf7bed29e1dcc5f72e1f0ebc3161380
@@ -45,11 +45,15 @@
 #define IDM_BLIST_CONTEXT_SHOWOFFLINE           40206
 #define IDM_BLIST_CONTEXT_ALIAS                 40207
 #define IDM_BLIST_CONTEXT_REMOVE                40208
+#define IDM_BLIST_CONTEXT_BLOCK                 40209
 
 #define IDM_CONV                                1003
 #define IDM_CONV_CONV_CLOSE                     40401
 
+/* For dynamic menu items not sent as WM_COMMAND notifications. */
+#define IDM_DYNAMIC_FIRST			50000
 
+
 #define IDR_ACCEL_MAIN                          2001
 
 
============================================================
--- vulture/vulture-res.rc	dfbf98a56dbd7d901356d923da3530f67a930847
+++ vulture/vulture-res.rc	90ee93a20dd3148415d547bfbeb66fcd0c497e8a
@@ -50,6 +50,7 @@ IDM_BLIST_CONTEXT MENUEX
 
 		MENUITEM "", 0, MFT_SEPARATOR
 
+		MENUITEM "&Block", IDM_BLIST_CONTEXT_BLOCK, MFT_STRING, MFS_GRAYED
 		MENUITEM "Show when &offline", IDM_BLIST_CONTEXT_SHOWOFFLINE, MFT_STRING, MFS_GRAYED
 		MENUITEM "Set &alias...", IDM_BLIST_CONTEXT_ALIAS, MFT_STRING, MFS_GRAYED
 		MENUITEM "&Remove from buddy list", IDM_BLIST_CONTEXT_REMOVE, MFT_STRING, MFS_GRAYED
============================================================
--- vulture/vulture.c	04f9b98a996dd13b06e462ba23f0204ee4788c7f
+++ vulture/vulture.c	84fb7c964b69dc4d3ad5f178e7158b36bd65fb11
@@ -257,3 +257,24 @@ gint VultureCompareTCHARStrings(gconstpo
 	UNREFERENCED_PARAMETER(lpvUnused);
 	return _tcscmp((LPCTSTR)lpvStr1, (LPCTSTR)lpvStr2);
 }
+
+
+/**
+ * Finds the position of a menu item given its ID.
+ *
+ * @param	hmenu	Menu.
+ * @param	uiID	ID of desired menu item.
+ *
+ * @return Index of item, or negative if not found.
+ */
+int VultureGetMenuPosFromID(HMENU hmenu, UINT uiID)
+{
+	int iCount = GetMenuItemCount(hmenu);
+	int i;
+
+	for(i = 0; i < iCount; i++)
+		if(GetMenuItemID(hmenu, i) == uiID)
+			return i;
+
+	return -1;
+}
============================================================
--- vulture/vulture.h	27c53cafd100bac89ff55196e5f36398a7db21ab
+++ vulture/vulture.h	88e53c7056d2a2f393c398d3855264c70e2df1a9
@@ -62,6 +62,7 @@ gint VultureCompareTCHARStrings(gconstpo
 void VultureTimetToSystemTime(time_t t, LPSYSTEMTIME lpsystime);
 LPTSTR VultureAmpersandify(LPCTSTR szUnderscored);
 gint VultureCompareTCHARStrings(gconstpointer lpvStr1, gconstpointer lpvStr2, gpointer lpvUnused);
+int VultureGetMenuPosFromID(HMENU hmenu, UINT uiID);
 
 
 
============================================================
--- vulture/vultureblist.c	00d43bf3136e54ba21a2d6a696c307e9eff9279a
+++ vulture/vultureblist.c	8dc08c3607840934ad2f34c5ecd7cafc19de7b4b
@@ -56,6 +56,7 @@ static void RemoveBListNode(HWND hwndBli
 static LRESULT CALLBACK StatusMsgBoxSubclassProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam);
 static void SetStatusMsg(HWND hwndStatusDlg);
 static void RemoveBListNode(HWND hwndBlistTree, VULTURE_BLIST_NODE *lpvbn);
+static void RunBuddyMenuCmd(VULTURE_BLIST_NODE *lpvblistnode, HMENU hmenu, int iCmd);
 
 
 #define BLIST_MARGIN 6
@@ -555,30 +556,6 @@ static INT_PTR CALLBACK BuddyListDlgProc
 
 		return TRUE;
 
-	case WM_COMMAND:
-		switch(LOWORD(wParam))
-		{
-		case IDM_BLIST_CONTEXT_IM:
-			{
-				TVITEM tvitem;
-				HWND hwndBlist = GetDlgItem(hwndDlg, IDC_TREE_BLIST);
-
-				if((tvitem.hItem = TreeView_GetSelection(hwndBlist)))
-				{
-					tvitem.mask = TVIF_PARAM;
-					TreeView_GetItem(hwndBlist, &tvitem);
-					
-					VultureEnqueueAsyncPurpleCall(PC_BLISTNODEACTIVATED, (VULTURE_BLIST_NODE*)tvitem.lParam);
-
-					return TRUE;
-				}
-			}
-
-			break;
-		}
-
-		break;
-
 	case WM_NOTIFY:
 		{
 			LPNMHDR lpnmhdr = (LPNMHDR)lParam;
@@ -621,7 +598,8 @@ static INT_PTR CALLBACK BuddyListDlgProc
 						{
 							VULTURE_BLIST_NODE *lpvblistnode;
 							HMENU hmenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDM_BLIST_CONTEXT));
-							int iMenuIndex = -1;
+							HMENU hmenuSubmenu = NULL;
+							GList *lpglistVMA = NULL;
 
 							/* Really select this node. */
 							TreeView_SelectItem(lpnmhdr->hwndFrom, tvitem.hItem);
@@ -631,30 +609,49 @@ static INT_PTR CALLBACK BuddyListDlgProc
 
 							lpvblistnode = (VULTURE_BLIST_NODE*)tvitem.lParam;
 
-							EnterCriticalSection(&lpvblistnode->cs);
-
-								switch(lpvblistnode->nodetype)
+							/* Reading lpvblistnode->nodetype is atomic and so
+							 * we don't need our critical section.
+							 */
+							switch(lpvblistnode->nodetype)
+							{
+							case PURPLE_BLIST_BUDDY_NODE:
 								{
-								case PURPLE_BLIST_BUDDY_NODE:
-									iMenuIndex = CMI_BUDDY;
-									break;
+									VULTURE_MAKE_CONTEXT_MENU vmcm;
 
-								default:
-									break;
+									vmcm.hmenu = hmenuSubmenu = GetSubMenu(hmenu, CMI_BUDDY);
+									vmcm.lpvblistnode = lpvblistnode;
+									vmcm.lplpglistVMA = &lpglistVMA;
+
+									VultureSingleSyncPurpleCall(PC_MAKEBUDDYMENU, &vmcm);
 								}
+								
+								break;
 
-							LeaveCriticalSection(&lpvblistnode->cs);
+							default:
+								break;
+							}
 
-							if(iMenuIndex >= 0)
+							if(hmenuSubmenu)
 							{
 								POINT ptMouse;
+								int iCmd;
 
 								GetCursorPos(&ptMouse);
-								TrackPopupMenu(GetSubMenu(hmenu, iMenuIndex), TPM_RIGHTBUTTON, ptMouse.x, ptMouse.y, 0, hwndDlg, NULL);
+								iCmd = TrackPopupMenu(hmenuSubmenu, TPM_RIGHTBUTTON | TPM_RETURNCMD, ptMouse.x, ptMouse.y, 0, hwndDlg, NULL);
+
+								if(iCmd != 0)
+									RunBuddyMenuCmd(lpvblistnode, hmenuSubmenu, iCmd);
 							}
 
+							/* Destroy menu. This will also destroy our modifications. */
 							DestroyMenu(hmenu);
 
+							/* Clean up any extra data we might have as a result of
+							 * having modified the menu.
+							 */
+							g_list_foreach(lpglistVMA, (GFunc)g_free, NULL);
+							g_list_free(lpglistVMA);
+
 							return TRUE;
 						}
 					}
@@ -873,3 +870,23 @@ static void RemoveBListNode(HWND hwndBli
 	 */
 	VultureBListNodeRelease(lpvbn);
 }
+
+
+/**
+ * Executes a menu command from the context menu for a buddy node in the buddy
+ * list.
+ *
+ * @param	lpvblistnode	List node to which the context menu relates.
+ * @param	hmenu		Context menu.
+ * @param	iCmd		Command ID.
+ */
+static void RunBuddyMenuCmd(VULTURE_BLIST_NODE *lpvblistnode, HMENU hmenu, int iCmd)
+{
+	UNREFERENCED_PARAMETER(hmenu);
+
+	switch(iCmd)
+	{
+	case IDM_BLIST_CONTEXT_IM:
+		VultureEnqueueAsyncPurpleCall(PC_BLISTNODEACTIVATED, lpvblistnode);
+	}
+}


More information about the Commits mailing list