windows-nt/Source/XPSP1/NT/shell/ext/ratings/common/iconlbox.cpp
2020-09-26 16:20:57 +08:00

403 lines
12 KiB
C++

/***************************************************************************/
/** Microsoft Windows **/
/** Copyright(c) Microsoft Corp., 1993 **/
/***************************************************************************/
/****************************************************************************
ICONLBOX.C
Implementation for IconListBox class
May 93, JimH
See ICONLBOX.H for details on use.
****************************************************************************/
#include "npcommon.h"
#include <windows.h>
#include <memory.h>
#include <iconlbox.h>
#if defined(DEBUG)
static const char szFileName[] = __FILE__;
#define _FILENAME_DEFINED_ONCE szFileName
#endif
#include <npassert.h>
/****************************************************************************
IconListBox constructor
Some initialization is done here, and some is done in SetHeight
(when window handles are known.)
****************************************************************************/
IconListBox::IconListBox(HINSTANCE hInst, int nCtlID,
int iconWidth, int iconHeight) :
_nCtlID(nCtlID), _hInst(hInst),
_iconWidth(iconWidth), _iconHeight(iconHeight),
_hbrSelected(NULL), _hbrUnselected(NULL),
_fCombo(FALSE), _cIcons(0), _cTabs(0),_iCurrentMaxHorzExt(0),
_hwndDialog(NULL), _hwndListBox(NULL)
{
_colSel = ::GetSysColor(COLOR_HIGHLIGHT);
_colSelText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
_colUnsel = ::GetSysColor(COLOR_WINDOW);
_colUnselText = ::GetSysColor(COLOR_WINDOWTEXT);
}
/****************************************************************************
IconListBox destructor
deletes any GDI objects created by IconListBox
****************************************************************************/
IconListBox::~IconListBox()
{
for (int i = 0; i < _cIcons; i++)
{
if (_aIcons[i].hbmSelected)
{
if (_aIcons[i].hbmSelected)
{
::DeleteObject(_aIcons[i].hbmSelected);
::DeleteObject(_aIcons[i].hbmUnselected);
}
// Subsequent _aIcons may have used the same bitmap.
// Mark those as already deleted.
for (int j = i + 1; j < _cIcons; j++)
{
if (_aIcons[j].nResID == _aIcons[i].nResID)
{
_aIcons[j].hbmSelected = NULL;
_aIcons[j].hbmUnselected = NULL;
}
}
}
}
if (_hbrSelected)
::DeleteObject(_hbrSelected);
if (_hbrUnselected)
::DeleteObject(_hbrUnselected);
}
/****************************************************************************
IconListBox::SetHeight
This function MUST be called in reponse to the WM_MEASUREITEM message.
It creates some GDI objects, and initializes class variables not known
at construction time.
****************************************************************************/
void IconListBox::SetHeight(HWND hwndDlg,
LPMEASUREITEMSTRUCT lpm,
int itemHeight) // defaults to 16
{
ASSERT(hwndDlg != NULL);
ASSERT((int)lpm->CtlID == _nCtlID);
_hwndDialog = hwndDlg;
_hwndListBox = ::GetDlgItem(_hwndDialog, _nCtlID);
// Determine if this is a combo box
char szClass[32];
GetClassName(_hwndListBox,szClass,sizeof(szClass));
if (::lstrcmpi(szClass,"combobox") == 0 )
_fCombo = TRUE;
// Create the background brushes used for filling listbox entries...
_hbrSelected = ::CreateSolidBrush(_colSel);
_hbrUnselected = ::CreateSolidBrush(_colUnsel);
// Calculate how to centre the text vertically in the listbox item.
TEXTMETRIC tm;
HDC hDC = ::GetDC(hwndDlg);
GetTextMetrics(hDC, &tm);
// Set the only lpm entry that matters
// allow larger height if passed in - but at least large enough
// to fit font.
lpm->itemHeight = max( itemHeight, tm.tmHeight + tm.tmExternalLeading );
_nTextOffset = tm.tmExternalLeading / 2 + 1;
::ReleaseDC(hwndDlg, hDC);
}
/****************************************************************************
IconListBox::DrawItem
This function MUST be called in response to the WM_DRAWITEM message.
It takes care of drawing listbox items in selected or unselected state.
Drawing and undrawing the focus rectangle takes advantage of the fact
that DrawFocusRect uses an XOR pen, and Windows is nice enough to assume
this in the order of the ODA_FOCUS messages.
****************************************************************************/
void IconListBox::DrawItem(LPDRAWITEMSTRUCT lpd)
{
ASSERT(_hwndDialog != NULL); // make sure SetHeight has been called
char string[MAXSTRINGLEN];
BOOL bSelected = (lpd->itemState & ODS_SELECTED);
GetString(lpd->itemID, string);
// fill entire rectangle with background color
::FillRect(lpd->hDC, &(lpd->rcItem),
bSelected ? _hbrSelected : _hbrUnselected);
// Look for registered icon to display, and paint it if found
for (int id = 0; id < _cIcons; id++)
if (_aIcons[id].nID == (int) lpd->itemData)
break;
if (id != _cIcons) // if we found a bitmap to display
{
HDC hdcMem = ::CreateCompatibleDC(lpd->hDC);
HBITMAP hOldBitmap = (HBITMAP)::SelectObject(hdcMem,
bSelected ? _aIcons[id].hbmSelected : _aIcons[id].hbmUnselected);
// draw bitmap ICONSPACE pixels from left and centred vertically
int x = lpd->rcItem.left + ICONSPACE;
int y = ((lpd->rcItem.bottom - lpd->rcItem.top) - _iconHeight) / 2;
y += lpd->rcItem.top;
::BitBlt(lpd->hDC, x, y, _iconWidth, _iconHeight, hdcMem,
_aIcons[id].x, _aIcons[id].y, SRCCOPY);
::SelectObject(hdcMem, hOldBitmap);
::DeleteDC(hdcMem);
}
if (lpd->itemState & ODS_FOCUS)
::DrawFocusRect(lpd->hDC, &(lpd->rcItem));
lpd->rcItem.left += (_iconWidth + (2 * ICONSPACE));
// Paint string
::SetTextColor(lpd->hDC, bSelected ? _colSelText : _colUnselText);
::SetBkColor(lpd->hDC, bSelected ? _colSel : _colUnsel);
(lpd->rcItem.top) += _nTextOffset;
if (_cTabs == 0) // if no tabs registered
{
::DrawText(lpd->hDC, string, lstrlen(string), &(lpd->rcItem),
DT_LEFT | DT_EXPANDTABS);
}
else
{
::TabbedTextOut(lpd->hDC, lpd->rcItem.left, lpd->rcItem.top,
string, lstrlen(string), _cTabs, _aTabs, 0);
}
}
/****************************************************************************
IconListBox::RegisterIcons
Icons must be registered before they can be referenced in AddString.
Note that if you are using several icons from the same bitmap (with different
x and y offsets) they must all have the same background color.
****************************************************************************/
void IconListBox::RegisterIcon( int nIconID, // caller's code
int nResID, // RC file id
int x, int y, // top left corner
COLORREF colTransparent) // def. bright green
{
ASSERT( _cIcons < MAXICONS );
_aIcons[_cIcons].nID = nIconID;
_aIcons[_cIcons].nResID = nResID;
_aIcons[_cIcons].x = x;
_aIcons[_cIcons].y = y;
// Check to see if we already have bitmaps for this resource ID
// (which may have different x and y offsets.)
for (int i = 0; i < _cIcons; i++)
{
if (_aIcons[i].nResID == nResID)
{
_aIcons[_cIcons].hbmSelected = _aIcons[i].hbmSelected;
_aIcons[_cIcons].hbmUnselected = _aIcons[i].hbmUnselected;
_cIcons++;
return;
}
}
// Otherwise, create new selected and unselected bitmaps
// Get pointer to DIB
HRSRC h = ::FindResource(_hInst, MAKEINTRESOURCE(nResID), RT_BITMAP);
if (h == NULL)
return;
HANDLE hRes = ::LoadResource(_hInst, h);
if (hRes == NULL)
return;
LPBITMAPINFOHEADER lpInfo = (LPBITMAPINFOHEADER) LockResource(hRes);
if (NULL == lpInfo)
return;
// Get pointers to start of color table, and start of actual bitmap bits
// Note that we make a copy of the bitmap header info and the color
// table. This is so applications that use iconlistbox can keep their
// resource segments read only.
LPBYTE lpBits = (LPBYTE)
(lpInfo + 1) + (1 << (lpInfo->biBitCount)) * sizeof(RGBQUAD);
int cbCopy = (int) (lpBits - (LPBYTE)lpInfo);
BYTE *lpCopy = new BYTE[cbCopy];
if (!lpCopy)
return;
memcpy(lpCopy, lpInfo, cbCopy);
RGBQUAD FAR *lpRGBQ =
(RGBQUAD FAR *) ((LPSTR)lpCopy + lpInfo->biSize);
// Find transparent color in color table
BOOL bFound = FALSE; // did we find a transparent match?
int nColorTableSize = (int) (lpBits - (LPBYTE)lpRGBQ);
nColorTableSize /= sizeof(RGBQUAD);
for (i = 0; i < nColorTableSize; i++)
{
if (colTransparent ==
RGB(lpRGBQ[i].rgbRed, lpRGBQ[i].rgbGreen, lpRGBQ[i].rgbBlue))
{
bFound = TRUE;
break;
}
}
// Replace the transparent color with the background for selected and
// unselected entries. Use these to create selected and unselected
// bitmaps, and restore color table.
RGBQUAD rgbqTemp; // color table entry to replace
HDC hDC = ::GetDC(_hwndDialog);
if (bFound)
{
rgbqTemp = lpRGBQ[i];
lpRGBQ[i].rgbRed = GetRValue(_colUnsel);
lpRGBQ[i].rgbBlue = GetBValue(_colUnsel);
lpRGBQ[i].rgbGreen = GetGValue(_colUnsel);
}
_aIcons[_cIcons].hbmUnselected = ::CreateDIBitmap(hDC,
(LPBITMAPINFOHEADER)lpCopy, CBM_INIT, lpBits,
(LPBITMAPINFO)lpCopy, DIB_RGB_COLORS);
if (bFound)
{
lpRGBQ[i].rgbRed = GetRValue(_colSel);
lpRGBQ[i].rgbBlue = GetBValue(_colSel);
lpRGBQ[i].rgbGreen = GetGValue(_colSel);
}
_aIcons[_cIcons].hbmSelected = ::CreateDIBitmap(hDC,
(LPBITMAPINFOHEADER)lpCopy, CBM_INIT, lpBits,
(LPBITMAPINFO)lpCopy, DIB_RGB_COLORS);
if (bFound)
lpRGBQ[i] = rgbqTemp; // restore original color table entry
::ReleaseDC(_hwndDialog, hDC);
::FreeResource(hRes);
delete [] lpCopy;
_cIcons++;
}
/****************************************************************************
IconListBox::SetTabStops
Since this is an owner-draw listbox, we can't rely on LB_SETTABS.
Instead, tabs are registered here and TabbedTextOut is used to display
strings. Dialogbox units have to be converted to pixels.
****************************************************************************/
void IconListBox::SetTabStops(int cTabs, const int *pTabs)
{
ASSERT(cTabs <= MAXTABS);
int nSize = (int) LOWORD(GetDialogBaseUnits());
for (int i = 0; i < cTabs; i++)
_aTabs[i] = ((nSize * pTabs[i]) / 4);
_cTabs = cTabs;
}
/****************************************************************************
IconListBox::UpdateHorizontalExtent
****************************************************************************/
int IconListBox::UpdateHorizontalExtent(int nIcon,const char *string)
{
ASSERT(_hwndDialog != NULL); // make sure SetHeight has been called
if (!string)
return 0;
// Calculate width in pixels for given string, taking into account icon, spacing and tabs
int iItemWidth = ICONSPACE + (_iconWidth + (2 * ICONSPACE));
HDC hDC = ::GetDC(_hwndDialog);
iItemWidth += LOWORD(GetTabbedTextExtent(hDC,string,::lstrlen(string),_cTabs, _aTabs));
::ReleaseDC(_hwndDialog, hDC);
// Update maximum value
_iCurrentMaxHorzExt = max(_iCurrentMaxHorzExt,iItemWidth);
return (int)SendDlgItemMessage(_hwndDialog,_nCtlID,
LB_SETHORIZONTALEXTENT,
(WPARAM)_iCurrentMaxHorzExt,0L);
}