windows-nt/Source/XPSP1/NT/admin/pchealth/sysinfo/control/pseudomenu.cpp
2020-09-26 16:20:57 +08:00

463 lines
14 KiB
C++

//=============================================================================
// Implementation for the pseudo menu and menu bar classes used by the
// msinfo control.
//=============================================================================
#include "stdafx.h"
#include "pseudomenu.h"
#include "resource.h"
//=============================================================================
// CPseudoMenu functions.
//=============================================================================
//-----------------------------------------------------------------------------
// Constructor and destructor are really simple.
//-----------------------------------------------------------------------------
CPseudoMenu::CPseudoMenu(LPCTSTR szCaption, COLORREF crNormal, COLORREF crHighlight) :
m_hMenu(NULL),
m_strCaption(szCaption),
m_crNormal(crNormal),
m_crHighlight(crHighlight),
m_fHighlight(FALSE)
{
m_rect.left = m_rect.right = m_rect.top = m_rect.bottom = 0;
};
CPseudoMenu::~CPseudoMenu()
{
if (m_hMenu)
::DestroyMenu(m_hMenu);
}
//-----------------------------------------------------------------------------
// Get the size of this menu. We'll need the DC for this.
//-----------------------------------------------------------------------------
void CPseudoMenu::GetSize(HDC hdc, int * pcx, int * pcy)
{
SIZE size;
// Temporarily adding on a find button using the menu bar. This will go
// eventually go away.
CString strCaption(m_strCaption);
if (strCaption.Left(1) == _T("\t"))
strCaption = strCaption.Mid(1);
if (::GetTextExtentPoint32(hdc, strCaption, strCaption.GetLength(), &size))
{
if (pcx)
*pcx = size.cx + size.cy;
if (pcy)
*pcy = size.cy;
m_rect.right = m_rect.left + size.cx + size.cy;
m_rect.bottom = m_rect.top + size.cy;
}
}
//-----------------------------------------------------------------------------
// Move the menu.
//-----------------------------------------------------------------------------
void CPseudoMenu::SetLocation(int cx, int cy)
{
int cxWidth = m_rect.right - m_rect.left;
int cyHeight = m_rect.bottom - m_rect.top;
m_rect.left = cx;
m_rect.top = cy;
m_rect.right = m_rect.left + cxWidth;
m_rect.bottom = m_rect.top + cyHeight;
};
//-----------------------------------------------------------------------------
// Update the colors.
//-----------------------------------------------------------------------------
void CPseudoMenu::UpdateColors(COLORREF crNormal, COLORREF crHighlight)
{
m_crNormal = crNormal;
m_crHighlight = crHighlight;
}
//-----------------------------------------------------------------------------
// Change the highlight status, and return if an actual change was made.
//-----------------------------------------------------------------------------
BOOL CPseudoMenu::SetHighlight(BOOL fHighlight)
{
BOOL fDifferent = (m_fHighlight != fHighlight);
m_fHighlight = fHighlight;
return fDifferent;
}
//-----------------------------------------------------------------------------
// Draw the menu caption, using the specified highlight (indicates if the
// mouse is over the menu).
//-----------------------------------------------------------------------------
void CPseudoMenu::Render(HDC hdc)
{
CDC dc;
dc.Attach(hdc);
// Temporarily adding on a find button using the menu bar. This will go
// eventually go away.
// Draw the menu caption.
int cyRectHeight = m_rect.bottom - m_rect.top;
int cySmall = (cyRectHeight - 3)/4;
int cyMedium = (cyRectHeight - 3)/2;
int cyTiny = (cyRectHeight - 3)/6;
// Draw the small arrow icon.
CBrush brush((m_fHighlight) ? m_crHighlight : m_crNormal);
CPen pen(PS_SOLID, 1,(m_fHighlight) ? m_crHighlight : m_crNormal);
CBrush * pOldBrush = dc.SelectObject(&brush);
CPen * pOldPen = dc.SelectObject(&pen);
if (m_strCaption.Left(1) != _T("\t"))
{
POINT aPoints[] = { {m_rect.left + cySmall, m_rect.top + cyMedium - cyTiny},
{m_rect.left + cyMedium + cySmall, m_rect.top + cyMedium - cyTiny},
{m_rect.left + cyMedium, m_rect.top + cyMedium + cySmall - cyTiny}};
dc.Polygon(aPoints, 3);
}
else
{
CGdiObject * pFontOld = dc.SelectStockObject(DEFAULT_GUI_FONT);
TEXTMETRIC metrics;
dc.GetTextMetrics(&metrics);
CSize size = dc.GetTextExtent(m_strCaption.Mid(1));
dc.SelectObject(pFontOld);
POINT aPoints[] = { {m_rect.left, m_rect.top + metrics.tmHeight},
{m_rect.left + size.cx, m_rect.top + metrics.tmHeight}};
dc.Polygon(aPoints, 2);
}
dc.SelectObject(pOldBrush);
dc.SelectObject(pOldPen);
// Temporarily adding on a find button using the menu bar. This will go
// eventually go away.
CString strCaption(m_strCaption);
if (strCaption.Left(1) == _T("\t"))
strCaption = strCaption.Mid(1);
CGdiObject * pFontOld = dc.SelectStockObject(DEFAULT_GUI_FONT);
COLORREF crTextOld = dc.SetTextColor((m_fHighlight) ? m_crHighlight : m_crNormal);
int nBkModeOld = dc.SetBkMode(TRANSPARENT);
RECT rectText;
::CopyRect(&rectText, &m_rect);
// The text needs to be offset over by the height (to allow for the arrow icon).
if (m_strCaption.Left(1) != _T("\t"))
rectText.left += cyRectHeight;
dc.DrawText(strCaption, strCaption.GetLength(), &rectText, 0);
dc.SelectObject(pFontOld);
dc.SetTextColor(crTextOld);
dc.SetBkMode(nBkModeOld);
dc.Detach();
}
//-----------------------------------------------------------------------------
// Attach the new HMENU and return the existing HMENU.
//-----------------------------------------------------------------------------
HMENU CPseudoMenu::AttachMenu(HMENU hmenu)
{
HMENU hmenuOriginal = m_hMenu;
m_hMenu = hmenu;
return (hmenuOriginal);
}
//-----------------------------------------------------------------------------
// Display the menu and track the user interaction with it until an item is
// selected. Return the ID of the item selected.
//-----------------------------------------------------------------------------
UINT CPseudoMenu::TrackMenu(HWND hwnd, POINT * pPoint)
{
// Temporarily adding on a find button using the menu bar. This will go
// eventually go away.
if (m_strCaption.Left(1) == _T("\t"))
return ID_EDIT_FIND;
UINT uReturn = 0;
const UINT uFlags = TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD | TPM_LEFTBUTTON;
if (m_hMenu)
uReturn = ::TrackPopupMenu(m_hMenu, uFlags, pPoint->x, pPoint->y, 0, hwnd, NULL);
return uReturn;
}
//=============================================================================
// CPseudoMenuBar functions.
//=============================================================================
//-----------------------------------------------------------------------------
// Constructor and destructor.
//-----------------------------------------------------------------------------
CPseudoMenuBar::CPseudoMenuBar()
{
m_rect.left = m_rect.right = m_rect.top = m_rect.bottom = 0;
for (int i = 0; i < MaxMenus; i++)
m_pmenus[i] = NULL;
m_ptOrigin.x = m_ptOrigin.y = 5;
}
CPseudoMenuBar::~CPseudoMenuBar()
{
for (int i = 0; i < MaxMenus; i++)
if (m_pmenus[i])
delete m_pmenus[i];
}
//-----------------------------------------------------------------------------
// Load the menu specified by the resource ID.
//-----------------------------------------------------------------------------
void CPseudoMenuBar::LoadFromResource(HINSTANCE hinstance, UINT uResourceID, COLORREF crNormal, COLORREF crHighlight)
{
HMENU hmenu = ::LoadMenu(hinstance, MAKEINTRESOURCE(uResourceID));
if (hmenu)
{
try
{
TCHAR szBuffer[MAX_PATH] = _T("");
MENUITEMINFO mii;
int index = 0;
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_TYPE;
mii.dwTypeData = szBuffer;
HMENU hmenuSub = ::GetSubMenu(hmenu, 0);
while (hmenuSub && index < MaxMenus)
{
mii.cch = MAX_PATH;
GetMenuItemInfo(hmenu, 0, TRUE, &mii);
CPseudoMenu * pMenu = new CPseudoMenu(szBuffer, crNormal, crHighlight);
pMenu->AttachMenu(hmenuSub);
InsertMenu(index++, pMenu);
::RemoveMenu(hmenu, 0, MF_BYPOSITION);
hmenuSub = ::GetSubMenu(hmenu, 0);
}
// Temporarily adding on a find button using the menu bar. This will go
// eventually go away. With 196808, it has.
//
// {
// CString strFindButton;
//
// ::AfxSetResourceHandle(_Module.GetResourceInstance());
// strFindButton.LoadString(IDS_FINDBUTTONCAP);
// strFindButton = CString(_T("\t")) + strFindButton;
// CPseudoMenu * pFind = new CPseudoMenu(strFindButton, crNormal, crHighlight);
// InsertMenu(index++, pFind);
// }
::DestroyMenu(hmenu);
}
catch (...)
{
::DestroyMenu(hmenu);
}
}
}
//-----------------------------------------------------------------------------
// Update the colors for each individual menu.
//-----------------------------------------------------------------------------
void CPseudoMenuBar::UpdateColors(COLORREF crNormal, COLORREF crHighlight)
{
for (int index = 0; index < MaxMenus; index++)
if (m_pmenus[index])
m_pmenus[index]->UpdateColors(crNormal, crHighlight);
}
//-----------------------------------------------------------------------------
// Insert the pseudo menu into the indicated index.
//-----------------------------------------------------------------------------
void CPseudoMenuBar::InsertMenu(int index, CPseudoMenu * pMenu)
{
if (index >= 0 && index < MaxMenus)
{
if (m_pmenus[index])
delete m_pmenus[index];
m_pmenus[index] = pMenu;
m_fNeedToComputeRect = TRUE;
}
}
//-----------------------------------------------------------------------------
// Return a pointer to the requested pseudo menu.
//-----------------------------------------------------------------------------
CPseudoMenu * CPseudoMenuBar::GetMenu(int index)
{
return (index >= 0 && index < MaxMenus) ? m_pmenus[index] : NULL;
}
//-----------------------------------------------------------------------------
// Get the point from which the menu should be launched. This will be
// converted into screen coordinates for the call to TrackMenu. An
// alternative version takes coordinates instead of an index.
//-----------------------------------------------------------------------------
void CPseudoMenuBar::GetMenuPoint(HDC hdc, int index, POINT * pPoint)
{
RecomputeRect(hdc);
if (index >= 0 && index < MaxMenus && m_pmenus[index])
m_pmenus[index]->GetMenuPoint(pPoint);
}
void CPseudoMenuBar::GetMenuPoint(HDC hdc, int cx, int cy, POINT * pPoint)
{
RecomputeRect(hdc);
for (int i = 0; i < MaxMenus; i++)
if (m_pmenus[i] && m_pmenus[i]->HitTest(cx, cy))
{
m_pmenus[i]->GetMenuPoint(pPoint);
break;
}
}
//-----------------------------------------------------------------------------
// Given the coordinates, determine if one of the menus should be drawn with
// a highlight. If the state of one or more menus changes, return TRUE so the
// caller knows the menu bar needs to be re-rendered.
//-----------------------------------------------------------------------------
BOOL CPseudoMenuBar::TrackHighlight(HDC hdc, int cx, int cy)
{
BOOL fReturn = FALSE;
RecomputeRect(hdc);
for (int i = 0; i < MaxMenus; i++)
if (m_pmenus[i])
fReturn |= m_pmenus[i]->SetHighlight(m_pmenus[i]->HitTest(cx, cy));
return fReturn;
}
//-----------------------------------------------------------------------------
// Set the menu bar so that none of the items are highlighted. Return whether
// we need to be repainted.
//-----------------------------------------------------------------------------
BOOL CPseudoMenuBar::NoHighlight()
{
BOOL fReturn = FALSE;
for (int i = 0; i < MaxMenus; i++)
if (m_pmenus[i])
fReturn |= m_pmenus[i]->SetHighlight(FALSE);
return fReturn;
}
//-----------------------------------------------------------------------------
// This is used to actually display the menu and allow the user to choose an
// option from it. The pPoint parameter is the screen point for the menu
// display. The cx and cy parameters are the local coordinates used to find
// the correct menu to show.
//-----------------------------------------------------------------------------
UINT CPseudoMenuBar::TrackMenu(HWND hwnd, POINT * pPoint, int cx, int cy)
{
for (int i = 0; i < MaxMenus; i++)
if (m_pmenus[i] && m_pmenus[i]->HitTest(cx, cy))
return m_pmenus[i]->TrackMenu(hwnd, pPoint);
return 0;
}
//-----------------------------------------------------------------------------
// Set the origin for the display of the menu bar.
//-----------------------------------------------------------------------------
void CPseudoMenuBar::SetOrigin(HDC hdc, POINT point)
{
m_ptOrigin = point;
m_fNeedToComputeRect = TRUE;
RecomputeRect(hdc);
}
//-----------------------------------------------------------------------------
// Rendering the menu bar consists of rendering each menu.
//-----------------------------------------------------------------------------
void CPseudoMenuBar::Render(HDC hdc)
{
RecomputeRect(hdc);
for (int i = 0; i < MaxMenus; i++)
if (m_pmenus[i])
m_pmenus[i]->Render(hdc);
}
//-----------------------------------------------------------------------------
// A private function used to place all the menus, and compute the bounding
// rectangle.
//-----------------------------------------------------------------------------
void CPseudoMenuBar::RecomputeRect(HDC hdc)
{
if (!m_fNeedToComputeRect)
return;
m_fNeedToComputeRect = FALSE;
int cx = 0, cy = 0;
int cxCurrent = m_ptOrigin.x;
for (int i = 0; i < MaxMenus; i++)
if (m_pmenus[i])
{
// Temporarily adding on a find button using the menu bar. This will go
// eventually go away.
if (m_pmenus[i]->GetCaption().Left(1) == _T("\t"))
{
// Move the button over to the right.
CDC dc;
dc.Attach(hdc);
CGdiObject * pFontOld = dc.SelectStockObject(DEFAULT_GUI_FONT);
CString strCaption = m_pmenus[i]->GetCaption().Mid(1);
CSize sizeText = dc.GetTextExtent(strCaption);
dc.SelectObject(pFontOld);
if ((m_winRect.right - sizeText.cx - 5) > cxCurrent)
cxCurrent = m_winRect.right - sizeText.cx - 5;
m_pmenus[i]->SetLocation(cxCurrent, m_ptOrigin.y);
m_pmenus[i]->GetSize(hdc, &cx, &cy);
cxCurrent += cx;
dc.Detach();
continue;
}
m_pmenus[i]->SetLocation(cxCurrent, m_ptOrigin.y);
m_pmenus[i]->GetSize(hdc, &cx, &cy);
cxCurrent += cx;
}
::SetRect(&m_rect, m_ptOrigin.x, m_ptOrigin.y, m_ptOrigin.x + cxCurrent - 5, m_ptOrigin.y + cy);
}