2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
11 #include <afs/param.h>
18 #include <WINNT/TaLocale.h>
19 #include <WINNT/subclass.h>
20 #include <WINNT/checklist.h>
24 * DEFINITIONS ________________________________________________________________
28 #define cxCHECKBOX (2+9+2)
29 #define cyCHECKBOX (2+9+2)
31 #define cxAFTER_CHECKBOX 4
34 #define LB_GETHIT (WM_USER+298)
35 #define LB_SETHIT (WM_USER+299) // int iItem=wp
38 * int LB_GetHit (HWND hList)
39 * void LB_SetHit (HWND hList, int iItem)
42 #define LB_GetHit(_hList) \
43 SendMessage(_hList,LB_GETHIT,(WPARAM)0,(LPARAM)0)
44 #define LB_SetHit(_hList,_ii) \
45 SendMessage(_hList,LB_SETHIT,(WPARAM)_ii,(LPARAM)0)
49 * VARIABLES __________________________________________________________________
56 * PROTOTYPES _________________________________________________________________
60 HRESULT CALLBACK CheckListProc (HWND hList, UINT msg, WPARAM wp, LPARAM lp);
62 HRESULT CALLBACK CheckList_DialogProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp);
64 void CheckList_OnDrawItem (HWND hList, int id, LPDRAWITEMSTRUCT lpds);
65 void CheckList_OnDrawCheckbox (HWND hList, int id, LPDRAWITEMSTRUCT lpds);
66 void CheckList_OnDrawText (HWND hList, int id, LPDRAWITEMSTRUCT lpds);
68 BOOL CheckList_OnHitTest (HWND hList, int id);
70 BOOL CheckList_OnGetHit (HWND hList, WPARAM wp, LPARAM lp);
71 BOOL CheckList_OnSetHit (HWND hList, WPARAM wp, LPARAM lp);
72 BOOL CheckList_OnGetCheck (HWND hList, WPARAM wp, LPARAM lp);
73 BOOL CheckList_OnSetCheck (HWND hList, WPARAM wp, LPARAM lp);
75 void CheckList_OnButtonDown (HWND hList);
76 void CheckList_OnButtonUp (HWND hList);
77 void CheckList_OnDoubleClick (HWND hList);
78 void CheckList_OnMouseMove (HWND hList);
80 void CheckList_OnSetCheck_Selected (HWND hList, BOOL fCheck);
82 void CheckList_RedrawCheck (HWND hList, int ii);
86 * ROUTINES ___________________________________________________________________
91 #define REALLOC(_a,_c,_r,_i) CheckListReallocFunction ((LPVOID*)&_a,sizeof(*_a),&_c,_r,_i)
92 BOOL CheckListReallocFunction (LPVOID *ppTarget, size_t cbElement, size_t *pcTarget, size_t cReq, size_t cInc)
97 if (cReq <= *pcTarget)
100 if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
103 if ((pNew = Allocate (cbElement * cNew)) == NULL)
105 memset (pNew, 0x00, cbElement * cNew);
109 memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
120 BOOL RegisterCheckListClass (void)
122 static BOOL fRegistered = FALSE;
127 GetClassInfo (THIS_HINST, TEXT("LISTBOX"), &wc);
128 procListbox = PtrToLong(wc.lpfnWndProc);
130 wc.style = CS_GLOBALCLASS;
131 wc.lpfnWndProc = (WNDPROC)CheckListProc;
132 wc.hInstance = THIS_HINST;
133 wc.hCursor = LoadCursor (NULL, MAKEINTRESOURCE (IDC_ARROW));
134 wc.hbrBackground = CreateSolidBrush (GetSysColor (COLOR_BTNFACE));
135 wc.lpszClassName = WC_CHECKLIST;
137 if (RegisterClass (&wc)) {
140 OutputDebugString("CheckList class registration failed\n");
148 BOOL IsCheckList (HWND hList)
150 TCHAR szClassName[256];
151 if (!GetClassName (hList, szClassName, 256))
154 if (lstrcmp (szClassName, WC_CHECKLIST))
161 HRESULT CALLBACK CheckListProc (HWND hList, UINT msg, WPARAM wp, LPARAM lp)
168 Subclass_AddHook (GetParent(hList), CheckList_DialogProc);
169 LB_SetHit (hList, -1);
173 Subclass_RemoveHook (GetParent(hList), CheckList_DialogProc);
178 GetClientRect (GetParent(hList), &rClient);
179 InvalidateRect (GetParent(hList), &rClient, FALSE);
180 UpdateWindow (GetParent(hList));
184 CheckList_OnButtonDown (hList);
185 if (GetCapture() == hList)
190 CallWindowProc ((WNDPROC)LongToPtr(procListbox), hList, WM_LBUTTONDOWN, wp, lp);
191 CallWindowProc ((WNDPROC)LongToPtr(procListbox), hList, WM_LBUTTONUP, wp, lp);
199 CheckList_OnButtonUp (hList);
202 case WM_LBUTTONDBLCLK:
203 CheckList_OnDoubleClick (hList);
207 CheckList_OnMouseMove (hList);
211 return CheckList_OnGetHit (hList, wp, lp);
214 return CheckList_OnSetHit (hList, wp, lp);
217 return CheckList_OnGetCheck (hList, wp, lp);
220 return CheckList_OnSetCheck (hList, wp, lp);
224 hResult = (BOOL)CallWindowProc ((WNDPROC)LongToPtr(procListbox), hList, msg, wp, lp);
226 hResult = (BOOL)DefWindowProc (hList, msg, wp, lp);
232 HRESULT CALLBACK CheckList_DialogProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
234 PVOID procOld = Subclass_FindNextHook (hDlg, CheckList_DialogProc);
239 LPMEASUREITEMSTRUCT lpms;
240 if ((lpms = (LPMEASUREITEMSTRUCT)lp) != NULL)
242 HDC hdc = GetDC (hDlg);
244 GetTextMetrics (hdc, &tm);
245 ReleaseDC (hDlg, hdc);
246 lpms->itemHeight = max( tm.tmHeight, cyCHECKBOX );
252 LPDRAWITEMSTRUCT lpds;
253 if ((lpds = (LPDRAWITEMSTRUCT)lp) != NULL)
255 CheckList_OnDrawItem (lpds->hwndItem, lpds->itemID, lpds);
260 case WM_CTLCOLORLISTBOX:
261 if (IsCheckList ((HWND)lp))
263 static COLORREF clrLast = (COLORREF)-1;
264 static HBRUSH hbrStatic = NULL;
265 COLORREF clrNew = GetSysColor (IsWindowEnabled((HWND)lp) ? COLOR_WINDOW : COLOR_BTNFACE);
266 if (clrNew != clrLast)
267 hbrStatic = CreateSolidBrush (clrLast = clrNew);
268 SetBkColor ((HDC)wp, clrLast);
269 return (BOOL)(INT_PTR)hbrStatic;
275 return CallWindowProc ((WNDPROC)procOld, hDlg, msg, wp, lp);
281 void CheckList_OnDrawItem (HWND hList, int id, LPDRAWITEMSTRUCT lpds)
285 CheckList_OnDrawCheckbox (hList, id, lpds);
286 CheckList_OnDrawText (hList, id, lpds);
291 void CheckList_OnDrawCheckbox (HWND hList, int id, LPDRAWITEMSTRUCT lpds)
293 // Step 1: erase around the checkbox.
295 COLORREF clrBack = GetSysColor (COLOR_WINDOW);
296 if (lpds->itemState & ODS_DISABLED)
297 clrBack = GetSysColor (COLOR_BTNFACE);
299 HBRUSH hbr = CreateSolidBrush (clrBack);
302 ptCheckbox.x = lpds->rcItem.left;
303 ptCheckbox.y = lpds->rcItem.top + ((lpds->rcItem.bottom - lpds->rcItem.top) - cyCHECKBOX) /2;
305 // step 1a: fill in above the checkbox
307 rr.top = lpds->rcItem.top;
308 rr.left = lpds->rcItem.left;
309 rr.right = lpds->rcItem.left + cxCHECKBOX;
310 rr.bottom = ptCheckbox.y;
311 FillRect (lpds->hDC, &rr, hbr);
313 // step 1b: fill in below the checkbox
314 rr.top = ptCheckbox.y + cyCHECKBOX;
315 rr.bottom = lpds->rcItem.bottom;
316 FillRect (lpds->hDC, &rr, hbr);
318 // step 1c: fill in to the right of the checkbox
319 rr.top = lpds->rcItem.top;
320 rr.left = lpds->rcItem.left + cxCHECKBOX;
321 rr.right = lpds->rcItem.left + cxCHECKBOX + cxAFTER_CHECKBOX;
322 rr.bottom = lpds->rcItem.bottom;
323 FillRect (lpds->hDC, &rr, hbr);
327 // Step 2: draw the checkbox itself
332 // step 2a: draw the btnshadow upper-left lines
333 hpNew = CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNSHADOW));
334 hpOld = (HPEN)SelectObject (lpds->hDC, hpNew);
335 MoveToEx (lpds->hDC, ptCheckbox.x, ptCheckbox.y +cyCHECKBOX -2, NULL);
336 LineTo (lpds->hDC, ptCheckbox.x, ptCheckbox.y);
337 LineTo (lpds->hDC, ptCheckbox.x +cxCHECKBOX-1, ptCheckbox.y);
338 SelectObject (lpds->hDC, hpOld);
339 DeleteObject (hpNew);
341 // step 2b: draw the black upper-left lines
342 hpNew = CreatePen (PS_SOLID, 1, RGB(0,0,0));
343 hpOld = (HPEN)SelectObject (lpds->hDC, hpNew);
344 MoveToEx (lpds->hDC, ptCheckbox.x+1, ptCheckbox.y+cyCHECKBOX-3, NULL);
345 LineTo (lpds->hDC, ptCheckbox.x+1, ptCheckbox.y+1);
346 LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-2, ptCheckbox.y+1);
347 SelectObject (lpds->hDC, hpOld);
348 DeleteObject (hpNew);
350 // step 2c: draw the btnface lower-right lines
351 hpNew = CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNFACE));
352 hpOld = (HPEN)SelectObject (lpds->hDC, hpNew);
353 MoveToEx (lpds->hDC, ptCheckbox.x+1, ptCheckbox.y+cyCHECKBOX-2, NULL);
354 LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-2, ptCheckbox.y+cyCHECKBOX-2);
355 LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-2, ptCheckbox.y);
356 SelectObject (lpds->hDC, hpOld);
357 DeleteObject (hpNew);
359 // step 2d: draw the btnhighlight lower-right lines
360 hpNew = CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNHIGHLIGHT));
361 hpOld = (HPEN)SelectObject (lpds->hDC, hpNew);
362 MoveToEx (lpds->hDC, ptCheckbox.x, ptCheckbox.y+cyCHECKBOX-1, NULL);
363 LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-1, ptCheckbox.y+cyCHECKBOX-1);
364 LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-1, ptCheckbox.y-1);
365 SelectObject (lpds->hDC, hpOld);
366 DeleteObject (hpNew);
368 // step 2e: draw the background field
370 BOOL fHit = CheckList_OnHitTest (hList, id);
371 BOOL fChecked = (BOOL)LB_GetCheck (hList, id);
373 clrBack = GetSysColor (COLOR_WINDOW);
374 if ( (lpds->itemState & ODS_DISABLED) ||
375 (fHit && (LB_GetHit(hList) == id)))
377 clrBack = GetSysColor (COLOR_BTNFACE);
380 rr.top = ptCheckbox.y +2;
381 rr.left = ptCheckbox.x +2;
382 rr.right = ptCheckbox.x +cxCHECKBOX -2;
383 rr.bottom = ptCheckbox.y +cyCHECKBOX -2;
385 hbr = CreateSolidBrush (clrBack);
386 FillRect (lpds->hDC, &rr, hbr);
389 // step 2f: draw the checkmark (if appropriate)
393 hpNew = CreatePen (PS_SOLID, 1, (lpds->itemState & ODS_DISABLED) ? GetSysColor(COLOR_BTNSHADOW) : RGB(0,0,0));
394 hpOld = (HPEN)SelectObject (lpds->hDC, hpNew);
396 MoveToEx (lpds->hDC, ptCheckbox.x +3, ptCheckbox.y+5, NULL);
397 LineTo (lpds->hDC, ptCheckbox.x +5, ptCheckbox.y+7);
398 LineTo (lpds->hDC, ptCheckbox.x+10, ptCheckbox.y+2);
400 MoveToEx (lpds->hDC, ptCheckbox.x +3, ptCheckbox.y+6, NULL);
401 LineTo (lpds->hDC, ptCheckbox.x +5, ptCheckbox.y+8);
402 LineTo (lpds->hDC, ptCheckbox.x+10, ptCheckbox.y+3);
404 MoveToEx (lpds->hDC, ptCheckbox.x +3, ptCheckbox.y+7, NULL);
405 LineTo (lpds->hDC, ptCheckbox.x +5, ptCheckbox.y+9);
406 LineTo (lpds->hDC, ptCheckbox.x+10, ptCheckbox.y+4);
408 SelectObject (lpds->hDC, hpOld);
409 DeleteObject (hpNew);
414 void CheckList_OnDrawText (HWND hList, int id, LPDRAWITEMSTRUCT lpds)
416 // Step 1: erase around the text.
418 COLORREF clrBack = GetSysColor (COLOR_WINDOW);
419 COLORREF clrFore = GetSysColor (COLOR_WINDOWTEXT);
420 if (lpds->itemState & ODS_DISABLED)
422 clrBack = GetSysColor (COLOR_BTNFACE);
423 clrFore = GetSysColor (COLOR_GRAYTEXT);
425 else if (lpds->itemState & ODS_SELECTED)
427 clrBack = GetSysColor (COLOR_HIGHLIGHT);
428 clrFore = GetSysColor (COLOR_HIGHLIGHTTEXT);
431 HBRUSH hbr = CreateSolidBrush (clrBack);
433 // step 1a: find out how big the text is, and where it should go
434 // (remember to add a few spaces to the front, so there's a little bit
435 // of highlighted leading space before the text begins)
436 TCHAR szText[256] = TEXT(" ");
437 SendMessage (hList, LB_GETTEXT, (WPARAM)id, (LPARAM)&szText[lstrlen(szText)]);
440 GetTextExtentPoint (lpds->hDC, szText, lstrlen(szText), &sText);
443 ptText.x = lpds->rcItem.left + cxCHECKBOX + cxAFTER_CHECKBOX;
444 ptText.y = lpds->rcItem.top + ((lpds->rcItem.bottom - lpds->rcItem.top) - sText.cy) /2;
446 // step 1b: fill in above the text
448 rr.top = lpds->rcItem.top;
450 rr.right = lpds->rcItem.right;
451 rr.bottom = ptText.y;
452 FillRect (lpds->hDC, &rr, hbr);
454 // step 1c: fill in below the text
456 rr.bottom = lpds->rcItem.bottom;
457 FillRect (lpds->hDC, &rr, hbr);
459 // step 1d: fill in to the right of the text
460 rr.top = lpds->rcItem.top;
461 rr.left = ptText.x + sText.cx;
462 rr.right = lpds->rcItem.right;
463 rr.bottom = lpds->rcItem.bottom;
464 FillRect (lpds->hDC, &rr, hbr);
468 // Step 2: draw the text itself
472 rr.right = ptText.x + sText.cx;
473 rr.bottom = ptText.y + sText.cy;
475 COLORREF clrBG = SetBkColor (lpds->hDC, clrBack);
476 COLORREF clrFG = SetTextColor (lpds->hDC, clrFore);
478 DRAWTEXTPARAMS Params;
479 memset (&Params, 0x00, sizeof(DRAWTEXTPARAMS));
480 Params.cbSize = sizeof(Params);
481 Params.iTabLength = 15;
483 SetBkMode (lpds->hDC, OPAQUE);
484 DrawTextEx (lpds->hDC, szText, -1, &rr, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_EXPANDTABS | DT_NOPREFIX | DT_TABSTOP | DT_NOCLIP, &Params);
486 SetTextColor (lpds->hDC, clrFG);
487 SetBkColor (lpds->hDC, clrBG);
489 // Step 3: draw the focus rect (if appropriate)
491 if (lpds->itemState & ODS_FOCUS)
493 rr.top = lpds->rcItem.top;
495 rr.right = lpds->rcItem.right;
496 rr.bottom = lpds->rcItem.bottom;
497 DrawFocusRect (lpds->hDC, &rr);
502 BOOL CheckList_OnHitTest (HWND hList, int id)
505 SendMessage (hList, LB_GETITEMRECT, (WPARAM)id, (LPARAM)&rr);
506 rr.right = rr.left + cxCHECKBOX;
508 DWORD dw = GetMessagePos();
509 POINT pt = { LOWORD(dw), HIWORD(dw) };
510 ScreenToClient (hList, &pt);
512 return PtInRect (&rr, pt);
516 BOOL CheckList_OnGetHit (HWND hList, WPARAM wp, LPARAM lp)
518 return (BOOL)GetWindowLongPtr (hList, GWLP_USERDATA);
522 BOOL CheckList_OnSetHit (HWND hList, WPARAM wp, LPARAM lp)
525 SetWindowLongPtr (hList, GWLP_USERDATA, iItem);
530 BOOL CheckList_OnGetCheck (HWND hList, WPARAM wp, LPARAM lp)
534 return (BOOL)SendMessage (hList, LB_GETITEMDATA, (WPARAM)iItem, 0);
538 BOOL CheckList_OnSetCheck (HWND hList, WPARAM wp, LPARAM lp)
541 BOOL fCheck = (BOOL)lp;
543 SendMessage (hList, LB_SETITEMDATA, (WPARAM)iItem, (LPARAM)fCheck);
545 CheckList_RedrawCheck (hList, iItem);
551 void CheckList_OnMouseMove (HWND hList)
553 if (GetCapture() == hList)
555 int ii = (int)LB_GetHit (hList);
558 CheckList_RedrawCheck (hList, ii);
564 void CheckList_OnButtonDown (HWND hList)
566 if (IsWindowEnabled (hList))
568 DWORD dw = GetMessagePos();
569 POINT pt = { LOWORD(dw), HIWORD(dw) };
570 ScreenToClient (hList, &pt);
572 int ii = (int)SendMessage (hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x,pt.y));
575 BOOL fHit = CheckList_OnHitTest (hList, ii);
579 LB_SetHit (hList, ii);
581 CheckList_RedrawCheck (hList, ii);
588 void CheckList_OnButtonUp (HWND hList)
590 if (GetCapture() == hList)
594 int ii = (int)LB_GetHit (hList);
597 LB_SetHit (hList, -1);
599 BOOL fHit = CheckList_OnHitTest (hList, ii);
602 CheckList_RedrawCheck (hList, ii);
605 BOOL fChecked = (BOOL)LB_GetCheck (hList, ii);
606 CheckList_OnSetCheck_Selected (hList, !fChecked);
613 void CheckList_OnDoubleClick (HWND hList)
615 if (IsWindowEnabled (hList))
617 DWORD dw = GetMessagePos();
618 POINT pt = { LOWORD(dw), HIWORD(dw) };
619 ScreenToClient (hList, &pt);
621 int ii = (int)SendMessage (hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x,pt.y));
624 BOOL fChecked = (BOOL)LB_GetCheck (hList, ii);
626 CheckList_OnSetCheck_Selected (hList, !fChecked);
632 void CheckList_OnSetCheck_Selected (HWND hList, BOOL fCheck)
634 static int *aSel = NULL;
635 static size_t cSel = 0;
637 if (GetWindowLong(hList,GWL_STYLE) & LBS_MULTIPLESEL)
639 size_t cReq = SendMessage(hList,LB_GETSELCOUNT,0,0);
640 if ((cReq) && (cReq != LB_ERR) && REALLOC(aSel,cSel,cReq,4))
642 size_t iMax = (size_t)SendMessage (hList, LB_GETSELITEMS, (WPARAM)cSel, (LPARAM)aSel);
645 for (size_t ii = 0; ii < iMax; ++ii)
647 LB_SetCheck (hList, aSel[ii], fCheck);
652 else // single-sel listbox
654 int ii = (int)SendMessage (hList, LB_GETCURSEL, 0, 0);
657 LB_SetCheck (hList, ii, fCheck);
661 SendMessage (GetParent(hList), WM_COMMAND, MAKELONG(GetWindowLong(hList,GWL_ID),LBN_CLICKED), (LPARAM)hList);
665 void CheckList_RedrawCheck (HWND hList, int ii)
668 SendMessage (hList, LB_GETITEMRECT, (WPARAM)ii, (LPARAM)&rr);
669 rr.right = rr.left + cxCHECKBOX;
670 InvalidateRect (hList, &rr, TRUE);
671 UpdateWindow (hList);