9 #include <WINNT/TaLocale.h>
10 #include <WINNT/subclass.h>
11 #include <WINNT/checklist.h>
15 * DEFINITIONS ________________________________________________________________
19 #define cxCHECKBOX (2+9+2)
20 #define cyCHECKBOX (2+9+2)
22 #define cxAFTER_CHECKBOX 4
25 #define LB_GETHIT (WM_USER+298)
26 #define LB_SETHIT (WM_USER+299) // int iItem=wp
29 * int LB_GetHit (HWND hList)
30 * void LB_SetHit (HWND hList, int iItem)
33 #define LB_GetHit(_hList) \
34 SendMessage(_hList,LB_GETHIT,(WPARAM)0,(LPARAM)0)
35 #define LB_SetHit(_hList,_ii) \
36 SendMessage(_hList,LB_SETHIT,(WPARAM)_ii,(LPARAM)0)
40 * VARIABLES __________________________________________________________________
47 * PROTOTYPES _________________________________________________________________
51 BOOL CALLBACK CheckListProc (HWND hList, UINT msg, WPARAM wp, LPARAM lp);
53 BOOL CALLBACK CheckList_DialogProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp);
55 void CheckList_OnDrawItem (HWND hList, int id, LPDRAWITEMSTRUCT lpds);
56 void CheckList_OnDrawCheckbox (HWND hList, int id, LPDRAWITEMSTRUCT lpds);
57 void CheckList_OnDrawText (HWND hList, int id, LPDRAWITEMSTRUCT lpds);
59 BOOL CheckList_OnHitTest (HWND hList, int id);
61 BOOL CheckList_OnGetHit (HWND hList, WPARAM wp, LPARAM lp);
62 BOOL CheckList_OnSetHit (HWND hList, WPARAM wp, LPARAM lp);
63 BOOL CheckList_OnGetCheck (HWND hList, WPARAM wp, LPARAM lp);
64 BOOL CheckList_OnSetCheck (HWND hList, WPARAM wp, LPARAM lp);
66 void CheckList_OnButtonDown (HWND hList);
67 void CheckList_OnButtonUp (HWND hList);
68 void CheckList_OnDoubleClick (HWND hList);
69 void CheckList_OnMouseMove (HWND hList);
71 void CheckList_OnSetCheck_Selected (HWND hList, BOOL fCheck);
73 void CheckList_RedrawCheck (HWND hList, int ii);
77 * ROUTINES ___________________________________________________________________
82 #define REALLOC(_a,_c,_r,_i) CheckListReallocFunction ((LPVOID*)&_a,sizeof(*_a),&_c,_r,_i)
83 BOOL CheckListReallocFunction (LPVOID *ppTarget, size_t cbElement, size_t *pcTarget, size_t cReq, size_t cInc)
88 if (cReq <= *pcTarget)
91 if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
94 if ((pNew = Allocate (cbElement * cNew)) == NULL)
96 memset (pNew, 0x00, cbElement * cNew);
100 memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
111 BOOL RegisterCheckListClass (void)
113 static BOOL fRegistered = FALSE;
118 GetClassInfo (THIS_HINST, TEXT("LISTBOX"), &wc);
119 procListbox = (LONG)wc.lpfnWndProc;
121 wc.style = CS_GLOBALCLASS;
122 wc.lpfnWndProc = (WNDPROC)CheckListProc;
123 wc.hInstance = THIS_HINST;
124 wc.hCursor = LoadCursor (NULL, MAKEINTRESOURCE (IDC_ARROW));
125 wc.hbrBackground = CreateSolidBrush (GetSysColor (COLOR_BTNFACE));
126 wc.lpszClassName = WC_CHECKLIST;
128 if (RegisterClass (&wc))
136 BOOL IsCheckList (HWND hList)
138 TCHAR szClassName[256];
139 if (!GetClassName (hList, szClassName, 256))
142 if (lstrcmp (szClassName, WC_CHECKLIST))
149 BOOL CALLBACK CheckListProc (HWND hList, UINT msg, WPARAM wp, LPARAM lp)
154 Subclass_AddHook (GetParent(hList), CheckList_DialogProc);
155 LB_SetHit (hList, -1);
159 Subclass_RemoveHook (GetParent(hList), CheckList_DialogProc);
164 GetClientRect (GetParent(hList), &rClient);
165 InvalidateRect (GetParent(hList), &rClient, FALSE);
166 UpdateWindow (GetParent(hList));
170 CheckList_OnButtonDown (hList);
171 if (GetCapture() == hList)
176 CallWindowProc ((WNDPROC)procListbox, hList, WM_LBUTTONDOWN, wp, lp);
177 CallWindowProc ((WNDPROC)procListbox, hList, WM_LBUTTONUP, wp, lp);
185 CheckList_OnButtonUp (hList);
188 case WM_LBUTTONDBLCLK:
189 CheckList_OnDoubleClick (hList);
193 CheckList_OnMouseMove (hList);
197 return CheckList_OnGetHit (hList, wp, lp);
200 return CheckList_OnSetHit (hList, wp, lp);
203 return CheckList_OnGetCheck (hList, wp, lp);
206 return CheckList_OnSetCheck (hList, wp, lp);
210 return CallWindowProc ((WNDPROC)procListbox, hList, msg, wp, lp);
212 return DefWindowProc (hList, msg, wp, lp);
216 BOOL CALLBACK CheckList_DialogProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
218 PVOID procOld = Subclass_FindNextHook (hDlg, CheckList_DialogProc);
223 LPMEASUREITEMSTRUCT lpms;
224 if ((lpms = (LPMEASUREITEMSTRUCT)lp) != NULL)
226 HDC hdc = GetDC (hDlg);
228 GetTextMetrics (hdc, &tm);
229 ReleaseDC (hDlg, hdc);
230 lpms->itemHeight = max( tm.tmHeight, cyCHECKBOX );
236 LPDRAWITEMSTRUCT lpds;
237 if ((lpds = (LPDRAWITEMSTRUCT)lp) != NULL)
239 CheckList_OnDrawItem (lpds->hwndItem, lpds->itemID, lpds);
244 case WM_CTLCOLORLISTBOX:
245 if (IsCheckList ((HWND)lp))
247 static COLORREF clrLast = (COLORREF)-1;
248 static HBRUSH hbrStatic = NULL;
249 COLORREF clrNew = GetSysColor (IsWindowEnabled((HWND)lp) ? COLOR_WINDOW : COLOR_BTNFACE);
250 if (clrNew != clrLast)
251 hbrStatic = CreateSolidBrush (clrLast = clrNew);
252 SetBkColor ((HDC)wp, clrLast);
253 return (BOOL)hbrStatic;
259 return CallWindowProc ((WNDPROC)procOld, hDlg, msg, wp, lp);
265 void CheckList_OnDrawItem (HWND hList, int id, LPDRAWITEMSTRUCT lpds)
269 CheckList_OnDrawCheckbox (hList, id, lpds);
270 CheckList_OnDrawText (hList, id, lpds);
275 void CheckList_OnDrawCheckbox (HWND hList, int id, LPDRAWITEMSTRUCT lpds)
277 // Step 1: erase around the checkbox.
279 COLORREF clrBack = GetSysColor (COLOR_WINDOW);
280 if (lpds->itemState & ODS_DISABLED)
281 clrBack = GetSysColor (COLOR_BTNFACE);
283 HBRUSH hbr = CreateSolidBrush (clrBack);
286 ptCheckbox.x = lpds->rcItem.left;
287 ptCheckbox.y = lpds->rcItem.top + ((lpds->rcItem.bottom - lpds->rcItem.top) - cyCHECKBOX) /2;
289 // step 1a: fill in above the checkbox
291 rr.top = lpds->rcItem.top;
292 rr.left = lpds->rcItem.left;
293 rr.right = lpds->rcItem.left + cxCHECKBOX;
294 rr.bottom = ptCheckbox.y;
295 FillRect (lpds->hDC, &rr, hbr);
297 // step 1b: fill in below the checkbox
298 rr.top = ptCheckbox.y + cyCHECKBOX;
299 rr.bottom = lpds->rcItem.bottom;
300 FillRect (lpds->hDC, &rr, hbr);
302 // step 1c: fill in to the right of the checkbox
303 rr.top = lpds->rcItem.top;
304 rr.left = lpds->rcItem.left + cxCHECKBOX;
305 rr.right = lpds->rcItem.left + cxCHECKBOX + cxAFTER_CHECKBOX;
306 rr.bottom = lpds->rcItem.bottom;
307 FillRect (lpds->hDC, &rr, hbr);
311 // Step 2: draw the checkbox itself
316 // step 2a: draw the btnshadow upper-left lines
317 hpNew = CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNSHADOW));
318 hpOld = (HPEN)SelectObject (lpds->hDC, hpNew);
319 MoveToEx (lpds->hDC, ptCheckbox.x, ptCheckbox.y +cyCHECKBOX -2, NULL);
320 LineTo (lpds->hDC, ptCheckbox.x, ptCheckbox.y);
321 LineTo (lpds->hDC, ptCheckbox.x +cxCHECKBOX-1, ptCheckbox.y);
322 SelectObject (lpds->hDC, hpOld);
323 DeleteObject (hpNew);
325 // step 2b: draw the black upper-left lines
326 hpNew = CreatePen (PS_SOLID, 1, RGB(0,0,0));
327 hpOld = (HPEN)SelectObject (lpds->hDC, hpNew);
328 MoveToEx (lpds->hDC, ptCheckbox.x+1, ptCheckbox.y+cyCHECKBOX-3, NULL);
329 LineTo (lpds->hDC, ptCheckbox.x+1, ptCheckbox.y+1);
330 LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-2, ptCheckbox.y+1);
331 SelectObject (lpds->hDC, hpOld);
332 DeleteObject (hpNew);
334 // step 2c: draw the btnface lower-right lines
335 hpNew = CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNFACE));
336 hpOld = (HPEN)SelectObject (lpds->hDC, hpNew);
337 MoveToEx (lpds->hDC, ptCheckbox.x+1, ptCheckbox.y+cyCHECKBOX-2, NULL);
338 LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-2, ptCheckbox.y+cyCHECKBOX-2);
339 LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-2, ptCheckbox.y);
340 SelectObject (lpds->hDC, hpOld);
341 DeleteObject (hpNew);
343 // step 2d: draw the btnhighlight lower-right lines
344 hpNew = CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNHIGHLIGHT));
345 hpOld = (HPEN)SelectObject (lpds->hDC, hpNew);
346 MoveToEx (lpds->hDC, ptCheckbox.x, ptCheckbox.y+cyCHECKBOX-1, NULL);
347 LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-1, ptCheckbox.y+cyCHECKBOX-1);
348 LineTo (lpds->hDC, ptCheckbox.x+cxCHECKBOX-1, ptCheckbox.y-1);
349 SelectObject (lpds->hDC, hpOld);
350 DeleteObject (hpNew);
352 // step 2e: draw the background field
354 BOOL fHit = CheckList_OnHitTest (hList, id);
355 BOOL fChecked = LB_GetCheck (hList, id);
357 clrBack = GetSysColor (COLOR_WINDOW);
358 if ( (lpds->itemState & ODS_DISABLED) ||
359 (fHit && (LB_GetHit(hList) == id)))
361 clrBack = GetSysColor (COLOR_BTNFACE);
364 rr.top = ptCheckbox.y +2;
365 rr.left = ptCheckbox.x +2;
366 rr.right = ptCheckbox.x +cxCHECKBOX -2;
367 rr.bottom = ptCheckbox.y +cyCHECKBOX -2;
369 hbr = CreateSolidBrush (clrBack);
370 FillRect (lpds->hDC, &rr, hbr);
373 // step 2f: draw the checkmark (if appropriate)
377 hpNew = CreatePen (PS_SOLID, 1, (lpds->itemState & ODS_DISABLED) ? GetSysColor(COLOR_BTNSHADOW) : RGB(0,0,0));
378 hpOld = (HPEN)SelectObject (lpds->hDC, hpNew);
380 MoveToEx (lpds->hDC, ptCheckbox.x +3, ptCheckbox.y+5, NULL);
381 LineTo (lpds->hDC, ptCheckbox.x +5, ptCheckbox.y+7);
382 LineTo (lpds->hDC, ptCheckbox.x+10, ptCheckbox.y+2);
384 MoveToEx (lpds->hDC, ptCheckbox.x +3, ptCheckbox.y+6, NULL);
385 LineTo (lpds->hDC, ptCheckbox.x +5, ptCheckbox.y+8);
386 LineTo (lpds->hDC, ptCheckbox.x+10, ptCheckbox.y+3);
388 MoveToEx (lpds->hDC, ptCheckbox.x +3, ptCheckbox.y+7, NULL);
389 LineTo (lpds->hDC, ptCheckbox.x +5, ptCheckbox.y+9);
390 LineTo (lpds->hDC, ptCheckbox.x+10, ptCheckbox.y+4);
392 SelectObject (lpds->hDC, hpOld);
393 DeleteObject (hpNew);
398 void CheckList_OnDrawText (HWND hList, int id, LPDRAWITEMSTRUCT lpds)
400 // Step 1: erase around the text.
402 COLORREF clrBack = GetSysColor (COLOR_WINDOW);
403 COLORREF clrFore = GetSysColor (COLOR_WINDOWTEXT);
404 if (lpds->itemState & ODS_DISABLED)
406 clrBack = GetSysColor (COLOR_BTNFACE);
407 clrFore = GetSysColor (COLOR_GRAYTEXT);
409 else if (lpds->itemState & ODS_SELECTED)
411 clrBack = GetSysColor (COLOR_HIGHLIGHT);
412 clrFore = GetSysColor (COLOR_HIGHLIGHTTEXT);
415 HBRUSH hbr = CreateSolidBrush (clrBack);
417 // step 1a: find out how big the text is, and where it should go
418 // (remember to add a few spaces to the front, so there's a little bit
419 // of highlighted leading space before the text begins)
420 TCHAR szText[256] = TEXT(" ");
421 SendMessage (hList, LB_GETTEXT, (WPARAM)id, (LPARAM)&szText[lstrlen(szText)]);
424 GetTextExtentPoint (lpds->hDC, szText, lstrlen(szText), &sText);
427 ptText.x = lpds->rcItem.left + cxCHECKBOX + cxAFTER_CHECKBOX;
428 ptText.y = lpds->rcItem.top + ((lpds->rcItem.bottom - lpds->rcItem.top) - sText.cy) /2;
430 // step 1b: fill in above the text
432 rr.top = lpds->rcItem.top;
434 rr.right = lpds->rcItem.right;
435 rr.bottom = ptText.y;
436 FillRect (lpds->hDC, &rr, hbr);
438 // step 1c: fill in below the text
440 rr.bottom = lpds->rcItem.bottom;
441 FillRect (lpds->hDC, &rr, hbr);
443 // step 1d: fill in to the right of the text
444 rr.top = lpds->rcItem.top;
445 rr.left = ptText.x + sText.cx;
446 rr.right = lpds->rcItem.right;
447 rr.bottom = lpds->rcItem.bottom;
448 FillRect (lpds->hDC, &rr, hbr);
452 // Step 2: draw the text itself
456 rr.right = ptText.x + sText.cx;
457 rr.bottom = ptText.y + sText.cy;
459 COLORREF clrBG = SetBkColor (lpds->hDC, clrBack);
460 COLORREF clrFG = SetTextColor (lpds->hDC, clrFore);
462 DRAWTEXTPARAMS Params;
463 memset (&Params, 0x00, sizeof(DRAWTEXTPARAMS));
464 Params.cbSize = sizeof(Params);
465 Params.iTabLength = 15;
467 SetBkMode (lpds->hDC, OPAQUE);
468 DrawTextEx (lpds->hDC, szText, -1, &rr, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_EXPANDTABS | DT_NOPREFIX | DT_TABSTOP | DT_NOCLIP, &Params);
470 SetTextColor (lpds->hDC, clrFG);
471 SetBkColor (lpds->hDC, clrBG);
473 // Step 3: draw the focus rect (if appropriate)
475 if (lpds->itemState & ODS_FOCUS)
477 rr.top = lpds->rcItem.top;
479 rr.right = lpds->rcItem.right;
480 rr.bottom = lpds->rcItem.bottom;
481 DrawFocusRect (lpds->hDC, &rr);
486 BOOL CheckList_OnHitTest (HWND hList, int id)
489 SendMessage (hList, LB_GETITEMRECT, (WPARAM)id, (LPARAM)&rr);
490 rr.right = rr.left + cxCHECKBOX;
492 DWORD dw = GetMessagePos();
493 POINT pt = { LOWORD(dw), HIWORD(dw) };
494 ScreenToClient (hList, &pt);
496 return PtInRect (&rr, pt);
500 BOOL CheckList_OnGetHit (HWND hList, WPARAM wp, LPARAM lp)
502 return (BOOL)GetWindowLong (hList, GWL_USERDATA);
506 BOOL CheckList_OnSetHit (HWND hList, WPARAM wp, LPARAM lp)
509 SetWindowLong (hList, GWL_USERDATA, iItem);
514 BOOL CheckList_OnGetCheck (HWND hList, WPARAM wp, LPARAM lp)
518 return (BOOL)SendMessage (hList, LB_GETITEMDATA, (WPARAM)iItem, 0);
522 BOOL CheckList_OnSetCheck (HWND hList, WPARAM wp, LPARAM lp)
525 BOOL fCheck = (BOOL)lp;
527 SendMessage (hList, LB_SETITEMDATA, (WPARAM)iItem, (LPARAM)fCheck);
529 CheckList_RedrawCheck (hList, iItem);
535 void CheckList_OnMouseMove (HWND hList)
537 if (GetCapture() == hList)
539 int ii = LB_GetHit (hList);
542 CheckList_RedrawCheck (hList, ii);
548 void CheckList_OnButtonDown (HWND hList)
550 if (IsWindowEnabled (hList))
552 DWORD dw = GetMessagePos();
553 POINT pt = { LOWORD(dw), HIWORD(dw) };
554 ScreenToClient (hList, &pt);
556 int ii = SendMessage (hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x,pt.y));
559 BOOL fHit = CheckList_OnHitTest (hList, ii);
563 LB_SetHit (hList, ii);
565 CheckList_RedrawCheck (hList, ii);
572 void CheckList_OnButtonUp (HWND hList)
574 if (GetCapture() == hList)
578 int ii = LB_GetHit (hList);
581 LB_SetHit (hList, -1);
583 BOOL fHit = CheckList_OnHitTest (hList, ii);
586 CheckList_RedrawCheck (hList, ii);
589 BOOL fChecked = LB_GetCheck (hList, ii);
590 CheckList_OnSetCheck_Selected (hList, !fChecked);
597 void CheckList_OnDoubleClick (HWND hList)
599 if (IsWindowEnabled (hList))
601 DWORD dw = GetMessagePos();
602 POINT pt = { LOWORD(dw), HIWORD(dw) };
603 ScreenToClient (hList, &pt);
605 int ii = SendMessage (hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x,pt.y));
608 BOOL fChecked = LB_GetCheck (hList, ii);
610 CheckList_OnSetCheck_Selected (hList, !fChecked);
616 void CheckList_OnSetCheck_Selected (HWND hList, BOOL fCheck)
618 static int *aSel = NULL;
619 static size_t cSel = 0;
621 if (GetWindowLong(hList,GWL_STYLE) & LBS_MULTIPLESEL)
623 size_t cReq = SendMessage(hList,LB_GETSELCOUNT,0,0);
624 if ((cReq) && (cReq != LB_ERR) && REALLOC(aSel,cSel,cReq,4))
626 size_t iMax = (size_t)SendMessage (hList, LB_GETSELITEMS, (WPARAM)cSel, (LPARAM)aSel);
629 for (size_t ii = 0; ii < iMax; ++ii)
631 LB_SetCheck (hList, aSel[ii], fCheck);
636 else // single-sel listbox
638 int ii = SendMessage (hList, LB_GETCURSEL, 0, 0);
641 LB_SetCheck (hList, ii, fCheck);
645 SendMessage (GetParent(hList), WM_COMMAND, MAKELONG(GetWindowLong(hList,GWL_ID),LBN_CLICKED), (LPARAM)hList);
649 void CheckList_RedrawCheck (HWND hList, int ii)
652 SendMessage (hList, LB_GETITEMRECT, (WPARAM)ii, (LPARAM)&rr);
653 rr.right = rr.left + cxCHECKBOX;
654 InvalidateRect (hList, &rr, TRUE);
655 UpdateWindow (hList);