mirror of https://github.com/lianthony/NT4.0
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
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
|
|
|