2 * ReactOS Access Control List Editor
3 * Copyright (C) 2004 ReactOS Team
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * PROJECT: ReactOS Access Control List Editor
22 * FILE: lib/aclui/checklist.c
23 * PURPOSE: Access Control List Editor
24 * PROGRAMMER: Thomas Weidenmueller <w3seek@reactos.com>
31 #define CI_TEXT_MARGIN_WIDTH (8)
32 #define CI_TEXT_MARGIN_HEIGHT (3)
33 #define CI_TEXT_SELECTIONMARGIN (1)
35 #define TIMER_ID_SETHITFOCUS (1)
36 #define TIMER_ID_RESETQUICKSEARCH (2)
38 #define DEFAULT_QUICKSEARCH_SETFOCUS_DELAY (2000)
39 #define DEFAULT_QUICKSEARCH_RESET_DELAY (3000)
41 typedef struct _CHECKITEM
43 struct _CHECKITEM
*Next
;
46 } CHECKITEM
, *PCHECKITEM
;
48 typedef struct _CHECKLISTWND
54 PCHECKITEM CheckItemListHead
;
60 PCHECKITEM FocusedCheckItem
;
61 UINT FocusedCheckItemBox
;
65 COLORREF TextColor
[2];
68 BOOL QuickSearchEnabled
;
69 PCHECKITEM QuickSearchHitItem
;
70 WCHAR QuickSearchText
[65];
71 UINT QuickSearchSetFocusDelay
;
72 UINT QuickSearchResetDelay
;
78 PCHECKITEM HoveredCheckItem
;
79 UINT HoveredCheckItemBox
;
84 } CHECKLISTWND
, *PCHECKLISTWND
;
86 static VOID
EscapeQuickSearch(IN PCHECKLISTWND infoPtr
);
88 static VOID
ChangeCheckItemHotTrack(IN PCHECKLISTWND infoPtr
,
89 IN PCHECKITEM NewHotTrack
,
90 IN UINT NewHotTrackBox
);
92 static VOID
ChangeCheckItemFocus(IN PCHECKLISTWND infoPtr
,
93 IN PCHECKITEM NewFocus
,
96 /******************************************************************************/
99 NotifyControlParent(IN PCHECKLISTWND infoPtr
,
105 if (infoPtr
->hNotify
!= NULL
)
107 LPNMHDR pnmh
= (LPNMHDR
)data
;
109 pnmh
->hwndFrom
= infoPtr
->hSelf
;
110 pnmh
->idFrom
= GetWindowLongPtr(infoPtr
->hSelf
,
114 Ret
= SendMessage(infoPtr
->hNotify
,
116 (WPARAM
)pnmh
->idFrom
,
124 FindCheckItemByIndex(IN PCHECKLISTWND infoPtr
,
127 PCHECKITEM Item
, Found
= NULL
;
131 for (Item
= infoPtr
->CheckItemListHead
;
149 CheckItemToIndex(IN PCHECKLISTWND infoPtr
,
155 for (CurItem
= infoPtr
->CheckItemListHead
, Index
= 0;
157 CurItem
= CurItem
->Next
, Index
++)
169 FindCheckItem(IN PCHECKLISTWND infoPtr
,
170 IN LPWSTR SearchText
)
173 SIZE_T Count
= wcslen(SearchText
);
175 for (CurItem
= infoPtr
->CheckItemListHead
;
177 CurItem
= CurItem
->Next
)
179 if ((CurItem
->State
& CIS_DISABLED
) != CIS_DISABLED
&&
180 !wcsnicmp(CurItem
->Name
, SearchText
, Count
))
190 FindFirstEnabledCheckBox(IN PCHECKLISTWND infoPtr
,
195 for (CurItem
= infoPtr
->CheckItemListHead
;
197 CurItem
= CurItem
->Next
)
199 if ((CurItem
->State
& CIS_DISABLED
) != CIS_DISABLED
)
201 /* return the Allow checkbox in case both check boxes are enabled! */
202 *CheckBox
= ((!(CurItem
->State
& CIS_ALLOWDISABLED
)) ? CLB_ALLOW
: CLB_DENY
);
211 FindLastEnabledCheckBox(IN PCHECKLISTWND infoPtr
,
215 PCHECKITEM LastEnabledItem
= NULL
;
217 for (CurItem
= infoPtr
->CheckItemListHead
;
219 CurItem
= CurItem
->Next
)
221 if ((CurItem
->State
& CIS_DISABLED
) != CIS_DISABLED
)
223 LastEnabledItem
= CurItem
;
227 if (LastEnabledItem
!= NULL
)
229 /* return the Deny checkbox in case both check boxes are enabled! */
230 *CheckBox
= ((!(LastEnabledItem
->State
& CIS_DENYDISABLED
)) ? CLB_DENY
: CLB_ALLOW
);
233 return LastEnabledItem
;
237 FindPreviousEnabledCheckBox(IN PCHECKLISTWND infoPtr
,
242 if (infoPtr
->FocusedCheckItem
!= NULL
)
244 Item
= infoPtr
->FocusedCheckItem
;
246 if (infoPtr
->FocusedCheckItemBox
== CLB_DENY
&&
247 !(Item
->State
& CIS_ALLOWDISABLED
))
249 /* currently an Deny checkbox is focused. return the Allow checkbox
251 *CheckBox
= CLB_ALLOW
;
259 for (CurItem
= infoPtr
->CheckItemListHead
;
260 CurItem
!= infoPtr
->FocusedCheckItem
;
261 CurItem
= CurItem
->Next
)
263 if ((CurItem
->State
& CIS_DISABLED
) != CIS_DISABLED
)
271 /* return the Deny checkbox in case both check boxes are enabled! */
272 *CheckBox
= ((!(Item
->State
& CIS_DENYDISABLED
)) ? CLB_DENY
: CLB_ALLOW
);
278 Item
= FindLastEnabledCheckBox(infoPtr
,
286 FindNextEnabledCheckBox(IN PCHECKLISTWND infoPtr
,
291 if (infoPtr
->FocusedCheckItem
!= NULL
)
293 Item
= infoPtr
->FocusedCheckItem
;
295 if (infoPtr
->FocusedCheckItemBox
!= CLB_DENY
&&
296 !(Item
->State
& CIS_DENYDISABLED
))
298 /* currently an Allow checkbox is focused. return the Deny checkbox
300 *CheckBox
= CLB_DENY
;
308 if ((Item
->State
& CIS_DISABLED
) != CIS_DISABLED
)
310 /* return the Allow checkbox in case both check boxes are enabled! */
311 *CheckBox
= ((!(Item
->State
& CIS_ALLOWDISABLED
)) ? CLB_ALLOW
: CLB_DENY
);
321 Item
= FindFirstEnabledCheckBox(infoPtr
,
329 FindEnabledCheckBox(IN PCHECKLISTWND infoPtr
,
330 IN BOOL ReverseSearch
,
337 Item
= FindPreviousEnabledCheckBox(infoPtr
,
342 Item
= FindNextEnabledCheckBox(infoPtr
,
350 PtToCheckItemBox(IN PCHECKLISTWND infoPtr
,
353 OUT BOOL
*DirectlyInCheckBox
)
355 INT FirstVisible
, Index
;
358 FirstVisible
= GetScrollPos(infoPtr
->hSelf
,
361 Index
= FirstVisible
+ (ppt
->y
/ infoPtr
->ItemHeight
);
363 Item
= FindCheckItemByIndex(infoPtr
,
369 cx
= infoPtr
->CheckBoxLeft
[CLB_ALLOW
] +
370 ((infoPtr
->CheckBoxLeft
[CLB_DENY
] - infoPtr
->CheckBoxLeft
[CLB_ALLOW
]) / 2);
372 *CheckBox
= ((ppt
->x
<= cx
) ? CLB_ALLOW
: CLB_DENY
);
374 if (DirectlyInCheckBox
!= NULL
)
376 INT y
= ppt
->y
% infoPtr
->ItemHeight
;
377 INT cxBox
= infoPtr
->ItemHeight
- (2 * CI_TEXT_MARGIN_HEIGHT
);
379 if ((y
>= CI_TEXT_MARGIN_HEIGHT
&&
380 y
< infoPtr
->ItemHeight
- CI_TEXT_MARGIN_HEIGHT
) &&
382 (((ppt
->x
>= (infoPtr
->CheckBoxLeft
[CLB_ALLOW
] - (cxBox
/ 2))) &&
383 (ppt
->x
< (infoPtr
->CheckBoxLeft
[CLB_ALLOW
] - (cxBox
/ 2) + cxBox
)))
385 ((ppt
->x
>= (infoPtr
->CheckBoxLeft
[CLB_DENY
] - (cxBox
/ 2))) &&
386 (ppt
->x
< (infoPtr
->CheckBoxLeft
[CLB_DENY
] - (cxBox
/ 2) + cxBox
)))))
388 *DirectlyInCheckBox
= TRUE
;
392 *DirectlyInCheckBox
= FALSE
;
401 ClearCheckItems(IN PCHECKLISTWND infoPtr
)
403 PCHECKITEM CurItem
, NextItem
;
405 CurItem
= infoPtr
->CheckItemListHead
;
406 while (CurItem
!= NULL
)
408 NextItem
= CurItem
->Next
;
409 HeapFree(GetProcessHeap(),
415 infoPtr
->CheckItemListHead
= NULL
;
416 infoPtr
->CheckItemCount
= 0;
420 DeleteCheckItem(IN PCHECKLISTWND infoPtr
,
424 PCHECKITEM
*PrevPtr
= &infoPtr
->CheckItemListHead
;
426 for (CurItem
= infoPtr
->CheckItemListHead
;
428 CurItem
= CurItem
->Next
)
432 if (Item
== infoPtr
->QuickSearchHitItem
&& infoPtr
->QuickSearchEnabled
)
434 EscapeQuickSearch(infoPtr
);
438 if (Item
== infoPtr
->HoveredCheckItem
)
440 ChangeCheckItemHotTrack(infoPtr
,
446 if (Item
== infoPtr
->FocusedCheckItem
)
448 ChangeCheckItemFocus(infoPtr
,
453 *PrevPtr
= CurItem
->Next
;
454 HeapFree(GetProcessHeap(),
457 infoPtr
->CheckItemCount
--;
461 PrevPtr
= &CurItem
->Next
;
468 AddCheckItem(IN PCHECKLISTWND infoPtr
,
475 PCHECKITEM
*PrevPtr
= &infoPtr
->CheckItemListHead
;
476 PCHECKITEM Item
= HeapAlloc(GetProcessHeap(),
478 sizeof(CHECKITEM
) + (wcslen(Name
) * sizeof(WCHAR
)));
481 for (CurItem
= infoPtr
->CheckItemListHead
, i
= 0;
483 CurItem
= CurItem
->Next
)
485 PrevPtr
= &CurItem
->Next
;
490 Item
->State
= State
& CIS_MASK
;
495 infoPtr
->CheckItemCount
++;
507 ClearCheckBoxes(IN PCHECKLISTWND infoPtr
)
512 for (CurItem
= infoPtr
->CheckItemListHead
;
514 CurItem
= CurItem
->Next
)
516 if (CurItem
->State
& (CIS_ALLOW
| CIS_DENY
))
518 CurItem
->State
&= ~(CIS_ALLOW
| CIS_DENY
);
527 UpdateControl(IN PCHECKLISTWND infoPtr
)
530 SCROLLINFO ScrollInfo
;
533 GetClientRect(infoPtr
->hSelf
,
536 ScrollInfo
.cbSize
= sizeof(ScrollInfo
);
537 ScrollInfo
.fMask
= SIF_PAGE
| SIF_RANGE
;
539 ScrollInfo
.nMax
= infoPtr
->CheckItemCount
;
540 ScrollInfo
.nPage
= ((rcClient
.bottom
- rcClient
.top
) + infoPtr
->ItemHeight
- 1) / infoPtr
->ItemHeight
;
542 ScrollInfo
.nTrackPos
= 0;
544 VisibleItems
= (rcClient
.bottom
- rcClient
.top
) / infoPtr
->ItemHeight
;
546 if (ScrollInfo
.nPage
== (UINT
)VisibleItems
&& ScrollInfo
.nMax
> 0)
551 SetScrollInfo(infoPtr
->hSelf
,
556 RedrawWindow(infoPtr
->hSelf
,
559 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
563 UpdateCheckItem(IN PCHECKLISTWND infoPtr
,
567 INT VisibleFirst
, VisibleItems
;
568 INT Index
= CheckItemToIndex(infoPtr
,
572 VisibleFirst
= GetScrollPos(infoPtr
->hSelf
,
575 if (Index
>= VisibleFirst
)
577 GetClientRect(infoPtr
->hSelf
,
580 VisibleItems
= ((rcClient
.bottom
- rcClient
.top
) + infoPtr
->ItemHeight
- 1) / infoPtr
->ItemHeight
;
582 if (Index
<= VisibleFirst
+ VisibleItems
)
586 rcUpdate
.left
= rcClient
.left
;
587 rcUpdate
.right
= rcClient
.right
;
588 rcUpdate
.top
= (Index
- VisibleFirst
) * infoPtr
->ItemHeight
;
589 rcUpdate
.bottom
= rcUpdate
.top
+ infoPtr
->ItemHeight
;
591 RedrawWindow(infoPtr
->hSelf
,
594 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
601 MakeCheckItemVisible(IN PCHECKLISTWND infoPtr
,
605 INT VisibleFirst
, VisibleItems
, NewPos
;
606 INT Index
= CheckItemToIndex(infoPtr
,
610 VisibleFirst
= GetScrollPos(infoPtr
->hSelf
,
613 if (Index
<= VisibleFirst
)
619 GetClientRect(infoPtr
->hSelf
,
622 VisibleItems
= (rcClient
.bottom
- rcClient
.top
) / infoPtr
->ItemHeight
;
623 if (Index
- VisibleItems
+ 1 > VisibleFirst
)
625 NewPos
= Index
- VisibleItems
+ 1;
629 NewPos
= VisibleFirst
;
633 if (VisibleFirst
!= NewPos
)
635 SCROLLINFO ScrollInfo
;
637 ScrollInfo
.cbSize
= sizeof(ScrollInfo
);
638 ScrollInfo
.fMask
= SIF_POS
;
639 ScrollInfo
.nPos
= NewPos
;
640 NewPos
= SetScrollInfo(infoPtr
->hSelf
,
645 if (VisibleFirst
!= NewPos
)
647 ScrollWindowEx(infoPtr
->hSelf
,
649 (NewPos
- VisibleFirst
) * infoPtr
->ItemHeight
,
654 SW_INVALIDATE
| SW_SCROLLCHILDREN
);
656 RedrawWindow(infoPtr
->hSelf
,
659 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
666 GetIdealItemHeight(IN PCHECKLISTWND infoPtr
)
668 HDC hdc
= GetDC(infoPtr
->hSelf
);
673 HGDIOBJ hOldFont
= SelectObject(hdc
,
676 if(GetTextMetrics(hdc
,
679 height
= tm
.tmHeight
;
689 ReleaseDC(infoPtr
->hSelf
,
698 RetChangeControlFont(IN PCHECKLISTWND infoPtr
,
702 HFONT hOldFont
= infoPtr
->hFont
;
703 infoPtr
->hFont
= hFont
;
705 if (hOldFont
!= hFont
)
707 infoPtr
->ItemHeight
= (2 * CI_TEXT_MARGIN_HEIGHT
) + GetIdealItemHeight(infoPtr
);
710 if (infoPtr
->ShowingCaret
)
713 CreateCaret(infoPtr
->hSelf
,
716 infoPtr
->ItemHeight
- (2 * CI_TEXT_MARGIN_HEIGHT
));
719 UpdateControl(infoPtr
);
726 CalculateCheckBoxStyle(IN BOOL Checked
,
735 BtnState
= (Enabled
?
736 (Pushed
? CBS_CHECKEDPRESSED
: (HotTrack
? CBS_CHECKEDHOT
: CBS_CHECKEDNORMAL
)) :
737 CBS_CHECKEDDISABLED
);
741 BtnState
= (Enabled
?
742 (Pushed
? CBS_UNCHECKEDPRESSED
: (HotTrack
? CBS_UNCHECKEDHOT
: CBS_UNCHECKEDNORMAL
)) :
743 CBS_UNCHECKEDDISABLED
);
751 PaintControl(IN PCHECKLISTWND infoPtr
,
756 PCHECKITEM FirstItem
, Item
;
758 UINT VisibleFirstIndex
= rcUpdate
->top
/ infoPtr
->ItemHeight
;
759 UINT LastTouchedIndex
= rcUpdate
->bottom
/ infoPtr
->ItemHeight
;
763 (HBRUSH
)(COLOR_WINDOW
+ 1));
765 GetClientRect(infoPtr
->hSelf
, &rcClient
);
767 ScrollPos
= GetScrollPos(infoPtr
->hSelf
,
770 FirstItem
= FindCheckItemByIndex(infoPtr
,
771 ScrollPos
+ VisibleFirstIndex
);
772 if (FirstItem
!= NULL
)
774 RECT TextRect
, ItemRect
, CheckBox
;
777 COLORREF OldTextColor
;
778 BOOL Enabled
, PrevEnabled
, IsPushed
;
785 Enabled
= IsWindowEnabled(infoPtr
->hSelf
);
786 PrevEnabled
= Enabled
;
789 ItemRect
.right
= rcClient
.right
;
790 ItemRect
.top
= VisibleFirstIndex
* infoPtr
->ItemHeight
;
792 TextRect
.left
= ItemRect
.left
+ CI_TEXT_MARGIN_WIDTH
;
793 TextRect
.right
= ItemRect
.right
- CI_TEXT_MARGIN_WIDTH
;
794 TextRect
.top
= ItemRect
.top
+ CI_TEXT_MARGIN_HEIGHT
;
801 OldTextColor
= SetTextColor(hDC
,
802 infoPtr
->TextColor
[Enabled
]);
804 hOldFont
= SelectObject(hDC
,
807 for (Item
= FirstItem
, CurrentIndex
= VisibleFirstIndex
;
808 Item
!= NULL
&& CurrentIndex
<= LastTouchedIndex
;
809 Item
= Item
->Next
, CurrentIndex
++)
811 TextRect
.bottom
= TextRect
.top
+ infoPtr
->ItemHeight
- (2 * CI_TEXT_MARGIN_HEIGHT
);
812 ItemRect
.bottom
= ItemRect
.top
+ infoPtr
->ItemHeight
;
819 if (Enabled
&& PrevEnabled
!= ((Item
->State
& CIS_DISABLED
) != CIS_DISABLED
))
821 PrevEnabled
= ((Item
->State
& CIS_DISABLED
) != CIS_DISABLED
);
824 infoPtr
->TextColor
[PrevEnabled
]);
828 ItemHovered
= (Enabled
&& infoPtr
->HoveredCheckItem
== Item
);
831 if (infoPtr
->QuickSearchHitItem
== Item
)
833 COLORREF OldBkColor
, OldFgColor
;
835 SIZE_T TextLen
, HighlightLen
= wcslen(infoPtr
->QuickSearchText
);
837 /* highlight the quicksearch text */
838 if (GetTextExtentPoint32(hDC
,
843 COLORREF HighlightTextColor
, HighlightBackground
;
844 RECT rcHighlight
= TextRect
;
846 HighlightTextColor
= GetSysColor(COLOR_HIGHLIGHTTEXT
);
847 HighlightBackground
= GetSysColor(COLOR_HIGHLIGHT
);
849 rcHighlight
.right
= rcHighlight
.left
+ TextSize
.cx
;
851 InflateRect(&rcHighlight
,
853 CI_TEXT_SELECTIONMARGIN
);
855 OldBkColor
= SetBkColor(hDC
,
856 HighlightBackground
);
857 OldFgColor
= SetTextColor(hDC
,
860 /* draw the highlighted text */
865 DT_LEFT
| DT_NOPREFIX
| DT_SINGLELINE
| DT_VCENTER
);
872 /* draw the remaining part of the text */
873 TextLen
= wcslen(Item
->Name
);
874 if (HighlightLen
< TextLen
)
876 rcHighlight
.left
= rcHighlight
.right
;
877 rcHighlight
.right
= TextRect
.right
;
880 Item
->Name
+ HighlightLen
,
883 DT_LEFT
| DT_NOPREFIX
| DT_SINGLELINE
| DT_VCENTER
);
894 DT_LEFT
| DT_NOPREFIX
| DT_SINGLELINE
| DT_VCENTER
);
897 CheckBox
.top
= TextRect
.top
;
898 CheckBox
.bottom
= TextRect
.bottom
;
900 /* draw the Allow checkbox */
901 IsPushed
= (Enabled
&& Item
== infoPtr
->FocusedCheckItem
&& infoPtr
->HasFocus
&&
902 !(Item
->State
& CIS_ALLOWDISABLED
) && infoPtr
->FocusedCheckItemBox
!= CLB_DENY
&&
903 infoPtr
->FocusedPushed
);
905 CheckBox
.left
= infoPtr
->CheckBoxLeft
[CLB_ALLOW
] - ((TextRect
.bottom
- TextRect
.top
) / 2);
906 CheckBox
.right
= CheckBox
.left
+ (TextRect
.bottom
- TextRect
.top
);
908 if (infoPtr
->ThemeHandle
!= NULL
)
910 INT BtnState
= CalculateCheckBoxStyle(Item
->State
& CIS_ALLOW
,
911 Enabled
&& !(Item
->State
& CIS_ALLOWDISABLED
),
912 (ItemHovered
&& infoPtr
->HoveredCheckItemBox
!= CLB_DENY
),
916 hDrawResult
= DrawThemeBackground(infoPtr
->ThemeHandle
,
926 hDrawResult
= E_FAIL
;
929 /* draw the standard checkbox if no themes are enabled or drawing the
930 themeed control failed */
931 if (FAILED(hDrawResult
))
934 DrawFrameControl(hDC
,
937 DFCS_BUTTONCHECK
| DFCS_FLAT
|
938 ((Item
->State
& CIS_ALLOWDISABLED
) || !Enabled
? DFCS_INACTIVE
: 0) |
939 ((Item
->State
& CIS_ALLOW
) ? DFCS_CHECKED
: 0) |
940 (IsPushed
? DFCS_PUSHED
: 0));
942 if (Item
== infoPtr
->FocusedCheckItem
&& infoPtr
->FocusVisible
&&
944 infoPtr
->FocusedCheckItemBox
!= CLB_DENY
)
946 RECT rcFocus
= CheckBox
;
948 InflateRect (&rcFocus
,
949 CI_TEXT_MARGIN_HEIGHT
,
950 CI_TEXT_MARGIN_HEIGHT
);
956 /* draw the Deny checkbox */
957 IsPushed
= (Enabled
&& Item
== infoPtr
->FocusedCheckItem
&& infoPtr
->HasFocus
&&
958 !(Item
->State
& CIS_DENYDISABLED
) && infoPtr
->FocusedCheckItemBox
== CLB_DENY
&&
959 infoPtr
->FocusedPushed
);
961 CheckBox
.left
= infoPtr
->CheckBoxLeft
[CLB_DENY
] - ((TextRect
.bottom
- TextRect
.top
) / 2);
962 CheckBox
.right
= CheckBox
.left
+ (TextRect
.bottom
- TextRect
.top
);
964 if (infoPtr
->ThemeHandle
!= NULL
)
966 INT BtnState
= CalculateCheckBoxStyle(Item
->State
& CIS_DENY
,
967 Enabled
&& !(Item
->State
& CIS_DENYDISABLED
),
968 (ItemHovered
&& infoPtr
->HoveredCheckItemBox
== CLB_DENY
),
971 hDrawResult
= DrawThemeBackground(infoPtr
->ThemeHandle
,
981 hDrawResult
= E_FAIL
;
984 /* draw the standard checkbox if no themes are enabled or drawing the
985 themeed control failed */
986 if (FAILED(hDrawResult
))
989 DrawFrameControl(hDC
,
992 DFCS_BUTTONCHECK
| DFCS_FLAT
|
993 ((Item
->State
& CIS_DENYDISABLED
) || !Enabled
? DFCS_INACTIVE
: 0) |
994 ((Item
->State
& CIS_DENY
) ? DFCS_CHECKED
: 0) |
995 (IsPushed
? DFCS_PUSHED
: 0));
997 if (infoPtr
->HasFocus
&& infoPtr
->FocusVisible
&&
998 Item
== infoPtr
->FocusedCheckItem
&&
999 infoPtr
->FocusedCheckItemBox
== CLB_DENY
)
1001 RECT rcFocus
= CheckBox
;
1003 InflateRect (&rcFocus
,
1004 CI_TEXT_MARGIN_HEIGHT
,
1005 CI_TEXT_MARGIN_HEIGHT
);
1011 TextRect
.top
+= infoPtr
->ItemHeight
;
1012 ItemRect
.top
+= infoPtr
->ItemHeight
;
1029 ChangeCheckItemFocus(IN PCHECKLISTWND infoPtr
,
1030 IN PCHECKITEM NewFocus
,
1031 IN UINT NewFocusBox
)
1033 if (NewFocus
!= infoPtr
->FocusedCheckItem
)
1035 PCHECKITEM OldFocus
= infoPtr
->FocusedCheckItem
;
1036 infoPtr
->FocusedCheckItem
= NewFocus
;
1037 infoPtr
->FocusedCheckItemBox
= NewFocusBox
;
1039 if (OldFocus
!= NULL
)
1041 UpdateCheckItem(infoPtr
,
1047 infoPtr
->FocusedCheckItemBox
= NewFocusBox
;
1050 if (NewFocus
!= NULL
)
1052 MakeCheckItemVisible(infoPtr
,
1054 UpdateCheckItem(infoPtr
,
1060 UpdateCheckItemBox(IN PCHECKLISTWND infoPtr
,
1065 INT VisibleFirst
, VisibleItems
;
1066 INT Index
= CheckItemToIndex(infoPtr
,
1070 VisibleFirst
= GetScrollPos(infoPtr
->hSelf
,
1073 if (Index
>= VisibleFirst
)
1075 GetClientRect(infoPtr
->hSelf
,
1078 VisibleItems
= ((rcClient
.bottom
- rcClient
.top
) + infoPtr
->ItemHeight
- 1) / infoPtr
->ItemHeight
;
1080 if (Index
<= VisibleFirst
+ VisibleItems
)
1084 rcUpdate
.left
= rcClient
.left
+ infoPtr
->CheckBoxLeft
[ItemBox
] - (infoPtr
->ItemHeight
/ 2);
1085 rcUpdate
.right
= rcUpdate
.left
+ infoPtr
->ItemHeight
;
1086 rcUpdate
.top
= ((Index
- VisibleFirst
) * infoPtr
->ItemHeight
);
1087 rcUpdate
.bottom
= rcUpdate
.top
+ infoPtr
->ItemHeight
;
1089 RedrawWindow(infoPtr
->hSelf
,
1092 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1100 ChangeCheckItemHotTrack(IN PCHECKLISTWND infoPtr
,
1101 IN PCHECKITEM NewHotTrack
,
1102 IN UINT NewHotTrackBox
)
1104 if (NewHotTrack
!= infoPtr
->HoveredCheckItem
)
1106 PCHECKITEM OldHotTrack
= infoPtr
->HoveredCheckItem
;
1107 UINT OldHotTrackBox
= infoPtr
->HoveredCheckItemBox
;
1109 infoPtr
->HoveredCheckItem
= NewHotTrack
;
1110 infoPtr
->HoveredCheckItemBox
= NewHotTrackBox
;
1112 if (OldHotTrack
!= NULL
)
1114 UpdateCheckItemBox(infoPtr
,
1121 infoPtr
->HoveredCheckItemBox
= NewHotTrackBox
;
1124 if (NewHotTrack
!= NULL
)
1126 UpdateCheckItemBox(infoPtr
,
1134 ChangeCheckBox(IN PCHECKLISTWND infoPtr
,
1135 IN PCHECKITEM CheckItem
,
1136 IN UINT CheckItemBox
)
1138 NMCHANGEITEMCHECKBOX CheckData
;
1139 DWORD OldState
= CheckItem
->State
;
1140 DWORD CheckedBit
= ((infoPtr
->FocusedCheckItemBox
== CLB_DENY
) ? CIS_DENY
: CIS_ALLOW
);
1141 BOOL Checked
= (CheckItem
->State
& CheckedBit
) != 0;
1143 CheckData
.OldState
= OldState
;
1144 CheckData
.NewState
= (Checked
? OldState
& ~CheckedBit
: OldState
| CheckedBit
);
1145 CheckData
.CheckBox
= infoPtr
->FocusedCheckItemBox
;
1146 CheckData
.Checked
= !Checked
;
1148 if (NotifyControlParent(infoPtr
,
1149 CLN_CHANGINGITEMCHECKBOX
,
1150 &CheckData
) != (LRESULT
)-1)
1152 CheckItem
->State
= CheckData
.NewState
;
1155 return (CheckItem
->State
!= OldState
);
1159 DisplayCaret(IN PCHECKLISTWND infoPtr
)
1161 if (IsWindowEnabled(infoPtr
->hSelf
) && !infoPtr
->ShowingCaret
)
1163 infoPtr
->ShowingCaret
= TRUE
;
1165 CreateCaret(infoPtr
->hSelf
,
1167 infoPtr
->CaretWidth
,
1168 infoPtr
->ItemHeight
- (2 * CI_TEXT_MARGIN_HEIGHT
));
1170 ShowCaret(infoPtr
->hSelf
);
1175 RemoveCaret(IN PCHECKLISTWND infoPtr
)
1177 if (IsWindowEnabled(infoPtr
->hSelf
) && infoPtr
->ShowingCaret
)
1179 infoPtr
->ShowingCaret
= FALSE
;
1181 HideCaret(infoPtr
->hSelf
);
1187 KillQuickSearchTimers(IN PCHECKLISTWND infoPtr
)
1189 KillTimer(infoPtr
->hSelf
,
1190 TIMER_ID_SETHITFOCUS
);
1191 KillTimer(infoPtr
->hSelf
,
1192 TIMER_ID_RESETQUICKSEARCH
);
1196 MapItemToRect(IN PCHECKLISTWND infoPtr
,
1197 IN PCHECKITEM CheckItem
,
1200 INT Index
= CheckItemToIndex(infoPtr
,
1207 GetClientRect(infoPtr
->hSelf
, &rcClient
);
1209 VisibleFirst
= GetScrollPos(infoPtr
->hSelf
,
1212 prcItem
->left
= rcClient
.left
;
1213 prcItem
->right
= rcClient
.right
;
1214 prcItem
->top
= (Index
- VisibleFirst
) * infoPtr
->ItemHeight
;
1215 prcItem
->bottom
= prcItem
->top
+ infoPtr
->ItemHeight
;
1222 prcItem
->bottom
= 0;
1227 UpdateCaretPos(IN PCHECKLISTWND infoPtr
)
1229 if (infoPtr
->ShowingCaret
&& infoPtr
->QuickSearchHitItem
!= NULL
)
1231 HDC hDC
= GetDC(infoPtr
->hSelf
);
1235 HGDIOBJ hOldFont
= SelectObject(hDC
,
1241 if (infoPtr
->QuickSearchText
[0] == L
'\0' ||
1242 GetTextExtentPoint32(hDC
,
1243 infoPtr
->QuickSearchHitItem
->Name
,
1244 wcslen(infoPtr
->QuickSearchText
),
1249 MapItemToRect(infoPtr
,
1250 infoPtr
->QuickSearchHitItem
,
1253 /* actually change the caret position */
1254 SetCaretPos(rcItem
.left
+ CI_TEXT_MARGIN_WIDTH
+ TextSize
.cx
,
1255 rcItem
.top
+ CI_TEXT_MARGIN_HEIGHT
);
1261 ReleaseDC(infoPtr
->hSelf
,
1268 EscapeQuickSearch(IN PCHECKLISTWND infoPtr
)
1270 if (infoPtr
->QuickSearchEnabled
&& infoPtr
->QuickSearchHitItem
!= NULL
)
1272 PCHECKITEM OldHit
= infoPtr
->QuickSearchHitItem
;
1274 infoPtr
->QuickSearchHitItem
= NULL
;
1275 infoPtr
->QuickSearchText
[0] = L
'\0';
1277 /* scroll back to the focused item */
1278 if (infoPtr
->FocusedCheckItem
!= NULL
)
1280 MakeCheckItemVisible(infoPtr
,
1281 infoPtr
->FocusedCheckItem
);
1284 /* repaint the old search hit item if it's still visible */
1285 UpdateCheckItem(infoPtr
,
1288 KillQuickSearchTimers(infoPtr
);
1290 RemoveCaret(infoPtr
);
1295 ChangeSearchHit(IN PCHECKLISTWND infoPtr
,
1296 IN PCHECKITEM NewHit
)
1298 PCHECKITEM OldHit
= infoPtr
->QuickSearchHitItem
;
1300 infoPtr
->QuickSearchHitItem
= NewHit
;
1302 if (OldHit
!= NewHit
)
1304 /* scroll to the new search hit */
1305 MakeCheckItemVisible(infoPtr
,
1308 /* repaint the old hit if present and visible */
1311 UpdateCheckItem(infoPtr
,
1316 /* show the caret the first time we find an item */
1317 DisplayCaret(infoPtr
);
1321 UpdateCaretPos(infoPtr
);
1323 UpdateCheckItem(infoPtr
,
1326 /* kill the reset timer and restart the set hit focus timer */
1327 KillTimer(infoPtr
->hSelf
,
1328 TIMER_ID_RESETQUICKSEARCH
);
1329 if (infoPtr
->QuickSearchSetFocusDelay
!= 0)
1331 SetTimer(infoPtr
->hSelf
,
1332 TIMER_ID_SETHITFOCUS
,
1333 infoPtr
->QuickSearchSetFocusDelay
,
1339 QuickSearchFindHit(IN PCHECKLISTWND infoPtr
,
1342 if (infoPtr
->QuickSearchEnabled
)
1352 Ret
= infoPtr
->QuickSearchHitItem
!= NULL
;
1355 /* NOTE: QuickSearchHitItem definitely has at least one
1356 enabled check box, the user can't search for disabled
1359 ChangeCheckItemFocus(infoPtr
,
1360 infoPtr
->QuickSearchHitItem
,
1361 ((!(infoPtr
->QuickSearchHitItem
->State
& CIS_ALLOWDISABLED
)) ? CLB_ALLOW
: CLB_DENY
));
1363 EscapeQuickSearch(infoPtr
);
1370 if (infoPtr
->QuickSearchHitItem
!= NULL
)
1372 INT SearchLen
= wcslen(infoPtr
->QuickSearchText
);
1375 /* delete the last character */
1376 infoPtr
->QuickSearchText
[--SearchLen
] = L
'\0';
1381 NewHit
= FindCheckItem(infoPtr
,
1382 infoPtr
->QuickSearchText
);
1386 /* change the search hit */
1387 ChangeSearchHit(infoPtr
,
1397 EscapeQuickSearch(infoPtr
);
1405 INT SearchLen
= wcslen(infoPtr
->QuickSearchText
);
1406 if (SearchLen
< (INT
)(sizeof(infoPtr
->QuickSearchText
) / sizeof(infoPtr
->QuickSearchText
[0])) - 1)
1408 infoPtr
->QuickSearchText
[SearchLen
++] = c
;
1409 infoPtr
->QuickSearchText
[SearchLen
] = L
'\0';
1411 NewHit
= FindCheckItem(infoPtr
,
1412 infoPtr
->QuickSearchText
);
1415 /* change the search hit */
1416 ChangeSearchHit(infoPtr
,
1423 /* reset the input */
1424 infoPtr
->QuickSearchText
[--SearchLen
] = L
'\0';
1436 static LRESULT CALLBACK
1437 CheckListWndProc(IN HWND hwnd
,
1442 PCHECKLISTWND infoPtr
;
1445 infoPtr
= (PCHECKLISTWND
)GetWindowLongPtr(hwnd
,
1448 if (infoPtr
== NULL
&& uMsg
!= WM_CREATE
)
1450 return DefWindowProc(hwnd
,
1466 if (GetUpdateRect(hwnd
,
1470 hdc
= (wParam
!= 0 ? (HDC
)wParam
: BeginPaint(hwnd
, &ps
));
1472 PaintControl(infoPtr
,
1489 HWND hWndCapture
= GetCapture();
1491 pt
.x
= (LONG
)LOWORD(lParam
);
1492 pt
.y
= (LONG
)HIWORD(lParam
);
1495 /* handle hovering checkboxes */
1496 if (hWndCapture
== NULL
&& infoPtr
->ThemeHandle
!= NULL
)
1498 TRACKMOUSEEVENT tme
;
1499 PCHECKITEM HotTrackItem
;
1500 UINT HotTrackItemBox
;
1502 HotTrackItem
= PtToCheckItemBox(infoPtr
,
1506 if (HotTrackItem
!= NULL
&& InCheckBox
)
1508 if (infoPtr
->HoveredCheckItem
!= HotTrackItem
||
1509 infoPtr
->HoveredCheckItemBox
!= HotTrackItemBox
)
1511 ChangeCheckItemHotTrack(infoPtr
,
1518 ChangeCheckItemHotTrack(infoPtr
,
1523 tme
.cbSize
= sizeof(tme
);
1524 tme
.dwFlags
= TME_LEAVE
;
1525 tme
.hwndTrack
= hwnd
;
1526 tme
.dwHoverTime
= infoPtr
->HoverTime
;
1528 TrackMouseEvent(&tme
);
1532 if (hWndCapture
== hwnd
&& infoPtr
->FocusedCheckItem
!= NULL
)
1538 PtItem
= PtToCheckItemBox(infoPtr
,
1543 OldPushed
= infoPtr
->FocusedPushed
;
1544 infoPtr
->FocusedPushed
= InCheckBox
&& infoPtr
->FocusedCheckItem
== PtItem
&&
1545 infoPtr
->FocusedCheckItemBox
== PtItemBox
;
1547 if (OldPushed
!= infoPtr
->FocusedPushed
)
1549 UpdateCheckItemBox(infoPtr
,
1550 infoPtr
->FocusedCheckItem
,
1551 infoPtr
->FocusedCheckItemBox
);
1560 SCROLLINFO ScrollInfo
;
1562 ScrollInfo
.cbSize
= sizeof(ScrollInfo
);
1563 ScrollInfo
.fMask
= SIF_RANGE
| SIF_POS
;
1565 if (GetScrollInfo(hwnd
,
1569 INT OldPos
= ScrollInfo
.nPos
;
1571 switch (LOWORD(wParam
))
1574 ScrollInfo
.nPos
= ScrollInfo
.nMax
;
1578 if (ScrollInfo
.nPos
< ScrollInfo
.nMax
)
1585 if (ScrollInfo
.nPos
> 0)
1596 /* don't use ScrollInfo.nPage because we should only scroll
1597 down by the number of completely visible list entries.
1598 nPage however also includes the partly cropped list
1599 item at the bottom of the control */
1601 GetClientRect(hwnd
, &rcClient
);
1602 ScrollLines
= max(1, (rcClient
.bottom
- rcClient
.top
) / infoPtr
->ItemHeight
);
1604 if (ScrollInfo
.nPos
+ ScrollLines
<= ScrollInfo
.nMax
)
1606 ScrollInfo
.nPos
+= ScrollLines
;
1610 ScrollInfo
.nPos
= ScrollInfo
.nMax
;
1620 /* don't use ScrollInfo.nPage because we should only scroll
1621 down by the number of completely visible list entries.
1622 nPage however also includes the partly cropped list
1623 item at the bottom of the control */
1625 GetClientRect(hwnd
, &rcClient
);
1626 ScrollLines
= max(1, (rcClient
.bottom
- rcClient
.top
) / infoPtr
->ItemHeight
);
1628 if (ScrollInfo
.nPos
>= ScrollLines
)
1630 ScrollInfo
.nPos
-= ScrollLines
;
1634 ScrollInfo
.nPos
= 0;
1639 case SB_THUMBPOSITION
:
1642 ScrollInfo
.nPos
= HIWORD(wParam
);
1647 ScrollInfo
.nPos
= 0;
1651 if (OldPos
!= ScrollInfo
.nPos
)
1653 ScrollInfo
.fMask
= SIF_POS
;
1655 ScrollInfo
.nPos
= SetScrollInfo(hwnd
,
1660 if (OldPos
!= ScrollInfo
.nPos
)
1662 ScrollWindowEx(hwnd
,
1664 (OldPos
- ScrollInfo
.nPos
) * infoPtr
->ItemHeight
,
1669 SW_INVALIDATE
| SW_SCROLLCHILDREN
);
1674 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1684 PCHECKITEM Item
= AddCheckItem(infoPtr
,
1690 UpdateControl(infoPtr
);
1691 Ret
= (LRESULT
)Index
;
1702 PCHECKITEM Item
= FindCheckItemByIndex(infoPtr
,
1706 Ret
= DeleteCheckItem(infoPtr
,
1710 UpdateControl(infoPtr
);
1720 case CLM_SETITEMSTATE
:
1722 PCHECKITEM Item
= FindCheckItemByIndex(infoPtr
,
1726 DWORD OldState
= Item
->State
;
1727 Item
->State
= (DWORD
)lParam
& CIS_MASK
;
1729 if (Item
->State
!= OldState
)
1731 /* revert the focus if the currently focused item is about
1733 if (Item
== infoPtr
->FocusedCheckItem
&&
1734 (Item
->State
& CIS_DISABLED
))
1736 if (infoPtr
->FocusedCheckItemBox
== CLB_DENY
)
1738 if (Item
->State
& CIS_DENYDISABLED
)
1740 infoPtr
->FocusedCheckItem
= NULL
;
1745 if (Item
->State
& CIS_ALLOWDISABLED
)
1747 infoPtr
->FocusedCheckItem
= NULL
;
1752 UpdateControl(infoPtr
);
1763 case CLM_GETITEMCOUNT
:
1765 Ret
= infoPtr
->CheckItemCount
;
1771 ClearCheckItems(infoPtr
);
1772 UpdateControl(infoPtr
);
1776 case CLM_SETCHECKBOXCOLUMN
:
1778 infoPtr
->CheckBoxLeft
[wParam
!= CLB_DENY
] = (INT
)lParam
;
1779 UpdateControl(infoPtr
);
1784 case CLM_GETCHECKBOXCOLUMN
:
1786 Ret
= (LRESULT
)infoPtr
->CheckBoxLeft
[wParam
!= CLB_DENY
];
1790 case CLM_CLEARCHECKBOXES
:
1792 Ret
= (LRESULT
)ClearCheckBoxes(infoPtr
);
1795 UpdateControl(infoPtr
);
1800 case CLM_ENABLEQUICKSEARCH
:
1804 EscapeQuickSearch(infoPtr
);
1806 infoPtr
->QuickSearchEnabled
= (wParam
!= 0);
1810 case CLM_SETQUICKSEARCH_TIMEOUT_RESET
:
1812 infoPtr
->QuickSearchResetDelay
= (UINT
)wParam
;
1816 case CLM_SETQUICKSEARCH_TIMEOUT_SETFOCUS
:
1818 infoPtr
->QuickSearchSetFocusDelay
= (UINT
)wParam
;
1824 Ret
= (LRESULT
)RetChangeControlFont(infoPtr
,
1832 Ret
= (LRESULT
)infoPtr
->hFont
;
1836 case WM_STYLECHANGED
:
1838 if (wParam
== (WPARAM
)GWL_STYLE
)
1840 UpdateControl(infoPtr
);
1847 EscapeQuickSearch(infoPtr
);
1849 UpdateControl(infoPtr
);
1856 UINT ScrollLines
= 3;
1858 SystemParametersInfo(SPI_GETWHEELSCROLLLINES
,
1862 ScrollDelta
= 0 - (SHORT
)HIWORD(wParam
);
1864 if (ScrollLines
!= 0 &&
1865 abs(ScrollDelta
) >= WHEEL_DELTA
)
1867 SCROLLINFO ScrollInfo
;
1869 ScrollInfo
.cbSize
= sizeof(ScrollInfo
);
1870 ScrollInfo
.fMask
= SIF_RANGE
| SIF_POS
;
1872 if (GetScrollInfo(hwnd
,
1876 INT OldPos
= ScrollInfo
.nPos
;
1878 ScrollInfo
.nPos
+= (ScrollDelta
/ WHEEL_DELTA
) * ScrollLines
;
1879 if (ScrollInfo
.nPos
< 0)
1880 ScrollInfo
.nPos
= 0;
1881 else if (ScrollInfo
.nPos
> ScrollInfo
.nMax
)
1882 ScrollInfo
.nPos
= ScrollInfo
.nMax
;
1884 if (OldPos
!= ScrollInfo
.nPos
)
1886 ScrollInfo
.fMask
= SIF_POS
;
1888 ScrollInfo
.nPos
= SetScrollInfo(hwnd
,
1893 if (OldPos
!= ScrollInfo
.nPos
)
1895 ScrollWindowEx(hwnd
,
1897 (OldPos
- ScrollInfo
.nPos
) * infoPtr
->ItemHeight
,
1902 SW_INVALIDATE
| SW_SCROLLCHILDREN
);
1907 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1917 infoPtr
->HasFocus
= TRUE
;
1919 if (infoPtr
->FocusedCheckItem
== NULL
)
1921 BOOL Shift
= GetKeyState(VK_SHIFT
) & 0x8000;
1922 infoPtr
->FocusedCheckItem
= FindEnabledCheckBox(infoPtr
,
1924 &infoPtr
->FocusedCheckItemBox
);
1926 if (infoPtr
->FocusedCheckItem
!= NULL
)
1928 MakeCheckItemVisible(infoPtr
,
1929 infoPtr
->FocusedCheckItem
);
1931 UpdateCheckItem(infoPtr
,
1932 infoPtr
->FocusedCheckItem
);
1939 EscapeQuickSearch(infoPtr
);
1941 infoPtr
->HasFocus
= FALSE
;
1942 if (infoPtr
->FocusedCheckItem
!= NULL
)
1944 infoPtr
->FocusedPushed
= FALSE
;
1946 UpdateCheckItem(infoPtr
,
1947 infoPtr
->FocusedCheckItem
);
1952 case WM_LBUTTONDBLCLK
:
1953 case WM_LBUTTONDOWN
:
1954 case WM_MBUTTONDOWN
:
1955 case WM_RBUTTONDOWN
:
1957 if (IsWindowEnabled(hwnd
))
1959 PCHECKITEM NewFocus
;
1960 UINT NewFocusBox
= 0;
1963 BOOL ChangeFocus
, Capture
= FALSE
;
1965 pt
.x
= (LONG
)LOWORD(lParam
);
1966 pt
.y
= (LONG
)HIWORD(lParam
);
1968 NewFocus
= PtToCheckItemBox(infoPtr
,
1972 if (NewFocus
!= NULL
)
1974 if (NewFocus
->State
& ((NewFocusBox
!= CLB_DENY
) ? CIS_ALLOWDISABLED
: CIS_DENYDISABLED
))
1976 /* the user clicked on a disabled checkbox, try to set
1977 the focus to the other one or not change it at all */
1981 ChangeFocus
= ((NewFocus
->State
& CIS_DISABLED
) != CIS_DISABLED
);
1984 NewFocusBox
= ((NewFocusBox
!= CLB_DENY
) ? CLB_DENY
: CLB_ALLOW
);
1992 if (InCheckBox
&& ChangeFocus
&& GetCapture() == NULL
&&
1993 (uMsg
== WM_LBUTTONDOWN
|| uMsg
== WM_LBUTTONDBLCLK
))
1995 infoPtr
->FocusedPushed
= TRUE
;
2006 if (infoPtr
->QuickSearchEnabled
&& infoPtr
->QuickSearchHitItem
!= NULL
&&
2007 infoPtr
->QuickSearchHitItem
!= NewFocus
)
2009 EscapeQuickSearch(infoPtr
);
2012 ChangeCheckItemFocus(infoPtr
,
2017 if (!infoPtr
->HasFocus
)
2032 if (GetCapture() == hwnd
)
2034 if (infoPtr
->FocusedCheckItem
!= NULL
&& infoPtr
->FocusedPushed
)
2041 pt
.x
= (LONG
)LOWORD(lParam
);
2042 pt
.y
= (LONG
)HIWORD(lParam
);
2044 infoPtr
->FocusedPushed
= FALSE
;
2046 PtItem
= PtToCheckItemBox(infoPtr
,
2051 if (PtItem
== infoPtr
->FocusedCheckItem
&& InCheckBox
&&
2052 PtItemBox
== infoPtr
->FocusedCheckItemBox
)
2054 UINT OtherBox
= ((PtItemBox
== CLB_ALLOW
) ? CLB_DENY
: CLB_ALLOW
);
2055 DWORD OtherStateMask
= ((OtherBox
== CLB_ALLOW
) ?
2056 (CIS_ALLOW
| CIS_ALLOWDISABLED
) :
2057 (CIS_DENY
| CIS_DENYDISABLED
));
2058 DWORD OtherStateOld
= PtItem
->State
& OtherStateMask
;
2059 if (ChangeCheckBox(infoPtr
,
2062 ((PtItem
->State
& OtherStateMask
) != OtherStateOld
))
2064 UpdateCheckItemBox(infoPtr
,
2065 infoPtr
->FocusedCheckItem
,
2070 UpdateCheckItemBox(infoPtr
,
2071 infoPtr
->FocusedCheckItem
,
2072 infoPtr
->FocusedCheckItemBox
);
2086 if (GetCapture() == NULL
&&
2087 !QuickSearchFindHit(infoPtr
,
2090 if (infoPtr
->FocusedCheckItem
!= NULL
&&
2091 (infoPtr
->QuickSearchHitItem
== NULL
||
2092 infoPtr
->QuickSearchHitItem
== infoPtr
->FocusedCheckItem
))
2094 BOOL OldPushed
= infoPtr
->FocusedPushed
;
2095 infoPtr
->FocusedPushed
= TRUE
;
2097 if (infoPtr
->FocusedPushed
!= OldPushed
)
2099 MakeCheckItemVisible(infoPtr
,
2100 infoPtr
->FocusedCheckItem
);
2102 UpdateCheckItemBox(infoPtr
,
2103 infoPtr
->FocusedCheckItem
,
2104 infoPtr
->FocusedCheckItemBox
);
2113 if (GetCapture() == NULL
&&
2114 !QuickSearchFindHit(infoPtr
,
2117 if (infoPtr
->FocusedCheckItem
!= NULL
&&
2118 infoPtr
->QuickSearchHitItem
== NULL
)
2121 DWORD OtherStateMask
;
2122 DWORD OtherStateOld
;
2124 MakeCheckItemVisible(infoPtr
,
2125 infoPtr
->FocusedCheckItem
);
2127 OtherBox
= ((infoPtr
->FocusedCheckItemBox
== CLB_ALLOW
) ? CLB_DENY
: CLB_ALLOW
);
2128 OtherStateMask
= ((OtherBox
== CLB_ALLOW
) ?
2129 (CIS_ALLOW
| CIS_ALLOWDISABLED
) :
2130 (CIS_DENY
| CIS_DENYDISABLED
));
2131 OtherStateOld
= infoPtr
->FocusedCheckItem
->State
& OtherStateMask
;
2132 if (ChangeCheckBox(infoPtr
,
2133 infoPtr
->FocusedCheckItem
,
2134 infoPtr
->FocusedCheckItemBox
))
2136 UpdateCheckItemBox(infoPtr
,
2137 infoPtr
->FocusedCheckItem
,
2138 infoPtr
->FocusedCheckItemBox
);
2139 if ((infoPtr
->FocusedCheckItem
->State
& OtherStateMask
) != OtherStateOld
)
2141 UpdateCheckItemBox(infoPtr
,
2142 infoPtr
->FocusedCheckItem
,
2153 if (GetCapture() == NULL
)
2155 PCHECKITEM NewFocus
;
2156 UINT NewFocusBox
= 0;
2157 BOOL Shift
= GetKeyState(VK_SHIFT
) & 0x8000;
2159 EscapeQuickSearch(infoPtr
);
2161 NewFocus
= FindEnabledCheckBox(infoPtr
,
2165 if (!infoPtr
->FocusVisible
)
2167 /* change the UI status */
2168 SendMessage(GetAncestor(hwnd
, GA_ROOT
),
2170 MAKEWPARAM(UIS_INITIALIZE
, UISF_HIDEFOCUS
),
2174 ChangeCheckItemFocus(infoPtr
,
2183 Ret
= DefWindowProc(hwnd
,
2195 if (wParam
== VK_SPACE
&& IsWindowEnabled(hwnd
) &&
2196 infoPtr
->FocusedCheckItem
!= NULL
&&
2197 infoPtr
->FocusedPushed
)
2199 UINT OtherBox
= ((infoPtr
->FocusedCheckItemBox
== CLB_ALLOW
) ? CLB_DENY
: CLB_ALLOW
);
2200 DWORD OtherStateMask
= ((OtherBox
== CLB_ALLOW
) ?
2201 (CIS_ALLOW
| CIS_ALLOWDISABLED
) :
2202 (CIS_DENY
| CIS_DENYDISABLED
));
2203 DWORD OtherStateOld
= infoPtr
->FocusedCheckItem
->State
& OtherStateMask
;
2205 infoPtr
->FocusedPushed
= FALSE
;
2207 if (ChangeCheckBox(infoPtr
,
2208 infoPtr
->FocusedCheckItem
,
2209 infoPtr
->FocusedCheckItemBox
))
2211 UpdateCheckItemBox(infoPtr
,
2212 infoPtr
->FocusedCheckItem
,
2213 infoPtr
->FocusedCheckItemBox
);
2215 if ((infoPtr
->FocusedCheckItem
->State
& OtherStateMask
) != OtherStateOld
)
2217 UpdateCheckItemBox(infoPtr
,
2218 infoPtr
->FocusedCheckItem
,
2231 virtKey
= (lParam
!= 0 ? (INT
)((LPMSG
)lParam
)->wParam
: 0);
2236 if (infoPtr
->QuickSearchEnabled
&& infoPtr
->QuickSearchHitItem
!= NULL
)
2238 Ret
|= DLGC_WANTCHARS
| DLGC_WANTMESSAGE
;
2242 Ret
|= DLGC_WANTMESSAGE
;
2251 BOOL Shift
= GetKeyState(VK_SHIFT
) & 0x8000;
2253 EnabledBox
= FindEnabledCheckBox(infoPtr
,
2256 Ret
|= (EnabledBox
? DLGC_WANTTAB
: DLGC_WANTCHARS
);
2262 if (infoPtr
->QuickSearchEnabled
)
2264 Ret
|= DLGC_WANTCHARS
;
2274 QuickSearchFindHit(infoPtr
,
2279 case WM_SYSCOLORCHANGE
:
2281 infoPtr
->TextColor
[0] = GetSysColor(COLOR_GRAYTEXT
);
2282 infoPtr
->TextColor
[1] = GetSysColor(COLOR_WINDOWTEXT
);
2289 if (infoPtr
->HoveredCheckItem
!= NULL
)
2291 /* reset and repaint the hovered check item box */
2292 ChangeCheckItemHotTrack(infoPtr
,
2299 case WM_THEMECHANGED
:
2301 if (infoPtr
->ThemeHandle
!= NULL
)
2303 CloseThemeData(infoPtr
->ThemeHandle
);
2304 infoPtr
->ThemeHandle
= NULL
;
2308 infoPtr
->ThemeHandle
= OpenThemeData(infoPtr
->hSelf
,
2315 case WM_SETTINGCHANGE
:
2317 DWORD OldCaretWidth
= infoPtr
->CaretWidth
;
2320 /* update the hover time */
2321 if (!SystemParametersInfo(SPI_GETMOUSEHOVERTIME
,
2323 &infoPtr
->HoverTime
,
2326 infoPtr
->HoverTime
= HOVER_DEFAULT
;
2330 /* update the caret */
2331 if (!SystemParametersInfo(SPI_GETCARETWIDTH
,
2333 &infoPtr
->CaretWidth
,
2336 infoPtr
->CaretWidth
= 2;
2338 if (OldCaretWidth
!= infoPtr
->CaretWidth
&& infoPtr
->ShowingCaret
)
2343 infoPtr
->CaretWidth
,
2344 infoPtr
->ItemHeight
- (2 * CI_TEXT_MARGIN_HEIGHT
));
2351 UpdateControl(infoPtr
);
2355 case WM_UPDATEUISTATE
:
2357 if (HIWORD(wParam
) & UISF_HIDEFOCUS
)
2359 BOOL OldFocusVisible
= infoPtr
->FocusVisible
;
2361 infoPtr
->FocusVisible
= (LOWORD(wParam
) == UIS_CLEAR
);
2363 if (infoPtr
->FocusVisible
!= OldFocusVisible
&&
2364 infoPtr
->FocusedCheckItem
!= NULL
)
2366 UpdateCheckItemBox(infoPtr
,
2367 infoPtr
->FocusedCheckItem
,
2368 infoPtr
->FocusedCheckItemBox
);
2378 case TIMER_ID_SETHITFOCUS
:
2380 /* kill the timer */
2384 if (infoPtr
->QuickSearchEnabled
&& infoPtr
->QuickSearchHitItem
!= NULL
)
2386 /* change the focus to the hit item, this item has to have
2387 at least one enabled checkbox! */
2388 ChangeCheckItemFocus(infoPtr
,
2389 infoPtr
->QuickSearchHitItem
,
2390 ((!(infoPtr
->QuickSearchHitItem
->State
& CIS_ALLOWDISABLED
)) ? CLB_ALLOW
: CLB_DENY
));
2392 /* start the timer to reset quicksearch */
2393 if (infoPtr
->QuickSearchResetDelay
!= 0)
2396 TIMER_ID_RESETQUICKSEARCH
,
2397 infoPtr
->QuickSearchResetDelay
,
2403 case TIMER_ID_RESETQUICKSEARCH
:
2405 /* kill the timer */
2409 /* escape quick search */
2410 EscapeQuickSearch(infoPtr
);
2419 infoPtr
= HeapAlloc(GetProcessHeap(),
2421 sizeof(CHECKLISTWND
));
2422 if (infoPtr
!= NULL
)
2426 infoPtr
->hSelf
= hwnd
;
2427 infoPtr
->hNotify
= ((LPCREATESTRUCTW
)lParam
)->hwndParent
;
2429 SetWindowLongPtr(hwnd
,
2431 (DWORD_PTR
)infoPtr
);
2433 infoPtr
->CheckItemListHead
= NULL
;
2434 infoPtr
->CheckItemCount
= 0;
2436 if (!SystemParametersInfo(SPI_GETCARETWIDTH
,
2438 &infoPtr
->CaretWidth
,
2441 infoPtr
->CaretWidth
= 2;
2443 infoPtr
->ItemHeight
= 10;
2444 infoPtr
->ShowingCaret
= FALSE
;
2446 infoPtr
->HasFocus
= FALSE
;
2447 infoPtr
->FocusedCheckItem
= NULL
;
2448 infoPtr
->FocusedCheckItemBox
= 0;
2449 infoPtr
->FocusedPushed
= FALSE
;
2451 infoPtr
->TextColor
[0] = GetSysColor(COLOR_GRAYTEXT
);
2452 infoPtr
->TextColor
[1] = GetSysColor(COLOR_WINDOWTEXT
);
2454 GetClientRect(hwnd
, &rcClient
);
2456 infoPtr
->CheckBoxLeft
[0] = rcClient
.right
- 30;
2457 infoPtr
->CheckBoxLeft
[1] = rcClient
.right
- 15;
2459 infoPtr
->QuickSearchEnabled
= FALSE
;
2460 infoPtr
->QuickSearchText
[0] = L
'\0';
2462 infoPtr
->QuickSearchSetFocusDelay
= DEFAULT_QUICKSEARCH_SETFOCUS_DELAY
;
2463 infoPtr
->QuickSearchResetDelay
= DEFAULT_QUICKSEARCH_RESET_DELAY
;
2466 infoPtr
->HoveredCheckItem
= NULL
;
2467 infoPtr
->HoveredCheckItemBox
= 0;
2468 if (!SystemParametersInfo(SPI_GETMOUSEHOVERTIME
,
2470 &infoPtr
->HoverTime
,
2473 infoPtr
->HoverTime
= HOVER_DEFAULT
;
2478 infoPtr
->ThemeHandle
= OpenThemeData(infoPtr
->hSelf
,
2483 infoPtr
->ThemeHandle
= NULL
;
2487 infoPtr
->FocusVisible
= !(SendMessage(hwnd
,
2490 0) & UISF_HIDEFOCUS
);
2501 if (infoPtr
->ShowingCaret
)
2506 ClearCheckItems(infoPtr
);
2509 if (infoPtr
->ThemeHandle
!= NULL
)
2511 CloseThemeData(infoPtr
->ThemeHandle
);
2515 HeapFree(GetProcessHeap(),
2518 SetWindowLongPtr(hwnd
,
2526 Ret
= DefWindowProc(hwnd
,
2538 RegisterCheckListControl(HINSTANCE hInstance
)
2542 wc
.style
= CS_DBLCLKS
;
2543 wc
.lpfnWndProc
= CheckListWndProc
;
2545 wc
.cbWndExtra
= sizeof(PCHECKLISTWND
);
2546 wc
.hInstance
= hInstance
;
2548 wc
.hCursor
= LoadCursor(0, (LPWSTR
)IDC_ARROW
);
2549 wc
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
2550 wc
.lpszMenuName
= NULL
;
2551 wc
.lpszClassName
= L
"CHECKLIST_ACLUI";
2553 return RegisterClass(&wc
) != 0;
2557 UnregisterCheckListControl(VOID
)
2559 UnregisterClass(L
"CHECKLIST_ACLUI",