windows-nt/Source/XPSP1/NT/enduser/netmeeting/nmutil/button.cpp
2020-09-26 16:20:57 +08:00

693 lines
15 KiB
C++

// File: BitmapButton.cpp
#include "precomp.h"
#include "GenControls.h"
#include <windowsx.h>
static const UINT IDT_FLASH = 1;
static const UINT FLASH_INTERVAL = 500;
CButton::CButton() :
m_pNotify(NULL)
{
m_sizeIcon.cx = 16;
m_sizeIcon.cy = 16;
}
CButton::~CButton()
{
}
BOOL CButton::Create(
HWND hWndParent,
INT_PTR nId,
LPCTSTR szTitle,
DWORD dwStyle,
IButtonChange *pNotify
)
{
if (!CFillWindow::Create(
hWndParent, // Window parent
nId, // ID of the child window
szTitle, // Window name
0, // Window style; WS_CHILD|WS_VISIBLE will be added to this
WS_EX_CONTROLPARENT // Extended window style
))
{
return(FALSE);
}
m_pNotify = pNotify;
if (NULL != m_pNotify)
{
m_pNotify->AddRef();
}
// Create the Win32 button
CreateWindowEx(0, TEXT("button"), szTitle,
dwStyle|WS_CHILD|WS_VISIBLE|BS_NOTIFY,
0, 0, 10, 10,
GetWindow(),
reinterpret_cast<HMENU>(nId),
reinterpret_cast<HINSTANCE>(GetWindowLongPtr(hWndParent, GWLP_HINSTANCE)),
NULL);
return(TRUE);
}
// Set the icon displayed with this button
void CButton::SetIcon(
HICON hIcon // The icon to use for this button
)
{
SendMessage(GetChild(), BM_SETIMAGE, IMAGE_ICON, reinterpret_cast<LPARAM>(hIcon));
m_sizeIcon.cx = 16;
m_sizeIcon.cy = 16;
// If we actually stored an icon, get its info
hIcon = GetIcon();
if (NULL != hIcon)
{
ICONINFO iconinfo;
if (GetIconInfo(hIcon, &iconinfo))
{
if (NULL != iconinfo.hbmColor)
{
CBitmapButton::GetBitmapSizes(&iconinfo.hbmColor, &m_sizeIcon, 1);
DeleteObject(iconinfo.hbmColor);
}
if (NULL != iconinfo.hbmMask)
{
DeleteObject(iconinfo.hbmMask);
}
}
}
}
// Get the icon displayed with this button
HICON CButton::GetIcon()
{
return(reinterpret_cast<HICON>(SendMessage(GetChild(), BM_GETIMAGE, IMAGE_ICON, 0)));
}
// Set the bitmap displayed with this button
void CButton::SetBitmap(
HBITMAP hBitmap // The bitmap to use for this button
)
{
SendMessage(GetChild(), BM_SETIMAGE, IMAGE_BITMAP, reinterpret_cast<LPARAM>(hBitmap));
}
// Get the bitmap displayed with this button
HBITMAP CButton::GetBitmap()
{
return(reinterpret_cast<HBITMAP>(SendMessage(GetChild(), BM_GETIMAGE, IMAGE_BITMAP, 0)));
}
// Get/set the checked state of the button
void CButton::SetChecked(
BOOL bCheck // TRUE if the button should be checked
)
{
Button_SetCheck(GetChild(), bCheck);
}
BOOL CButton::IsChecked()
{
return(Button_GetCheck(GetChild()));
}
void CButton::GetDesiredSize(SIZE *psize)
{
static const int DefDlgUnitWidth = 50;
static const int DefDlgUnitHeight = 14;
static const int PushButtonBorder = 4;
static const int CheckLeftBorder = 5;
static const int CheckOtherBorder = 1;
HWND child = GetChild();
SIZE sizeMinPush = { 0, 0 };
*psize = sizeMinPush;
DWORD dwStyle = GetWindowLong(GetChild(), GWL_STYLE);
switch (dwStyle&(BS_ICON|BS_BITMAP))
{
case BS_ICON:
{
*psize = m_sizeIcon;
break;
}
case BS_BITMAP:
{
HBITMAP hImg = GetBitmap();
if (NULL == hImg)
{
break;
}
CBitmapButton::GetBitmapSizes(&hImg, psize, 1);
break;
}
default: // Text
{
// HACKHACK georgep: Button text should not be too large
TCHAR szTitle[80];
GetWindowText(child, szTitle, ARRAY_ELEMENTS(szTitle));
HDC hdc = GetDC(child);
HFONT hf = GetWindowFont(child);
HFONT hOld = reinterpret_cast<HFONT>(SelectObject(hdc, hf));
GetTextExtentPoint(hdc, szTitle, lstrlen(szTitle), psize);
TEXTMETRIC tm;
GetTextMetrics(hdc, &tm);
sizeMinPush.cx = tm.tmAveCharWidth * DefDlgUnitWidth / 4;
sizeMinPush.cy = tm.tmHeight * DefDlgUnitHeight / 8;
SelectObject(hdc, hOld);
ReleaseDC(child, hdc);
break;
}
}
switch (dwStyle&(BS_PUSHBUTTON|BS_CHECKBOX|BS_RADIOBUTTON))
{
case BS_CHECKBOX:
case BS_RADIOBUTTON:
{
psize->cx += CheckLeftBorder + GetSystemMetrics(SM_CXMENUCHECK) + CheckOtherBorder;
psize->cy += CheckOtherBorder*2;
int cy = GetSystemMetrics(SM_CYMENUCHECK);
psize->cy = max(psize->cy, cy);
break;
}
case BS_PUSHBUTTON:
default:
psize->cx += PushButtonBorder*2;
psize->cy += PushButtonBorder*2;
psize->cx = max(psize->cx, sizeMinPush.cx);
psize->cy = max(psize->cy, sizeMinPush.cy);
break;
}
}
LRESULT CButton::ProcessMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
HANDLE_MSG(hwnd, WM_COMMAND , OnCommand);
case WM_DESTROY:
if (NULL != m_pNotify)
{
m_pNotify->Release();
m_pNotify = NULL;
}
break;
}
return(CFillWindow::ProcessMessage(hwnd, message, wParam, lParam));
}
void CButton::OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
// Change the HWND to this and forward to the parent
HWND hwndThis = GetWindow();
switch (codeNotify)
{
case BN_CLICKED:
if (NULL != m_pNotify)
{
m_pNotify->OnClick(this);
break;
}
FORWARD_WM_COMMAND(GetParent(hwndThis), id, hwndThis, codeNotify, ::SendMessage);
break;
case BN_SETFOCUS:
SetHotControl(this);
break;
}
}
CBitmapButton::CBitmapButton() :
m_hbStates(NULL),
m_nInputStates(0),
m_nCustomStates(0),
m_nCustomState(0),
m_bHot(FALSE),
m_nFlashState(NoFlash)
{
}
CBitmapButton::~CBitmapButton()
{
if (NULL != m_hbStates)
{
DeleteObject(m_hbStates);
m_hbStates = NULL;
}
}
BOOL CBitmapButton::Create(
HWND hWndParent, // The parent of the button
int nId, // The ID for WM_COMMAND messages
HBITMAP hbStates, // The 2D array of bitmaps for the states of the button,
// vertically in the order specified in the StateBitmaps enum
// and horizontally in the custom states order
UINT nInputStates, // The number of input states (Normal, Pressed, Hot, Disabled)
UINT nCustomStates, // The number of custom states
IButtonChange *pNotify // The click handler
)
{
// Copy the bitmap handle; note that we now own this bitmap, even if the
// create fails
m_hbStates = hbStates;
// Must have a "normal" bitmap
ASSERT(NULL!=hbStates && Normal<nInputStates && 1<=nCustomStates);
if (!CButton::Create(
hWndParent, // Window parent
nId, // ID of the child window
TEXT("NMButton"), // Window name
BS_OWNERDRAW|BS_NOTIFY|BS_PUSHBUTTON|WS_TABSTOP, // Window style; WS_CHILD|WS_VISIBLE will be added to this
pNotify
))
{
return(FALSE);
}
m_nInputStates = nInputStates;
m_nCustomStates = nCustomStates;
return(TRUE);
}
// Creates the button, using the bitmaps specified
BOOL CBitmapButton::Create(
HWND hWndParent, // The parent of the button
int nId, // The ID for WM_COMMAND messages
HINSTANCE hInst, // The instance to load the bitmap from
int nIdBitmap, // The ID of the bitmap to use
BOOL bTranslateColors, // Use system background colors
UINT nInputStates, // The number of input states (Normal, Pressed, Hot, Disabled)
UINT nCustomStates, // The number of custom states
IButtonChange *pNotify // The click handler
)
{
HBITMAP hb;
LoadBitmaps(hInst, &nIdBitmap, &hb, 1, bTranslateColors);
return(Create(hWndParent, nId, hb, nInputStates, nCustomStates, pNotify));
}
// Return the size of the "normal" bitmap.
void CBitmapButton::GetDesiredSize(SIZE *ppt)
{
// Note that I don't want CButton::GetDesiredSize
CGenWindow::GetDesiredSize(ppt);
BITMAP bm;
// HACKHACK georgep: Only based on the normal bitmap
if (NULL == m_hbStates || 0 == m_nInputStates || 0 == m_nCustomStates
|| 0 == GetObject(m_hbStates, sizeof(BITMAP), &bm))
{
return;
}
ppt->cx += bm.bmWidth/m_nCustomStates;
ppt->cy += bm.bmHeight/m_nInputStates;
}
#if FALSE
void DumpWindow(HWND hwnd, LPCTSTR pszPrefix)
{
TCHAR szTemp[80];
wsprintf(szTemp, TEXT("%s: %d "), pszPrefix, GetWindowLong(hwnd, GWL_ID));
GetWindowText(hwnd, szTemp+lstrlen(szTemp), ARRAY_ELEMENTS(szTemp)-lstrlen(szTemp));
lstrcat(szTemp, TEXT("\n"));
OutputDebugString(szTemp);
}
#endif // FALSE
LRESULT CBitmapButton::ProcessMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
HANDLE_MSG(hwnd, WM_DRAWITEM , OnDrawItem);
HANDLE_MSG(hwnd, WM_SETCURSOR, OnSetCursor);
HANDLE_MSG(hwnd, WM_TIMER , OnTimer);
case WM_ENABLE:
SchedulePaint();
break;
}
return(CButton::ProcessMessage(hwnd, message, wParam, lParam));
}
void CBitmapButton::OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT * lpDrawItem)
{
int nState = Normal;
int state = lpDrawItem->itemState;
// If pressed or selected, show the pressed bitmap
if ((((state&ODS_DISABLED) == ODS_DISABLED) || !IsWindowEnabled(GetWindow())) && m_nInputStates > Disabled)
{
nState = Disabled;
}
// If pressed or selected, show the pressed bitmap
else if ((state&ODS_SELECTED) == ODS_SELECTED && m_nInputStates > Pressed)
{
nState = Pressed;
}
// If hot, show the hot bitmap
else if ((m_nFlashState != ForceNormal) && ((m_nFlashState == ForceHot) || IsHot()) && m_nInputStates > Hot)
{
nState = Hot;
}
// Otherwise show the normal bitmap
else
{
nState = Normal;
}
// Draw in the upper left
HDC hdcDraw = lpDrawItem->hDC;
HDC hdcTemp = CreateCompatibleDC(hdcDraw);
if (NULL != hdcTemp)
{
HPALETTE hPal = GetPalette();
HPALETTE hOld = NULL;
if (NULL != hPal)
{
hOld = SelectPalette(hdcDraw, hPal, TRUE);
RealizePalette(hdcDraw);
SelectPalette(hdcTemp, hPal, TRUE);
RealizePalette(hdcTemp);
}
// This will tell me the size of an individual bitmap
SIZE size;
// Do not use an override
CBitmapButton::GetDesiredSize(&size);
if (NULL != SelectObject(hdcTemp, m_hbStates))
{
BitBlt(hdcDraw,
lpDrawItem->rcItem.left, lpDrawItem->rcItem.top,
size.cx, size.cy,
hdcTemp, m_nCustomState*size.cx, nState*size.cy, SRCCOPY);
// BUGBUG georgep: We should clear any "uncovered" area here
}
DeleteDC(hdcTemp);
if (NULL != hPal)
{
SelectPalette(hdcDraw, hOld, TRUE);
}
}
FORWARD_WM_DRAWITEM(hwnd, lpDrawItem, CButton::ProcessMessage);
}
BOOL CBitmapButton::OnSetCursor(HWND hwnd, HWND hwndCursor, UINT codeHitTest, UINT msg)
{
SetHotControl(this);
return(FORWARD_WM_SETCURSOR(hwnd,hwndCursor, codeHitTest, msg, CButton::ProcessMessage));
}
void CBitmapButton::SetCustomState(UINT nCustomState)
{
ASSERT(m_nCustomState < m_nCustomStates);
if (m_nCustomState == nCustomState)
{
// Nothing to do
return;
}
m_nCustomState = nCustomState;
SchedulePaint();
}
void CBitmapButton::SetHot(BOOL bHot)
{
bHot = (bHot != FALSE);
if (m_bHot == bHot)
{
return;
}
m_bHot = bHot;
SchedulePaint();
}
// Change to flashing mode
void CBitmapButton::SetFlashing(int nSeconds)
{
HWND hwndThis = GetWindow();
if (0 == nSeconds)
{
KillTimer(hwndThis, IDT_FLASH);
// This means to stop flashing
if (IsFlashing())
{
m_nFlashState = NoFlash;
SchedulePaint();
}
}
else
{
if (NULL == hwndThis)
{
// I need a window to do this
return;
}
m_endFlashing = GetTickCount() + nSeconds*1000;
if (!IsFlashing())
{
SetTimer(hwndThis, IDT_FLASH, FLASH_INTERVAL, NULL);
OnTimer(hwndThis, IDT_FLASH);
}
}
}
void CBitmapButton::OnTimer(HWND hwnd, UINT id)
{
if (IDT_FLASH == id)
{
if (static_cast<int>(GetTickCount() - m_endFlashing) > 0)
{
SetFlashing(0);
}
else
{
m_nFlashState = (ForceNormal==m_nFlashState ? ForceHot : ForceNormal);
SchedulePaint();
}
}
}
// Helper function for getting the sizes of an array of bitmaps
void CBitmapButton::GetBitmapSizes(HBITMAP parts[], SIZE sizes[], int nParts)
{
for (--nParts; nParts>=0; --nParts)
{
if (NULL == parts[nParts])
{
sizes[nParts].cx = sizes[nParts].cy = 0;
continue;
}
BITMAP bm;
GetObject(parts[nParts], sizeof(bm), &bm);
sizes[nParts].cx = bm.bmWidth;
sizes[nParts].cy = bm.bmHeight;
}
}
// I would really rather just use LoadImage with the proper flags, but it turns
// out that Win95 then tries to write into a read-only resource, which faults.
// So I have to make a copy of the BITMAPINFO with the color table and change
// it myself.
static HBITMAP MyLoadImage(HINSTANCE hInst, int id)
{
// Load up the bitmap resource bits
HRSRC hFound = FindResource(hInst, MAKEINTRESOURCE(id), RT_BITMAP);
if (NULL == hFound)
{
return(NULL);
}
HGLOBAL hLoaded = LoadResource(hInst, hFound);
if (NULL == hLoaded)
{
return(NULL);
}
HBITMAP ret = NULL;
LPVOID lpBits = LockResource(hLoaded);
if (NULL != lpBits)
{
BITMAPINFO *pbmi = reinterpret_cast<BITMAPINFO*>(lpBits);
// create a "shortcut"
BITMAPINFOHEADER &bmih = pbmi->bmiHeader;
// Only deal with 8bpp, uncompressed image
if (bmih.biSize == sizeof(BITMAPINFOHEADER)
&& 1 == bmih.biPlanes
&& BI_RGB == bmih.biCompression)
{
// Determine the length of the color table
UINT nColors = bmih.biClrUsed;
if (0 == nColors)
{
nColors = 1 << bmih.biBitCount;
}
ASSERT(nColors <= static_cast<UINT>(1<<bmih.biBitCount));
// Make a copy of the BITMAPINFO and color table so I can change
// the value of one of the table entries.
struct
{
BITMAPINFO bmi;
RGBQUAD rgb[256];
} mbmi;
CopyMemory(&mbmi, pbmi, sizeof(BITMAPINFOHEADER)+nColors*sizeof(RGBQUAD));
// This is a "packed DIB" so the pixels are immediately after the
// color table
LPBYTE pPixels = reinterpret_cast<LPBYTE>(&pbmi->bmiColors[nColors]);
BYTE byFirst = pPixels[0];
switch (bmih.biBitCount)
{
case 8:
break;
case 4:
byFirst = (byFirst >> 4) & 0x0f;
break;
case 1:
byFirst = (byFirst >> 7) & 0x01;
break;
default:
goto CleanUp;
}
ASSERT(static_cast<UINT>(byFirst) < nColors);
// Change the value of the first pixel to be the 3DFace color
RGBQUAD &rgbChange = mbmi.bmi.bmiColors[byFirst];
COLORREF cr3DFace = GetSysColor(COLOR_3DFACE);
rgbChange.rgbRed = GetRValue(cr3DFace);
rgbChange.rgbGreen = GetGValue(cr3DFace);
rgbChange.rgbBlue = GetBValue(cr3DFace);
// Create the DIB section and copy the bits into it
LPVOID lpDIBBits;
ret = CreateDIBSection(NULL, &mbmi.bmi, DIB_RGB_COLORS,
&lpDIBBits, NULL, 0);
if (NULL != ret)
{
// Round the width up to the nearest DWORD
int widthBytes = (bmih.biWidth*bmih.biBitCount+7)/8;
widthBytes = (widthBytes+3)&~3;
CopyMemory(lpDIBBits, pPixels, widthBytes*bmih.biHeight);
}
}
CleanUp:
UnlockResource(hLoaded);
}
FreeResource(hLoaded);
return(ret);
}
//Helper function for loading up a bunch of bitmaps
void CBitmapButton::LoadBitmaps(
HINSTANCE hInst, // The instance to load the bitmap from
const int ids[], // Array of bitmap ID's
HBITMAP bms[], // Array of HBITMAP's for storing the result
int nBmps, // Number of entries in the arrays
BOOL bTranslateColors // Use system background colors
)
{
for (--nBmps; nBmps>=0; --nBmps)
{
if (0 == ids[nBmps])
{
bms[nBmps] = NULL;
}
else
{
// #define TRYBMPFILE
#ifdef TRYBMPFILE
bms[nBmps] = NULL;
// This is useful for the designer to try out different bitmaps
TCHAR szFile[80];
wsprintf(szFile, TEXT("%d.bmp"), ids[nBmps]);
if (((DWORD)-1) != GetFileAttributes(szFile))
{
int nLoadFlags = LR_CREATEDIBSECTION;
if (bTranslateColors)
{
nLoadFlags |= LR_LOADMAP3DCOLORS|LR_LOADTRANSPARENT;
}
bms[nBmps] = (HBITMAP)LoadImage(_Module.GetModuleInstance(),
szFile, IMAGE_BITMAP, 0, 0, nLoadFlags|LR_LOADFROMFILE);
}
if (NULL == bms[nBmps])
#endif // TRYBMPFILE
{
if (bTranslateColors)
{
//
// LAURABU 2/21/99 -- LoadImage with translated colors only works
// on Win9x if your resources
// are NOT read-only, since Win9x tries to write into the resource
// memory temporarily. It faults if not.
//
bms[nBmps] = MyLoadImage(hInst, ids[nBmps]);
}
if (NULL == bms[nBmps])
{
bms[nBmps] = (HBITMAP)LoadImage(hInst,
MAKEINTRESOURCE(ids[nBmps]), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
}
}
}
}
}