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