1414 lines
45 KiB
C
1414 lines
45 KiB
C
/** Microsoft Windows **/
|
|
/** Copyright(c) Microsoft Corp., 1991, 1992 **/
|
|
|
|
/*
|
|
* MODULE NAME: BTNLIST.C
|
|
|
|
* AUTHOR: John Rivard
|
|
* Microsoft Corp.
|
|
* (johnri@microsoft.com)
|
|
|
|
* 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 "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, NEAR *PBLINFO;
|
|
|
|
// 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
|
|
HBITMAP hbmpUp; // bitmap for up button
|
|
HBITMAP hbmpDown; // bitmap for down button
|
|
RECT rcText; // text rectangle for up button
|
|
char szText[1]; // button text
|
|
} LISTBUTTONDATA, NEAR *PLISTBUTTONDATA;
|
|
|
|
|
|
/******* 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 NEAR PASCAL BL_OnCreate(HWND hwnd, CREATESTRUCT FAR* lpCreateStruct);
|
|
void NEAR PASCAL BL_OnDestroy(HWND hwnd);
|
|
BOOL NEAR PASCAL BL_OnSetFocus(HWND hwnd, HWND hwndOldFocus);
|
|
void NEAR PASCAL BL_OnKillFocus(HWND hwnd, HWND hwndOldFocus);
|
|
void NEAR PASCAL BL_OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT FAR* lpDrawItem);
|
|
void NEAR PASCAL BL_OnMeasureItem(HWND hwnd, MEASUREITEMSTRUCT FAR* lpMeasureItem);
|
|
int NEAR PASCAL BL_OnCompareItem(HWND hwnd, const COMPAREITEMSTRUCT FAR* lpCompareItem);
|
|
int NEAR PASCAL BL_OnCharToItem(HWND hwnd, UINT ch, HWND hwndListbox, int iCaret);
|
|
void NEAR PASCAL BL_OnDeleteItem(HWND hwnd, const DELETEITEMSTRUCT FAR* lpDeleteItem);
|
|
UINT NEAR PASCAL BL_OnGetDlgCode(HWND hwnd, MSG FAR* lpmsg);
|
|
HBRUSH NEAR PASCAL BL_OnCtlColor(HWND hwnd, HDC hdc, HWND hwndChild, int type);
|
|
void NEAR PASCAL BL_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
|
|
LRESULT NEAR PASCAL BL_OnButtonListBox(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
|
|
// SubListBoxProc Message Handlers
|
|
void NEAR PASCAL Sub_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
|
|
LONG NEAR PASCAL Sub_OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags);
|
|
void NEAR PASCAL Sub_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags);
|
|
void NEAR PASCAL Sub_OnKey(HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags);
|
|
|
|
// Miscellaneous functions
|
|
PLISTBUTTONDATA NEAR PASCAL CreateListButton(HWND hLB, CREATELISTBUTTON FAR* lpCLB);
|
|
VOID NEAR PASCAL DeleteListButton(PLISTBUTTONDATA pLBD);
|
|
HBITMAP NEAR PASCAL CreateButtonBitmap(HWND hLB, int nWidth, int nHeight, BOOL fButtonDown, HBITMAP hUserBitmap, LPCSTR lpszUserText, LPRECT rcText);
|
|
|
|
// Debugging
|
|
#ifdef DEBUG
|
|
static void cdecl DebugPrintf(LPCSTR lpsz, ...);
|
|
#define DEBUGPRINTF(arglist) DebugPrintf arglist
|
|
#else
|
|
#define DEBUGPRINTF(arglist)
|
|
#endif
|
|
|
|
|
|
/******* Global Data *****/
|
|
|
|
|
|
// Global - REVIEW_32
|
|
|
|
static BOOL fInitResult = FALSE; // result of initialization
|
|
static WNDPROC lpDefListBoxProc = NULL; // Default ListBox proc
|
|
static HBRUSH hBrushBackground = NULL; // Control background brush
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
#pragma code_seg(CODESEG_INIT)
|
|
|
|
BOOL FAR PASCAL InitButtonListBoxClass(HINSTANCE hInstance)
|
|
{
|
|
WNDCLASS wc;
|
|
|
|
// Button List Control Class
|
|
if (!GetClassInfo(hInstance, s_szBUTTONLISTBOX, &wc))
|
|
{
|
|
#ifndef WIN32
|
|
extern LRESULT CALLBACK _ButtonListBoxProc(HWND, UINT, WPARAM, LPARAM);
|
|
wc.lpfnWndProc = _ButtonListBoxProc;
|
|
#else
|
|
wc.lpfnWndProc = (WNDPROC)ButtonListBoxProc;
|
|
#endif
|
|
fInitResult = FALSE;
|
|
|
|
wc.style = CS_DBLCLKS | CS_PARENTDC | CS_GLOBALCLASS;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = sizeof(PBLINFO);
|
|
wc.hInstance = hInstance;
|
|
wc.hIcon = NULL;
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
|
|
wc.lpszMenuName = (LPCSTR)NULL;
|
|
wc.lpszClassName = s_szBUTTONLISTBOX;
|
|
|
|
if (!RegisterClass(&wc))
|
|
return FALSE;
|
|
|
|
hBrushBackground = GetStockObject(GRAY_BRUSH);
|
|
if (!hBrushBackground)
|
|
return FALSE;
|
|
}
|
|
|
|
return (fInitResult = TRUE);
|
|
}
|
|
|
|
#pragma code_seg()
|
|
|
|
|
|
LRESULT CALLBACK ButtonListBoxProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
/*
|
|
* 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.
|
|
*/
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
HANDLE_MSG(hwnd, WM_DRAWITEM, BL_OnDrawItem); // Draw a button
|
|
HANDLE_MSG(hwnd, WM_MEASUREITEM, BL_OnMeasureItem); // Measure a button
|
|
HANDLE_MSG(hwnd, WM_COMPAREITEM, BL_OnCompareItem); // Compare two buttons
|
|
HANDLE_MSG(hwnd, WM_CHARTOITEM, BL_OnCharToItem);// Keyboard jump
|
|
HANDLE_MSG(hwnd, WM_DELETEITEM, BL_OnDeleteItem);// Delete a button
|
|
HANDLE_MSG(hwnd, WM_SETFOCUS, BL_OnSetFocus);// Set focus to child listbox
|
|
HANDLE_MSG(hwnd, WM_CREATE, BL_OnCreate);// Init buttonlistbox
|
|
HANDLE_MSG(hwnd, WM_DESTROY, BL_OnDestroy);// Cleanup buttonlistbox
|
|
HANDLE_MSG(hwnd, WM_GETDLGCODE, BL_OnGetDlgCode);// Tell dlg mgr about us
|
|
|
|
// Set control bkgnd color
|
|
#ifdef WIN32
|
|
HANDLE_MSG(hwnd, WM_CTLCOLORLISTBOX, BL_OnCtlColor);
|
|
#else // WIN32
|
|
HANDLE_MSG(hwnd, WM_CTLCOLOR, BL_OnCtlColor);
|
|
#endif
|
|
|
|
HANDLE_MSG(hwnd, WM_COMMAND, BL_OnCommand);// Forward commands from the child listbox
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
PLISTBUTTONDATA NEAR PASCAL GetListButtonData(HWND hLB, int iItem)
|
|
{
|
|
DWORD dw;
|
|
|
|
dw = ListBox_GetItemData(hLB, iItem);
|
|
if (dw == LB_ERR)
|
|
return NULL;
|
|
else
|
|
return (PLISTBUTTONDATA)(UINT)dw;
|
|
}
|
|
|
|
|
|
LRESULT NEAR PASCAL BL_OnButtonListBox(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM 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
|
|
*/
|
|
{
|
|
PLISTBUTTONDATA pLBD;
|
|
PBLINFO pbli;
|
|
int cItems;
|
|
HWND hLB;
|
|
|
|
hLB = GetDlgItem(hwnd, ID_LISTBOX);
|
|
pbli = (PBLINFO)GetWindowInt(hwnd, 0);
|
|
|
|
// Handle button list box messages
|
|
|
|
switch (uMsg)
|
|
{
|
|
case BL_ADDBUTTON:
|
|
if (!pbli)
|
|
return BL_ERR;
|
|
cItems = ListBox_GetCount(hLB);
|
|
DEBUGPRINTF(("AddButton: Max %d, Count %d", pbli->cButtonMax, cItems));
|
|
if (cItems >= pbli->cButtonMax)
|
|
{
|
|
DEBUGPRINTF(("AddButton: BL_ERR"));
|
|
return BL_ERR;
|
|
}
|
|
pLBD = CreateListButton(hLB, (CREATELISTBUTTON FAR*)lParam);
|
|
if (!pLBD)
|
|
return BL_ERRSPACE;
|
|
else
|
|
return ListBox_AddItemData(hLB, (UINT)pLBD);
|
|
case BL_DELETEBUTTON:
|
|
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:
|
|
pLBD = GetListButtonData(hLB, wParam);
|
|
if (pLBD)
|
|
return (LRESULT)pLBD->dwItemData;
|
|
else
|
|
return (LRESULT)BL_ERR;
|
|
case BL_GETITEMRECT:
|
|
return ListBox_GetItemRect(hLB, wParam, lParam);
|
|
case BL_GETTEXT:
|
|
pLBD = GetListButtonData(hLB, wParam);
|
|
if (pLBD) {
|
|
lstrcpy((LPSTR)lParam, pLBD->szText);
|
|
return (LRESULT)lstrlen(pLBD->szText);
|
|
}
|
|
else
|
|
return BL_ERR;
|
|
case BL_GETTEXTLEN:
|
|
pLBD = GetListButtonData(hLB, wParam);
|
|
if (pLBD)
|
|
return (LRESULT)lstrlen(pLBD->szText);
|
|
else
|
|
return (LRESULT)BL_ERR;
|
|
case BL_GETTOPINDEX:
|
|
return ListBox_GetTopIndex(hLB);
|
|
case BL_INSERTBUTTON:
|
|
if (!pbli)
|
|
return BL_ERR;
|
|
cItems = ListBox_GetCount(hLB);
|
|
if (cItems >= pbli->cButtonMax)
|
|
return BL_ERR;
|
|
pLBD = CreateListButton(hLB, (CREATELISTBUTTON FAR*)lParam);
|
|
if (!pLBD)
|
|
return BL_ERRSPACE;
|
|
else
|
|
return ListBox_InsertItemData(hLB, wParam, (UINT)pLBD);
|
|
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:
|
|
pLBD = GetListButtonData(hLB, wParam);
|
|
if (pLBD)
|
|
{
|
|
pLBD->dwItemData = (DWORD)lParam;
|
|
return (LRESULT)BL_OKAY;
|
|
}
|
|
else
|
|
return (LRESULT)BL_ERR;
|
|
case BL_SETTOPINDEX:
|
|
return ListBox_SetTopIndex(hLB, wParam);
|
|
default:
|
|
DEBUGPRINTF(("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 NEAR PASCAL BL_OnCreate(HWND hwnd, CREATESTRUCT FAR* lpCS)
|
|
{
|
|
typedef enum tagORIENTATION { VERTICAL, HORIZONTAL } ORIENTATION;
|
|
ORIENTATION orientation;
|
|
BOOL fNoScroll;
|
|
PBLINFO 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;
|
|
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 = (PBLINFO)LocalAlloc(LPTR, sizeof(BLINFO));
|
|
if (!pbli)
|
|
{
|
|
DEBUGPRINTF(("BL_OnCreate: could not allocate pbli"));
|
|
return FALSE;
|
|
}
|
|
SetWindowInt(hwnd, 0, (int)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 * g_cxBorder + ((pbli->fNoScroll) ? 0 : MulDiv(pbli->cxButton, 2, 3));
|
|
|
|
lpCS->cy = pbli->cyButton;
|
|
lpCS->cy += (pbli->fNoScroll) ? 2 * g_cyBorder : (g_cyHScroll + g_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 * g_cyBorder + ((pbli->fNoScroll) ? 0 : MulDiv(pbli->cyButton, 2, 3));
|
|
|
|
lpCS->cx = pbli->cxButton;
|
|
lpCS->cx += (pbli->fNoScroll) ? 2 * g_cxBorder : (g_cxVScroll + g_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,
|
|
"ListBox", // class
|
|
"", // window name
|
|
dwListBoxStyle, // style
|
|
0, // left
|
|
0, // top
|
|
lpCS->cx - 2 * g_cxBorder, // width
|
|
lpCS->cy - 2 * g_cyBorder, // height
|
|
hwnd, // parent
|
|
(HMENU)ID_LISTBOX, // control id of the child listbox
|
|
HINST_THISDLL, // instance
|
|
NULL // no createparams
|
|
);
|
|
|
|
if (!hListBox)
|
|
{
|
|
DEBUGPRINTF(("BL_OnCreate: could not create child listbox"));
|
|
LocalFree((HLOCAL)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;
|
|
}
|
|
|
|
|
|
void NEAR PASCAL BL_OnDestroy(HWND hwnd)
|
|
/*
|
|
* 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.
|
|
*/
|
|
{
|
|
PBLINFO pbli;
|
|
|
|
// Free up the button list info data
|
|
pbli = (PBLINFO)GetWindowInt(hwnd, 0);
|
|
if (pbli != NULL)
|
|
LocalFree((HLOCAL)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.
|
|
*/
|
|
BOOL NEAR PASCAL 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));
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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 NEAR PASCAL BL_OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT FAR* lpDrawItem)
|
|
{
|
|
PLISTBUTTONDATA pLBD;
|
|
HDC hMemoryDC;
|
|
HBITMAP hOldBitmap;
|
|
HBITMAP hBitmap;
|
|
PBLINFO pbli;
|
|
|
|
pLBD = (PLISTBUTTONDATA)(UINT)(lpDrawItem->itemData);
|
|
if (!pLBD)
|
|
return;
|
|
pbli = (PBLINFO)GetWindowInt(hwnd, 0);
|
|
if (!pbli)
|
|
return;
|
|
|
|
/* Draw the standard button */
|
|
|
|
if ((lpDrawItem->itemAction & ODA_DRAWENTIRE) || (lpDrawItem->itemAction & ODA_SELECT))
|
|
{
|
|
hBitmap = (pLBD->fButtonDown) ? pLBD->hbmpDown : pLBD->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, &pLBD->rcText);
|
|
OffsetRect(&rcFocus, lpDrawItem->rcItem.left, lpDrawItem->rcItem.top);
|
|
InflateRect(&rcFocus, 1, 1);
|
|
if (pLBD->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 NEAR PASCAL BL_OnMeasureItem(HWND hwnd, MEASUREITEMSTRUCT FAR* lpMeasureItem)
|
|
{
|
|
PBLINFO pbli;
|
|
|
|
pbli = (PBLINFO)GetWindowInt(hwnd, 0);
|
|
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 NEAR PASCAL BL_OnCompareItem(HWND hwnd, const COMPAREITEMSTRUCT FAR* lpCompareItem)
|
|
{
|
|
PLISTBUTTONDATA pLBD1, pLBD2;
|
|
|
|
Reference(hwnd);
|
|
|
|
pLBD1 = (PLISTBUTTONDATA)(UINT)lpCompareItem->itemData1;
|
|
pLBD2 = (PLISTBUTTONDATA)(UINT)lpCompareItem->itemData2;
|
|
|
|
return lstrcmpi(pLBD1->szText, pLBD2->szText);
|
|
}
|
|
|
|
|
|
/*
|
|
* 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 NEAR PASCAL BL_OnCharToItem(HWND hwnd, UINT ch, HWND hwndListbox, int iCaret)
|
|
{
|
|
PLISTBUTTONDATA pLBD;
|
|
int cbItems;
|
|
int nItem;
|
|
|
|
Reference(hwnd);
|
|
|
|
cbItems = ListBox_GetCount(hwndListbox);
|
|
for (nItem = iCaret + 1; nItem < cbItems; nItem++)
|
|
{
|
|
pLBD = GetListButtonData(hwndListbox, nItem);
|
|
if (!pLBD)
|
|
return -1;
|
|
if ((pLBD->chUpper == ch) || (pLBD->chLower == ch))
|
|
{
|
|
ListBox_SetCurSel(hwndListbox, nItem);
|
|
return -2;
|
|
}
|
|
}
|
|
|
|
for (nItem = 0; nItem < iCaret; nItem++)
|
|
{
|
|
pLBD = GetListButtonData(hwndListbox, nItem);
|
|
if (!pLBD)
|
|
return -1;
|
|
if ((pLBD->chUpper == ch) || (pLBD->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 NEAR PASCAL BL_OnDeleteItem(HWND hwnd, const DELETEITEMSTRUCT FAR* lpDeleteItem)
|
|
{
|
|
PLISTBUTTONDATA pLBD;
|
|
DELETEITEMSTRUCT di;
|
|
|
|
pLBD = (PLISTBUTTONDATA)(UINT)lpDeleteItem->itemData;
|
|
if (!pLBD)
|
|
return;
|
|
|
|
di.CtlType = lpDeleteItem->CtlType;
|
|
di.CtlID = GetDlgCtrlID(hwnd);
|
|
di.itemID = lpDeleteItem->itemID;
|
|
di.hwndItem = hwnd;
|
|
di.itemData = pLBD->dwItemData;
|
|
|
|
SendMessage(GetParent(hwnd), WM_DELETEITEM, (WPARAM)di.CtlID, (LPARAM)(LPSTR)&di);
|
|
|
|
DeleteListButton(pLBD);
|
|
}
|
|
|
|
|
|
/*
|
|
* 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 NEAR PASCAL 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 NEAR PASCAL 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 NEAR PASCAL 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 NEAR PASCAL CreateButtonBitmap(HWND hLB, int nWidth, int nHeight, BOOL fButtonDown, HBITMAP hUserBitmap, LPCSTR lpszUserText, LPRECT rcText)
|
|
{
|
|
HDC hdc;
|
|
HDC hMemoryDC;
|
|
HBRUSH hOldBrush;
|
|
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;
|
|
char szText[40];
|
|
int cbText;
|
|
|
|
HFONT hFontText;
|
|
HFONT hOldFont;
|
|
|
|
DWORD dwLBStyle;
|
|
BOOL fRightBorder;
|
|
BOOL fBottomBorder;
|
|
LOGFONT lf;
|
|
#ifndef WIN32
|
|
LOGFONT_32 lf32;
|
|
#endif
|
|
|
|
#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
|
|
// BUGBUG, not error checks!
|
|
hdc = GetDC(NULL);
|
|
hMemoryDC = CreateCompatibleDC(hdc);
|
|
hBitmap = CreateCompatibleBitmap(hdc, nWidth, nHeight);
|
|
hOldBitmap = SelectObject(hMemoryDC, hBitmap);
|
|
GetObject(hBitmap, sizeof(BITMAP), &bmButton);
|
|
|
|
// Draw the button face
|
|
hOldBrush = SelectObject(hMemoryDC, g_hbrBtnFace);
|
|
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, g_hbrBtnShadow);
|
|
PatBlt(hMemoryDC, 0, 0, nWidth, 1, PATCOPY);
|
|
PatBlt(hMemoryDC, 0, 0, 1, nHeight, PATCOPY);
|
|
}
|
|
else
|
|
{
|
|
SelectObject(hMemoryDC, g_hbrBtnHighlight);
|
|
PatBlt(hMemoryDC, 0, 0, nWidth, 2, PATCOPY);
|
|
PatBlt(hMemoryDC, 0, 0, 2, nHeight, PATCOPY);
|
|
|
|
SelectObject(hMemoryDC, g_hbrBtnShadow);
|
|
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
|
|
#ifdef WIN32
|
|
SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, FALSE);
|
|
#else
|
|
SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf32), &lf32, FALSE);
|
|
|
|
lf.lfHeight = (int)lf32.lfHeight;
|
|
lf.lfWidth = (int)lf32.lfWidth;
|
|
lf.lfEscapement = (int)lf32.lfEscapement;
|
|
lf.lfOrientation = (int)lf32.lfOrientation;
|
|
lf.lfWeight = (int)lf32.lfWeight;
|
|
hmemcpy(&lf.lfItalic, &lf32.lfCommon, sizeof(COMMONFONT));
|
|
#endif
|
|
|
|
hFontText = CreateFontIndirect(&lf);
|
|
|
|
cbText = lstrlen(lpszUserText);
|
|
#ifdef WIN32
|
|
// BUGBUG: I see no reason why the WIN32 side should assume that
|
|
// lpszUserText will fit into szText.
|
|
lstrcpy(szText, lpszUserText);
|
|
#else
|
|
//sizeof(c_szEllipses) is 0, since this is an extern string
|
|
//lstrcpyn(szText, lpszUserText, sizeof(szText) - sizeof(s_szElipsis));
|
|
lstrcpyn(szText, lpszUserText, sizeof(szText));
|
|
#endif
|
|
|
|
hOldFont = SelectObject(hMemoryDC, hFontText);
|
|
SetTextColor(hMemoryDC, g_clrBtnText);
|
|
SetBkMode(hMemoryDC, TRANSPARENT);
|
|
SetTextAlign(hMemoryDC, TA_TOP);
|
|
|
|
MGetTextExtent(hMemoryDC, szText, cbText, &textWidth, &textHeight);
|
|
|
|
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)
|
|
{
|
|
int nWidthText;
|
|
int cbTextT;
|
|
MGetTextExtent(hMemoryDC, c_szEllipses, lstrlen(c_szEllipses), &nWidthText, NULL);
|
|
nWidthT -= nWidthText;
|
|
|
|
// BUGBUG: if the first character's extent is > nWidthT, we clobber cyDest.
|
|
// fortunately it isn't in use yet... We'll also not truncate the text
|
|
// for the elipsis.
|
|
for (cbTextT = 0; cbTextT < cbText; cbTextT++)
|
|
{
|
|
MGetTextExtent(hMemoryDC, szText, cbTextT, &nWidthText, NULL);
|
|
if (nWidthText > nWidthT)
|
|
break;
|
|
}
|
|
szText[--cbTextT] = 0;
|
|
lstrcat(szText, c_szEllipses);
|
|
cbText = lstrlen(szText);
|
|
}
|
|
|
|
if (fButtonDown)
|
|
{
|
|
xDest += 2;
|
|
yDest += 2;
|
|
OffsetRect(rcText, 2, 2);
|
|
}
|
|
|
|
ExtTextOut(hMemoryDC, xDest, yDest, ETO_CLIPPED, rcText, szText, cbText, 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(hdc);
|
|
hOldUserBitmap = SelectObject(hUserDC, hUserBitmap);
|
|
if (hOldUserBitmap)
|
|
{
|
|
BitBlt(hMemoryDC, xDest, yDest, cxDest, cyDest, hUserDC, 0, 0, SRCCOPY);
|
|
SelectObject(hUserDC, hOldUserBitmap);
|
|
}
|
|
DeleteDC(hUserDC);
|
|
}
|
|
|
|
// Cleanup
|
|
SelectObject(hMemoryDC, hOldBrush);
|
|
SelectObject(hMemoryDC, hOldBitmap);
|
|
SelectObject(hMemoryDC, hOldFont);
|
|
DeleteObject(hFontText);
|
|
DeleteDC(hMemoryDC);
|
|
ReleaseDC(NULL, hdc);
|
|
|
|
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: void
|
|
* 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.
|
|
*/
|
|
void NEAR PASCAL Sub_OnLButtonDown(HWND hLB, BOOL fDoubleClick, int x, int y, UINT keyFlags)
|
|
{
|
|
PLISTBUTTONDATA pLBD;
|
|
int nItem;
|
|
int cbItem;
|
|
RECT rcItem;
|
|
POINT pt;
|
|
PBLINFO pbli;
|
|
|
|
Reference(fDoubleClick);
|
|
|
|
pbli = (PBLINFO)GetWindowInt(GetParent(hLB), 0);
|
|
if (!pbli)
|
|
return;
|
|
|
|
cbItem = ListBox_GetCount(hLB);
|
|
pt.x = x;
|
|
pt.y = y;
|
|
|
|
for (nItem = 0; nItem < cbItem; nItem++)
|
|
{
|
|
pLBD = GetListButtonData(hLB, nItem);
|
|
if (!pLBD)
|
|
return;
|
|
|
|
ListBox_GetItemRect(hLB, nItem, &rcItem);
|
|
if (PtInRect(&rcItem, pt))
|
|
{
|
|
pLBD->fButtonDown = TRUE;
|
|
pbli->nTrackButton = nItem;
|
|
InvalidateRect(hLB, &rcItem, FALSE);
|
|
}
|
|
else
|
|
pLBD->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: void
|
|
* 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.
|
|
*/
|
|
LONG NEAR PASCAL Sub_OnLButtonUp(HWND hLB, int x, int y, UINT keyFlags)
|
|
{
|
|
PLISTBUTTONDATA pLBD;
|
|
int nItem;
|
|
int cbItem;
|
|
RECT rcItem;
|
|
POINT pt;
|
|
PBLINFO pbli;
|
|
int nPressedItem = -1;
|
|
|
|
pbli = (PBLINFO)GetWindowInt(GetParent(hLB), 0);
|
|
if (!pbli)
|
|
return 0;
|
|
|
|
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 0;
|
|
}
|
|
|
|
for (nItem = 0; nItem < cbItem; nItem++)
|
|
{
|
|
pLBD = GetListButtonData(hLB, nItem);
|
|
if (!pLBD)
|
|
return 0;
|
|
if (pLBD->fButtonDown)
|
|
nPressedItem = nItem;
|
|
pLBD->fButtonDown = FALSE;
|
|
}
|
|
|
|
if (nPressedItem != -1)
|
|
{
|
|
// BOOM! We got a button press
|
|
pLBD = GetListButtonData(hLB, nPressedItem);
|
|
if (!pLBD)
|
|
return 0;
|
|
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));
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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: void
|
|
* 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.
|
|
*/
|
|
void NEAR PASCAL Sub_OnMouseMove(HWND hLB, int x, int y, UINT keyFlags)
|
|
{
|
|
PLISTBUTTONDATA pLBD;
|
|
int nItem;
|
|
RECT rcItem;
|
|
POINT pt;
|
|
PBLINFO 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;
|
|
}
|
|
|
|
pbli = (PBLINFO)GetWindowInt(GetParent(hLB), 0);
|
|
if (!pbli)
|
|
return;
|
|
pt.x = x;
|
|
pt.y = y;
|
|
|
|
nItem = pbli->nTrackButton;
|
|
if (nItem == -1)
|
|
return;
|
|
|
|
ListBox_GetItemRect(hLB, nItem, &rcItem);
|
|
pLBD = GetListButtonData(hLB, nItem);
|
|
if (!pLBD)
|
|
return;
|
|
fInRect = PtInRect(&rcItem, pt);
|
|
|
|
if (fInRect != pLBD->fButtonDown)
|
|
{
|
|
pLBD->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: void
|
|
* 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.
|
|
*/
|
|
|
|
void NEAR PASCAL Sub_OnKey(HWND hLB, UINT vk, BOOL fDown, int cRepeat, UINT flags)
|
|
{
|
|
PLISTBUTTONDATA pLBD;
|
|
int nItem;
|
|
RECT rcItem;
|
|
PBLINFO pbli;
|
|
|
|
pbli = (PBLINFO)GetWindowInt(GetParent(hLB), 0);
|
|
if (!pbli)
|
|
return;
|
|
if (pbli->nTrackButton >= 0)
|
|
return;
|
|
|
|
if (vk == VK_SPACE)
|
|
{
|
|
nItem = ListBox_GetCurSel(hLB);
|
|
if (nItem >= 0)
|
|
{
|
|
pLBD = GetListButtonData(hLB, nItem);
|
|
if (!pLBD)
|
|
return;
|
|
|
|
if (pLBD->fButtonDown != fDown)
|
|
{
|
|
pLBD->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));
|
|
}
|
|
|
|
|
|
PLISTBUTTONDATA NEAR PASCAL CreateListButton(HWND hLB, CREATELISTBUTTON FAR* lpCLB)
|
|
/*
|
|
* NAME: CreateListButton
|
|
* SYNOPSIS: Setup internal fields in the LISTBUTTONDATA structure
|
|
* ENTRY: hLB HWND window handle of child listbox
|
|
* pLBD PLISTBUTTONDATA ptr to input structure
|
|
* EXIT: return PLISTBUTTONDATA 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.
|
|
*/
|
|
{
|
|
PBLINFO pbli;
|
|
PLISTBUTTONDATA pNewLBD;
|
|
|
|
if (!lpCLB)
|
|
{
|
|
DEBUGPRINTF(("CreateListButton: !lpCLB"));
|
|
return NULL;
|
|
}
|
|
if (lpCLB->cbSize != sizeof(CREATELISTBUTTON))
|
|
{
|
|
DEBUGPRINTF(("CreateListButton: lpCLB->cbSize wrong"));
|
|
return NULL;
|
|
}
|
|
if (!lpCLB->hBitmap)
|
|
{
|
|
DEBUGPRINTF(("CreateListButton: !lpCLB->hBitmap"));
|
|
return NULL;
|
|
}
|
|
if (!lpCLB->lpszText)
|
|
{
|
|
DEBUGPRINTF(("CreateListButton: !lpCLB->lpszText"));
|
|
return NULL;
|
|
}
|
|
|
|
pbli = (PBLINFO)GetWindowInt(GetParent(hLB), 0);
|
|
if (!pbli)
|
|
{
|
|
DEBUGPRINTF(("CreateListButton: !pbli"));
|
|
return NULL;
|
|
}
|
|
|
|
pNewLBD = (PLISTBUTTONDATA)LocalAlloc(LPTR, sizeof(LISTBUTTONDATA) + lstrlen(lpCLB->lpszText));
|
|
if (!pNewLBD)
|
|
{
|
|
DEBUGPRINTF(("CreateListButton: !LocalAlloc pNewLBD"));
|
|
return NULL;
|
|
}
|
|
|
|
// copy over user item data
|
|
pNewLBD->dwItemData = lpCLB->dwItemData;
|
|
|
|
lstrcpy(pNewLBD->szText, lpCLB->lpszText);
|
|
|
|
// init internal fields
|
|
pNewLBD->fButtonDown = FALSE;
|
|
pNewLBD->chUpper = (UINT)LOWORD(AnsiUpper((LPSTR)(DWORD)(BYTE)pNewLBD->szText[0]));
|
|
pNewLBD->chLower = (UINT)LOWORD(AnsiLower((LPSTR)(DWORD)(BYTE)pNewLBD->szText[0]));
|
|
|
|
// create the up and down bitmaps
|
|
|
|
pNewLBD->hbmpUp = CreateButtonBitmap(hLB,
|
|
pbli->cxButton,
|
|
pbli->cyButton,
|
|
FALSE, // button not down
|
|
lpCLB->hBitmap,
|
|
pNewLBD->szText,
|
|
&pNewLBD->rcText);
|
|
if (!pNewLBD->hbmpUp)
|
|
{
|
|
DEBUGPRINTF(("CreateListButton: !CreateButtonBitmap() UP"));
|
|
goto error;
|
|
}
|
|
|
|
pNewLBD->hbmpDown = CreateButtonBitmap(hLB,
|
|
pbli->cxButton,
|
|
pbli->cyButton,
|
|
TRUE, // button down
|
|
lpCLB->hBitmap,
|
|
pNewLBD->szText,
|
|
&pNewLBD->rcText);
|
|
if (!pNewLBD->hbmpDown)
|
|
{
|
|
DEBUGPRINTF(("CreateListButton: !CreateButtonBitmap() DOWN"));
|
|
goto error;
|
|
}
|
|
|
|
return pNewLBD;
|
|
|
|
error:
|
|
DeleteListButton(pNewLBD);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
VOID NEAR PASCAL DeleteListButton(PLISTBUTTONDATA pLBD)
|
|
/*
|
|
* NAME: DeleteListButton
|
|
* SYNOPSIS: Free memory and internal objects in allocated from CreateListButton
|
|
* ENTRY: pLBD PLISTBUTTONDATA
|
|
* EXIT: void
|
|
*/
|
|
{
|
|
if (pLBD)
|
|
{
|
|
if (pLBD->hbmpUp)
|
|
DeleteObject(pLBD->hbmpUp);
|
|
if (pLBD->hbmpDown)
|
|
DeleteObject(pLBD->hbmpDown);
|
|
|
|
LocalFree((HLOCAL)pLBD);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
static void cdecl DebugPrintf(LPCSTR lpsz, int first, ...)
|
|
{
|
|
char sz[256];
|
|
wvsprintf(sz, lpsz, &first);
|
|
OutputDebugString(sz);
|
|
OutputDebugString("\n\r");
|
|
}
|
|
#endif
|