Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1724 lines
53 KiB

/**********************************************************************/
/** Microsoft Windows **/
/** Copyright(c) Microsoft Corp., 1991, 1992 **/
/**********************************************************************/
/*
* MODULE NAME: BTNLIST.C
*
* AUTHOR: John Rivard
* Microsoft Corp.
* ([email protected])
*
* SHORT DESCRIPTION: Button ListBox Control
*
*
* FUNCTIONS: InitButtonListBoxClass
* UnInitButtonListBoxClass
*
* ButtonListBoxProc
* BL_OnCreate
* BL_OnDestroy
* BL_OnSetFocus
* BL_OnKillFocus
* BL_OnDrawItem
* BL_OnMeasureItem
* BL_OnCompareItem
* BL_OnCharToItem
* BL_OnDeleteItem
* BL_OnGetDlgCode
* BL_OnCtlColor
* BL_OnCommand
*
* SubListBoxProc
* Sub_OnLButtonDown
* Sub_OnLButtonUp
* Sub_OnMouseMove
* Sub_OnKey
*
* CreateListButton
* DeleteListButton
* CreateButtonBitmap
*
* FILE HISTORY:
*
* johnri 03-09-92 Create.
* johnri 04-29-92 Port from standalone DLL to COMMCTRL.DLL
*
\**********************************************************************/
#include "ctlspriv.h" /* commctrl private definitions */
/******* Definitions and typedefs ******************************************/
/* Use the first definition if you want to clean up unreferenced params
*/
#if 0
#define Reference(x)
#else
#define Reference(x) (x) = (x)
#endif
// Standard list box control for button listbox
#define LISTBOX TEXT("ListBox")
#define ID_LISTBOX 1
// Button listbox info; data for the entire control
#define GWL_BLINFO 0
typedef struct tagBLINFO
{
BOOL fNoScroll;
int cxButton;
int cyButton;
int nTrackButton;
int cButtonMax;
} BLINFO;
// List button data; data for each button
typedef struct tagLBD
{
DWORD dwItemData; // user item data for
// LB_SETITEMDATA and LB_GETITEMDATA
BOOL fButtonDown;// TRUE if button pressed
UINT chUpper; // button key uppercase
UINT chLower; // button key lowercase
PTSTR pszText; // button text
HBITMAP hbmpUp; // bitmap for up button
HBITMAP hbmpDown; // bitmap for down button
RECT rcText; // text rectangle for up button
} LISTBUTTONDATA;
/******* Internal Function Declarations ************************************/
// Control and Subclass Window Procedures
LRESULT CALLBACK ButtonListBoxProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK SubListBoxProc(HWND, UINT, WPARAM, LPARAM);
// ButtonListBoxProc Message Handlers
BOOL BL_OnCreate(HWND hwnd, CREATESTRUCT FAR* lpCreateStruct);
void BL_OnDestroy(HWND hwnd);
void BL_OnSetFocus(HWND hwnd, HWND hwndOldFocus);
void BL_OnKillFocus(HWND hwnd, HWND hwndOldFocus);
void BL_OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT FAR* lpDrawItem);
void BL_OnMeasureItem(HWND hwnd, MEASUREITEMSTRUCT FAR* lpMeasureItem);
int BL_OnCompareItem(HWND hwnd, const COMPAREITEMSTRUCT FAR* lpCompareItem);
int BL_OnCharToItem(HWND hwnd, UINT ch, HWND hwndListbox, int iCaret);
void BL_OnDeleteItem(HWND hwnd, const DELETEITEMSTRUCT FAR* lpDeleteItem);
UINT BL_OnGetDlgCode(HWND hwnd, MSG FAR* lpmsg);
HBRUSH BL_OnCtlColor(HWND hwnd, HDC hdc, HWND hwndChild, int type);
void BL_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
LRESULT BL_OnButtonListBox(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// SubListBoxProc Message Handlers
LRESULT Sub_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
LRESULT Sub_OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags);
LRESULT Sub_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags);
LRESULT Sub_OnKey(HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags);
// Miscellaneous functions
LISTBUTTONDATA FAR* CreateListButton(HWND hLB,CREATELISTBUTTON * lpCLB, BOOL fAnsi);
VOID DeleteListButton(LISTBUTTONDATA FAR* lpLBD);
HBITMAP CreateButtonBitmap(HWND hLB, int nWidth, int nHeight, BOOL fButtonDown,
HBITMAP hUserBitmap, LPCTSTR lpszUserText,
LPRECT rcText);
// Debugging
#ifdef DEBUG
Static void DEBUG CDECL DebugPrintf(LPCTSTR lpsz, ...);
#define DEBUGPRINTF(arglist) DebugPrintf arglist
#else
#define DEBUGPRINTF(arglist)
#endif
/******* Global Data *******************************************************/
// Module Global Data
static BOOL fInitResult = FALSE; // result of initialization
static WNDPROC lpDefListBoxProc = NULL; // Default ListBox proc
static HBRUSH hBrushBackground = NULL; // Control background brush
static WCHAR szBUTTONLISTBOX[] = BUTTONLISTBOX;
/**********************************************************************\
*
* NAME: InitButtonListBoxClass
*
* SYNOPSIS: Init the control class and module.
*
* ENTRY: hInstance HINSTANCE DLL instance handle
*
* EXIT: return BOOL Result of initialization
*
* NOTES: If called more than once it only returns the result
* the first initialization.
*
\**********************************************************************/
BOOL FAR PASCAL InitButtonListBoxClass(HINSTANCE hInstance)
{
WNDCLASS wc;
// Button List Control Class
if (!GetClassInfo(hInstance, szBUTTONLISTBOX, &wc))
{
fInitResult = FALSE;
wc.style = CS_DBLCLKS|CS_PARENTDC|CS_GLOBALCLASS;
wc.lpfnWndProc = (WNDPROC)ButtonListBoxProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = sizeof(BLINFO FAR*);
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = szBUTTONLISTBOX;
if (!RegisterClass(&wc))
return FALSE;
hBrushBackground = GetStockObject(GRAY_BRUSH);
if (!hBrushBackground)
return FALSE;
}
return (fInitResult = TRUE);
}
/**********************************************************************\
*
* NAME: ButtonListBoxProc
*
* SYNOPSIS: Window proc for class buttonlistbox.
*
* ENTRY: hwnd HWND Window handle
* uMsg UINT Window message
* wParam WPARAM message dependent param
* lParam LPARAM message dependent param
*
* EXIT: return LRESULT message dependent
*
* NOTES: This window proc handles message to the Button ListBox
* control. Since the button listbox is implemented by
* using a child listbox control, all listbox messages are
* forwarded to the child listbox.
*
* Other messages are handled to provide the specific
* functionality of the button listbox control and to process
* the owner-draw messages from the child listbox.
*
\**********************************************************************/
LRESULT CALLBACK ButtonListBoxProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
// Draw a button
HANDLE_MSG(hwnd,WM_DRAWITEM, BL_OnDrawItem);
// Measure a button
HANDLE_MSG(hwnd,WM_MEASUREITEM, BL_OnMeasureItem);
// Compare two buttons
HANDLE_MSG(hwnd,WM_COMPAREITEM, BL_OnCompareItem);
// Keyboard jump
HANDLE_MSG(hwnd,WM_CHARTOITEM, BL_OnCharToItem);
// Delete a button
HANDLE_MSG(hwnd,WM_DELETEITEM, BL_OnDeleteItem);
// Set focus to child listbox
HANDLE_MSG(hwnd,WM_SETFOCUS, BL_OnSetFocus);
// Init buttonlistbox
HANDLE_MSG(hwnd,WM_CREATE, BL_OnCreate);
// Cleanup buttonlistbox
HANDLE_MSG(hwnd,WM_DESTROY, BL_OnDestroy);
// Tell dlg mgr about us
HANDLE_MSG(hwnd,WM_GETDLGCODE, BL_OnGetDlgCode);
// Set control bkgnd color
HANDLE_MSG(hwnd,WM_CTLCOLORLISTBOX, BL_OnCtlColor);
// Forward commands from the child listbox
HANDLE_MSG(hwnd,WM_COMMAND, BL_OnCommand);
default:
// Forward button listbox messages to button listbox msg handler
if (uMsg >= WM_USER)
return BL_OnButtonListBox(hwnd,uMsg,wParam,lParam);
// Pass all other messages to the default window proc
else
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
}
/**********************************************************************\
*
* NAME: BL_OnButtonListBox
*
* SYNOPSIS: Handle list box messages for button listbox
*
* ENTRY: hLB HWND window handle of child listbox
* uMsg UINT listbox message
* wParam WPARAM message dependent
* lParam LPARAM message dependent
*
* EXIT: return LRESULT message dependent
*
\**********************************************************************/
LRESULT BL_OnButtonListBox(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LISTBUTTONDATA FAR* lpLBD;
BLINFO FAR* pbli;
int cItems;
HWND hLB;
hLB = GetDlgItem(hwnd,ID_LISTBOX);
// Handle button list box messages
switch (uMsg)
{
// UNICODE-ANSI
case BL_ADDBUTTONA:
case BL_ADDBUTTONW:
pbli = (BLINFO *)GetWindowLong(hwnd,GWL_BLINFO);
if (!pbli)
return BL_ERR;
cItems = ListBox_GetCount(hLB);
DEBUGPRINTF((TEXT("AddButton: Max %d, Count %d"), pbli->cButtonMax, cItems));
if (cItems >= pbli->cButtonMax)
{
DEBUGPRINTF((TEXT("AddButton: BL_ERR")));
return BL_ERR;
}
lpLBD = CreateListButton(hLB,(CREATELISTBUTTONW *)lParam, (uMsg == BL_ADDBUTTONA));
if (!lpLBD)
return BL_ERRSPACE;
else
return ListBox_AddItemData(hLB,lpLBD);
// UNICODE-ANSI
case BL_DELETEBUTTONA:
case BL_DELETEBUTTONW:
return ListBox_DeleteString(hLB,wParam);
case BL_GETCARETINDEX:
return ListBox_GetCaretIndex(hLB);
case BL_GETCOUNT:
return ListBox_GetCount(hLB);
case BL_GETCURSEL:
return ListBox_GetCurSel(hLB);
case BL_GETITEMDATA:
lpLBD = (LISTBUTTONDATA *)ListBox_GetItemData(hLB,wParam);
if ((LONG)lpLBD != (LONG)BL_ERR)
return (LRESULT)lpLBD->dwItemData;
else
return (LRESULT)BL_ERR;
case BL_GETITEMRECT:
return ListBox_GetItemRect(hLB,wParam,lParam);
// UNICODE-ANSI
case BL_GETTEXTA:
case BL_GETTEXTW:
lpLBD = (LISTBUTTONDATA *)ListBox_GetItemData (hLB, wParam);
if ((LONG)lpLBD != (LONG)BL_ERR)
if (!lParam) {
return BL_ERR;
}
if (uMsg == BL_GETTEXTA)
{
BOOL fDefCharUsed;
DWORD cchLen = lstrlen(lpLBD->pszText)+1;
LPSTR lpTemp;
// Thunk unicode pszText to ansi lparam
if (!(lpTemp = (LPSTR) LocalAlloc (LMEM_ZEROINIT, cchLen)))
{
DEBUGPRINTF((TEXT("GETTEXT: BL_ERR")));
return((LRESULT)BL_ERR);
}
// One may ask why do the lstrcpy when the lParam can be created directly,
// butthe nature of failure should the lparam buffer be too small would
// then be different.
if (WideCharToMultiByte(CP_ACP, 0, lpLBD->pszText, -1,
lpTemp, cchLen, NULL, &fDefCharUsed)) {
lstrcpyA ((LPSTR)lParam, lpTemp);
}
} else {
lstrcpy ((LPWSTR)lParam, lpLBD->pszText);
}
return (LRESULT)lstrlen(lpLBD->pszText);
case BL_GETTEXTLEN:
lpLBD = (LISTBUTTONDATA FAR*) ListBox_GetItemData (hLB, wParam);
if ((LONG)lpLBD != (LONG)BL_ERR)
return (LRESULT)lstrlen(lpLBD->pszText);
else
return (LRESULT)BL_ERR;
case BL_GETTOPINDEX:
return ListBox_GetTopIndex(hLB);
// UNICODE-ANSI
case BL_INSERTBUTTONA:
case BL_INSERTBUTTONW:
pbli = (BLINFO FAR*)GetWindowLong(hwnd,GWL_BLINFO);
if (!pbli)
return BL_ERR;
cItems = ListBox_GetCount(hLB);
if (cItems >= pbli->cButtonMax)
return BL_ERR;
lpLBD = CreateListButton(hLB,(CREATELISTBUTTON *)lParam, (uMsg == BL_INSERTBUTTONA));
if (!lpLBD)
return BL_ERRSPACE;
else
return ListBox_InsertItemData(hLB,wParam,lpLBD);
case BL_RESETCONTENT:
return ListBox_ResetContent(hLB);
case BL_SETCARETINDEX:
return ListBox_SetCaretIndex(hLB,wParam);
case BL_SETCURSEL:
return ListBox_SetCurSel(hLB,wParam);
case BL_SETITEMDATA:
lpLBD = (LISTBUTTONDATA FAR*)ListBox_GetItemData(hLB,wParam);
if ((LONG)lpLBD != (LONG)BL_ERR)
{
lpLBD->dwItemData = (DWORD)lParam;
return (LRESULT)BL_OKAY;
}
else
return (LRESULT)BL_ERR;
case BL_SETTOPINDEX:
return ListBox_SetTopIndex(hLB,wParam);
default:
DEBUGPRINTF((TEXT("BL_OnButtonListBox: unknown message %d"), uMsg));
return (LRESULT)BL_ERR;
}
}
/**********************************************************************\
*
* NAME: BL_OnCreate
*
* SYNOPSIS: Handle WM_CREATE for button listbox
*
* ENTRY: hwnd HWND window handle
* lpCS CREATESTRUCT FAR* window create data
*
* EXIT: return BOOL TRUE if success, else false
*
* NOTES: When a button listbox is created it must position itself
* along one of the edges of the parent dialog as specified
* by the style bits and create the child listbox.
*
* The dimensions of the buttons within the child listbox
* are determined by the cx and cy parameters in the
* CREATESTRUCT. (For other controls, these would indicate
* the width and height of the entire control, but that is
* determined by the parent dialog window size.) The cx and
* cy values come from the CONTROL statement in the dialog
* template.
*
* This function subclasses the child listbox with the
* SubListBoxProc.
*
\**********************************************************************/
BOOL BL_OnCreate(HWND hwnd, CREATESTRUCT FAR* lpCS)
{
typedef enum tagORIENTATION { VERTICAL, HORIZONTAL } ORIENTATION;
ORIENTATION orientation;
BOOL fNoScroll;
BLINFO FAR* pbli;
DWORD dwStyle;
DWORD dwListBoxStyle;
HWND hListBox;
BYTE buttonsPerListbox;
// Setup the users styles and get the button dimensions from the style
dwStyle = lpCS->style;
orientation = (dwStyle & BLS_VERTICAL)? VERTICAL : HORIZONTAL;
fNoScroll = (dwStyle & BLS_NOSCROLL) != 0L;
if ((buttonsPerListbox = (BYTE)(dwStyle & BLS_NUMBUTTONS)) == 0)
buttonsPerListbox = 1;
// force a border around the control by default and get rid of
// the non-standard window styles.
dwStyle |= WS_BORDER;
// elminate
// dwStyle &= WS_VALID;
SetWindowLong(hwnd,GWL_STYLE,dwStyle);
// Allocate a global structure for holding data for the entire
// button listbox control and set the pointer in the window
pbli = GlobalAllocPtr (GHND, sizeof(BLINFO));
if (!pbli)
{
DEBUGPRINTF((TEXT("BL_OnCreate: could not allocate pbli")));
return FALSE;
}
SetWindowLong (hwnd, GWL_BLINFO, (LONG)(DWORD)pbli);
// Init values for pbli
pbli->fNoScroll = fNoScroll;
pbli->nTrackButton = -1; // no button currently down
pbli->cButtonMax = 3000; // very large integer
pbli->cxButton = lpCS->cx;
pbli->cyButton = lpCS->cy;
/* Adust the width and height of the control to fit the
* requested number of buttons
*/
if (orientation == HORIZONTAL)
{
lpCS->cx = buttonsPerListbox * pbli->cxButton
+ 2 * GetSystemMetrics(SM_CXBORDER)
+ ((pbli->fNoScroll) ? 0 : MulDiv(pbli->cxButton,2,3));
lpCS->cy = pbli->cyButton;
lpCS->cy += (pbli->fNoScroll) ?
2 * GetSystemMetrics(SM_CYBORDER) :
(GetSystemMetrics(SM_CYHSCROLL)
+ GetSystemMetrics(SM_CYBORDER));
/* if no scrollbar, calculate the max number of buttons that fit */
if (pbli->fNoScroll)
pbli->cButtonMax = MulDiv(lpCS->cx,1,pbli->cxButton);
}
else
{
lpCS->cy = buttonsPerListbox * pbli->cyButton
+ 2 * GetSystemMetrics(SM_CYBORDER)
+ ((pbli->fNoScroll)? 0: MulDiv(pbli->cyButton,2,3));
lpCS->cx = pbli->cxButton;
lpCS->cx += (pbli->fNoScroll) ?
2 * GetSystemMetrics(SM_CXBORDER) :
(GetSystemMetrics(SM_CXVSCROLL)
+ GetSystemMetrics(SM_CXBORDER));
/* if no scrollbar, calculate the max number of buttons that fit */
if (pbli->fNoScroll)
pbli->cButtonMax = MulDiv(lpCS->cy,1,pbli->cyButton);
}
/* Now change the control size to fit the calculated buttons */
SetWindowPos (hwnd, NULL, lpCS->x, lpCS->y, lpCS->cx, lpCS->cy,
SWP_NOZORDER | SWP_NOACTIVATE);
// Set the standard style bits for all button child listboxes
// Set style for vertical/horizontal
// Set style for no scrollbars
dwListBoxStyle = WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_BORDER
| LBS_NOTIFY
| LBS_SORT
| LBS_NOINTEGRALHEIGHT
| LBS_OWNERDRAWFIXED
| LBS_WANTKEYBOARDINPUT
| LBS_DISABLENOSCROLL;
if (orientation == HORIZONTAL)
dwListBoxStyle |= (WS_HSCROLL | LBS_MULTICOLUMN);
else
dwListBoxStyle |= WS_VSCROLL;
if (fNoScroll)
dwListBoxStyle &= ~(WS_HSCROLL | WS_VSCROLL);
// Create the child list box
hListBox = CreateWindowEx (WS_EX_NOPARENTNOTIFY,
TEXT("ListBox"), // class
TEXT(""), // window name
dwListBoxStyle, // style
0, // left
0, // top
lpCS->cx - 2 * GetSystemMetrics(SM_CXBORDER), // width
lpCS->cy - 2 * GetSystemMetrics(SM_CYBORDER), // height
hwnd, // parent
(HMENU)ID_LISTBOX, // control id of the child listbox
hInst, // instance
NULL // no createparams
);
if (!hListBox)
{
DEBUGPRINTF((TEXT("BL_OnCreate: could not create child listbox")));
GlobalFreePtr(pbli);
return FALSE;
}
// Sub-class the list box
// Note that window procedures in protect mode only DLL's may be called
// directly.
if (!lpDefListBoxProc)
lpDefListBoxProc = (WNDPROC)GetWindowLong(hListBox,GWL_WNDPROC);
SetWindowLong(hListBox,GWL_WNDPROC,(LONG)SubListBoxProc);
return TRUE;
}
/**********************************************************************\
*
* NAME: BL_OnDestroy
*
* SYNOPSIS: Handle WM_DESTROY for button listbox
*
* ENTRY: hwnd HWND Window handle
*
* EXIT: void
*
* NOTES: Clean up memory allocated in BL_OnCreate.
*
\**********************************************************************/
void BL_OnDestroy(HWND hwnd)
{
BLINFO FAR* pbli;
// Free up the button list info data
if ((pbli = (BLINFO FAR*)GetWindowLong(hwnd,GWL_BLINFO)) != NULL)
GlobalFreePtr(pbli);
}
/**********************************************************************\
*
* NAME: BL_OnSetFocus
*
* SYNOPSIS: Handle WM_SETFOCUS for button listbox
*
* ENTRY: hwnd HWND Window getting focus
* hwndOldFocus HWND Window losing focus
*
* EXIT: void
*
* NOTES: Pass focus to child listbox.
*
\**********************************************************************/
void BL_OnSetFocus(HWND hwnd, HWND hwndOldFocus)
{
HWND hLB;
Reference(hwndOldFocus);
hLB = GetDlgItem(hwnd,ID_LISTBOX);
SetFocus(hLB);
// Be sure there is alwyas a current selection
// or else the spacebar will not select a button.
if (ListBox_GetCurSel(hLB) == LB_ERR )
ListBox_SetCurSel(hLB,ListBox_GetCaretIndex(hLB));
}
/**********************************************************************\
*
* NAME: BL_OnDrawItem
*
* SYNOPSIS: Handle WM_DRAWITEM for button listbox
*
* ENTRY: hwnd HWND Window handle
* lpDrawItem DRAWITEMSTRUCT FAR*
*
* EXIT: void
*
* NOTES: BitBlt the up or down button and draw the focus rect.
*
\**********************************************************************/
void BL_OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT FAR* lpDrawItem)
{
LISTBUTTONDATA FAR* lpLBD;
HDC hMemoryDC;
HBITMAP hOldBitmap;
HBITMAP hBitmap;
BLINFO FAR* pbli;
lpLBD = (LISTBUTTONDATA FAR*)lpDrawItem->itemData;
if (!lpLBD)
return;
pbli = (BLINFO FAR*)GetWindowLong(hwnd,GWL_BLINFO);
if (!pbli)
return;
/****************************/
/* Draw the standard button */
/****************************/
if ((lpDrawItem->itemAction & ODA_DRAWENTIRE)
|| (lpDrawItem->itemAction & ODA_SELECT))
{
hBitmap = (lpLBD->fButtonDown) ?
lpLBD->hbmpDown :
lpLBD->hbmpUp;
hMemoryDC = CreateCompatibleDC(lpDrawItem->hDC);
hOldBitmap = SelectObject(hMemoryDC,hBitmap);
if (hOldBitmap)
{
BitBlt(lpDrawItem->hDC,
lpDrawItem->rcItem.left,
lpDrawItem->rcItem.top,
lpDrawItem->rcItem.right - lpDrawItem->rcItem.left,
lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top,
hMemoryDC, 0,0,SRCCOPY);
SelectObject(hMemoryDC, hOldBitmap);
}
DeleteDC(hMemoryDC);
}
/******************************/
/* Draw the focus rect */
/******************************/
if (lpDrawItem->itemAction & ODA_FOCUS)
{
RECT rcFocus;
CopyRect(&rcFocus,&lpLBD->rcText);
OffsetRect(&rcFocus,lpDrawItem->rcItem.left,lpDrawItem->rcItem.top);
InflateRect(&rcFocus,1,1);
if (lpLBD->fButtonDown)
OffsetRect(&rcFocus,2,2);
DrawFocusRect(lpDrawItem->hDC,&rcFocus);
}
}
/**********************************************************************\
*
* NAME: BL_OnMeasureItem
*
* SYNOPSIS: Handle WM_MEASUREITEM for button listbox
*
* ENTRY: hwnd HWND window handle
* lpMeasureItem MEASUREITEM FAR*
*
* EXIT: void
*
* NOTES: Return the item width and height for the button listbox.
* The item width and height are not equal to the button
* width and height since we want the button borders to
* overlap by 1 pixel.
*
\**********************************************************************/
void BL_OnMeasureItem(HWND hwnd, MEASUREITEMSTRUCT FAR* lpMeasureItem)
{
BLINFO FAR* pbli;
pbli = (BLINFO FAR*)GetWindowLong(hwnd,GWL_BLINFO);
if (!pbli)
return;
lpMeasureItem->itemWidth = pbli->cxButton;
lpMeasureItem->itemHeight = pbli->cyButton;
}
/**********************************************************************\
*
* NAME: BL_OnCompareItem
*
* SYNOPSIS: Handle WM_COMPAREITEM for button listbox
*
* ENTRY: hwnd HWND window handle
* lpCompareItem COMPAREITEMSTRUCT FAR*
*
* EXIT: int -1 if item1 < item2
* 0 if item1 == item 2
* 1 if item2 > item2
*
* NOTES: The comparison is based on the button text.
*
\**********************************************************************/
int BL_OnCompareItem(HWND hwnd, const COMPAREITEMSTRUCT * lpCompareItem)
{
LISTBUTTONDATA * lpLBD1;
LISTBUTTONDATA * lpLBD2;
Reference(hwnd);
lpLBD1 = (LISTBUTTONDATA FAR*)lpCompareItem->itemData1;
lpLBD2 = (LISTBUTTONDATA FAR*)lpCompareItem->itemData2;
return _wcsicmp(lpLBD1->pszText,lpLBD2->pszText);
}
/**********************************************************************\
*
* NAME: BL_OnCharToItem
*
* SYNOPSIS: Handle WM_CHARTOITEM for button listbox
*
* ENTRY: hwnd HWND window handle
* ch UINT character input
* hwndListBox HWND listbox control handle
* iCaret int current caret position
*
* EXIT: return int -2 if button selected
* -1 if not
*
* NOTES: Find next button whose text begins with ch starting
* with the current button.
*
\**********************************************************************/
int BL_OnCharToItem(HWND hwnd, UINT ch, HWND hwndListbox, int iCaret)
{
LISTBUTTONDATA FAR* lpLBD;
int cbItems;
int nItem;
Reference(hwnd);
cbItems = ListBox_GetCount(hwndListbox);
for (nItem = iCaret + 1; nItem < cbItems; nItem++)
{
lpLBD = (LISTBUTTONDATA FAR*)ListBox_GetItemData(hwndListbox,nItem);
if (!lpLBD)
return -1;
if ((lpLBD->chUpper == ch) || (lpLBD->chLower == ch))
{
ListBox_SetCurSel(hwndListbox,nItem);
return -2;
}
}
for (nItem = 0; nItem < iCaret; nItem++)
{
lpLBD = (LISTBUTTONDATA FAR*)ListBox_GetItemData(hwndListbox,nItem);
if (!lpLBD)
return -1;
if ((lpLBD->chUpper == ch) || (lpLBD->chLower == ch)) {
ListBox_SetCurSel(hwndListbox,nItem);
return -2;
}
}
return -1;
}
/**********************************************************************\
*
* NAME: BL_OnDeleteItem
*
* SYNOPSIS: Handle WM_DELETEITEM for button listbox
*
* ENTRY: hwnd HWND window handle
* lpDeleteItem DELETEITEMSTRUCT FAR*
*
* EXIT: void
*
* NOTES: Clean up the stuff that we created in CreateListButton and
* forward the message to the parent dialog.
*
\**********************************************************************/
void BL_OnDeleteItem(HWND hwnd, const DELETEITEMSTRUCT FAR* lpDeleteItem)
{
LISTBUTTONDATA FAR* lpLBD;
DELETEITEMSTRUCT di;
lpLBD = (LISTBUTTONDATA FAR*)lpDeleteItem->itemData;
if (!lpLBD)
return;
di.CtlType = lpDeleteItem->CtlType;
di.CtlID = GetDlgCtrlID(hwnd);
di.itemID = lpDeleteItem->itemID;
di.hwndItem = hwnd;
di.itemData = lpLBD->dwItemData;
SendMessage(GetParent(hwnd), WM_DELETEITEM, (WPARAM)di.CtlID, (LPARAM)(LPTSTR)&di);
DeleteListButton(lpLBD);
}
/**********************************************************************\
*
* NAME: BL_OnGetDlgCode
*
* SYNOPSIS: Handle WM_GETDLGCODE for button listbox
*
* ENTRY: hwnd HWND window handle
* lpmsg MSG FAR*
*
* EXIT: return UINT dialog code
*
* NOTES: Get the code from the child listbox and also set
* the button bit.
*
\**********************************************************************/
UINT BL_OnGetDlgCode(HWND hwnd, MSG FAR* lpmsg)
{
UINT uCode;
uCode = FORWARD_WM_GETDLGCODE(GetDlgItem (hwnd, ID_LISTBOX), lpmsg, SendMessage);
uCode |= DLGC_BUTTON;
return uCode;
}
/**********************************************************************\
*
* NAME: BL_OnCtlColor
*
* SYNOPSIS: Handle WM_CTLCOLOR for button listbox
*
* ENTRY: hwnd HWND window handle
* hcd HDC dc of window
* hwndChild HWND control handle
* type int control type
*
* EXIT: return HBRUSH Button listbox bg color
*
\**********************************************************************/
HBRUSH BL_OnCtlColor(HWND hwnd, HDC hdc, HWND hwndChild, int type)
{
Reference(hwnd);
Reference(hdc);
Reference(hwndChild);
Reference(type);
return hBrushBackground;
}
/**********************************************************************\
*
* NAME: BL_OnCommand
*
* SYNOPSIS: Handle WM_COMMAND for button listbox
*
* ENTRY: hwnd HWND window handle
* hwndCtl HWND control handle
* codeNotiry UINT notify code from control
*
* EXIT: void
*
* NOTES: Pass the message to the parent dialog as though the
* button listbox generated it instead of the child listbox.
*
\**********************************************************************/
void BL_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
id = GetDlgCtrlID(hwnd);
hwndCtl = hwnd;
hwnd = GetParent(hwnd);
UpdateWindow(hwndCtl);
FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, SendMessage);
}
/**********************************************************************\
*
* NAME: CreateButtonBitmap
*
* SYNOPSIS: Create a new bitmap for the up or down button.
*
* ENTRY: nWidth int width of button
* nHeight int height of button
* fButtonDown BOOL TRUE if button pressed
* hUserBitmap HBITMAP user bitmap to draw on button
* lpszUserText LPCSTR user text to draw on button
* rcText LPRECT rect of text in button
*
* EXIT: return HBITMAP new bitmap for button
* rcText LPRECT rect of button text
*
\**********************************************************************/
HBITMAP CreateButtonBitmap(HWND hLB, int nWidth, int nHeight, BOOL fButtonDown,
HBITMAP hUserBitmap, LPCTSTR lpszUserText,
LPRECT rcText)
{
HWND hWndDesktop;
HDC hDCDesktop;
HDC hMemoryDC;
HBRUSH hOldBrush;
HBRUSH hBrushFace;
HBRUSH hBrushHighlight;
HBRUSH hBrushShadow;
HBRUSH hBlackBrush;
HBITMAP hBitmap;
HBITMAP hOldBitmap;
BITMAP bmButton;
HDC hUserDC;
HBITMAP hOldUserBitmap;
BITMAP bm;
int nWidthT, nHeightT;
int textWidth, textHeight;
int xDest, yDest;
int cxDest, cyDest;
LPTSTR lpText;
int cchText;
HFONT hFontText;
HFONT hOldFont;
DWORD dwLBStyle;
BOOL fRightBorder;
BOOL fBottomBorder;
SIZE Size;
#define BORDER_WIDTH 1
#define HILIGHT_WIDTH 2
#define FACE_BORDER_WIDTH (BORDER_WIDTH+HILIGHT_WIDTH+1)
#define PICT_BORDER_WIDTH (FACE_BORDER_WIDTH+0)
#define TEXT_BORDER_WIDTH (FACE_BORDER_WIDTH+0)
#define TEXTLINES 1
// Get the listbox style
dwLBStyle = (DWORD)GetWindowLong(hLB,GWL_STYLE);
fRightBorder = ((dwLBStyle & LBS_MULTICOLUMN) != 0L);
fBottomBorder = !fRightBorder;
// Create drawing DC and bitmap based upon the desktop window
hWndDesktop = GetDesktopWindow();
hDCDesktop = GetDC(hWndDesktop);
hMemoryDC = CreateCompatibleDC(hDCDesktop);
hBitmap = CreateCompatibleBitmap(hDCDesktop,nWidth,nHeight);
hOldBitmap = SelectObject(hMemoryDC,hBitmap);
GetObject(hBitmap,sizeof(BITMAP),&bmButton);
// Create brushes for the button
hBrushFace = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
hBrushHighlight = CreateSolidBrush(GetSysColor(COLOR_BTNHIGHLIGHT));
hBrushShadow = CreateSolidBrush(GetSysColor(COLOR_BTNSHADOW));
// Draw the button face
hOldBrush = SelectObject(hMemoryDC,hBrushFace);
PatBlt(hMemoryDC,0,0,nWidth,nHeight,PATCOPY);
// Draw the button border
hBlackBrush = GetStockObject(BLACK_BRUSH);
SelectObject(hMemoryDC,hBlackBrush);
if (fRightBorder)
PatBlt(hMemoryDC, nWidth - 1, 0, 1, nHeight, PATCOPY);
if (fBottomBorder)
PatBlt(hMemoryDC, 0, nHeight - 1, nWidth, 1, PATCOPY);
// subtract out the border
if (fRightBorder)
nWidth--;
if (fBottomBorder)
nHeight--;
// Draw the highlights and shadow
if (fButtonDown)
{
SelectObject(hMemoryDC, hBrushShadow);
PatBlt(hMemoryDC, 0, 0, nWidth, 1, PATCOPY);
PatBlt(hMemoryDC, 0, 0, 1, nHeight, PATCOPY);
}
else
{
SelectObject(hMemoryDC, hBrushHighlight);
PatBlt(hMemoryDC, 0, 0, nWidth, 2, PATCOPY);
PatBlt(hMemoryDC, 0, 0, 2, nHeight, PATCOPY);
SelectObject(hMemoryDC, hBrushShadow);
PatBlt(hMemoryDC, nWidth - 2, 1,1, nHeight - 1, PATCOPY);
PatBlt(hMemoryDC, nWidth - 1, 0,1, nHeight, PATCOPY);
PatBlt(hMemoryDC, 1, nHeight - 2, nWidth - 1, 1, PATCOPY);
PatBlt(hMemoryDC, 0, nHeight - 1, nWidth, 1, PATCOPY);
}
// Superimpose the user text in lower 1/3 of button
if (bJapan)
{
hFontText = CreateFont(
-8, // 8 point always
0,0,0,FW_DONTCARE,
FALSE,FALSE,FALSE,
SHIFTJIS_CHARSET,
OUT_DEVICE_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_SWISS, // | 0x04,
TEXT("System"));
}
else
{
hFontText = CreateFont (-8, // 8 point always
0,0,0,FW_DONTCARE,
FALSE,FALSE,FALSE,
ANSI_CHARSET,
OUT_DEVICE_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_SWISS, // | 0x04,
UNICODE_FONT_NAME);
}
cchText = lstrlen(lpszUserText);
lpText = GlobalAllocPtr(GHND, ByteCountOf(cchText + 8)); // extra room for elipsis
lstrcpy(lpText, lpszUserText);
hOldFont = SelectObject(hMemoryDC, hFontText);
SetTextColor(hMemoryDC, GetSysColor(COLOR_BTNTEXT));
SetBkMode(hMemoryDC, TRANSPARENT);
SetTextAlign(hMemoryDC, TA_TOP);
GetTextExtentPoint(hMemoryDC, lpText, cchText, &Size);
textWidth = Size.cx;
textHeight = Size.cy;
nWidthT = nWidth - 2 * TEXT_BORDER_WIDTH;
nHeightT = nHeight - 2 * TEXT_BORDER_WIDTH;
xDest = TEXT_BORDER_WIDTH + ((textWidth < nWidthT ) ?
(nWidthT - textWidth) / 2 : 0);
yDest = TEXT_BORDER_WIDTH + nHeightT - TEXTLINES * (textHeight);
rcText->top = yDest;
rcText->left = xDest;
rcText->right = rcText->left + min(textWidth, nWidthT);
rcText->bottom = rcText->top + TEXTLINES * (textHeight);
// Elipsize text if needed
if (textWidth > nWidthT)
{
TCHAR szElipsis[] = TEXT("...");
int nWidthText;
int cchTextT;
SIZE Size;
GetTextExtentPoint(hMemoryDC, szElipsis, lstrlen(szElipsis), &Size);
nWidthText = Size.cx;
nWidthT -= nWidthText;
for (cchTextT = 0; cchTextT < cchText; cchTextT++)
{
GetTextExtentPoint(hMemoryDC, lpText, cchTextT, &Size);
nWidthText = Size.cx;
if (nWidthText > nWidthT)
break;
}
lpText[--cchTextT] = (TCHAR) 0;
lstrcat(lpText, szElipsis);
cchText = lstrlen(lpText);
}
if (fButtonDown)
{
xDest += 2;
yDest += 2;
OffsetRect(rcText,2,2);
}
ExtTextOut(hMemoryDC, xDest, yDest, ETO_CLIPPED, rcText, lpText, cchText, NULL);
if (fButtonDown)
OffsetRect(rcText, -2, -2);
// if the bitmaps are compatible,
// Superimpose the user bitmap centered horizontally and
// vertically in above rcText
GetObject(hUserBitmap,sizeof(BITMAP),&bm);
if (bm.bmPlanes == bmButton.bmPlanes)
{
nWidthT = nWidth - 2 * PICT_BORDER_WIDTH;
nHeightT = rcText->top - PICT_BORDER_WIDTH;
xDest = PICT_BORDER_WIDTH + ((bm.bmWidth < nWidthT ) ?
(nWidthT - bm.bmWidth) / 2 : 0);
cxDest = (bm.bmWidth < nWidthT ) ?
bm.bmWidth : nWidthT;
yDest = PICT_BORDER_WIDTH + ((bm.bmHeight < nHeightT) ?
(nHeightT - bm.bmHeight) / 2 : 0);
cyDest = (bm.bmHeight < nHeightT) ?
bm.bmHeight : nHeightT;
if (fButtonDown)
{
xDest += 2;
yDest += 2;
}
hUserDC = CreateCompatibleDC(hDCDesktop);
hOldUserBitmap = SelectObject(hUserDC, hUserBitmap);
if (hOldUserBitmap)
{
BitBlt(hMemoryDC, xDest, yDest, cxDest, cyDest, hUserDC, 0, 0, SRCCOPY);
SelectObject(hUserDC, hOldUserBitmap);
}
DeleteDC(hUserDC);
}
// Cleanup
GlobalFreePtr(lpText);
SelectObject(hMemoryDC, hOldBrush);
SelectObject(hMemoryDC, hOldBitmap);
SelectObject(hMemoryDC, hOldFont);
DeleteObject(hBrushFace);
DeleteObject(hBrushHighlight);
DeleteObject(hBrushShadow);
DeleteObject(hFontText);
DeleteDC(hMemoryDC);
ReleaseDC(hWndDesktop, hDCDesktop);
return hBitmap;
}
/**********************************************************************\
*
* NAME: SubListBoxProc
*
* SYNOPSIS: ListBox subclassing window proc for the button listbox
* child listbox.
*
* ENTRY: hwnd HWND Window handle of listbox
* uMsg UINT Window message
* wParam WPARAM message dependent param
* lParam LPARAM message dependent param
*
* EXIT: return LRESULT message dependent
*
* NOTES: This window proc handles messages to perform hit-testing
* on the button items in the listbox and does pre-processing
* of messages that add or change item data.
*
* Messages that are not explicitly handled are forwarded
* to the default listbox window proc.
*
\**********************************************************************/
LRESULT CALLBACK SubListBoxProc(HWND hLB, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
HANDLE_MSG(hLB, WM_LBUTTONDOWN, Sub_OnLButtonDown);
HANDLE_MSG(hLB, WM_LBUTTONUP, Sub_OnLButtonUp);
HANDLE_MSG(hLB, WM_MOUSEMOVE, Sub_OnMouseMove);
HANDLE_MSG(hLB, WM_KEYDOWN, Sub_OnKey);
HANDLE_MSG(hLB, WM_KEYUP, Sub_OnKey);
}
return CallWindowProc(lpDefListBoxProc, hLB, uMsg, wParam, lParam);
}
/**********************************************************************\
*
* NAME: Sub_OnLButtonDown
*
* SYNOPSIS: Handle the WM_LBUTTONDOWN message for the child listbox
*
* ENTRY: hLB HWND window handle of listbox
* fDoubldClick BOOL TRUE if double click, else FALSE
* x int horizontal mouse coordinate
* y int vertical mouse coordinate
* keyFlags UINT flags from the VK message
*
* EXIT: NULL
*
* NOTES: On a mouse button down, check to see which item the mouse
* is in. Set the fButtonDown flag for the pressed button
* and invalidate the button so that it will be drawn pressed.
*
* Save the item number of the pressed button for use in the
* Sub_OnLButtonUp and Sub_OnMouseMove handlers.
*
\**********************************************************************/
LRESULT
Sub_OnLButtonDown(HWND hLB, BOOL fDoubleClick, int x, int y, UINT keyFlags)
{
LISTBUTTONDATA FAR* lpLBD;
int nItem;
int cbItem;
RECT rcItem;
POINT pt;
BLINFO FAR* pbli;
Reference(fDoubleClick);
pbli = (BLINFO FAR*)GetWindowLong(GetParent(hLB),GWL_BLINFO);
if (!pbli)
return 0;
cbItem = ListBox_GetCount(hLB);
pt.x = x;
pt.y = y;
for (nItem = 0; nItem < cbItem; nItem++)
{
lpLBD = (LISTBUTTONDATA FAR*)ListBox_GetItemData(hLB, nItem);
if (!lpLBD)
return 0L;
ListBox_GetItemRect(hLB, nItem, &rcItem);
if (PtInRect(&rcItem,pt))
{
lpLBD->fButtonDown = TRUE;
pbli->nTrackButton = nItem;
InvalidateRect(hLB,&rcItem,FALSE);
}
else
lpLBD->fButtonDown = FALSE;
}
// tell list box this item was pressed
CallWindowProc(lpDefListBoxProc,hLB,WM_LBUTTONDOWN,(WPARAM)keyFlags,MAKELPARAM(x,y));
}
/**********************************************************************\
*
* NAME: Sub_OnLButtonUp
*
* SYNOPSIS: Handle the WM_LBUTTONUP message for the child listbox
*
* ENTRY: hLB HWND window handle of listbox
* x int horizontal mouse coordinate
* y int vertical mouse coordinate
* keyFlags UINT flags from the VK message
*
* EXIT: NULL
*
* NOTES: If we're not tracking a button press, forward the message
* to the default listbox proc.
*
* Otherwise, set all buttons to up and test if the mouse
* went up in the pressed button. If so, send the doubld click
* message to the listbox to cause a WM_COMMAND:BLN_CLICKED
* notification from the listbox.
*
\**********************************************************************/
LRESULT
Sub_OnLButtonUp(HWND hLB, int x, int y, UINT keyFlags)
{
LISTBUTTONDATA FAR* lpLBD;
int nItem;
int cbItem;
RECT rcItem;
POINT pt;
BLINFO FAR* pbli;
int nPressedItem = -1;
pbli = (BLINFO FAR*)GetWindowLong(GetParent(hLB), GWL_BLINFO);
if (!pbli)
return 0L;
cbItem = ListBox_GetCount(hLB);
pt.x = x;
pt.y = y;
if (pbli->nTrackButton == -1)
{
CallWindowProc(lpDefListBoxProc, hLB, WM_LBUTTONUP, (WPARAM)keyFlags, MAKELPARAM(x,y));
return 0L;
}
for (nItem = 0; nItem < cbItem; nItem++)
{
lpLBD = (LISTBUTTONDATA FAR*)ListBox_GetItemData(hLB, nItem);
if (!lpLBD)
return 0L;
if (lpLBD->fButtonDown)
nPressedItem = nItem;
lpLBD->fButtonDown = FALSE;
}
if (nPressedItem != -1)
{
// BOOM! We got a button press
lpLBD = (LISTBUTTONDATA FAR*)ListBox_GetItemData(hLB, nPressedItem);
if (!lpLBD)
return 0L;
ListBox_GetItemRect(hLB, nPressedItem, &rcItem);
InvalidateRect(hLB, &rcItem, FALSE);
CallWindowProc(lpDefListBoxProc, hLB, WM_LBUTTONDBLCLK, (WPARAM)keyFlags, MAKELPARAM(x,y));
}
pbli->nTrackButton = -1;
CallWindowProc(lpDefListBoxProc, hLB, WM_LBUTTONUP, (WPARAM)keyFlags, MAKELPARAM(x,y));
}
/**********************************************************************\
*
* NAME: Sub_OnMouseMove
*
* SYNOPSIS: Handle the WM_MOUSEMOVE message for the child listbox
*
* ENTRY: hLB HWND window handle of the listbox
* x int horizontal mouse coordinate
* y int vertical mouse coordinate
* keyFlags UINT flags from the VK message
*
* EXIT: NULL
*
* NOTES: If we're not tracking a button press, forward the
* message to the default listbox proc.
*
* Otherwise, if the mouse enters or leaves the button rectangle,
* redraw the button in the up or down state.
*
\**********************************************************************/
LRESULT
Sub_OnMouseMove(HWND hLB, int x, int y, UINT keyFlags)
{
LISTBUTTONDATA FAR* lpLBD;
int nItem;
RECT rcItem;
POINT pt;
BLINFO FAR* pbli;
BOOL fInRect;
// Pass to listbox if not button down
if (!(keyFlags & MK_LBUTTON))
{
CallWindowProc(lpDefListBoxProc, hLB, WM_MOUSEMOVE, (WPARAM)keyFlags, MAKELPARAM(x,y));
return 0L;
}
pbli = (BLINFO FAR*)GetWindowLong(GetParent(hLB), GWL_BLINFO);
if (!pbli)
return 0L;
pt.x = x;
pt.y = y;
nItem = pbli->nTrackButton;
if (nItem == -1)
return 0L;
ListBox_GetItemRect(hLB, nItem, &rcItem);
lpLBD = (LISTBUTTONDATA FAR*)ListBox_GetItemData(hLB,nItem);
if (!lpLBD)
return 0L;
fInRect = PtInRect(&rcItem, pt);
if (fInRect != lpLBD->fButtonDown)
{
lpLBD->fButtonDown = fInRect;
InvalidateRect(hLB, &rcItem, FALSE);
}
}
/**********************************************************************\
*
* NAME: Sub_OnKey
*
* SYNOPSIS: Handle the WM_KEYDOWN and WM_KEYUP message for the
* child listbox
*
* ENTRY: hLB HWND window handle of the listbox
* fDown BOOL TRUE if keydown, else false
* cRepeat int repeat count
* flags UINT flags from the VK message
*
* EXIT: NULL
*
* NOTES: If the spacebar goes down, paint the current button
* as pressed. When it goes up, paint the current button
* as uppressed and send a double click to the listbox
* for the button to cause a WM_COMMAND:BLN_CLICKED
* notification from the listbox.
*
\**********************************************************************/
LRESULT
Sub_OnKey(HWND hLB, UINT vk, BOOL fDown, int cRepeat, UINT flags)
{
LISTBUTTONDATA FAR* lpLBD;
int nItem;
RECT rcItem;
BLINFO FAR* pbli;
pbli = (BLINFO FAR*)GetWindowLong(GetParent(hLB), GWL_BLINFO);
if (!pbli)
return 0L;
if (pbli->nTrackButton >= 0)
return 0L;
if (vk == VK_SPACE)
{
nItem = ListBox_GetCurSel(hLB);
if (nItem >= 0)
{
lpLBD = (LISTBUTTONDATA FAR*)ListBox_GetItemData(hLB, nItem);
if (!lpLBD)
return 0L;
if (lpLBD->fButtonDown != fDown)
{
lpLBD->fButtonDown = fDown;
ListBox_GetItemRect(hLB, nItem, &rcItem);
InvalidateRect(hLB, &rcItem, FALSE);
// When the key goes up, fake a double click
// to generate a WM_COMMAND notification
if (!fDown)
CallWindowProc(lpDefListBoxProc, hLB, WM_LBUTTONDBLCLK, (WPARAM)flags,
MAKELPARAM(rcItem.left, rcItem.top));
}
}
}
CallWindowProc(lpDefListBoxProc,
hLB,
fDown? WM_KEYDOWN : WM_KEYUP,
(WPARAM)vk,
MAKELPARAM((UINT)cRepeat, flags));
return 0L;
}
/**********************************************************************\
*
* NAME: CreateListButton
*
* SYNOPSIS: Setup internal fields in the LISTBUTTONDATA structure
*
* ENTRY: hLB HWND window handle of child listbox
* lpLBD LISTBUTTONDATA FAR* ptr to input structure
*
* EXIT: return LISTBUTTONDATA FAR* ptr to output structure
* NULL if an error occurs
*
* NOTES: This function should is called in response to the
* BL_ADDBUTTON and BL_INSERTBUTTON messages.
*
\**********************************************************************/
LISTBUTTONDATA FAR* CreateListButton(HWND hLB,CREATELISTBUTTON * lpCLB, BOOL fAnsi)
{
BLINFO FAR* pbli;
LISTBUTTONDATA NEAR* pNewLBD;
DWORD cchTextLen;
if (!lpCLB)
{
DEBUGPRINTF((TEXT("CreateListButton: !lpCLB")));
return NULL;
}
if (lpCLB->cbSize != sizeof(CREATELISTBUTTON))
{
DEBUGPRINTF((TEXT("CreateListButton: lpCLB->cbSize wrong")));
return NULL;
}
if (!lpCLB->hBitmap)
{
DEBUGPRINTF((TEXT("CreateListButton: !lpCLB->hBitmap")));
return NULL;
}
if (!lpCLB->lpszText)
{
DEBUGPRINTF((TEXT("CreateListButton: !lpCLB->lpszText")));
return NULL;
}
pbli = (BLINFO FAR*)GetWindowLong(GetParent(hLB),GWL_BLINFO);
if (!pbli)
{
DEBUGPRINTF((TEXT("CreateListButton: !pbli")));
return NULL;
}
pNewLBD = (LISTBUTTONDATA*)LocalAlloc(LPTR, sizeof(LISTBUTTONDATA));
if (!pNewLBD)
{
DEBUGPRINTF((TEXT("CreateListButton: !LocalAlloc pNewLBD")));
return NULL;
}
// copy over user item data
pNewLBD->dwItemData = lpCLB->dwItemData;
// copy the button text
if (fAnsi)
cchTextLen = lstrlenA((LPSTR)lpCLB->lpszText) + 1;
else
cchTextLen = lstrlen(lpCLB->lpszText) + 1;
pNewLBD->pszText = (LPTSTR) LocalAlloc (LPTR, ByteCountOf(cchTextLen));
if (!pNewLBD->pszText)
{
DEBUGPRINTF((TEXT("CreateListButton: !LocalAlloc pNewLBD->pszText")));
goto error;
}
if (fAnsi)
{
if (!MultiByteToWideChar (CP_ACP, 0, (LPSTR)lpCLB->lpszText,
-1, (LPWSTR)pNewLBD->pszText, cchTextLen))
{
DEBUGPRINTF((TEXT("CreateListButton: MultiByteToWideChar failed on old pCLB->lpszText")));
goto error;
}
}
else
lstrcpy(pNewLBD->pszText, lpCLB->lpszText);
// init internal fields
pNewLBD->fButtonDown = FALSE;
pNewLBD->chUpper = (UINT)LOWORD(CharUpper((LPTSTR)pNewLBD->pszText[0]));
pNewLBD->chLower = (UINT)LOWORD(CharLower((LPTSTR)pNewLBD->pszText[0]));
// create the up and down bitmaps
pNewLBD->hbmpUp = CreateButtonBitmap(hLB,
pbli->cxButton,
pbli->cyButton,
FALSE, // button not down
lpCLB->hBitmap,
pNewLBD->pszText,
&pNewLBD->rcText);
if (!pNewLBD->hbmpUp)
{
DEBUGPRINTF((TEXT("CreateListButton: !LocalAlloc pNewLBD->pszText")));
goto error;
}
pNewLBD->hbmpDown = CreateButtonBitmap(hLB,
pbli->cxButton,
pbli->cyButton,
TRUE, // button down
lpCLB->hBitmap,
pNewLBD->pszText,
&pNewLBD->rcText);
if (!pNewLBD->hbmpDown)
{
DEBUGPRINTF((TEXT("CreateListButton: !CreateButtonBitmap")));
goto error;
}
return (LISTBUTTONDATA FAR*)pNewLBD;
error:
DeleteListButton(pNewLBD);
return NULL;
}
/**********************************************************************\
*
* NAME: DeleteListButton
*
* SYNOPSIS: Free memory and internal objects in allocated from
* CreateListButton
*
* ENTRY: lpLBD LISTBUTTONDATA FAR*
*
* EXIT: void
*
\**********************************************************************/
VOID DeleteListButton(LISTBUTTONDATA FAR* lpLBD)
{
LISTBUTTONDATA NEAR* pLBD;
pLBD = (LISTBUTTONDATA NEAR*)OFFSETOF(lpLBD);
if (pLBD)
{
if (pLBD->pszText != NULL)
{
LocalFree((HLOCAL) pLBD->pszText);
}
if (pLBD->hbmpUp)
DeleteObject(pLBD->hbmpUp);
if (pLBD->hbmpDown)
DeleteObject(pLBD->hbmpDown);
if (pLBD != NULL)
LocalFree((HLOCAL) pLBD);
}
}
#ifdef DEBUG
Static void CDECL DebugPrintf(LPTCSTR lpsz, ...)
{
TCHAR sz[256];
va_list vaArgs;
va_start(vaArgs, lpsz);
wvsprintf(sz, lpsz, vaArgs);
va_end(vaArgs);
OutputDebugString(sz);
OutputDebugString(TEXT("\n\r"));
}
#endif