857 lines
21 KiB
C
857 lines
21 KiB
C
/*
|
|
* Process Hacker -
|
|
* extended menus
|
|
*
|
|
* Copyright (C) 2010-2011 wj32
|
|
*
|
|
* This file is part of Process Hacker.
|
|
*
|
|
* Process Hacker is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Process Hacker is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <ph.h>
|
|
#include <guisup.h>
|
|
#include <emenu.h>
|
|
|
|
static const PH_FLAG_MAPPING EMenuTypeMappings[] =
|
|
{
|
|
{ PH_EMENU_MENUBARBREAK, MFT_MENUBARBREAK },
|
|
{ PH_EMENU_MENUBREAK, MFT_MENUBREAK },
|
|
{ PH_EMENU_RADIOCHECK, MFT_RADIOCHECK }
|
|
};
|
|
|
|
static const PH_FLAG_MAPPING EMenuStateMappings[] =
|
|
{
|
|
{ PH_EMENU_CHECKED, MFS_CHECKED },
|
|
{ PH_EMENU_DEFAULT, MFS_DEFAULT },
|
|
{ PH_EMENU_DISABLED, MFS_DISABLED },
|
|
{ PH_EMENU_HIGHLIGHT, MFS_HILITE }
|
|
};
|
|
|
|
PPH_EMENU_ITEM PhAllocateEMenuItem(
|
|
VOID
|
|
)
|
|
{
|
|
PPH_EMENU_ITEM item;
|
|
|
|
item = PhAllocate(sizeof(PH_EMENU_ITEM));
|
|
memset(item, 0, sizeof(PH_EMENU_ITEM));
|
|
|
|
return item;
|
|
}
|
|
|
|
/**
|
|
* Creates a menu item.
|
|
*
|
|
* \param Flags A combination of the following:
|
|
* \li \c PH_EMENU_DISABLED The menu item is greyed and cannot be selected.
|
|
* \li \c PH_EMENU_CHECKED A check mark is displayed.
|
|
* \li \c PH_EMENU_HIGHLIGHT The menu item is highlighted.
|
|
* \li \c PH_EMENU_MENUBARBREAK Places the menu item in a new column, separated by a vertical line.
|
|
* \li \c PH_EMENU_MENUBREAK Places the menu item in a new column, with no vertical line.
|
|
* \li \c PH_EMENU_DEFAULT The menu item is displayed as the default item. This causes the text to
|
|
* be bolded.
|
|
* \li \c PH_EMENU_RADIOCHECK Uses a radio-button mark instead of a check mark.
|
|
* \param Id A unique identifier for the menu item.
|
|
* \param Text The text displayed for the menu item.
|
|
* \param Bitmap A bitmap image for the menu item.
|
|
* \param Context A user-defined value.
|
|
*/
|
|
PPH_EMENU_ITEM PhCreateEMenuItem(
|
|
_In_ ULONG Flags,
|
|
_In_ ULONG Id,
|
|
_In_ PWSTR Text,
|
|
_In_opt_ HBITMAP Bitmap,
|
|
_In_opt_ PVOID Context
|
|
)
|
|
{
|
|
PPH_EMENU_ITEM item;
|
|
|
|
item = PhAllocateEMenuItem();
|
|
|
|
item->Flags = Flags;
|
|
item->Id = Id;
|
|
item->Text = Text;
|
|
item->Bitmap = Bitmap;
|
|
|
|
item->Context = Context;
|
|
|
|
return item;
|
|
}
|
|
|
|
/**
|
|
* Frees resources used by a menu item and its children.
|
|
*
|
|
* \param Item The menu item.
|
|
*
|
|
* \remarks The menu item is NOT automatically removed from its parent. It is safe to call this
|
|
* function while enumerating menu items.
|
|
*/
|
|
VOID PhpDestroyEMenuItem(
|
|
_In_ PPH_EMENU_ITEM Item
|
|
)
|
|
{
|
|
if (Item->DeleteFunction)
|
|
Item->DeleteFunction(Item);
|
|
|
|
if ((Item->Flags & PH_EMENU_TEXT_OWNED) && Item->Text)
|
|
PhFree(Item->Text);
|
|
if ((Item->Flags & PH_EMENU_BITMAP_OWNED) && Item->Bitmap)
|
|
DeleteObject(Item->Bitmap);
|
|
|
|
if (Item->Items)
|
|
{
|
|
ULONG i;
|
|
|
|
for (i = 0; i < Item->Items->Count; i++)
|
|
{
|
|
PhpDestroyEMenuItem(Item->Items->Items[i]);
|
|
}
|
|
|
|
PhDereferenceObject(Item->Items);
|
|
}
|
|
|
|
PhFree(Item);
|
|
}
|
|
|
|
/**
|
|
* Frees resources used by a menu item and its children.
|
|
*
|
|
* \param Item The menu item.
|
|
*
|
|
* \remarks The menu item is automatically removed from its parent.
|
|
*/
|
|
VOID PhDestroyEMenuItem(
|
|
_In_ PPH_EMENU_ITEM Item
|
|
)
|
|
{
|
|
// Remove the item from its parent, if it has one.
|
|
if (Item->Parent)
|
|
PhRemoveEMenuItem(NULL, Item, -1);
|
|
|
|
PhpDestroyEMenuItem(Item);
|
|
}
|
|
|
|
/**
|
|
* Finds a child menu item.
|
|
*
|
|
* \param Item The parent menu item.
|
|
* \param Flags A combination of the following:
|
|
* \li \c PH_EMENU_FIND_DESCEND Searches recursively within child menu items.
|
|
* \li \c PH_EMENU_FIND_STARTSWITH Performs a partial text search instead of an exact search.
|
|
* \li \c PH_EMENU_FIND_LITERAL Performs a literal search instead of ignoring prefix characters
|
|
* (ampersands).
|
|
* \param Text The text of the menu item to find. If NULL, the text is ignored.
|
|
* \param Id The identifier of the menu item to find. If 0, the identifier is ignored.
|
|
*
|
|
* \return The found menu item, or NULL if the menu item could not be found.
|
|
*/
|
|
PPH_EMENU_ITEM PhFindEMenuItem(
|
|
_In_ PPH_EMENU_ITEM Item,
|
|
_In_ ULONG Flags,
|
|
_In_opt_ PWSTR Text,
|
|
_In_opt_ ULONG Id
|
|
)
|
|
{
|
|
return PhFindEMenuItemEx(Item, Flags, Text, Id, NULL, NULL);
|
|
}
|
|
|
|
/**
|
|
* Finds a child menu item.
|
|
*
|
|
* \param Item The parent menu item.
|
|
* \param Flags A combination of the following:
|
|
* \li \c PH_EMENU_FIND_DESCEND Searches recursively within child menu items.
|
|
* \li \c PH_EMENU_FIND_STARTSWITH Performs a partial text search instead of an exact search.
|
|
* \li \c PH_EMENU_FIND_LITERAL Performs a literal search instead of ignoring prefix characters
|
|
* (ampersands).
|
|
* \param Text The text of the menu item to find. If NULL, the text is ignored.
|
|
* \param Id The identifier of the menu item to find. If 0, the identifier is ignored.
|
|
* \param FoundParent A variable which receives the parent of the found menu item.
|
|
* \param FoundIndex A variable which receives the index of the found menu item.
|
|
*
|
|
* \return The found menu item, or NULL if the menu item could not be found.
|
|
*/
|
|
PPH_EMENU_ITEM PhFindEMenuItemEx(
|
|
_In_ PPH_EMENU_ITEM Item,
|
|
_In_ ULONG Flags,
|
|
_In_opt_ PWSTR Text,
|
|
_In_opt_ ULONG Id,
|
|
_Out_opt_ PPH_EMENU_ITEM *FoundParent,
|
|
_Out_opt_ PULONG FoundIndex
|
|
)
|
|
{
|
|
PH_STRINGREF searchText;
|
|
ULONG i;
|
|
PPH_EMENU_ITEM item;
|
|
|
|
if (!Item->Items)
|
|
return NULL;
|
|
|
|
if (Text && (Flags & PH_EMENU_FIND_LITERAL))
|
|
PhInitializeStringRef(&searchText, Text);
|
|
|
|
for (i = 0; i < Item->Items->Count; i++)
|
|
{
|
|
item = Item->Items->Items[i];
|
|
|
|
if (Text)
|
|
{
|
|
if (Flags & PH_EMENU_FIND_LITERAL)
|
|
{
|
|
PH_STRINGREF text;
|
|
|
|
PhInitializeStringRef(&text, item->Text);
|
|
|
|
if (Flags & PH_EMENU_FIND_STARTSWITH)
|
|
{
|
|
if (PhStartsWithStringRef(&text, &searchText, TRUE))
|
|
goto FoundItemHere;
|
|
}
|
|
else
|
|
{
|
|
if (PhEqualStringRef(&text, &searchText, TRUE))
|
|
goto FoundItemHere;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (PhCompareUnicodeStringZIgnoreMenuPrefix(Text, item->Text,
|
|
TRUE, !!(Flags & PH_EMENU_FIND_STARTSWITH)) == 0)
|
|
goto FoundItemHere;
|
|
}
|
|
}
|
|
|
|
if (Id && item->Id == Id)
|
|
goto FoundItemHere;
|
|
|
|
if (Flags & PH_EMENU_FIND_DESCEND)
|
|
{
|
|
PPH_EMENU_ITEM foundItem;
|
|
PPH_EMENU_ITEM foundParent;
|
|
ULONG foundIndex;
|
|
|
|
foundItem = PhFindEMenuItemEx(item, Flags, Text, Id, &foundParent, &foundIndex);
|
|
|
|
if (foundItem)
|
|
{
|
|
if (FoundParent)
|
|
*FoundParent = foundParent;
|
|
if (FoundIndex)
|
|
*FoundIndex = foundIndex;
|
|
|
|
return foundItem;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
|
|
FoundItemHere:
|
|
if (FoundParent)
|
|
*FoundParent = Item;
|
|
if (FoundIndex)
|
|
*FoundIndex = i;
|
|
|
|
return item;
|
|
}
|
|
|
|
/**
|
|
* Determines the index of a menu item.
|
|
*
|
|
* \param Parent The parent menu item.
|
|
* \param Item The child menu item.
|
|
*
|
|
* \return The index of the menu item, or -1 if the menu item was not found in the parent menu item.
|
|
*/
|
|
ULONG PhIndexOfEMenuItem(
|
|
_In_ PPH_EMENU_ITEM Parent,
|
|
_In_ PPH_EMENU_ITEM Item
|
|
)
|
|
{
|
|
if (!Parent->Items)
|
|
return -1;
|
|
|
|
return PhFindItemList(Parent->Items, Item);
|
|
}
|
|
|
|
/**
|
|
* Inserts a menu item into a parent menu item.
|
|
*
|
|
* \param Parent The parent menu item.
|
|
* \param Item The menu item to insert.
|
|
* \param Index The index at which to insert the menu item. If the index is too large, the menu item
|
|
* is inserted at the last position.
|
|
*/
|
|
VOID PhInsertEMenuItem(
|
|
_Inout_ PPH_EMENU_ITEM Parent,
|
|
_Inout_ PPH_EMENU_ITEM Item,
|
|
_In_ ULONG Index
|
|
)
|
|
{
|
|
// Remove the item from its old parent if it has one.
|
|
if (Item->Parent)
|
|
PhRemoveEMenuItem(Item->Parent, Item, 0);
|
|
|
|
if (!Parent->Items)
|
|
Parent->Items = PhCreateList(16);
|
|
|
|
if (Index > Parent->Items->Count)
|
|
Index = Parent->Items->Count;
|
|
|
|
if (Index == -1)
|
|
PhAddItemList(Parent->Items, Item);
|
|
else
|
|
PhInsertItemList(Parent->Items, Index, Item);
|
|
|
|
Item->Parent = Parent;
|
|
}
|
|
|
|
/**
|
|
* Removes a menu item from its parent.
|
|
*
|
|
* \param Parent The parent menu item. If \a Item is NULL, this parameter must be specified.
|
|
* \param Item The child menu item. This may be NULL if \a Index is specified.
|
|
* \param Index The index of the menu item to remove. If \a Item is specified, this parameter is
|
|
* ignored.
|
|
*/
|
|
BOOLEAN PhRemoveEMenuItem(
|
|
_Inout_opt_ PPH_EMENU_ITEM Parent,
|
|
_In_opt_ PPH_EMENU_ITEM Item,
|
|
_In_opt_ ULONG Index
|
|
)
|
|
{
|
|
if (Item)
|
|
{
|
|
if (!Parent)
|
|
Parent = Item->Parent;
|
|
if (!Parent->Items)
|
|
return FALSE;
|
|
|
|
Index = PhFindItemList(Parent->Items, Item);
|
|
|
|
if (Index == -1)
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (!Parent)
|
|
return FALSE;
|
|
if (!Parent->Items)
|
|
return FALSE;
|
|
}
|
|
|
|
Item = Parent->Items->Items[Index];
|
|
PhRemoveItemList(Parent->Items, Index);
|
|
Item->Parent = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Removes all children from a menu item.
|
|
*
|
|
* \param Parent The parent menu item.
|
|
*/
|
|
VOID PhRemoveAllEMenuItems(
|
|
_Inout_ PPH_EMENU_ITEM Parent
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
if (!Parent->Items)
|
|
return;
|
|
|
|
for (i = 0; i < Parent->Items->Count; i++)
|
|
{
|
|
PhpDestroyEMenuItem(Parent->Items->Items[i]);
|
|
}
|
|
|
|
PhClearList(Parent->Items);
|
|
}
|
|
|
|
/**
|
|
* Creates a root menu.
|
|
*/
|
|
PPH_EMENU PhCreateEMenu(
|
|
VOID
|
|
)
|
|
{
|
|
PPH_EMENU menu;
|
|
|
|
menu = PhAllocate(sizeof(PH_EMENU));
|
|
memset(menu, 0, sizeof(PH_EMENU));
|
|
menu->Items = PhCreateList(16);
|
|
|
|
return menu;
|
|
}
|
|
|
|
/**
|
|
* Frees resources used by a root menu and its children.
|
|
*
|
|
* \param Menu A root menu.
|
|
*/
|
|
VOID PhDestroyEMenu(
|
|
_In_ PPH_EMENU Menu
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
for (i = 0; i < Menu->Items->Count; i++)
|
|
{
|
|
PhpDestroyEMenuItem(Menu->Items->Items[i]);
|
|
}
|
|
|
|
PhDereferenceObject(Menu->Items);
|
|
PhFree(Menu);
|
|
}
|
|
|
|
/**
|
|
* Initializes a data structure containing additional information resulting from a call to
|
|
* PhEMenuToHMenu().
|
|
*/
|
|
VOID PhInitializeEMenuData(
|
|
_Out_ PPH_EMENU_DATA Data
|
|
)
|
|
{
|
|
Data->IdToItem = PhCreateList(16);
|
|
}
|
|
|
|
/**
|
|
* Frees resources used by a data structure initialized by PhInitializeEMenuData().
|
|
*/
|
|
VOID PhDeleteEMenuData(
|
|
_Inout_ PPH_EMENU_DATA Data
|
|
)
|
|
{
|
|
PhDereferenceObject(Data->IdToItem);
|
|
}
|
|
|
|
/**
|
|
* Converts an EMENU to a Windows menu object.
|
|
*
|
|
* \param Menu The menu item to convert.
|
|
* \param Flags A combination of the following:
|
|
* \li \c PH_EMENU_CONVERT_ID Automatically assigns a unique identifier to each converted menu item.
|
|
* The resulting mappings are placed in \a Data.
|
|
* \param Data Additional data resulting from the conversion. The data structure must be initialized
|
|
* by PhInitializeEMenuData() prior to calling this function.
|
|
*
|
|
* \return A menu handle. The menu object must be destroyed using DestroyMenu() when it is no longer
|
|
* needed.
|
|
*/
|
|
HMENU PhEMenuToHMenu(
|
|
_In_ PPH_EMENU_ITEM Menu,
|
|
_In_ ULONG Flags,
|
|
_Inout_opt_ PPH_EMENU_DATA Data
|
|
)
|
|
{
|
|
HMENU menuHandle;
|
|
|
|
menuHandle = CreatePopupMenu();
|
|
|
|
if (!menuHandle)
|
|
return NULL;
|
|
|
|
PhEMenuToHMenu2(menuHandle, Menu, Flags, Data);
|
|
|
|
if (!(Menu->Flags & PH_EMENU_SEPARATECHECKSPACE))
|
|
{
|
|
MENUINFO menuInfo;
|
|
|
|
memset(&menuInfo, 0, sizeof(MENUINFO));
|
|
menuInfo.cbSize = sizeof(MENUINFO);
|
|
menuInfo.fMask = MIM_STYLE;
|
|
menuInfo.dwStyle = MNS_CHECKORBMP;
|
|
SetMenuInfo(menuHandle, &menuInfo);
|
|
}
|
|
|
|
return menuHandle;
|
|
}
|
|
|
|
/**
|
|
* Converts an EMENU to a Windows menu object.
|
|
*
|
|
* \param MenuHandle A handle to a Windows menu object.
|
|
* \param Menu The menu item to convert. The items are appended to \a MenuHandle.
|
|
* \param Flags A combination of the following:
|
|
* \li \c PH_EMENU_CONVERT_ID Automatically assigns a unique identifier to each converted menu item.
|
|
* The resulting mappings are placed in \a Data.
|
|
* \param Data Additional data resulting from the conversion. The data structure must be initialized
|
|
* by PhInitializeEMenuData() prior to calling this function.
|
|
*/
|
|
VOID PhEMenuToHMenu2(
|
|
_In_ HMENU MenuHandle,
|
|
_In_ PPH_EMENU_ITEM Menu,
|
|
_In_ ULONG Flags,
|
|
_Inout_opt_ PPH_EMENU_DATA Data
|
|
)
|
|
{
|
|
ULONG i;
|
|
PPH_EMENU_ITEM item;
|
|
MENUITEMINFO menuItemInfo;
|
|
|
|
for (i = 0; i < Menu->Items->Count; i++)
|
|
{
|
|
item = Menu->Items->Items[i];
|
|
|
|
memset(&menuItemInfo, 0, sizeof(MENUITEMINFO));
|
|
menuItemInfo.cbSize = sizeof(MENUITEMINFO);
|
|
|
|
// Type
|
|
|
|
menuItemInfo.fMask = MIIM_FTYPE | MIIM_STATE;
|
|
|
|
if (item->Flags & PH_EMENU_SEPARATOR)
|
|
{
|
|
menuItemInfo.fType = MFT_SEPARATOR;
|
|
}
|
|
else
|
|
{
|
|
menuItemInfo.fType = MFT_STRING;
|
|
menuItemInfo.fMask |= MIIM_STRING;
|
|
menuItemInfo.dwTypeData = item->Text;
|
|
}
|
|
|
|
PhMapFlags1(
|
|
&menuItemInfo.fType,
|
|
item->Flags,
|
|
EMenuTypeMappings,
|
|
sizeof(EMenuTypeMappings) / sizeof(PH_FLAG_MAPPING)
|
|
);
|
|
|
|
// Bitmap
|
|
|
|
if (item->Bitmap)
|
|
{
|
|
menuItemInfo.fMask |= MIIM_BITMAP;
|
|
menuItemInfo.hbmpItem = item->Bitmap;
|
|
}
|
|
|
|
// Id
|
|
|
|
if (Flags & PH_EMENU_CONVERT_ID)
|
|
{
|
|
ULONG id;
|
|
|
|
if (Data)
|
|
{
|
|
PhAddItemList(Data->IdToItem, item);
|
|
id = Data->IdToItem->Count;
|
|
|
|
menuItemInfo.fMask |= MIIM_ID;
|
|
menuItemInfo.wID = id;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (item->Id)
|
|
{
|
|
menuItemInfo.fMask |= MIIM_ID;
|
|
menuItemInfo.wID = item->Id;
|
|
}
|
|
}
|
|
|
|
// State
|
|
|
|
PhMapFlags1(
|
|
&menuItemInfo.fState,
|
|
item->Flags,
|
|
EMenuStateMappings,
|
|
sizeof(EMenuStateMappings) / sizeof(PH_FLAG_MAPPING)
|
|
);
|
|
|
|
// Context
|
|
|
|
menuItemInfo.fMask |= MIIM_DATA;
|
|
menuItemInfo.dwItemData = (ULONG_PTR)item;
|
|
|
|
// Submenu
|
|
|
|
if (item->Items && item->Items->Count != 0)
|
|
{
|
|
menuItemInfo.fMask |= MIIM_SUBMENU;
|
|
menuItemInfo.hSubMenu = PhEMenuToHMenu(item, Flags, Data);
|
|
}
|
|
|
|
InsertMenuItem(MenuHandle, MAXINT, TRUE, &menuItemInfo);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converts a Windows menu object to an EMENU.
|
|
*
|
|
* \param MenuItem The menu item in which the converted menu items will be placed.
|
|
* \param MenuHandle A menu handle.
|
|
*/
|
|
VOID PhHMenuToEMenuItem(
|
|
_Inout_ PPH_EMENU_ITEM MenuItem,
|
|
_In_ HMENU MenuHandle
|
|
)
|
|
{
|
|
ULONG i;
|
|
ULONG count;
|
|
|
|
count = GetMenuItemCount(MenuHandle);
|
|
|
|
if (count != -1)
|
|
{
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
MENUITEMINFO menuItemInfo;
|
|
WCHAR buffer[256];
|
|
PPH_EMENU_ITEM menuItem;
|
|
|
|
menuItemInfo.cbSize = sizeof(menuItemInfo);
|
|
menuItemInfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING | MIIM_SUBMENU;
|
|
menuItemInfo.cch = sizeof(buffer) / sizeof(WCHAR);
|
|
menuItemInfo.dwTypeData = buffer;
|
|
|
|
if (!GetMenuItemInfo(MenuHandle, i, TRUE, &menuItemInfo))
|
|
continue;
|
|
|
|
menuItem = PhCreateEMenuItem(
|
|
PH_EMENU_TEXT_OWNED,
|
|
menuItemInfo.wID,
|
|
PhDuplicateStringZ(buffer),
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (menuItemInfo.fType & MFT_SEPARATOR)
|
|
menuItem->Flags |= PH_EMENU_SEPARATOR;
|
|
|
|
PhMapFlags2(
|
|
&menuItem->Flags,
|
|
menuItemInfo.fType,
|
|
EMenuTypeMappings,
|
|
sizeof(EMenuTypeMappings) / sizeof(PH_FLAG_MAPPING)
|
|
);
|
|
PhMapFlags2(
|
|
&menuItem->Flags,
|
|
menuItemInfo.fState,
|
|
EMenuStateMappings,
|
|
sizeof(EMenuStateMappings) / sizeof(PH_FLAG_MAPPING)
|
|
);
|
|
|
|
if (menuItemInfo.hSubMenu)
|
|
PhHMenuToEMenuItem(menuItem, menuItemInfo.hSubMenu);
|
|
|
|
PhInsertEMenuItem(MenuItem, menuItem, -1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads a menu resource and converts it to an EMENU.
|
|
*
|
|
* \param MenuItem The menu item in which the converted menu items will be placed.
|
|
* \param InstanceHandle The module containing the menu resource.
|
|
* \param Resource The resource identifier.
|
|
* \param SubMenuIndex The index of the sub menu to use, or -1 to use the root menu.
|
|
*/
|
|
VOID PhLoadResourceEMenuItem(
|
|
_Inout_ PPH_EMENU_ITEM MenuItem,
|
|
_In_ HINSTANCE InstanceHandle,
|
|
_In_ PWSTR Resource,
|
|
_In_ ULONG SubMenuIndex
|
|
)
|
|
{
|
|
HMENU menu;
|
|
HMENU realMenu;
|
|
|
|
menu = LoadMenu(InstanceHandle, Resource);
|
|
|
|
if (SubMenuIndex != -1)
|
|
realMenu = GetSubMenu(menu, SubMenuIndex);
|
|
else
|
|
realMenu = menu;
|
|
|
|
PhHMenuToEMenuItem(MenuItem, realMenu);
|
|
|
|
DestroyMenu(menu);
|
|
}
|
|
|
|
/**
|
|
* Displays a menu.
|
|
*
|
|
* \param Menu A menu.
|
|
* \param WindowHandle The window that owns the popup menu.
|
|
* \param Flags A combination of the following:
|
|
* \li \c PH_EMENU_SHOW_SEND_COMMAND A WM_COMMAND message is sent to the window when the user clicks
|
|
* on a menu item.
|
|
* \li \c PH_EMENU_SHOW_LEFTRIGHT The user can select menu items with both the left and right mouse
|
|
* buttons.
|
|
* \param Align The alignment of the menu.
|
|
* \param X The horizontal location of the menu.
|
|
* \param Y The vertical location of the menu.
|
|
*
|
|
* \return The selected menu item, or NULL if the menu was cancelled.
|
|
*/
|
|
PPH_EMENU_ITEM PhShowEMenu(
|
|
_In_ PPH_EMENU Menu,
|
|
_In_ HWND WindowHandle,
|
|
_In_ ULONG Flags,
|
|
_In_ ULONG Align,
|
|
_In_ ULONG X,
|
|
_In_ ULONG Y
|
|
)
|
|
{
|
|
PPH_EMENU_ITEM selectedItem;
|
|
ULONG result;
|
|
ULONG flags;
|
|
PH_EMENU_DATA data;
|
|
HMENU popupMenu;
|
|
|
|
selectedItem = NULL;
|
|
flags = TPM_RETURNCMD | TPM_NONOTIFY;
|
|
|
|
// Flags
|
|
|
|
if (Flags & PH_EMENU_SHOW_LEFTRIGHT)
|
|
flags |= TPM_RIGHTBUTTON;
|
|
else
|
|
flags |= TPM_LEFTBUTTON;
|
|
|
|
// Align
|
|
|
|
if (Align & PH_ALIGN_LEFT)
|
|
flags |= TPM_LEFTALIGN;
|
|
else if (Align & PH_ALIGN_RIGHT)
|
|
flags |= TPM_RIGHTALIGN;
|
|
else
|
|
flags |= TPM_CENTERALIGN;
|
|
|
|
if (Align & PH_ALIGN_TOP)
|
|
flags |= TPM_TOPALIGN;
|
|
else if (Align & PH_ALIGN_BOTTOM)
|
|
flags |= TPM_BOTTOMALIGN;
|
|
else
|
|
flags |= TPM_VCENTERALIGN;
|
|
|
|
PhInitializeEMenuData(&data);
|
|
|
|
if (popupMenu = PhEMenuToHMenu(Menu, PH_EMENU_CONVERT_ID, &data))
|
|
{
|
|
result = TrackPopupMenu(
|
|
popupMenu,
|
|
flags,
|
|
X,
|
|
Y,
|
|
0,
|
|
WindowHandle,
|
|
NULL
|
|
);
|
|
|
|
if (result != 0)
|
|
{
|
|
selectedItem = data.IdToItem->Items[result - 1];
|
|
}
|
|
|
|
DestroyMenu(popupMenu);
|
|
}
|
|
|
|
PhDeleteEMenuData(&data);
|
|
|
|
if ((Flags & PH_EMENU_SHOW_SEND_COMMAND) && selectedItem && selectedItem->Id != 0)
|
|
SendMessage(WindowHandle, WM_COMMAND, MAKEWPARAM(selectedItem->Id, 0), 0);
|
|
|
|
return selectedItem;
|
|
}
|
|
|
|
/**
|
|
* Sets the flags of a menu item.
|
|
*
|
|
* \param Item The parent menu item.
|
|
* \param Id The identifier of the child menu item.
|
|
* \param Mask The flags to modify.
|
|
* \param Value The new value of the flags.
|
|
*/
|
|
BOOLEAN PhSetFlagsEMenuItem(
|
|
_Inout_ PPH_EMENU_ITEM Item,
|
|
_In_ ULONG Id,
|
|
_In_ ULONG Mask,
|
|
_In_ ULONG Value
|
|
)
|
|
{
|
|
PPH_EMENU_ITEM item;
|
|
|
|
item = PhFindEMenuItem(Item, PH_EMENU_FIND_DESCEND, NULL, Id);
|
|
|
|
if (item)
|
|
{
|
|
item->Flags &= ~Mask;
|
|
item->Flags |= Value;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets flags for all children of a menu item.
|
|
*
|
|
* \param Item The parent menu item.
|
|
* \param Mask The flags to modify.
|
|
* \param Value The new value of the flags.
|
|
*/
|
|
VOID PhSetFlagsAllEMenuItems(
|
|
_In_ PPH_EMENU_ITEM Item,
|
|
_In_ ULONG Mask,
|
|
_In_ ULONG Value
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
for (i = 0; i < Item->Items->Count; i++)
|
|
{
|
|
PPH_EMENU_ITEM item = Item->Items->Items[i];
|
|
|
|
item->Flags &= ~Mask;
|
|
item->Flags |= Value;
|
|
}
|
|
}
|
|
|
|
VOID PhModifyEMenuItem(
|
|
_Inout_ PPH_EMENU_ITEM Item,
|
|
_In_ ULONG ModifyFlags,
|
|
_In_ ULONG OwnedFlags,
|
|
_In_opt_ PWSTR Text,
|
|
_In_opt_ HBITMAP Bitmap
|
|
)
|
|
{
|
|
if (ModifyFlags & PH_EMENU_MODIFY_TEXT)
|
|
{
|
|
if ((Item->Flags & PH_EMENU_TEXT_OWNED) && Item->Text)
|
|
PhFree(Item->Text);
|
|
|
|
Item->Text = Text;
|
|
Item->Flags &= ~PH_EMENU_TEXT_OWNED;
|
|
Item->Flags |= OwnedFlags & PH_EMENU_TEXT_OWNED;
|
|
}
|
|
|
|
if (ModifyFlags & PH_EMENU_MODIFY_BITMAP)
|
|
{
|
|
if ((Item->Flags & PH_EMENU_BITMAP_OWNED) && Item->Bitmap)
|
|
DeleteObject(Item->Bitmap);
|
|
|
|
Item->Bitmap = Bitmap;
|
|
Item->Flags &= ~PH_EMENU_BITMAP_OWNED;
|
|
Item->Flags |= OwnedFlags & PH_EMENU_BITMAP_OWNED;
|
|
}
|
|
}
|