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 BOOL CALLBACK CheckListProc (HWND hList, UINT msg, WPARAM wp, LPARAM lp);
62 BOOL 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 = (LONG)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))
145 BOOL IsCheckList (HWND hList)
147 TCHAR szClassName[256];
148 if (!GetClassName (hList, szClassName, 256))
151 if (lstrcmp (szClassName, WC_CHECKLIST))
158 BOOL CALLBACK CheckListProc (HWND hList, UINT msg, WPARAM wp, LPARAM lp)
163 Subclass_AddHook (GetParent(hList), CheckList_DialogProc);
164 LB_SetHit (hList, -1);
168 Subclass_RemoveHook (GetParent(hList), CheckList_DialogProc);
173 GetClientRect (GetParent(hList), &rClient);
174 InvalidateRect (GetParent(hList), &rClient, FALSE);
175 UpdateWindow (GetParent(hList));
179 CheckList_OnButtonDown (hList);
180 if (GetCapture() == hList)
185 CallWindowProc ((WNDPROC)procListbox, hList, WM_LBUTTONDOWN, wp, lp);
186 CallWindowProc ((WNDPROC)procListbox, hList, WM_LBUTTONUP, wp, lp);
194 CheckList_OnButtonUp (hList);
197 case WM_LBUTTONDBLCLK:
198 CheckList_OnDoubleClick (hList);
202 CheckList_OnMouseMove (hList);
206 return CheckList_OnGetHit (hList, wp, lp);
209 return CheckList_OnSetHit (hList, wp, lp);
212 return CheckList_OnGetCheck (hList, wp, lp);
215 return CheckList_OnSetCheck (hList, wp, lp);
219 return CallWindowProc ((WNDPROC)procListbox, hList, msg, wp, lp);
221 return DefWindowProc (hList, msg, wp, lp);
225 BOOL CALLBACK CheckList_DialogProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
227 PVOID procOld = Subclass_FindNextHook (hDlg, CheckList_DialogProc);
232 LPMEASUREITEMSTRUCT lpms;
233 if ((lpms = (LPMEASUREITEMSTRUCT)lp) != NULL)
235 HDC hdc = GetDC (hDlg);
237 GetTextMetrics (hdc, &tm);
238 ReleaseDC (hDlg, hdc);
239 lpms->itemHeight = max( tm.tmHeight, cyCHECKBOX );
245 LPDRAWITEMSTRUCT lpds;
246 if ((lpds = (LPDRAWITEMSTRUCT)lp) != NULL)
248 CheckList_OnDrawItem (lpds->hwndItem, lpds->itemID, lpds);
253 case WM_CTLCOLORLISTBOX:
254 if (IsCheckList ((HWND)lp))
256 static COLORREF clrLast = (COLORREF)-1;
257 static HBRUSH hbrStatic = NULL;
258 COLORREF clrNew = GetSysColor (IsWindowEnabled((HWND)lp) ? COLOR_WINDOW : COLOR_BTNFACE);
259 if (clrNew != clrLast)
260 hbrStatic = CreateSolidBrush (clrLast = clrNew);
261 SetBkColor ((HDC)wp, clrLast);
262 return (BOOL)hbrStatic;
268 return CallWindowProc ((WNDPROC)procOld, hDlg, msg, wp, lp);
274 void CheckList_OnDrawItem (HWND hList, int id, LPDRAWITEMSTRUCT lpds)
278 CheckList_OnDrawCheckbox (hList, id, lpds);
279 CheckList_OnDrawText (hList, id, lpds);
284 void CheckList_OnDrawCheckbox (HWND hList, int id, LPDRAWITEMSTRUCT lpds)
286 // Step 1: erase around the checkbox.
288 COLORREF clrBack = GetSysColor (COLOR_WINDOW);
289 if (lpds->itemState & ODS_DISABLED)
290 clrBack = GetSysColor (COLOR_BTNFACE);
292 HBRUSH hbr = CreateSolidBrush (clrBack);
295 ptCheckbox.x = lpds->rcItem.left;
296 ptCheckbox.y = lpds->rcItem.top + ((lpds->rcItem.bottom - lpds->rcItem.top) - cyCHECKBOX) /2;
298 // step 1a: fill in above the checkbox
300 rr.top = lpds->rcItem.top;
301 rr.left = lpds->rcItem.left;
302 rr.right = lpds->rcItem.left + cxCHECKBOX;
303 rr.bottom = ptCheckbox.y;
304 FillRect (lpds->hDC, &rr, hbr);
306 // step 1b: fill in below the checkbox
307 rr.top = ptCheckbox.y + cyCHECKBOX;
308 rr.bottom = lpds->rcItem.bottom;
309 FillRect (lpds->hDC, &rr, hbr);
311 // step 1c: fill in to the right of the checkbox
312 rr.top = lpds->rcItem.top;
313 rr.left = lpds->rcItem.left + cxCHECKBOX;
314 rr.right = lpds->rcItem.left + cxCHECKBOX + cxAFTER_CHECKBOX;
315 rr.bottom = lpds->rcItem.bottom;
316 FillRect (lpds->hDC, &rr, hbr);
320 // Step 2: draw the checkbox itself
325 // step 2a: draw the btnshadow upper-left lines
326 hpNew = CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNSHADOW));
327 hpOld = (HPEN)SelectObject (lpds->hDC, hpNew);
328 MoveToEx (lpds->hDC, ptCheckbox.x, ptCheckbox.y +cyCHECKBOX -2, NULL);
329 LineTo (lpds->hDC, ptCheckbox.x, ptCheckbox.y);
330 LineTo (lpds->hDC, ptCheckbox.x +cxCHECKBOX-1, ptCheckbox.y);
331 SelectObject (lpds->hDC, hpOld);
332 DeleteObject (hpNew);
334 // step 2b: draw the black upper-left lines
335 hpNew = CreatePen (PS_SOLID, 1, RGB(0,0,0));
336 hpOld = (HPEN)SelectObject (lpds->hDC, hpNew);
337 MoveToEx (lpds->hDC, ptCheckbox.x+1, ptCheckbox.y+cyCHECKBOX-3, NULL);
338 LineTo (lpds->hDC, ptCheckbox.x+1, ptCheckbox.y+1);
339 LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-2, ptCheckbox.y+1);
340 SelectObject (lpds->hDC, hpOld);
341 DeleteObject (hpNew);
343 // step 2c: draw the btnface lower-right lines
344 hpNew = CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNFACE));
345 hpOld = (HPEN)SelectObject (lpds->hDC, hpNew);
346 MoveToEx (lpds->hDC, ptCheckbox.x+1, ptCheckbox.y+cyCHECKBOX-2, NULL);
347 LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-2, ptCheckbox.y+cyCHECKBOX-2);
348 LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-2, ptCheckbox.y);
349 SelectObject (lpds->hDC, hpOld);
350 DeleteObject (hpNew);
352 // step 2d: draw the btnhighlight lower-right lines
353 hpNew = CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNHIGHLIGHT));
354 hpOld = (HPEN)SelectObject (lpds->hDC, hpNew);
355 MoveToEx (lpds->hDC, ptCheckbox.x, ptCheckbox.y+cyCHECKBOX-1, NULL);
356 LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-1, ptCheckbox.y+cyCHECKBOX-1);
357 LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-1, ptCheckbox.y-1);
358 SelectObject (lpds->hDC, hpOld);
359 DeleteObject (hpNew);
361 // step 2e: draw the background field
363 BOOL fHit = CheckList_OnHitTest (hList, id);
364 BOOL fChecked = LB_GetCheck (hList, id);
366 clrBack = GetSysColor (COLOR_WINDOW);
367 if ( (lpds->itemState & ODS_DISABLED) ||
368 (fHit && (LB_GetHit(hList) == id)))
370 clrBack = GetSysColor (COLOR_BTNFACE);
373 rr.top = ptCheckbox.y +2;
374 rr.left = ptCheckbox.x +2;
375 rr.right = ptCheckbox.x +cxCHECKBOX -2;
376 rr.bottom = ptCheckbox.y +cyCHECKBOX -2;
378 hbr = CreateSolidBrush (clrBack);
379 FillRect (lpds->hDC, &rr, hbr);
382 // step 2f: draw the checkmark (if appropriate)
386 hpNew = CreatePen (PS_SOLID, 1, (lpds->itemState & ODS_DISABLED) ? GetSysColor(COLOR_BTNSHADOW) : RGB(0,0,0));
387 hpOld = (HPEN)SelectObject (lpds->hDC, hpNew);
389 MoveToEx (lpds->hDC, ptCheckbox.x +3, ptCheckbox.y+5, NULL);
390 LineTo (lpds->hDC, ptCheckbox.x +5, ptCheckbox.y+7);
391 LineTo (lpds->hDC, ptCheckbox.x+10, ptCheckbox.y+2);
393 MoveToEx (lpds->hDC, ptCheckbox.x +3, ptCheckbox.y+6, NULL);
394 LineTo (lpds->hDC, ptCheckbox.x +5, ptCheckbox.y+8);
395 LineTo (lpds->hDC, ptCheckbox.x+10, ptCheckbox.y+3);
397 MoveToEx (lpds->hDC, ptCheckbox.x +3, ptCheckbox.y+7, NULL);
398 LineTo (lpds->hDC, ptCheckbox.x +5, ptCheckbox.y+9);
399 LineTo (lpds->hDC, ptCheckbox.x+10, ptCheckbox.y+4);
401 SelectObject (lpds->hDC, hpOld);
402 DeleteObject (hpNew);
407 void CheckList_OnDrawText (HWND hList, int id, LPDRAWITEMSTRUCT lpds)
409 // Step 1: erase around the text.
411 COLORREF clrBack = GetSysColor (COLOR_WINDOW);
412 COLORREF clrFore = GetSysColor (COLOR_WINDOWTEXT);
413 if (lpds->itemState & ODS_DISABLED)
415 clrBack = GetSysColor (COLOR_BTNFACE);
416 clrFore = GetSysColor (COLOR_GRAYTEXT);
418 else if (lpds->itemState & ODS_SELECTED)
420 clrBack = GetSysColor (COLOR_HIGHLIGHT);
421 clrFore = GetSysColor (COLOR_HIGHLIGHTTEXT);
424 HBRUSH hbr = CreateSolidBrush (clrBack);
426 // step 1a: find out how big the text is, and where it should go
427 // (remember to add a few spaces to the front, so there's a little bit
428 // of highlighted leading space before the text begins)
429 TCHAR szText[256] = TEXT(" ");
430 SendMessage (hList, LB_GETTEXT, (WPARAM)id, (LPARAM)&szText[lstrlen(szText)]);
433 GetTextExtentPoint (lpds->hDC, szText, lstrlen(szText), &sText);
436 ptText.x = lpds->rcItem.left + cxCHECKBOX + cxAFTER_CHECKBOX;
437 ptText.y = lpds->rcItem.top + ((lpds->rcItem.bottom - lpds->rcItem.top) - sText.cy) /2;
439 // step 1b: fill in above the text
441 rr.top = lpds->rcItem.top;
443 rr.right = lpds->rcItem.right;
444 rr.bottom = ptText.y;
445 FillRect (lpds->hDC, &rr, hbr);
447 // step 1c: fill in below the text
449 rr.bottom = lpds->rcItem.bottom;
450 FillRect (lpds->hDC, &rr, hbr);
452 // step 1d: fill in to the right of the text
453 rr.top = lpds->rcItem.top;
454 rr.left = ptText.x + sText.cx;
455 rr.right = lpds->rcItem.right;
456 rr.bottom = lpds->rcItem.bottom;
457 FillRect (lpds->hDC, &rr, hbr);
461 // Step 2: draw the text itself
465 rr.right = ptText.x + sText.cx;
466 rr.bottom = ptText.y + sText.cy;
468 COLORREF clrBG = SetBkColor (lpds->hDC, clrBack);
469 COLORREF clrFG = SetTextColor (lpds->hDC, clrFore);
471 DRAWTEXTPARAMS Params;
472 memset (&Params, 0x00, sizeof(DRAWTEXTPARAMS));
473 Params.cbSize = sizeof(Params);
474 Params.iTabLength = 15;
476 SetBkMode (lpds->hDC, OPAQUE);
477 DrawTextEx (lpds->hDC, szText, -1, &rr, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_EXPANDTABS | DT_NOPREFIX | DT_TABSTOP | DT_NOCLIP, &Params);
479 SetTextColor (lpds->hDC, clrFG);
480 SetBkColor (lpds->hDC, clrBG);
482 // Step 3: draw the focus rect (if appropriate)
484 if (lpds->itemState & ODS_FOCUS)
486 rr.top = lpds->rcItem.top;
488 rr.right = lpds->rcItem.right;
489 rr.bottom = lpds->rcItem.bottom;
490 DrawFocusRect (lpds->hDC, &rr);
495 BOOL CheckList_OnHitTest (HWND hList, int id)
498 SendMessage (hList, LB_GETITEMRECT, (WPARAM)id, (LPARAM)&rr);
499 rr.right = rr.left + cxCHECKBOX;
501 DWORD dw = GetMessagePos();
502 POINT pt = { LOWORD(dw), HIWORD(dw) };
503 ScreenToClient (hList, &pt);
505 return PtInRect (&rr, pt);
509 BOOL CheckList_OnGetHit (HWND hList, WPARAM wp, LPARAM lp)
511 return (BOOL)GetWindowLong (hList, GWL_USERDATA);
515 BOOL CheckList_OnSetHit (HWND hList, WPARAM wp, LPARAM lp)
518 SetWindowLong (hList, GWL_USERDATA, iItem);
523 BOOL CheckList_OnGetCheck (HWND hList, WPARAM wp, LPARAM lp)
527 return (BOOL)SendMessage (hList, LB_GETITEMDATA, (WPARAM)iItem, 0);
531 BOOL CheckList_OnSetCheck (HWND hList, WPARAM wp, LPARAM lp)
534 BOOL fCheck = (BOOL)lp;
536 SendMessage (hList, LB_SETITEMDATA, (WPARAM)iItem, (LPARAM)fCheck);
538 CheckList_RedrawCheck (hList, iItem);
544 void CheckList_OnMouseMove (HWND hList)
546 if (GetCapture() == hList)
548 int ii = LB_GetHit (hList);
551 CheckList_RedrawCheck (hList, ii);
557 void CheckList_OnButtonDown (HWND hList)
559 if (IsWindowEnabled (hList))
561 DWORD dw = GetMessagePos();
562 POINT pt = { LOWORD(dw), HIWORD(dw) };
563 ScreenToClient (hList, &pt);
565 int ii = SendMessage (hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x,pt.y));
568 BOOL fHit = CheckList_OnHitTest (hList, ii);
572 LB_SetHit (hList, ii);
574 CheckList_RedrawCheck (hList, ii);
581 void CheckList_OnButtonUp (HWND hList)
583 if (GetCapture() == hList)
587 int ii = LB_GetHit (hList);
590 LB_SetHit (hList, -1);
592 BOOL fHit = CheckList_OnHitTest (hList, ii);
595 CheckList_RedrawCheck (hList, ii);
598 BOOL fChecked = LB_GetCheck (hList, ii);
599 CheckList_OnSetCheck_Selected (hList, !fChecked);
606 void CheckList_OnDoubleClick (HWND hList)
608 if (IsWindowEnabled (hList))
610 DWORD dw = GetMessagePos();
611 POINT pt = { LOWORD(dw), HIWORD(dw) };
612 ScreenToClient (hList, &pt);
614 int ii = SendMessage (hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x,pt.y));
617 BOOL fChecked = LB_GetCheck (hList, ii);
619 CheckList_OnSetCheck_Selected (hList, !fChecked);
625 void CheckList_OnSetCheck_Selected (HWND hList, BOOL fCheck)
627 static int *aSel = NULL;
628 static size_t cSel = 0;
630 if (GetWindowLong(hList,GWL_STYLE) & LBS_MULTIPLESEL)
632 size_t cReq = SendMessage(hList,LB_GETSELCOUNT,0,0);
633 if ((cReq) && (cReq != LB_ERR) && REALLOC(aSel,cSel,cReq,4))
635 size_t iMax = (size_t)SendMessage (hList, LB_GETSELITEMS, (WPARAM)cSel, (LPARAM)aSel);
638 for (size_t ii = 0; ii < iMax; ++ii)
640 LB_SetCheck (hList, aSel[ii], fCheck);
645 else // single-sel listbox
647 int ii = SendMessage (hList, LB_GETCURSEL, 0, 0);
650 LB_SetCheck (hList, ii, fCheck);
654 SendMessage (GetParent(hList), WM_COMMAND, MAKELONG(GetWindowLong(hList,GWL_ID),LBN_CLICKED), (LPARAM)hList);
658 void CheckList_RedrawCheck (HWND hList, int ii)
661 SendMessage (hList, LB_GETITEMRECT, (WPARAM)ii, (LPARAM)&rr);
662 rr.right = rr.left + cxCHECKBOX;
663 InvalidateRect (hList, &rr, TRUE);
664 UpdateWindow (hList);