/* * Copyright 2000, International Business Machines Corporation and others. * All Rights Reserved. * * This software has been released under the terms of the IBM Public * License. For details, see the LICENSE file in the top-level source * directory or online at http://www.openafs.org/dl/license10.html */ extern "C" { #include #include } #include #include #include #include #include #include /* * DEFINITIONS ________________________________________________________________ * */ #define cxCHECKBOX (2+9+2) #define cyCHECKBOX (2+9+2) #define cxAFTER_CHECKBOX 4 #define LB_GETHIT (WM_USER+298) #define LB_SETHIT (WM_USER+299) // int iItem=wp /* * int LB_GetHit (HWND hList) * void LB_SetHit (HWND hList, int iItem) * */ #define LB_GetHit(_hList) \ SendMessage(_hList,LB_GETHIT,(WPARAM)0,(LPARAM)0) #define LB_SetHit(_hList,_ii) \ SendMessage(_hList,LB_SETHIT,(WPARAM)_ii,(LPARAM)0) /* * VARIABLES __________________________________________________________________ * */ LONG procListbox = 0; /* * PROTOTYPES _________________________________________________________________ * */ BOOL CALLBACK CheckListProc (HWND hList, UINT msg, WPARAM wp, LPARAM lp); BOOL CALLBACK CheckList_DialogProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp); void CheckList_OnDrawItem (HWND hList, int id, LPDRAWITEMSTRUCT lpds); void CheckList_OnDrawCheckbox (HWND hList, int id, LPDRAWITEMSTRUCT lpds); void CheckList_OnDrawText (HWND hList, int id, LPDRAWITEMSTRUCT lpds); BOOL CheckList_OnHitTest (HWND hList, int id); BOOL CheckList_OnGetHit (HWND hList, WPARAM wp, LPARAM lp); BOOL CheckList_OnSetHit (HWND hList, WPARAM wp, LPARAM lp); BOOL CheckList_OnGetCheck (HWND hList, WPARAM wp, LPARAM lp); BOOL CheckList_OnSetCheck (HWND hList, WPARAM wp, LPARAM lp); void CheckList_OnButtonDown (HWND hList); void CheckList_OnButtonUp (HWND hList); void CheckList_OnDoubleClick (HWND hList); void CheckList_OnMouseMove (HWND hList); void CheckList_OnSetCheck_Selected (HWND hList, BOOL fCheck); void CheckList_RedrawCheck (HWND hList, int ii); /* * ROUTINES ___________________________________________________________________ * */ #ifndef REALLOC #define REALLOC(_a,_c,_r,_i) CheckListReallocFunction ((LPVOID*)&_a,sizeof(*_a),&_c,_r,_i) BOOL CheckListReallocFunction (LPVOID *ppTarget, size_t cbElement, size_t *pcTarget, size_t cReq, size_t cInc) { LPVOID pNew; size_t cNew; if (cReq <= *pcTarget) return TRUE; if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0) return FALSE; if ((pNew = Allocate (cbElement * cNew)) == NULL) return FALSE; memset (pNew, 0x00, cbElement * cNew); if (*pcTarget != 0) { memcpy (pNew, *ppTarget, cbElement * (*pcTarget)); Free (*ppTarget); } *ppTarget = pNew; *pcTarget = cNew; return TRUE; } #endif BOOL RegisterCheckListClass (void) { static BOOL fRegistered = FALSE; if (!fRegistered) { WNDCLASS wc; GetClassInfo (THIS_HINST, TEXT("LISTBOX"), &wc); procListbox = (LONG)wc.lpfnWndProc; wc.style = CS_GLOBALCLASS; wc.lpfnWndProc = (WNDPROC)CheckListProc; wc.hInstance = THIS_HINST; wc.hCursor = LoadCursor (NULL, MAKEINTRESOURCE (IDC_ARROW)); wc.hbrBackground = CreateSolidBrush (GetSysColor (COLOR_BTNFACE)); wc.lpszClassName = WC_CHECKLIST; if (RegisterClass (&wc)) fRegistered = TRUE; } return fRegistered; } BOOL IsCheckList (HWND hList) { TCHAR szClassName[256]; if (!GetClassName (hList, szClassName, 256)) return FALSE; if (lstrcmp (szClassName, WC_CHECKLIST)) return FALSE; return TRUE; } BOOL CALLBACK CheckListProc (HWND hList, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_CREATE: Subclass_AddHook (GetParent(hList), CheckList_DialogProc); LB_SetHit (hList, -1); break; case WM_DESTROY: Subclass_RemoveHook (GetParent(hList), CheckList_DialogProc); break; case WM_ENABLE: RECT rClient; GetClientRect (GetParent(hList), &rClient); InvalidateRect (GetParent(hList), &rClient, FALSE); UpdateWindow (GetParent(hList)); break; case WM_LBUTTONDOWN: CheckList_OnButtonDown (hList); if (GetCapture() == hList) { if (procListbox) { ReleaseCapture (); CallWindowProc ((WNDPROC)procListbox, hList, WM_LBUTTONDOWN, wp, lp); CallWindowProc ((WNDPROC)procListbox, hList, WM_LBUTTONUP, wp, lp); SetCapture (hList); } return TRUE; } break; case WM_LBUTTONUP: CheckList_OnButtonUp (hList); break; case WM_LBUTTONDBLCLK: CheckList_OnDoubleClick (hList); break; case WM_MOUSEMOVE: CheckList_OnMouseMove (hList); break; case LB_GETHIT: return CheckList_OnGetHit (hList, wp, lp); case LB_SETHIT: return CheckList_OnSetHit (hList, wp, lp); case LB_GETCHECK: return CheckList_OnGetCheck (hList, wp, lp); case LB_SETCHECK: return CheckList_OnSetCheck (hList, wp, lp); } if (procListbox) return CallWindowProc ((WNDPROC)procListbox, hList, msg, wp, lp); else return DefWindowProc (hList, msg, wp, lp); } BOOL CALLBACK CheckList_DialogProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { PVOID procOld = Subclass_FindNextHook (hDlg, CheckList_DialogProc); switch (msg) { case WM_MEASUREITEM: LPMEASUREITEMSTRUCT lpms; if ((lpms = (LPMEASUREITEMSTRUCT)lp) != NULL) { HDC hdc = GetDC (hDlg); TEXTMETRIC tm; GetTextMetrics (hdc, &tm); ReleaseDC (hDlg, hdc); lpms->itemHeight = max( tm.tmHeight, cyCHECKBOX ); return TRUE; } break; case WM_DRAWITEM: LPDRAWITEMSTRUCT lpds; if ((lpds = (LPDRAWITEMSTRUCT)lp) != NULL) { CheckList_OnDrawItem (lpds->hwndItem, lpds->itemID, lpds); return TRUE; } break; case WM_CTLCOLORLISTBOX: if (IsCheckList ((HWND)lp)) { static COLORREF clrLast = (COLORREF)-1; static HBRUSH hbrStatic = NULL; COLORREF clrNew = GetSysColor (IsWindowEnabled((HWND)lp) ? COLOR_WINDOW : COLOR_BTNFACE); if (clrNew != clrLast) hbrStatic = CreateSolidBrush (clrLast = clrNew); SetBkColor ((HDC)wp, clrLast); return (BOOL)hbrStatic; } break; } if (procOld) return CallWindowProc ((WNDPROC)procOld, hDlg, msg, wp, lp); else return FALSE; } void CheckList_OnDrawItem (HWND hList, int id, LPDRAWITEMSTRUCT lpds) { if (id >= 0) { CheckList_OnDrawCheckbox (hList, id, lpds); CheckList_OnDrawText (hList, id, lpds); } } void CheckList_OnDrawCheckbox (HWND hList, int id, LPDRAWITEMSTRUCT lpds) { // Step 1: erase around the checkbox. // COLORREF clrBack = GetSysColor (COLOR_WINDOW); if (lpds->itemState & ODS_DISABLED) clrBack = GetSysColor (COLOR_BTNFACE); HBRUSH hbr = CreateSolidBrush (clrBack); POINT ptCheckbox; ptCheckbox.x = lpds->rcItem.left; ptCheckbox.y = lpds->rcItem.top + ((lpds->rcItem.bottom - lpds->rcItem.top) - cyCHECKBOX) /2; // step 1a: fill in above the checkbox RECT rr; rr.top = lpds->rcItem.top; rr.left = lpds->rcItem.left; rr.right = lpds->rcItem.left + cxCHECKBOX; rr.bottom = ptCheckbox.y; FillRect (lpds->hDC, &rr, hbr); // step 1b: fill in below the checkbox rr.top = ptCheckbox.y + cyCHECKBOX; rr.bottom = lpds->rcItem.bottom; FillRect (lpds->hDC, &rr, hbr); // step 1c: fill in to the right of the checkbox rr.top = lpds->rcItem.top; rr.left = lpds->rcItem.left + cxCHECKBOX; rr.right = lpds->rcItem.left + cxCHECKBOX + cxAFTER_CHECKBOX; rr.bottom = lpds->rcItem.bottom; FillRect (lpds->hDC, &rr, hbr); DeleteObject (hbr); // Step 2: draw the checkbox itself // HPEN hpOld; HPEN hpNew; // step 2a: draw the btnshadow upper-left lines hpNew = CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNSHADOW)); hpOld = (HPEN)SelectObject (lpds->hDC, hpNew); MoveToEx (lpds->hDC, ptCheckbox.x, ptCheckbox.y +cyCHECKBOX -2, NULL); LineTo (lpds->hDC, ptCheckbox.x, ptCheckbox.y); LineTo (lpds->hDC, ptCheckbox.x +cxCHECKBOX-1, ptCheckbox.y); SelectObject (lpds->hDC, hpOld); DeleteObject (hpNew); // step 2b: draw the black upper-left lines hpNew = CreatePen (PS_SOLID, 1, RGB(0,0,0)); hpOld = (HPEN)SelectObject (lpds->hDC, hpNew); MoveToEx (lpds->hDC, ptCheckbox.x+1, ptCheckbox.y+cyCHECKBOX-3, NULL); LineTo (lpds->hDC, ptCheckbox.x+1, ptCheckbox.y+1); LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-2, ptCheckbox.y+1); SelectObject (lpds->hDC, hpOld); DeleteObject (hpNew); // step 2c: draw the btnface lower-right lines hpNew = CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNFACE)); hpOld = (HPEN)SelectObject (lpds->hDC, hpNew); MoveToEx (lpds->hDC, ptCheckbox.x+1, ptCheckbox.y+cyCHECKBOX-2, NULL); LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-2, ptCheckbox.y+cyCHECKBOX-2); LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-2, ptCheckbox.y); SelectObject (lpds->hDC, hpOld); DeleteObject (hpNew); // step 2d: draw the btnhighlight lower-right lines hpNew = CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNHIGHLIGHT)); hpOld = (HPEN)SelectObject (lpds->hDC, hpNew); MoveToEx (lpds->hDC, ptCheckbox.x, ptCheckbox.y+cyCHECKBOX-1, NULL); LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-1, ptCheckbox.y+cyCHECKBOX-1); LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-1, ptCheckbox.y-1); SelectObject (lpds->hDC, hpOld); DeleteObject (hpNew); // step 2e: draw the background field // BOOL fHit = CheckList_OnHitTest (hList, id); BOOL fChecked = LB_GetCheck (hList, id); clrBack = GetSysColor (COLOR_WINDOW); if ( (lpds->itemState & ODS_DISABLED) || (fHit && (LB_GetHit(hList) == id))) { clrBack = GetSysColor (COLOR_BTNFACE); } rr.top = ptCheckbox.y +2; rr.left = ptCheckbox.x +2; rr.right = ptCheckbox.x +cxCHECKBOX -2; rr.bottom = ptCheckbox.y +cyCHECKBOX -2; hbr = CreateSolidBrush (clrBack); FillRect (lpds->hDC, &rr, hbr); DeleteObject (hbr); // step 2f: draw the checkmark (if appropriate) // if (fChecked) { hpNew = CreatePen (PS_SOLID, 1, (lpds->itemState & ODS_DISABLED) ? GetSysColor(COLOR_BTNSHADOW) : RGB(0,0,0)); hpOld = (HPEN)SelectObject (lpds->hDC, hpNew); MoveToEx (lpds->hDC, ptCheckbox.x +3, ptCheckbox.y+5, NULL); LineTo (lpds->hDC, ptCheckbox.x +5, ptCheckbox.y+7); LineTo (lpds->hDC, ptCheckbox.x+10, ptCheckbox.y+2); MoveToEx (lpds->hDC, ptCheckbox.x +3, ptCheckbox.y+6, NULL); LineTo (lpds->hDC, ptCheckbox.x +5, ptCheckbox.y+8); LineTo (lpds->hDC, ptCheckbox.x+10, ptCheckbox.y+3); MoveToEx (lpds->hDC, ptCheckbox.x +3, ptCheckbox.y+7, NULL); LineTo (lpds->hDC, ptCheckbox.x +5, ptCheckbox.y+9); LineTo (lpds->hDC, ptCheckbox.x+10, ptCheckbox.y+4); SelectObject (lpds->hDC, hpOld); DeleteObject (hpNew); } } void CheckList_OnDrawText (HWND hList, int id, LPDRAWITEMSTRUCT lpds) { // Step 1: erase around the text. // COLORREF clrBack = GetSysColor (COLOR_WINDOW); COLORREF clrFore = GetSysColor (COLOR_WINDOWTEXT); if (lpds->itemState & ODS_DISABLED) { clrBack = GetSysColor (COLOR_BTNFACE); clrFore = GetSysColor (COLOR_GRAYTEXT); } else if (lpds->itemState & ODS_SELECTED) { clrBack = GetSysColor (COLOR_HIGHLIGHT); clrFore = GetSysColor (COLOR_HIGHLIGHTTEXT); } HBRUSH hbr = CreateSolidBrush (clrBack); // step 1a: find out how big the text is, and where it should go // (remember to add a few spaces to the front, so there's a little bit // of highlighted leading space before the text begins) TCHAR szText[256] = TEXT(" "); SendMessage (hList, LB_GETTEXT, (WPARAM)id, (LPARAM)&szText[lstrlen(szText)]); SIZE sText; GetTextExtentPoint (lpds->hDC, szText, lstrlen(szText), &sText); POINT ptText; ptText.x = lpds->rcItem.left + cxCHECKBOX + cxAFTER_CHECKBOX; ptText.y = lpds->rcItem.top + ((lpds->rcItem.bottom - lpds->rcItem.top) - sText.cy) /2; // step 1b: fill in above the text RECT rr; rr.top = lpds->rcItem.top; rr.left = ptText.x; rr.right = lpds->rcItem.right; rr.bottom = ptText.y; FillRect (lpds->hDC, &rr, hbr); // step 1c: fill in below the text rr.top = ptText.y; rr.bottom = lpds->rcItem.bottom; FillRect (lpds->hDC, &rr, hbr); // step 1d: fill in to the right of the text rr.top = lpds->rcItem.top; rr.left = ptText.x + sText.cx; rr.right = lpds->rcItem.right; rr.bottom = lpds->rcItem.bottom; FillRect (lpds->hDC, &rr, hbr); DeleteObject (hbr); // Step 2: draw the text itself // rr.top = ptText.y; rr.left = ptText.x; rr.right = ptText.x + sText.cx; rr.bottom = ptText.y + sText.cy; COLORREF clrBG = SetBkColor (lpds->hDC, clrBack); COLORREF clrFG = SetTextColor (lpds->hDC, clrFore); DRAWTEXTPARAMS Params; memset (&Params, 0x00, sizeof(DRAWTEXTPARAMS)); Params.cbSize = sizeof(Params); Params.iTabLength = 15; SetBkMode (lpds->hDC, OPAQUE); DrawTextEx (lpds->hDC, szText, -1, &rr, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_EXPANDTABS | DT_NOPREFIX | DT_TABSTOP | DT_NOCLIP, &Params); SetTextColor (lpds->hDC, clrFG); SetBkColor (lpds->hDC, clrBG); // Step 3: draw the focus rect (if appropriate) // if (lpds->itemState & ODS_FOCUS) { rr.top = lpds->rcItem.top; rr.left = ptText.x; rr.right = lpds->rcItem.right; rr.bottom = lpds->rcItem.bottom; DrawFocusRect (lpds->hDC, &rr); } } BOOL CheckList_OnHitTest (HWND hList, int id) { RECT rr; SendMessage (hList, LB_GETITEMRECT, (WPARAM)id, (LPARAM)&rr); rr.right = rr.left + cxCHECKBOX; DWORD dw = GetMessagePos(); POINT pt = { LOWORD(dw), HIWORD(dw) }; ScreenToClient (hList, &pt); return PtInRect (&rr, pt); } BOOL CheckList_OnGetHit (HWND hList, WPARAM wp, LPARAM lp) { return (BOOL)GetWindowLong (hList, GWL_USERDATA); } BOOL CheckList_OnSetHit (HWND hList, WPARAM wp, LPARAM lp) { int iItem = (int)wp; SetWindowLong (hList, GWL_USERDATA, iItem); return TRUE; } BOOL CheckList_OnGetCheck (HWND hList, WPARAM wp, LPARAM lp) { int iItem = (int)wp; return (BOOL)SendMessage (hList, LB_GETITEMDATA, (WPARAM)iItem, 0); } BOOL CheckList_OnSetCheck (HWND hList, WPARAM wp, LPARAM lp) { int iItem = (int)wp; BOOL fCheck = (BOOL)lp; SendMessage (hList, LB_SETITEMDATA, (WPARAM)iItem, (LPARAM)fCheck); CheckList_RedrawCheck (hList, iItem); return TRUE; } void CheckList_OnMouseMove (HWND hList) { if (GetCapture() == hList) { int ii = LB_GetHit (hList); if (ii != -1) { CheckList_RedrawCheck (hList, ii); } } } void CheckList_OnButtonDown (HWND hList) { if (IsWindowEnabled (hList)) { DWORD dw = GetMessagePos(); POINT pt = { LOWORD(dw), HIWORD(dw) }; ScreenToClient (hList, &pt); int ii = SendMessage (hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x,pt.y)); if (HIWORD(ii) == 0) { BOOL fHit = CheckList_OnHitTest (hList, ii); if (fHit) { SetCapture (hList); LB_SetHit (hList, ii); CheckList_RedrawCheck (hList, ii); } } } } void CheckList_OnButtonUp (HWND hList) { if (GetCapture() == hList) { ReleaseCapture(); int ii = LB_GetHit (hList); if (ii != -1) { LB_SetHit (hList, -1); BOOL fHit = CheckList_OnHitTest (hList, ii); if (!fHit) CheckList_RedrawCheck (hList, ii); else { BOOL fChecked = LB_GetCheck (hList, ii); CheckList_OnSetCheck_Selected (hList, !fChecked); } } } } void CheckList_OnDoubleClick (HWND hList) { if (IsWindowEnabled (hList)) { DWORD dw = GetMessagePos(); POINT pt = { LOWORD(dw), HIWORD(dw) }; ScreenToClient (hList, &pt); int ii = SendMessage (hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x,pt.y)); if (HIWORD(ii) == 0) { BOOL fChecked = LB_GetCheck (hList, ii); CheckList_OnSetCheck_Selected (hList, !fChecked); } } } void CheckList_OnSetCheck_Selected (HWND hList, BOOL fCheck) { static int *aSel = NULL; static size_t cSel = 0; if (GetWindowLong(hList,GWL_STYLE) & LBS_MULTIPLESEL) { size_t cReq = SendMessage(hList,LB_GETSELCOUNT,0,0); if ((cReq) && (cReq != LB_ERR) && REALLOC(aSel,cSel,cReq,4)) { size_t iMax = (size_t)SendMessage (hList, LB_GETSELITEMS, (WPARAM)cSel, (LPARAM)aSel); if (iMax != LB_ERR) { for (size_t ii = 0; ii < iMax; ++ii) { LB_SetCheck (hList, aSel[ii], fCheck); } } } } else // single-sel listbox { int ii = SendMessage (hList, LB_GETCURSEL, 0, 0); if (ii != LB_ERR) { LB_SetCheck (hList, ii, fCheck); } } SendMessage (GetParent(hList), WM_COMMAND, MAKELONG(GetWindowLong(hList,GWL_ID),LBN_CLICKED), (LPARAM)hList); } void CheckList_RedrawCheck (HWND hList, int ii) { RECT rr; SendMessage (hList, LB_GETITEMRECT, (WPARAM)ii, (LPARAM)&rr); rr.right = rr.left + cxCHECKBOX; InvalidateRect (hList, &rr, TRUE); UpdateWindow (hList); }