- Update address of Free Software Foundation.
[reactos.git] / reactos / dll / win32 / aclui / checklist.c
1 /*
2 * ReactOS Access Control List Editor
3 * Copyright (C) 2004-2005 ReactOS Team
4 *
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.
9 *
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.
14 *
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 /* $Id$
20 *
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>
25 *
26 * UPDATE HISTORY:
27 * 07/01/2005 Created
28 */
29 #include <precomp.h>
30
31 #define NDEBUG
32 #include <debug.h>
33
34 static const WCHAR szCheckListWndClass[] = L"CHECKLIST_ACLUI";
35
36 #define CI_TEXT_MARGIN_WIDTH (8)
37 #define CI_TEXT_MARGIN_HEIGHT (3)
38 #define CI_TEXT_SELECTIONMARGIN (1)
39
40 #define TIMER_ID_SETHITFOCUS (1)
41 #define TIMER_ID_RESETQUICKSEARCH (2)
42
43 #define DEFAULT_QUICKSEARCH_SETFOCUS_DELAY (2000)
44 #define DEFAULT_QUICKSEARCH_RESET_DELAY (3000)
45
46 typedef struct _CHECKITEM
47 {
48 struct _CHECKITEM *Next;
49 ACCESS_MASK AccessMask;
50 DWORD State;
51 WCHAR Name[1];
52 } CHECKITEM, *PCHECKITEM;
53
54 typedef struct _CHECKLISTWND
55 {
56 HWND hSelf;
57 HWND hNotify;
58 HFONT hFont;
59
60 PCHECKITEM CheckItemListHead;
61 UINT CheckItemCount;
62
63 INT ItemHeight;
64
65 PCHECKITEM FocusedCheckItem;
66 UINT FocusedCheckItemBox;
67
68 COLORREF TextColor[2];
69 INT CheckBoxLeft[2];
70
71 PCHECKITEM QuickSearchHitItem;
72 WCHAR QuickSearchText[65];
73 UINT QuickSearchSetFocusDelay;
74 UINT QuickSearchResetDelay;
75
76 DWORD CaretWidth;
77
78 DWORD UIState;
79
80 #if SUPPORT_UXTHEME
81 PCHECKITEM HoveredCheckItem;
82 UINT HoveredCheckItemBox;
83 UINT HoverTime;
84
85 HTHEME ThemeHandle;
86 #endif
87
88 UINT HasFocus : 1;
89 UINT FocusedPushed : 1;
90 UINT QuickSearchEnabled : 1;
91 UINT ShowingCaret : 1;
92 } CHECKLISTWND, *PCHECKLISTWND;
93
94 static VOID EscapeQuickSearch(IN PCHECKLISTWND infoPtr);
95 #if SUPPORT_UXTHEME
96 static VOID ChangeCheckItemHotTrack(IN PCHECKLISTWND infoPtr,
97 IN PCHECKITEM NewHotTrack,
98 IN UINT NewHotTrackBox);
99 #endif
100 static VOID ChangeCheckItemFocus(IN PCHECKLISTWND infoPtr,
101 IN PCHECKITEM NewFocus,
102 IN UINT NewFocusBox);
103
104 /******************************************************************************/
105
106 static LRESULT
107 NotifyControlParent(IN PCHECKLISTWND infoPtr,
108 IN UINT code,
109 IN OUT PVOID data)
110 {
111 LRESULT Ret = 0;
112
113 if (infoPtr->hNotify != NULL)
114 {
115 LPNMHDR pnmh = (LPNMHDR)data;
116
117 pnmh->hwndFrom = infoPtr->hSelf;
118 pnmh->idFrom = GetWindowLongPtr(infoPtr->hSelf,
119 GWLP_ID);
120 pnmh->code = code;
121
122 Ret = SendMessage(infoPtr->hNotify,
123 WM_NOTIFY,
124 (WPARAM)pnmh->idFrom,
125 (LPARAM)pnmh);
126 }
127
128 return Ret;
129 }
130
131 static PCHECKITEM
132 FindCheckItemByIndex(IN PCHECKLISTWND infoPtr,
133 IN INT Index)
134 {
135 PCHECKITEM Item, Found = NULL;
136
137 if (Index >= 0)
138 {
139 for (Item = infoPtr->CheckItemListHead;
140 Item != NULL;
141 Item = Item->Next)
142 {
143 if (Index == 0)
144 {
145 Found = Item;
146 break;
147 }
148
149 Index--;
150 }
151 }
152
153 return Found;
154 }
155
156 static INT
157 FindCheckItemIndexByAccessMask(IN PCHECKLISTWND infoPtr,
158 IN ACCESS_MASK AccessMask)
159 {
160 PCHECKITEM Item;
161 INT Index = 0, Found = -1;
162
163 for (Item = infoPtr->CheckItemListHead;
164 Item != NULL;
165 Item = Item->Next)
166 {
167 if (Item->AccessMask == AccessMask)
168 {
169 Found = Index;
170 break;
171 }
172
173 Index++;
174 }
175
176 return Found;
177 }
178
179 static INT
180 CheckItemToIndex(IN PCHECKLISTWND infoPtr,
181 IN PCHECKITEM Item)
182 {
183 PCHECKITEM CurItem;
184 INT Index;
185
186 for (CurItem = infoPtr->CheckItemListHead, Index = 0;
187 CurItem != NULL;
188 CurItem = CurItem->Next, Index++)
189 {
190 if (CurItem == Item)
191 {
192 return Index;
193 }
194 }
195
196 return -1;
197 }
198
199 static PCHECKITEM
200 FindCheckItem(IN PCHECKLISTWND infoPtr,
201 IN LPWSTR SearchText)
202 {
203 PCHECKITEM CurItem;
204 SIZE_T Count = wcslen(SearchText);
205
206 for (CurItem = infoPtr->CheckItemListHead;
207 CurItem != NULL;
208 CurItem = CurItem->Next)
209 {
210 if ((CurItem->State & CIS_DISABLED) != CIS_DISABLED &&
211 !_wcsnicmp(CurItem->Name,
212 SearchText, Count))
213 {
214 break;
215 }
216 }
217
218 return CurItem;
219 }
220
221 static PCHECKITEM
222 FindFirstEnabledCheckBox(IN PCHECKLISTWND infoPtr,
223 OUT UINT *CheckBox)
224 {
225 PCHECKITEM CurItem;
226
227 for (CurItem = infoPtr->CheckItemListHead;
228 CurItem != NULL;
229 CurItem = CurItem->Next)
230 {
231 if ((CurItem->State & CIS_DISABLED) != CIS_DISABLED)
232 {
233 /* return the Allow checkbox in case both check boxes are enabled! */
234 *CheckBox = ((!(CurItem->State & CIS_ALLOWDISABLED)) ? CLB_ALLOW : CLB_DENY);
235 break;
236 }
237 }
238
239 return CurItem;
240 }
241
242 static PCHECKITEM
243 FindLastEnabledCheckBox(IN PCHECKLISTWND infoPtr,
244 OUT UINT *CheckBox)
245 {
246 PCHECKITEM CurItem;
247 PCHECKITEM LastEnabledItem = NULL;
248
249 for (CurItem = infoPtr->CheckItemListHead;
250 CurItem != NULL;
251 CurItem = CurItem->Next)
252 {
253 if ((CurItem->State & CIS_DISABLED) != CIS_DISABLED)
254 {
255 LastEnabledItem = CurItem;
256 }
257 }
258
259 if (LastEnabledItem != NULL)
260 {
261 /* return the Deny checkbox in case both check boxes are enabled! */
262 *CheckBox = ((!(LastEnabledItem->State & CIS_DENYDISABLED)) ? CLB_DENY : CLB_ALLOW);
263 }
264
265 return LastEnabledItem;
266 }
267
268 static PCHECKITEM
269 FindPreviousEnabledCheckBox(IN PCHECKLISTWND infoPtr,
270 OUT UINT *CheckBox)
271 {
272 PCHECKITEM Item;
273
274 if (infoPtr->FocusedCheckItem != NULL)
275 {
276 Item = infoPtr->FocusedCheckItem;
277
278 if (infoPtr->FocusedCheckItemBox == CLB_DENY &&
279 !(Item->State & CIS_ALLOWDISABLED))
280 {
281 /* currently an Deny checkbox is focused. return the Allow checkbox
282 if it's enabled */
283 *CheckBox = CLB_ALLOW;
284 }
285 else
286 {
287 PCHECKITEM CurItem;
288
289 Item = NULL;
290
291 for (CurItem = infoPtr->CheckItemListHead;
292 CurItem != infoPtr->FocusedCheckItem;
293 CurItem = CurItem->Next)
294 {
295 if ((CurItem->State & CIS_DISABLED) != CIS_DISABLED)
296 {
297 Item = CurItem;
298 }
299 }
300
301 if (Item != NULL)
302 {
303 /* return the Deny checkbox in case both check boxes are enabled! */
304 *CheckBox = ((!(Item->State & CIS_DENYDISABLED)) ? CLB_DENY : CLB_ALLOW);
305 }
306 }
307 }
308 else
309 {
310 Item = FindLastEnabledCheckBox(infoPtr,
311 CheckBox);
312 }
313
314 return Item;
315 }
316
317 static PCHECKITEM
318 FindNextEnabledCheckBox(IN PCHECKLISTWND infoPtr,
319 OUT UINT *CheckBox)
320 {
321 PCHECKITEM Item;
322
323 if (infoPtr->FocusedCheckItem != NULL)
324 {
325 Item = infoPtr->FocusedCheckItem;
326
327 if (infoPtr->FocusedCheckItemBox != CLB_DENY &&
328 !(Item->State & CIS_DENYDISABLED))
329 {
330 /* currently an Allow checkbox is focused. return the Deny checkbox
331 if it's enabled */
332 *CheckBox = CLB_DENY;
333 }
334 else
335 {
336 Item = Item->Next;
337
338 while (Item != NULL)
339 {
340 if ((Item->State & CIS_DISABLED) != CIS_DISABLED)
341 {
342 /* return the Allow checkbox in case both check boxes are enabled! */
343 *CheckBox = ((!(Item->State & CIS_ALLOWDISABLED)) ? CLB_ALLOW : CLB_DENY);
344 break;
345 }
346
347 Item = Item->Next;
348 }
349 }
350 }
351 else
352 {
353 Item = FindFirstEnabledCheckBox(infoPtr,
354 CheckBox);
355 }
356
357 return Item;
358 }
359
360 static PCHECKITEM
361 FindEnabledCheckBox(IN PCHECKLISTWND infoPtr,
362 IN BOOL ReverseSearch,
363 OUT UINT *CheckBox)
364 {
365 PCHECKITEM Item;
366
367 if (ReverseSearch)
368 {
369 Item = FindPreviousEnabledCheckBox(infoPtr,
370 CheckBox);
371 }
372 else
373 {
374 Item = FindNextEnabledCheckBox(infoPtr,
375 CheckBox);
376 }
377
378 return Item;
379 }
380
381 static PCHECKITEM
382 PtToCheckItemBox(IN PCHECKLISTWND infoPtr,
383 IN PPOINT ppt,
384 OUT UINT *CheckBox,
385 OUT BOOL *DirectlyInCheckBox)
386 {
387 INT FirstVisible, Index;
388 PCHECKITEM Item;
389
390 FirstVisible = GetScrollPos(infoPtr->hSelf,
391 SB_VERT);
392
393 Index = FirstVisible + (ppt->y / infoPtr->ItemHeight);
394
395 Item = FindCheckItemByIndex(infoPtr,
396 Index);
397 if (Item != NULL)
398 {
399 INT cx;
400
401 cx = infoPtr->CheckBoxLeft[CLB_ALLOW] +
402 ((infoPtr->CheckBoxLeft[CLB_DENY] - infoPtr->CheckBoxLeft[CLB_ALLOW]) / 2);
403
404 *CheckBox = ((ppt->x <= cx) ? CLB_ALLOW : CLB_DENY);
405
406 if (DirectlyInCheckBox != NULL)
407 {
408 INT y = ppt->y % infoPtr->ItemHeight;
409 INT cxBox = infoPtr->ItemHeight - (2 * CI_TEXT_MARGIN_HEIGHT);
410
411 if ((y >= CI_TEXT_MARGIN_HEIGHT &&
412 y < infoPtr->ItemHeight - CI_TEXT_MARGIN_HEIGHT) &&
413
414 (((ppt->x >= (infoPtr->CheckBoxLeft[CLB_ALLOW] - (cxBox / 2))) &&
415 (ppt->x < (infoPtr->CheckBoxLeft[CLB_ALLOW] - (cxBox / 2) + cxBox)))
416 ||
417 ((ppt->x >= (infoPtr->CheckBoxLeft[CLB_DENY] - (cxBox / 2))) &&
418 (ppt->x < (infoPtr->CheckBoxLeft[CLB_DENY] - (cxBox / 2) + cxBox)))))
419 {
420 *DirectlyInCheckBox = TRUE;
421 }
422 else
423 {
424 *DirectlyInCheckBox = FALSE;
425 }
426 }
427 }
428
429 return Item;
430 }
431
432 static VOID
433 ClearCheckItems(IN PCHECKLISTWND infoPtr)
434 {
435 PCHECKITEM CurItem, NextItem;
436
437 CurItem = infoPtr->CheckItemListHead;
438 while (CurItem != NULL)
439 {
440 NextItem = CurItem->Next;
441 HeapFree(GetProcessHeap(),
442 0,
443 CurItem);
444 CurItem = NextItem;
445 }
446
447 infoPtr->CheckItemListHead = NULL;
448 infoPtr->CheckItemCount = 0;
449 }
450
451 static BOOL
452 DeleteCheckItem(IN PCHECKLISTWND infoPtr,
453 IN PCHECKITEM Item)
454 {
455 PCHECKITEM CurItem;
456 PCHECKITEM *PrevPtr = &infoPtr->CheckItemListHead;
457
458 for (CurItem = infoPtr->CheckItemListHead;
459 CurItem != NULL;
460 CurItem = CurItem->Next)
461 {
462 if (CurItem == Item)
463 {
464 if (Item == infoPtr->QuickSearchHitItem && infoPtr->QuickSearchEnabled)
465 {
466 EscapeQuickSearch(infoPtr);
467 }
468
469 #if SUPPORT_UXTHEME
470 if (Item == infoPtr->HoveredCheckItem)
471 {
472 ChangeCheckItemHotTrack(infoPtr,
473 NULL,
474 0);
475 }
476 #endif
477
478 if (Item == infoPtr->FocusedCheckItem)
479 {
480 ChangeCheckItemFocus(infoPtr,
481 NULL,
482 0);
483 }
484
485 *PrevPtr = CurItem->Next;
486 HeapFree(GetProcessHeap(),
487 0,
488 CurItem);
489 infoPtr->CheckItemCount--;
490 return TRUE;
491 }
492
493 PrevPtr = &CurItem->Next;
494 }
495
496 return FALSE;
497 }
498
499 static PCHECKITEM
500 AddCheckItem(IN PCHECKLISTWND infoPtr,
501 IN LPWSTR Name,
502 IN DWORD State,
503 IN ACCESS_MASK AccessMask,
504 OUT INT *Index)
505 {
506 PCHECKITEM CurItem;
507 INT i;
508 PCHECKITEM *PrevPtr = &infoPtr->CheckItemListHead;
509 PCHECKITEM Item = HeapAlloc(GetProcessHeap(),
510 0,
511 sizeof(CHECKITEM) + (wcslen(Name) * sizeof(WCHAR)));
512 if (Item != NULL)
513 {
514 for (CurItem = infoPtr->CheckItemListHead, i = 0;
515 CurItem != NULL;
516 CurItem = CurItem->Next)
517 {
518 PrevPtr = &CurItem->Next;
519 i++;
520 }
521
522 Item->Next = NULL;
523 Item->AccessMask = AccessMask;
524 Item->State = State & CIS_MASK;
525 wcscpy(Item->Name,
526 Name);
527
528 *PrevPtr = Item;
529 infoPtr->CheckItemCount++;
530
531 if (Index != NULL)
532 {
533 *Index = i;
534 }
535 }
536
537 return Item;
538 }
539
540 static UINT
541 ClearCheckBoxes(IN PCHECKLISTWND infoPtr)
542 {
543 PCHECKITEM CurItem;
544 UINT nUpdated = 0;
545
546 for (CurItem = infoPtr->CheckItemListHead;
547 CurItem != NULL;
548 CurItem = CurItem->Next)
549 {
550 if (CurItem->State & (CIS_ALLOW | CIS_DENY))
551 {
552 CurItem->State &= ~(CIS_ALLOW | CIS_DENY);
553 nUpdated++;
554 }
555 }
556
557 return nUpdated;
558 }
559
560 static VOID
561 UpdateControl(IN PCHECKLISTWND infoPtr)
562 {
563 RECT rcClient;
564 SCROLLINFO ScrollInfo;
565 INT VisibleItems;
566
567 GetClientRect(infoPtr->hSelf,
568 &rcClient);
569
570 ScrollInfo.cbSize = sizeof(ScrollInfo);
571 ScrollInfo.fMask = SIF_PAGE | SIF_RANGE;
572 ScrollInfo.nMin = 0;
573 ScrollInfo.nMax = infoPtr->CheckItemCount;
574 ScrollInfo.nPage = ((rcClient.bottom - rcClient.top) + infoPtr->ItemHeight - 1) / infoPtr->ItemHeight;
575 ScrollInfo.nPos = 0;
576 ScrollInfo.nTrackPos = 0;
577
578 VisibleItems = (rcClient.bottom - rcClient.top) / infoPtr->ItemHeight;
579
580 if (ScrollInfo.nPage == (UINT)VisibleItems && ScrollInfo.nMax > 0)
581 {
582 ScrollInfo.nMax--;
583 }
584
585 SetScrollInfo(infoPtr->hSelf,
586 SB_VERT,
587 &ScrollInfo,
588 TRUE);
589
590 RedrawWindow(infoPtr->hSelf,
591 NULL,
592 NULL,
593 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN);
594 }
595
596 static VOID
597 UpdateCheckItem(IN PCHECKLISTWND infoPtr,
598 IN PCHECKITEM Item)
599 {
600 RECT rcClient;
601 INT VisibleFirst, VisibleItems;
602 INT Index = CheckItemToIndex(infoPtr,
603 Item);
604 if (Index != -1)
605 {
606 VisibleFirst = GetScrollPos(infoPtr->hSelf,
607 SB_VERT);
608
609 if (Index >= VisibleFirst)
610 {
611 GetClientRect(infoPtr->hSelf,
612 &rcClient);
613
614 VisibleItems = ((rcClient.bottom - rcClient.top) + infoPtr->ItemHeight - 1) / infoPtr->ItemHeight;
615
616 if (Index <= VisibleFirst + VisibleItems)
617 {
618 RECT rcUpdate;
619
620 rcUpdate.left = rcClient.left;
621 rcUpdate.right = rcClient.right;
622 rcUpdate.top = (Index - VisibleFirst) * infoPtr->ItemHeight;
623 rcUpdate.bottom = rcUpdate.top + infoPtr->ItemHeight;
624
625 RedrawWindow(infoPtr->hSelf,
626 &rcUpdate,
627 NULL,
628 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN);
629 }
630 }
631 }
632 }
633
634 static VOID
635 MakeCheckItemVisible(IN PCHECKLISTWND infoPtr,
636 IN PCHECKITEM Item)
637 {
638 RECT rcClient;
639 INT VisibleFirst, VisibleItems, NewPos;
640 INT Index = CheckItemToIndex(infoPtr,
641 Item);
642 if (Index != -1)
643 {
644 VisibleFirst = GetScrollPos(infoPtr->hSelf,
645 SB_VERT);
646
647 if (Index <= VisibleFirst)
648 {
649 NewPos = Index;
650 }
651 else
652 {
653 GetClientRect(infoPtr->hSelf,
654 &rcClient);
655
656 VisibleItems = (rcClient.bottom - rcClient.top) / infoPtr->ItemHeight;
657 if (Index - VisibleItems + 1 > VisibleFirst)
658 {
659 NewPos = Index - VisibleItems + 1;
660 }
661 else
662 {
663 NewPos = VisibleFirst;
664 }
665 }
666
667 if (VisibleFirst != NewPos)
668 {
669 SCROLLINFO ScrollInfo;
670
671 ScrollInfo.cbSize = sizeof(ScrollInfo);
672 ScrollInfo.fMask = SIF_POS;
673 ScrollInfo.nPos = NewPos;
674 NewPos = SetScrollInfo(infoPtr->hSelf,
675 SB_VERT,
676 &ScrollInfo,
677 TRUE);
678
679 if (VisibleFirst != NewPos)
680 {
681 ScrollWindowEx(infoPtr->hSelf,
682 0,
683 (NewPos - VisibleFirst) * infoPtr->ItemHeight,
684 NULL,
685 NULL,
686 NULL,
687 NULL,
688 SW_INVALIDATE | SW_SCROLLCHILDREN);
689
690 RedrawWindow(infoPtr->hSelf,
691 NULL,
692 NULL,
693 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN);
694 }
695 }
696 }
697 }
698
699 static UINT
700 GetIdealItemHeight(IN PCHECKLISTWND infoPtr)
701 {
702 HDC hdc = GetDC(infoPtr->hSelf);
703 if(hdc != NULL)
704 {
705 UINT height;
706 TEXTMETRIC tm;
707 HGDIOBJ hOldFont = SelectObject(hdc,
708 infoPtr->hFont);
709
710 if(GetTextMetrics(hdc,
711 &tm))
712 {
713 height = tm.tmHeight;
714 }
715 else
716 {
717 height = 2;
718 }
719
720 SelectObject(hdc,
721 hOldFont);
722
723 ReleaseDC(infoPtr->hSelf,
724 hdc);
725
726 return height;
727 }
728 return 0;
729 }
730
731 static HFONT
732 RetChangeControlFont(IN PCHECKLISTWND infoPtr,
733 IN HFONT hFont,
734 IN BOOL Redraw)
735 {
736 HFONT hOldFont = infoPtr->hFont;
737 infoPtr->hFont = hFont;
738
739 if (hOldFont != hFont)
740 {
741 infoPtr->ItemHeight = (2 * CI_TEXT_MARGIN_HEIGHT) + GetIdealItemHeight(infoPtr);
742 }
743
744 if (infoPtr->ShowingCaret)
745 {
746 DestroyCaret();
747 CreateCaret(infoPtr->hSelf,
748 NULL,
749 0,
750 infoPtr->ItemHeight - (2 * CI_TEXT_MARGIN_HEIGHT));
751 }
752
753 UpdateControl(infoPtr);
754
755 return hOldFont;
756 }
757
758 #if SUPPORT_UXTHEME
759 static INT
760 CalculateCheckBoxStyle(IN BOOL Checked,
761 IN BOOL Enabled,
762 IN BOOL HotTrack,
763 IN BOOL Pushed)
764 {
765 INT BtnState;
766
767 if (Checked)
768 {
769 BtnState = (Enabled ?
770 (Pushed ? CBS_CHECKEDPRESSED : (HotTrack ? CBS_CHECKEDHOT : CBS_CHECKEDNORMAL)) :
771 CBS_CHECKEDDISABLED);
772 }
773 else
774 {
775 BtnState = (Enabled ?
776 (Pushed ? CBS_UNCHECKEDPRESSED : (HotTrack ? CBS_UNCHECKEDHOT : CBS_UNCHECKEDNORMAL)) :
777 CBS_UNCHECKEDDISABLED);
778 }
779
780 return BtnState;
781 }
782 #endif
783
784 static VOID
785 PaintControl(IN PCHECKLISTWND infoPtr,
786 IN HDC hDC,
787 IN PRECT rcUpdate)
788 {
789 INT ScrollPos;
790 PCHECKITEM FirstItem, Item;
791 RECT rcClient;
792 UINT VisibleFirstIndex = rcUpdate->top / infoPtr->ItemHeight;
793 UINT LastTouchedIndex = rcUpdate->bottom / infoPtr->ItemHeight;
794
795 FillRect(hDC,
796 rcUpdate,
797 (HBRUSH)(COLOR_WINDOW + 1));
798
799 GetClientRect(infoPtr->hSelf,
800 &rcClient);
801
802 ScrollPos = GetScrollPos(infoPtr->hSelf,
803 SB_VERT);
804
805 FirstItem = FindCheckItemByIndex(infoPtr,
806 ScrollPos + VisibleFirstIndex);
807 if (FirstItem != NULL)
808 {
809 RECT TextRect, ItemRect, CheckBox;
810 HFONT hOldFont;
811 DWORD CurrentIndex;
812 COLORREF OldTextColor;
813 BOOL Enabled, PrevEnabled, IsPushed;
814 POINT hOldBrushOrg;
815 #if SUPPORT_UXTHEME
816 HRESULT hDrawResult;
817 BOOL ItemHovered;
818 #endif
819
820 Enabled = IsWindowEnabled(infoPtr->hSelf);
821 PrevEnabled = Enabled;
822
823 ItemRect.left = 0;
824 ItemRect.right = rcClient.right;
825 ItemRect.top = VisibleFirstIndex * infoPtr->ItemHeight;
826
827 TextRect.left = ItemRect.left + CI_TEXT_MARGIN_WIDTH;
828 TextRect.right = ItemRect.right - CI_TEXT_MARGIN_WIDTH;
829 TextRect.top = ItemRect.top + CI_TEXT_MARGIN_HEIGHT;
830
831 SetBrushOrgEx(hDC,
832 ItemRect.left,
833 ItemRect.top,
834 &hOldBrushOrg);
835
836 OldTextColor = SetTextColor(hDC,
837 infoPtr->TextColor[Enabled]);
838
839 hOldFont = SelectObject(hDC,
840 infoPtr->hFont);
841
842 for (Item = FirstItem, CurrentIndex = VisibleFirstIndex;
843 Item != NULL && CurrentIndex <= LastTouchedIndex;
844 Item = Item->Next, CurrentIndex++)
845 {
846 TextRect.bottom = TextRect.top + infoPtr->ItemHeight - (2 * CI_TEXT_MARGIN_HEIGHT);
847 ItemRect.bottom = ItemRect.top + infoPtr->ItemHeight;
848
849 SetBrushOrgEx(hDC,
850 ItemRect.left,
851 ItemRect.top,
852 NULL);
853
854 if (Enabled && PrevEnabled != ((Item->State & CIS_DISABLED) != CIS_DISABLED))
855 {
856 PrevEnabled = ((Item->State & CIS_DISABLED) != CIS_DISABLED);
857
858 SetTextColor(hDC,
859 infoPtr->TextColor[PrevEnabled]);
860 }
861
862 #if SUPPORT_UXTHEME
863 ItemHovered = (Enabled && infoPtr->HoveredCheckItem == Item);
864 #endif
865
866 if (infoPtr->QuickSearchHitItem == Item)
867 {
868 COLORREF OldBkColor, OldFgColor;
869 SIZE TextSize;
870 SIZE_T TextLen, HighlightLen = wcslen(infoPtr->QuickSearchText);
871
872 /* highlight the quicksearch text */
873 if (GetTextExtentPoint32(hDC,
874 Item->Name,
875 HighlightLen,
876 &TextSize))
877 {
878 COLORREF HighlightTextColor, HighlightBackground;
879 RECT rcHighlight = TextRect;
880
881 HighlightTextColor = GetSysColor(COLOR_HIGHLIGHTTEXT);
882 HighlightBackground = GetSysColor(COLOR_HIGHLIGHT);
883
884 rcHighlight.right = rcHighlight.left + TextSize.cx;
885
886 InflateRect(&rcHighlight,
887 0,
888 CI_TEXT_SELECTIONMARGIN);
889
890 OldBkColor = SetBkColor(hDC,
891 HighlightBackground);
892 OldFgColor = SetTextColor(hDC,
893 HighlightTextColor);
894
895 /* draw the highlighted text */
896 DrawText(hDC,
897 Item->Name,
898 HighlightLen,
899 &rcHighlight,
900 DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
901
902 SetBkColor(hDC,
903 OldBkColor);
904 SetTextColor(hDC,
905 OldFgColor);
906
907 /* draw the remaining part of the text */
908 TextLen = wcslen(Item->Name);
909 if (HighlightLen < TextLen)
910 {
911 rcHighlight.left = rcHighlight.right;
912 rcHighlight.right = TextRect.right;
913
914 DrawText(hDC,
915 Item->Name + HighlightLen,
916 -1,
917 &rcHighlight,
918 DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
919 }
920 }
921 }
922 else
923 {
924 /* draw the text */
925 DrawText(hDC,
926 Item->Name,
927 -1,
928 &TextRect,
929 DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
930 }
931
932 CheckBox.top = TextRect.top;
933 CheckBox.bottom = TextRect.bottom;
934
935 /* draw the Allow checkbox */
936 IsPushed = (Enabled && Item == infoPtr->FocusedCheckItem && infoPtr->HasFocus &&
937 !(Item->State & CIS_ALLOWDISABLED) && infoPtr->FocusedCheckItemBox != CLB_DENY &&
938 infoPtr->FocusedPushed);
939
940 CheckBox.left = infoPtr->CheckBoxLeft[CLB_ALLOW] - ((TextRect.bottom - TextRect.top) / 2);
941 CheckBox.right = CheckBox.left + (TextRect.bottom - TextRect.top);
942 #if SUPPORT_UXTHEME
943 if (infoPtr->ThemeHandle != NULL)
944 {
945 INT BtnState = CalculateCheckBoxStyle(Item->State & CIS_ALLOW,
946 Enabled && !(Item->State & CIS_ALLOWDISABLED),
947 (ItemHovered && infoPtr->HoveredCheckItemBox != CLB_DENY),
948 IsPushed);
949
950
951 hDrawResult = DrawThemeBackground(infoPtr->ThemeHandle,
952 hDC,
953 BP_CHECKBOX,
954 BtnState,
955 &CheckBox,
956 NULL);
957
958 }
959 else
960 {
961 hDrawResult = E_FAIL;
962 }
963
964 /* draw the standard checkbox if no themes are enabled or drawing the
965 themeed control failed */
966 if (FAILED(hDrawResult))
967 #endif
968 {
969 DrawFrameControl(hDC,
970 &CheckBox,
971 DFC_BUTTON,
972 DFCS_BUTTONCHECK | DFCS_FLAT |
973 ((Item->State & CIS_ALLOWDISABLED) || !Enabled ? DFCS_INACTIVE : 0) |
974 ((Item->State & CIS_ALLOW) ? DFCS_CHECKED : 0) |
975 (IsPushed ? DFCS_PUSHED : 0));
976 }
977 if (Item == infoPtr->FocusedCheckItem && !(infoPtr->UIState & UISF_HIDEFOCUS) &&
978 infoPtr->HasFocus &&
979 infoPtr->FocusedCheckItemBox != CLB_DENY)
980 {
981 RECT rcFocus = CheckBox;
982
983 InflateRect (&rcFocus,
984 CI_TEXT_MARGIN_HEIGHT,
985 CI_TEXT_MARGIN_HEIGHT);
986
987 DrawFocusRect(hDC,
988 &rcFocus);
989 }
990
991 /* draw the Deny checkbox */
992 IsPushed = (Enabled && Item == infoPtr->FocusedCheckItem && infoPtr->HasFocus &&
993 !(Item->State & CIS_DENYDISABLED) && infoPtr->FocusedCheckItemBox == CLB_DENY &&
994 infoPtr->FocusedPushed);
995
996 CheckBox.left = infoPtr->CheckBoxLeft[CLB_DENY] - ((TextRect.bottom - TextRect.top) / 2);
997 CheckBox.right = CheckBox.left + (TextRect.bottom - TextRect.top);
998 #if SUPPORT_UXTHEME
999 if (infoPtr->ThemeHandle != NULL)
1000 {
1001 INT BtnState = CalculateCheckBoxStyle(Item->State & CIS_DENY,
1002 Enabled && !(Item->State & CIS_DENYDISABLED),
1003 (ItemHovered && infoPtr->HoveredCheckItemBox == CLB_DENY),
1004 IsPushed);
1005
1006 hDrawResult = DrawThemeBackground(infoPtr->ThemeHandle,
1007 hDC,
1008 BP_CHECKBOX,
1009 BtnState,
1010 &CheckBox,
1011 NULL);
1012
1013 }
1014 else
1015 {
1016 hDrawResult = E_FAIL;
1017 }
1018
1019 /* draw the standard checkbox if no themes are enabled or drawing the
1020 themeed control failed */
1021 if (FAILED(hDrawResult))
1022 #endif
1023 {
1024 DrawFrameControl(hDC,
1025 &CheckBox,
1026 DFC_BUTTON,
1027 DFCS_BUTTONCHECK | DFCS_FLAT |
1028 ((Item->State & CIS_DENYDISABLED) || !Enabled ? DFCS_INACTIVE : 0) |
1029 ((Item->State & CIS_DENY) ? DFCS_CHECKED : 0) |
1030 (IsPushed ? DFCS_PUSHED : 0));
1031 }
1032 if (infoPtr->HasFocus && !(infoPtr->UIState & UISF_HIDEFOCUS) &&
1033 Item == infoPtr->FocusedCheckItem &&
1034 infoPtr->FocusedCheckItemBox == CLB_DENY)
1035 {
1036 RECT rcFocus = CheckBox;
1037
1038 InflateRect (&rcFocus,
1039 CI_TEXT_MARGIN_HEIGHT,
1040 CI_TEXT_MARGIN_HEIGHT);
1041
1042 DrawFocusRect(hDC,
1043 &rcFocus);
1044 }
1045
1046 TextRect.top += infoPtr->ItemHeight;
1047 ItemRect.top += infoPtr->ItemHeight;
1048 }
1049
1050 SelectObject(hDC,
1051 hOldFont);
1052
1053 SetTextColor(hDC,
1054 OldTextColor);
1055
1056 SetBrushOrgEx(hDC,
1057 hOldBrushOrg.x,
1058 hOldBrushOrg.y,
1059 NULL);
1060 }
1061 }
1062
1063 static VOID
1064 ChangeCheckItemFocus(IN PCHECKLISTWND infoPtr,
1065 IN PCHECKITEM NewFocus,
1066 IN UINT NewFocusBox)
1067 {
1068 if (NewFocus != infoPtr->FocusedCheckItem)
1069 {
1070 PCHECKITEM OldFocus = infoPtr->FocusedCheckItem;
1071 infoPtr->FocusedCheckItem = NewFocus;
1072 infoPtr->FocusedCheckItemBox = NewFocusBox;
1073
1074 if (OldFocus != NULL)
1075 {
1076 UpdateCheckItem(infoPtr,
1077 OldFocus);
1078 }
1079 }
1080 else
1081 {
1082 infoPtr->FocusedCheckItemBox = NewFocusBox;
1083 }
1084
1085 if (NewFocus != NULL)
1086 {
1087 MakeCheckItemVisible(infoPtr,
1088 NewFocus);
1089 UpdateCheckItem(infoPtr,
1090 NewFocus);
1091 }
1092 }
1093
1094 static VOID
1095 UpdateCheckItemBox(IN PCHECKLISTWND infoPtr,
1096 IN PCHECKITEM Item,
1097 IN UINT ItemBox)
1098 {
1099 RECT rcClient;
1100 INT VisibleFirst, VisibleItems;
1101 INT Index = CheckItemToIndex(infoPtr,
1102 Item);
1103 if (Index != -1)
1104 {
1105 VisibleFirst = GetScrollPos(infoPtr->hSelf,
1106 SB_VERT);
1107
1108 if (Index >= VisibleFirst)
1109 {
1110 GetClientRect(infoPtr->hSelf,
1111 &rcClient);
1112
1113 VisibleItems = ((rcClient.bottom - rcClient.top) + infoPtr->ItemHeight - 1) / infoPtr->ItemHeight;
1114
1115 if (Index <= VisibleFirst + VisibleItems)
1116 {
1117 RECT rcUpdate;
1118
1119 rcUpdate.left = rcClient.left + infoPtr->CheckBoxLeft[ItemBox] - (infoPtr->ItemHeight / 2);
1120 rcUpdate.right = rcUpdate.left + infoPtr->ItemHeight;
1121 rcUpdate.top = ((Index - VisibleFirst) * infoPtr->ItemHeight);
1122 rcUpdate.bottom = rcUpdate.top + infoPtr->ItemHeight;
1123
1124 RedrawWindow(infoPtr->hSelf,
1125 &rcUpdate,
1126 NULL,
1127 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN);
1128 }
1129 }
1130 }
1131 }
1132
1133 #if SUPPORT_UXTHEME
1134 static VOID
1135 ChangeCheckItemHotTrack(IN PCHECKLISTWND infoPtr,
1136 IN PCHECKITEM NewHotTrack,
1137 IN UINT NewHotTrackBox)
1138 {
1139 if (NewHotTrack != infoPtr->HoveredCheckItem)
1140 {
1141 PCHECKITEM OldHotTrack = infoPtr->HoveredCheckItem;
1142 UINT OldHotTrackBox = infoPtr->HoveredCheckItemBox;
1143
1144 infoPtr->HoveredCheckItem = NewHotTrack;
1145 infoPtr->HoveredCheckItemBox = NewHotTrackBox;
1146
1147 if (OldHotTrack != NULL)
1148 {
1149 UpdateCheckItemBox(infoPtr,
1150 OldHotTrack,
1151 OldHotTrackBox);
1152 }
1153 }
1154 else
1155 {
1156 infoPtr->HoveredCheckItemBox = NewHotTrackBox;
1157 }
1158
1159 if (NewHotTrack != NULL)
1160 {
1161 UpdateCheckItemBox(infoPtr,
1162 NewHotTrack,
1163 NewHotTrackBox);
1164 }
1165 }
1166 #endif
1167
1168 static BOOL
1169 ChangeCheckBox(IN PCHECKLISTWND infoPtr,
1170 IN PCHECKITEM CheckItem,
1171 IN UINT CheckItemBox)
1172 {
1173 NMCHANGEITEMCHECKBOX CheckData;
1174 DWORD OldState = CheckItem->State;
1175 DWORD CheckedBit = ((infoPtr->FocusedCheckItemBox == CLB_DENY) ? CIS_DENY : CIS_ALLOW);
1176 BOOL Checked = (CheckItem->State & CheckedBit) != 0;
1177
1178 CheckData.OldState = OldState;
1179 CheckData.NewState = (Checked ? OldState & ~CheckedBit : OldState | CheckedBit);
1180 CheckData.CheckBox = infoPtr->FocusedCheckItemBox;
1181 CheckData.Checked = !Checked;
1182
1183 if (NotifyControlParent(infoPtr,
1184 CLN_CHANGINGITEMCHECKBOX,
1185 &CheckData) != (LRESULT)-1)
1186 {
1187 CheckItem->State = CheckData.NewState;
1188 }
1189
1190 return (CheckItem->State != OldState);
1191 }
1192
1193 static VOID
1194 DisplayCaret(IN PCHECKLISTWND infoPtr)
1195 {
1196 if (IsWindowEnabled(infoPtr->hSelf) && !infoPtr->ShowingCaret)
1197 {
1198 infoPtr->ShowingCaret = TRUE;
1199
1200 CreateCaret(infoPtr->hSelf,
1201 NULL,
1202 infoPtr->CaretWidth,
1203 infoPtr->ItemHeight - (2 * CI_TEXT_MARGIN_HEIGHT));
1204
1205 ShowCaret(infoPtr->hSelf);
1206 }
1207 }
1208
1209 static VOID
1210 RemoveCaret(IN PCHECKLISTWND infoPtr)
1211 {
1212 if (IsWindowEnabled(infoPtr->hSelf) && infoPtr->ShowingCaret)
1213 {
1214 infoPtr->ShowingCaret = FALSE;
1215
1216 HideCaret(infoPtr->hSelf);
1217 DestroyCaret();
1218 }
1219 }
1220
1221 static VOID
1222 KillQuickSearchTimers(IN PCHECKLISTWND infoPtr)
1223 {
1224 KillTimer(infoPtr->hSelf,
1225 TIMER_ID_SETHITFOCUS);
1226 KillTimer(infoPtr->hSelf,
1227 TIMER_ID_RESETQUICKSEARCH);
1228 }
1229
1230 static VOID
1231 MapItemToRect(IN PCHECKLISTWND infoPtr,
1232 IN PCHECKITEM CheckItem,
1233 OUT RECT *prcItem)
1234 {
1235 INT Index = CheckItemToIndex(infoPtr,
1236 CheckItem);
1237 if (Index != -1)
1238 {
1239 RECT rcClient;
1240 INT VisibleFirst;
1241
1242 GetClientRect(infoPtr->hSelf,
1243 &rcClient);
1244
1245 VisibleFirst = GetScrollPos(infoPtr->hSelf,
1246 SB_VERT);
1247
1248 prcItem->left = rcClient.left;
1249 prcItem->right = rcClient.right;
1250 prcItem->top = (Index - VisibleFirst) * infoPtr->ItemHeight;
1251 prcItem->bottom = prcItem->top + infoPtr->ItemHeight;
1252 }
1253 else
1254 {
1255 prcItem->left = 0;
1256 prcItem->top = 0;
1257 prcItem->right = 0;
1258 prcItem->bottom = 0;
1259 }
1260 }
1261
1262 static VOID
1263 UpdateCaretPos(IN PCHECKLISTWND infoPtr)
1264 {
1265 if (infoPtr->ShowingCaret && infoPtr->QuickSearchHitItem != NULL)
1266 {
1267 HDC hDC = GetDC(infoPtr->hSelf);
1268 if (hDC != NULL)
1269 {
1270 SIZE TextSize;
1271 HGDIOBJ hOldFont = SelectObject(hDC,
1272 infoPtr->hFont);
1273
1274 TextSize.cx = 0;
1275 TextSize.cy = 0;
1276
1277 if (infoPtr->QuickSearchText[0] == L'\0' ||
1278 GetTextExtentPoint32(hDC,
1279 infoPtr->QuickSearchHitItem->Name,
1280 wcslen(infoPtr->QuickSearchText),
1281 &TextSize))
1282 {
1283 RECT rcItem;
1284
1285 MapItemToRect(infoPtr,
1286 infoPtr->QuickSearchHitItem,
1287 &rcItem);
1288
1289 /* actually change the caret position */
1290 SetCaretPos(rcItem.left + CI_TEXT_MARGIN_WIDTH + TextSize.cx,
1291 rcItem.top + CI_TEXT_MARGIN_HEIGHT);
1292 }
1293
1294 SelectObject(hDC,
1295 hOldFont);
1296
1297 ReleaseDC(infoPtr->hSelf,
1298 hDC);
1299 }
1300 }
1301 }
1302
1303 static VOID
1304 EscapeQuickSearch(IN PCHECKLISTWND infoPtr)
1305 {
1306 if (infoPtr->QuickSearchEnabled && infoPtr->QuickSearchHitItem != NULL)
1307 {
1308 PCHECKITEM OldHit = infoPtr->QuickSearchHitItem;
1309
1310 infoPtr->QuickSearchHitItem = NULL;
1311 infoPtr->QuickSearchText[0] = L'\0';
1312
1313 /* scroll back to the focused item */
1314 if (infoPtr->FocusedCheckItem != NULL)
1315 {
1316 MakeCheckItemVisible(infoPtr,
1317 infoPtr->FocusedCheckItem);
1318 }
1319
1320 /* repaint the old search hit item if it's still visible */
1321 UpdateCheckItem(infoPtr,
1322 OldHit);
1323
1324 KillQuickSearchTimers(infoPtr);
1325
1326 RemoveCaret(infoPtr);
1327 }
1328 }
1329
1330 static VOID
1331 ChangeSearchHit(IN PCHECKLISTWND infoPtr,
1332 IN PCHECKITEM NewHit)
1333 {
1334 PCHECKITEM OldHit = infoPtr->QuickSearchHitItem;
1335
1336 infoPtr->QuickSearchHitItem = NewHit;
1337
1338 if (OldHit != NewHit)
1339 {
1340 /* scroll to the new search hit */
1341 MakeCheckItemVisible(infoPtr,
1342 NewHit);
1343
1344 /* repaint the old hit if present and visible */
1345 if (OldHit != NULL)
1346 {
1347 UpdateCheckItem(infoPtr,
1348 OldHit);
1349 }
1350 else
1351 {
1352 /* show the caret the first time we find an item */
1353 DisplayCaret(infoPtr);
1354 }
1355 }
1356
1357 UpdateCaretPos(infoPtr);
1358
1359 UpdateCheckItem(infoPtr,
1360 NewHit);
1361
1362 /* kill the reset timer and restart the set hit focus timer */
1363 KillTimer(infoPtr->hSelf,
1364 TIMER_ID_RESETQUICKSEARCH);
1365 if (infoPtr->QuickSearchSetFocusDelay != 0)
1366 {
1367 SetTimer(infoPtr->hSelf,
1368 TIMER_ID_SETHITFOCUS,
1369 infoPtr->QuickSearchSetFocusDelay,
1370 NULL);
1371 }
1372 }
1373
1374 static BOOL
1375 QuickSearchFindHit(IN PCHECKLISTWND infoPtr,
1376 IN WCHAR c)
1377 {
1378 if (infoPtr->QuickSearchEnabled)
1379 {
1380 BOOL Ret = FALSE;
1381 PCHECKITEM NewHit;
1382
1383 switch (c)
1384 {
1385 case '\r':
1386 case '\n':
1387 {
1388 Ret = infoPtr->QuickSearchHitItem != NULL;
1389 if (Ret)
1390 {
1391 /* NOTE: QuickSearchHitItem definitely has at least one
1392 enabled check box, the user can't search for disabled
1393 check items */
1394
1395 ChangeCheckItemFocus(infoPtr,
1396 infoPtr->QuickSearchHitItem,
1397 ((!(infoPtr->QuickSearchHitItem->State & CIS_ALLOWDISABLED)) ? CLB_ALLOW : CLB_DENY));
1398
1399 EscapeQuickSearch(infoPtr);
1400 }
1401 break;
1402 }
1403
1404 case VK_BACK:
1405 {
1406 if (infoPtr->QuickSearchHitItem != NULL)
1407 {
1408 INT SearchLen = wcslen(infoPtr->QuickSearchText);
1409 if (SearchLen > 0)
1410 {
1411 /* delete the last character */
1412 infoPtr->QuickSearchText[--SearchLen] = L'\0';
1413
1414 if (SearchLen > 0)
1415 {
1416 /* search again */
1417 NewHit = FindCheckItem(infoPtr,
1418 infoPtr->QuickSearchText);
1419
1420 if (NewHit != NULL)
1421 {
1422 /* change the search hit */
1423 ChangeSearchHit(infoPtr,
1424 NewHit);
1425
1426 Ret = TRUE;
1427 }
1428 }
1429 }
1430
1431 if (!Ret)
1432 {
1433 EscapeQuickSearch(infoPtr);
1434 }
1435 }
1436 break;
1437 }
1438
1439 default:
1440 {
1441 INT SearchLen = wcslen(infoPtr->QuickSearchText);
1442 if (SearchLen < (INT)(sizeof(infoPtr->QuickSearchText) / sizeof(infoPtr->QuickSearchText[0])) - 1)
1443 {
1444 infoPtr->QuickSearchText[SearchLen++] = c;
1445 infoPtr->QuickSearchText[SearchLen] = L'\0';
1446
1447 NewHit = FindCheckItem(infoPtr,
1448 infoPtr->QuickSearchText);
1449 if (NewHit != NULL)
1450 {
1451 /* change the search hit */
1452 ChangeSearchHit(infoPtr,
1453 NewHit);
1454
1455 Ret = TRUE;
1456 }
1457 else
1458 {
1459 /* reset the input */
1460 infoPtr->QuickSearchText[--SearchLen] = L'\0';
1461 }
1462 }
1463 break;
1464 }
1465 }
1466 return Ret;
1467 }
1468
1469 return FALSE;
1470 }
1471
1472 static LRESULT CALLBACK
1473 CheckListWndProc(IN HWND hwnd,
1474 IN UINT uMsg,
1475 IN WPARAM wParam,
1476 IN LPARAM lParam)
1477 {
1478 PCHECKLISTWND infoPtr;
1479 LRESULT Ret;
1480
1481 infoPtr = (PCHECKLISTWND)GetWindowLongPtr(hwnd,
1482 0);
1483
1484 if (infoPtr == NULL && uMsg != WM_CREATE)
1485 {
1486 goto HandleDefaultMessage;
1487 }
1488
1489 Ret = 0;
1490
1491 switch (uMsg)
1492 {
1493 case WM_PAINT:
1494 {
1495 HDC hdc;
1496 RECT rcUpdate;
1497 PAINTSTRUCT ps;
1498
1499 if (GetUpdateRect(hwnd,
1500 &rcUpdate,
1501 FALSE))
1502 {
1503 hdc = (wParam != 0 ? (HDC)wParam : BeginPaint(hwnd, &ps));
1504
1505 if (hdc != NULL)
1506 {
1507 PaintControl(infoPtr,
1508 hdc,
1509 &rcUpdate);
1510
1511 if (wParam == 0)
1512 {
1513 EndPaint(hwnd,
1514 &ps);
1515 }
1516 }
1517 }
1518 break;
1519 }
1520
1521 case WM_MOUSEMOVE:
1522 {
1523 POINT pt;
1524 BOOL InCheckBox;
1525 HWND hWndCapture = GetCapture();
1526
1527 pt.x = (LONG)LOWORD(lParam);
1528 pt.y = (LONG)HIWORD(lParam);
1529
1530 #if SUPPORT_UXTHEME
1531 /* handle hovering checkboxes */
1532 if (hWndCapture == NULL && infoPtr->ThemeHandle != NULL)
1533 {
1534 TRACKMOUSEEVENT tme;
1535 PCHECKITEM HotTrackItem;
1536 UINT HotTrackItemBox;
1537
1538 HotTrackItem = PtToCheckItemBox(infoPtr,
1539 &pt,
1540 &HotTrackItemBox,
1541 &InCheckBox);
1542 if (HotTrackItem != NULL && InCheckBox)
1543 {
1544 if (infoPtr->HoveredCheckItem != HotTrackItem ||
1545 infoPtr->HoveredCheckItemBox != HotTrackItemBox)
1546 {
1547 ChangeCheckItemHotTrack(infoPtr,
1548 HotTrackItem,
1549 HotTrackItemBox);
1550 }
1551 }
1552 else
1553 {
1554 ChangeCheckItemHotTrack(infoPtr,
1555 NULL,
1556 0);
1557 }
1558
1559 tme.cbSize = sizeof(tme);
1560 tme.dwFlags = TME_LEAVE;
1561 tme.hwndTrack = hwnd;
1562 tme.dwHoverTime = infoPtr->HoverTime;
1563
1564 TrackMouseEvent(&tme);
1565 }
1566 #endif
1567
1568 if (hWndCapture == hwnd && infoPtr->FocusedCheckItem != NULL)
1569 {
1570 PCHECKITEM PtItem;
1571 UINT PtItemBox;
1572 UINT OldPushed;
1573
1574 PtItem = PtToCheckItemBox(infoPtr,
1575 &pt,
1576 &PtItemBox,
1577 &InCheckBox);
1578
1579 OldPushed = infoPtr->FocusedPushed;
1580 infoPtr->FocusedPushed = InCheckBox && infoPtr->FocusedCheckItem == PtItem &&
1581 infoPtr->FocusedCheckItemBox == PtItemBox;
1582
1583 if (OldPushed != infoPtr->FocusedPushed)
1584 {
1585 UpdateCheckItemBox(infoPtr,
1586 infoPtr->FocusedCheckItem,
1587 infoPtr->FocusedCheckItemBox);
1588 }
1589 }
1590
1591 break;
1592 }
1593
1594 case WM_VSCROLL:
1595 {
1596 SCROLLINFO ScrollInfo;
1597
1598 ScrollInfo.cbSize = sizeof(ScrollInfo);
1599 ScrollInfo.fMask = SIF_RANGE | SIF_POS;
1600
1601 if (GetScrollInfo(hwnd,
1602 SB_VERT,
1603 &ScrollInfo))
1604 {
1605 INT OldPos = ScrollInfo.nPos;
1606
1607 switch (LOWORD(wParam))
1608 {
1609 case SB_BOTTOM:
1610 ScrollInfo.nPos = ScrollInfo.nMax;
1611 break;
1612
1613 case SB_LINEDOWN:
1614 if (ScrollInfo.nPos < ScrollInfo.nMax)
1615 {
1616 ScrollInfo.nPos++;
1617 }
1618 break;
1619
1620 case SB_LINEUP:
1621 if (ScrollInfo.nPos > 0)
1622 {
1623 ScrollInfo.nPos--;
1624 }
1625 break;
1626
1627 case SB_PAGEDOWN:
1628 {
1629 RECT rcClient;
1630 INT ScrollLines;
1631
1632 /* don't use ScrollInfo.nPage because we should only scroll
1633 down by the number of completely visible list entries.
1634 nPage however also includes the partly cropped list
1635 item at the bottom of the control */
1636
1637 GetClientRect(hwnd,
1638 &rcClient);
1639
1640 ScrollLines = max(1,
1641 (rcClient.bottom - rcClient.top) / infoPtr->ItemHeight);
1642
1643 if (ScrollInfo.nPos + ScrollLines <= ScrollInfo.nMax)
1644 {
1645 ScrollInfo.nPos += ScrollLines;
1646 }
1647 else
1648 {
1649 ScrollInfo.nPos = ScrollInfo.nMax;
1650 }
1651 break;
1652 }
1653
1654 case SB_PAGEUP:
1655 {
1656 RECT rcClient;
1657 INT ScrollLines;
1658
1659 /* don't use ScrollInfo.nPage because we should only scroll
1660 down by the number of completely visible list entries.
1661 nPage however also includes the partly cropped list
1662 item at the bottom of the control */
1663
1664 GetClientRect(hwnd,
1665 &rcClient);
1666
1667 ScrollLines = max(1,
1668 (rcClient.bottom - rcClient.top) / infoPtr->ItemHeight);
1669
1670 if (ScrollInfo.nPos >= ScrollLines)
1671 {
1672 ScrollInfo.nPos -= ScrollLines;
1673 }
1674 else
1675 {
1676 ScrollInfo.nPos = 0;
1677 }
1678 break;
1679 }
1680
1681 case SB_THUMBPOSITION:
1682 case SB_THUMBTRACK:
1683 {
1684 ScrollInfo.nPos = HIWORD(wParam);
1685 break;
1686 }
1687
1688 case SB_TOP:
1689 ScrollInfo.nPos = 0;
1690 break;
1691 }
1692
1693 if (OldPos != ScrollInfo.nPos)
1694 {
1695 ScrollInfo.fMask = SIF_POS;
1696
1697 ScrollInfo.nPos = SetScrollInfo(hwnd,
1698 SB_VERT,
1699 &ScrollInfo,
1700 TRUE);
1701
1702 if (OldPos != ScrollInfo.nPos)
1703 {
1704 ScrollWindowEx(hwnd,
1705 0,
1706 (OldPos - ScrollInfo.nPos) * infoPtr->ItemHeight,
1707 NULL,
1708 NULL,
1709 NULL,
1710 NULL,
1711 SW_INVALIDATE | SW_SCROLLCHILDREN);
1712
1713 RedrawWindow(hwnd,
1714 NULL,
1715 NULL,
1716 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN);
1717 }
1718 }
1719 }
1720 break;
1721 }
1722
1723 case CLM_ADDITEM:
1724 {
1725 INT Index = -1;
1726 PCHECKITEM Item = AddCheckItem(infoPtr,
1727 (LPWSTR)lParam,
1728 CIS_NONE,
1729 (ACCESS_MASK)wParam,
1730 &Index);
1731 if (Item != NULL)
1732 {
1733 UpdateControl(infoPtr);
1734 Ret = (LRESULT)Index;
1735 }
1736 else
1737 {
1738 Ret = (LRESULT)-1;
1739 }
1740 break;
1741 }
1742
1743 case CLM_DELITEM:
1744 {
1745 PCHECKITEM Item = FindCheckItemByIndex(infoPtr,
1746 wParam);
1747 if (Item != NULL)
1748 {
1749 Ret = DeleteCheckItem(infoPtr,
1750 Item);
1751 if (Ret)
1752 {
1753 UpdateControl(infoPtr);
1754 }
1755 }
1756 else
1757 {
1758 Ret = FALSE;
1759 }
1760 break;
1761 }
1762
1763 case CLM_SETITEMSTATE:
1764 {
1765 PCHECKITEM Item = FindCheckItemByIndex(infoPtr,
1766 wParam);
1767 if (Item != NULL)
1768 {
1769 DWORD OldState = Item->State;
1770 Item->State = (DWORD)lParam & CIS_MASK;
1771
1772 if (Item->State != OldState)
1773 {
1774 /* revert the focus if the currently focused item is about
1775 to be disabled */
1776 if (Item == infoPtr->FocusedCheckItem &&
1777 (Item->State & CIS_DISABLED))
1778 {
1779 if (infoPtr->FocusedCheckItemBox == CLB_DENY)
1780 {
1781 if (Item->State & CIS_DENYDISABLED)
1782 {
1783 infoPtr->FocusedCheckItem = NULL;
1784 }
1785 }
1786 else
1787 {
1788 if (Item->State & CIS_ALLOWDISABLED)
1789 {
1790 infoPtr->FocusedCheckItem = NULL;
1791 }
1792 }
1793 }
1794
1795 UpdateControl(infoPtr);
1796 }
1797 Ret = TRUE;
1798 }
1799 break;
1800 }
1801
1802 case CLM_GETITEMCOUNT:
1803 {
1804 Ret = infoPtr->CheckItemCount;
1805 break;
1806 }
1807
1808 case CLM_CLEAR:
1809 {
1810 ClearCheckItems(infoPtr);
1811 UpdateControl(infoPtr);
1812 break;
1813 }
1814
1815 case CLM_SETCHECKBOXCOLUMN:
1816 {
1817 infoPtr->CheckBoxLeft[wParam != CLB_DENY] = (INT)lParam;
1818 UpdateControl(infoPtr);
1819 Ret = 1;
1820 break;
1821 }
1822
1823 case CLM_GETCHECKBOXCOLUMN:
1824 {
1825 Ret = (LRESULT)infoPtr->CheckBoxLeft[wParam != CLB_DENY];
1826 break;
1827 }
1828
1829 case CLM_CLEARCHECKBOXES:
1830 {
1831 Ret = (LRESULT)ClearCheckBoxes(infoPtr);
1832 if (Ret)
1833 {
1834 UpdateControl(infoPtr);
1835 }
1836 break;
1837 }
1838
1839 case CLM_ENABLEQUICKSEARCH:
1840 {
1841 if (wParam == 0)
1842 {
1843 EscapeQuickSearch(infoPtr);
1844 }
1845 infoPtr->QuickSearchEnabled = (wParam != 0);
1846 break;
1847 }
1848
1849 case CLM_SETQUICKSEARCH_TIMEOUT_RESET:
1850 {
1851 infoPtr->QuickSearchResetDelay = (UINT)wParam;
1852 break;
1853 }
1854
1855 case CLM_SETQUICKSEARCH_TIMEOUT_SETFOCUS:
1856 {
1857 infoPtr->QuickSearchSetFocusDelay = (UINT)wParam;
1858 break;
1859 }
1860
1861 case CLM_FINDITEMBYACCESSMASK:
1862 {
1863 Ret = (LRESULT)FindCheckItemIndexByAccessMask(infoPtr,
1864 (ACCESS_MASK)wParam);
1865 break;
1866 }
1867
1868 case WM_SETFONT:
1869 {
1870 Ret = (LRESULT)RetChangeControlFont(infoPtr,
1871 (HFONT)wParam,
1872 (BOOL)LOWORD(lParam));
1873 break;
1874 }
1875
1876 case WM_GETFONT:
1877 {
1878 Ret = (LRESULT)infoPtr->hFont;
1879 break;
1880 }
1881
1882 case WM_STYLECHANGED:
1883 {
1884 if (wParam == (WPARAM)GWL_STYLE)
1885 {
1886 UpdateControl(infoPtr);
1887 }
1888 break;
1889 }
1890
1891 case WM_ENABLE:
1892 {
1893 EscapeQuickSearch(infoPtr);
1894
1895 UpdateControl(infoPtr);
1896 break;
1897 }
1898
1899 case WM_MOUSEWHEEL:
1900 {
1901 SHORT ScrollDelta;
1902 UINT ScrollLines = 3;
1903
1904 SystemParametersInfo(SPI_GETWHEELSCROLLLINES,
1905 0,
1906 &ScrollLines,
1907 0);
1908 ScrollDelta = 0 - (SHORT)HIWORD(wParam);
1909
1910 if (ScrollLines != 0 &&
1911 abs(ScrollDelta) >= WHEEL_DELTA)
1912 {
1913 SCROLLINFO ScrollInfo;
1914
1915 ScrollInfo.cbSize = sizeof(ScrollInfo);
1916 ScrollInfo.fMask = SIF_RANGE | SIF_POS;
1917
1918 if (GetScrollInfo(hwnd,
1919 SB_VERT,
1920 &ScrollInfo))
1921 {
1922 INT OldPos = ScrollInfo.nPos;
1923
1924 ScrollInfo.nPos += (ScrollDelta / WHEEL_DELTA) * ScrollLines;
1925 if (ScrollInfo.nPos < 0)
1926 ScrollInfo.nPos = 0;
1927 else if (ScrollInfo.nPos > ScrollInfo.nMax)
1928 ScrollInfo.nPos = ScrollInfo.nMax;
1929
1930 if (OldPos != ScrollInfo.nPos)
1931 {
1932 ScrollInfo.fMask = SIF_POS;
1933
1934 ScrollInfo.nPos = SetScrollInfo(hwnd,
1935 SB_VERT,
1936 &ScrollInfo,
1937 TRUE);
1938
1939 if (OldPos != ScrollInfo.nPos)
1940 {
1941 ScrollWindowEx(hwnd,
1942 0,
1943 (OldPos - ScrollInfo.nPos) * infoPtr->ItemHeight,
1944 NULL,
1945 NULL,
1946 NULL,
1947 NULL,
1948 SW_INVALIDATE | SW_SCROLLCHILDREN);
1949
1950 RedrawWindow(hwnd,
1951 NULL,
1952 NULL,
1953 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN);
1954 }
1955 }
1956 }
1957 }
1958 break;
1959 }
1960
1961 case WM_SETFOCUS:
1962 {
1963 infoPtr->HasFocus = TRUE;
1964
1965 if (infoPtr->FocusedCheckItem == NULL)
1966 {
1967 BOOL Shift = GetKeyState(VK_SHIFT) & 0x8000;
1968 infoPtr->FocusedCheckItem = FindEnabledCheckBox(infoPtr,
1969 Shift,
1970 &infoPtr->FocusedCheckItemBox);
1971 }
1972 if (infoPtr->FocusedCheckItem != NULL)
1973 {
1974 MakeCheckItemVisible(infoPtr,
1975 infoPtr->FocusedCheckItem);
1976
1977 UpdateCheckItem(infoPtr,
1978 infoPtr->FocusedCheckItem);
1979 }
1980 break;
1981 }
1982
1983 case WM_KILLFOCUS:
1984 {
1985 EscapeQuickSearch(infoPtr);
1986
1987 infoPtr->HasFocus = FALSE;
1988 if (infoPtr->FocusedCheckItem != NULL)
1989 {
1990 infoPtr->FocusedPushed = FALSE;
1991
1992 UpdateCheckItem(infoPtr,
1993 infoPtr->FocusedCheckItem);
1994 }
1995 break;
1996 }
1997
1998 case WM_LBUTTONDBLCLK:
1999 case WM_LBUTTONDOWN:
2000 case WM_MBUTTONDOWN:
2001 case WM_RBUTTONDOWN:
2002 {
2003 if (IsWindowEnabled(hwnd))
2004 {
2005 PCHECKITEM NewFocus;
2006 UINT NewFocusBox = 0;
2007 BOOL InCheckBox;
2008 POINT pt;
2009 BOOL ChangeFocus, Capture = FALSE;
2010
2011 pt.x = (LONG)LOWORD(lParam);
2012 pt.y = (LONG)HIWORD(lParam);
2013
2014 NewFocus = PtToCheckItemBox(infoPtr,
2015 &pt,
2016 &NewFocusBox,
2017 &InCheckBox);
2018 if (NewFocus != NULL)
2019 {
2020 if (NewFocus->State & ((NewFocusBox != CLB_DENY) ? CIS_ALLOWDISABLED : CIS_DENYDISABLED))
2021 {
2022 /* the user clicked on a disabled checkbox, try to set
2023 the focus to the other one or not change it at all */
2024
2025 InCheckBox = FALSE;
2026
2027 ChangeFocus = ((NewFocus->State & CIS_DISABLED) != CIS_DISABLED);
2028 if (ChangeFocus)
2029 {
2030 NewFocusBox = ((NewFocusBox != CLB_DENY) ? CLB_DENY : CLB_ALLOW);
2031 }
2032 }
2033 else
2034 {
2035 ChangeFocus = TRUE;
2036 }
2037
2038 if (InCheckBox && ChangeFocus && GetCapture() == NULL &&
2039 (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONDBLCLK))
2040 {
2041 infoPtr->FocusedPushed = TRUE;
2042 Capture = TRUE;
2043 }
2044 }
2045 else
2046 {
2047 ChangeFocus = TRUE;
2048 }
2049
2050 if (ChangeFocus)
2051 {
2052 if (infoPtr->QuickSearchEnabled && infoPtr->QuickSearchHitItem != NULL &&
2053 infoPtr->QuickSearchHitItem != NewFocus)
2054 {
2055 EscapeQuickSearch(infoPtr);
2056 }
2057
2058 ChangeCheckItemFocus(infoPtr,
2059 NewFocus,
2060 NewFocusBox);
2061 }
2062
2063 if (!infoPtr->HasFocus)
2064 {
2065 SetFocus(hwnd);
2066 }
2067
2068 if (Capture)
2069 {
2070 SetCapture(hwnd);
2071 }
2072 }
2073 break;
2074 }
2075
2076 case WM_LBUTTONUP:
2077 {
2078 if (GetCapture() == hwnd)
2079 {
2080 if (infoPtr->FocusedCheckItem != NULL && infoPtr->FocusedPushed)
2081 {
2082 PCHECKITEM PtItem;
2083 UINT PtItemBox;
2084 BOOL InCheckBox;
2085 POINT pt;
2086
2087 pt.x = (LONG)LOWORD(lParam);
2088 pt.y = (LONG)HIWORD(lParam);
2089
2090 infoPtr->FocusedPushed = FALSE;
2091
2092 PtItem = PtToCheckItemBox(infoPtr,
2093 &pt,
2094 &PtItemBox,
2095 &InCheckBox);
2096
2097 if (PtItem == infoPtr->FocusedCheckItem && InCheckBox &&
2098 PtItemBox == infoPtr->FocusedCheckItemBox)
2099 {
2100 UINT OtherBox = ((PtItemBox == CLB_ALLOW) ? CLB_DENY : CLB_ALLOW);
2101 DWORD OtherStateMask = ((OtherBox == CLB_ALLOW) ?
2102 (CIS_ALLOW | CIS_ALLOWDISABLED) :
2103 (CIS_DENY | CIS_DENYDISABLED));
2104 DWORD OtherStateOld = PtItem->State & OtherStateMask;
2105 if (ChangeCheckBox(infoPtr,
2106 PtItem,
2107 PtItemBox) &&
2108 ((PtItem->State & OtherStateMask) != OtherStateOld))
2109 {
2110 UpdateCheckItemBox(infoPtr,
2111 infoPtr->FocusedCheckItem,
2112 OtherBox);
2113 }
2114 }
2115
2116 UpdateCheckItemBox(infoPtr,
2117 infoPtr->FocusedCheckItem,
2118 infoPtr->FocusedCheckItemBox);
2119 }
2120
2121 ReleaseCapture();
2122 }
2123 break;
2124 }
2125
2126 case WM_KEYDOWN:
2127 {
2128 switch (wParam)
2129 {
2130 case VK_SPACE:
2131 {
2132 if (GetCapture() == NULL &&
2133 !QuickSearchFindHit(infoPtr,
2134 L' '))
2135 {
2136 if (infoPtr->FocusedCheckItem != NULL &&
2137 (infoPtr->QuickSearchHitItem == NULL ||
2138 infoPtr->QuickSearchHitItem == infoPtr->FocusedCheckItem))
2139 {
2140 UINT OldPushed = infoPtr->FocusedPushed;
2141 infoPtr->FocusedPushed = TRUE;
2142
2143 if (infoPtr->FocusedPushed != OldPushed)
2144 {
2145 MakeCheckItemVisible(infoPtr,
2146 infoPtr->FocusedCheckItem);
2147
2148 UpdateCheckItemBox(infoPtr,
2149 infoPtr->FocusedCheckItem,
2150 infoPtr->FocusedCheckItemBox);
2151 }
2152 }
2153 }
2154 break;
2155 }
2156
2157 case VK_RETURN:
2158 {
2159 if (GetCapture() == NULL &&
2160 !QuickSearchFindHit(infoPtr,
2161 L'\n'))
2162 {
2163 if (infoPtr->FocusedCheckItem != NULL &&
2164 infoPtr->QuickSearchHitItem == NULL)
2165 {
2166 UINT OtherBox;
2167 DWORD OtherStateMask;
2168 DWORD OtherStateOld;
2169
2170 MakeCheckItemVisible(infoPtr,
2171 infoPtr->FocusedCheckItem);
2172
2173 OtherBox = ((infoPtr->FocusedCheckItemBox == CLB_ALLOW) ? CLB_DENY : CLB_ALLOW);
2174 OtherStateMask = ((OtherBox == CLB_ALLOW) ?
2175 (CIS_ALLOW | CIS_ALLOWDISABLED) :
2176 (CIS_DENY | CIS_DENYDISABLED));
2177 OtherStateOld = infoPtr->FocusedCheckItem->State & OtherStateMask;
2178 if (ChangeCheckBox(infoPtr,
2179 infoPtr->FocusedCheckItem,
2180 infoPtr->FocusedCheckItemBox))
2181 {
2182 UpdateCheckItemBox(infoPtr,
2183 infoPtr->FocusedCheckItem,
2184 infoPtr->FocusedCheckItemBox);
2185 if ((infoPtr->FocusedCheckItem->State & OtherStateMask) != OtherStateOld)
2186 {
2187 UpdateCheckItemBox(infoPtr,
2188 infoPtr->FocusedCheckItem,
2189 OtherBox);
2190 }
2191 }
2192 }
2193 }
2194 break;
2195 }
2196
2197 case VK_TAB:
2198 {
2199 if (GetCapture() == NULL)
2200 {
2201 PCHECKITEM NewFocus;
2202 UINT NewFocusBox = 0;
2203 BOOL Shift = GetKeyState(VK_SHIFT) & 0x8000;
2204
2205 EscapeQuickSearch(infoPtr);
2206
2207 NewFocus = FindEnabledCheckBox(infoPtr,
2208 Shift,
2209 &NewFocusBox);
2210
2211 /* update the UI status */
2212 SendMessage(GetAncestor(hwnd,
2213 GA_PARENT),
2214 WM_CHANGEUISTATE,
2215 MAKEWPARAM(UIS_INITIALIZE,
2216 0),
2217 0);
2218
2219 ChangeCheckItemFocus(infoPtr,
2220 NewFocus,
2221 NewFocusBox);
2222 }
2223 break;
2224 }
2225
2226 default:
2227 {
2228 goto HandleDefaultMessage;
2229 }
2230 }
2231 break;
2232 }
2233
2234 case WM_KEYUP:
2235 {
2236 if (wParam == VK_SPACE && IsWindowEnabled(hwnd) &&
2237 infoPtr->FocusedCheckItem != NULL &&
2238 infoPtr->FocusedPushed)
2239 {
2240 UINT OtherBox = ((infoPtr->FocusedCheckItemBox == CLB_ALLOW) ? CLB_DENY : CLB_ALLOW);
2241 DWORD OtherStateMask = ((OtherBox == CLB_ALLOW) ?
2242 (CIS_ALLOW | CIS_ALLOWDISABLED) :
2243 (CIS_DENY | CIS_DENYDISABLED));
2244 DWORD OtherStateOld = infoPtr->FocusedCheckItem->State & OtherStateMask;
2245
2246 infoPtr->FocusedPushed = FALSE;
2247
2248 if (ChangeCheckBox(infoPtr,
2249 infoPtr->FocusedCheckItem,
2250 infoPtr->FocusedCheckItemBox))
2251 {
2252 UpdateCheckItemBox(infoPtr,
2253 infoPtr->FocusedCheckItem,
2254 infoPtr->FocusedCheckItemBox);
2255
2256 if ((infoPtr->FocusedCheckItem->State & OtherStateMask) != OtherStateOld)
2257 {
2258 UpdateCheckItemBox(infoPtr,
2259 infoPtr->FocusedCheckItem,
2260 OtherBox);
2261 }
2262 }
2263 }
2264 break;
2265 }
2266
2267 case WM_GETDLGCODE:
2268 {
2269 INT virtKey;
2270
2271 Ret = 0;
2272 virtKey = (lParam != 0 ? (INT)((LPMSG)lParam)->wParam : 0);
2273 switch (virtKey)
2274 {
2275 case VK_RETURN:
2276 {
2277 if (infoPtr->QuickSearchEnabled && infoPtr->QuickSearchHitItem != NULL)
2278 {
2279 Ret |= DLGC_WANTCHARS | DLGC_WANTMESSAGE;
2280 }
2281 else
2282 {
2283 Ret |= DLGC_WANTMESSAGE;
2284 }
2285 break;
2286 }
2287
2288 case VK_TAB:
2289 {
2290 UINT CheckBox;
2291 BOOL EnabledBox;
2292 BOOL Shift = GetKeyState(VK_SHIFT) & 0x8000;
2293
2294 EnabledBox = FindEnabledCheckBox(infoPtr,
2295 Shift,
2296 &CheckBox) != NULL;
2297 Ret |= (EnabledBox ? DLGC_WANTTAB : DLGC_WANTCHARS);
2298 break;
2299 }
2300
2301 default:
2302 {
2303 if (infoPtr->QuickSearchEnabled)
2304 {
2305 Ret |= DLGC_WANTCHARS;
2306 }
2307 break;
2308 }
2309 }
2310 break;
2311 }
2312
2313 case WM_CHAR:
2314 {
2315 QuickSearchFindHit(infoPtr,
2316 (WCHAR)wParam);
2317 break;
2318 }
2319
2320 case WM_SYSCOLORCHANGE:
2321 {
2322 infoPtr->TextColor[0] = GetSysColor(COLOR_GRAYTEXT);
2323 infoPtr->TextColor[1] = GetSysColor(COLOR_WINDOWTEXT);
2324 break;
2325 }
2326
2327 #if SUPPORT_UXTHEME
2328 case WM_MOUSELEAVE:
2329 {
2330 if (infoPtr->HoveredCheckItem != NULL)
2331 {
2332 /* reset and repaint the hovered check item box */
2333 ChangeCheckItemHotTrack(infoPtr,
2334 NULL,
2335 0);
2336 }
2337 break;
2338 }
2339
2340 case WM_THEMECHANGED:
2341 {
2342 if (infoPtr->ThemeHandle != NULL)
2343 {
2344 CloseThemeData(infoPtr->ThemeHandle);
2345 infoPtr->ThemeHandle = NULL;
2346 }
2347 if (IsAppThemed())
2348 {
2349 infoPtr->ThemeHandle = OpenThemeData(infoPtr->hSelf,
2350 L"BUTTON");
2351 }
2352 break;
2353 }
2354 #endif
2355
2356 case WM_SETTINGCHANGE:
2357 {
2358 DWORD OldCaretWidth = infoPtr->CaretWidth;
2359
2360 #if SUPPORT_UXTHEME
2361 /* update the hover time */
2362 if (!SystemParametersInfo(SPI_GETMOUSEHOVERTIME,
2363 0,
2364 &infoPtr->HoverTime,
2365 0))
2366 {
2367 infoPtr->HoverTime = HOVER_DEFAULT;
2368 }
2369 #endif
2370
2371 /* update the caret */
2372 if (!SystemParametersInfo(SPI_GETCARETWIDTH,
2373 0,
2374 &infoPtr->CaretWidth,
2375 0))
2376 {
2377 infoPtr->CaretWidth = 2;
2378 }
2379 if (OldCaretWidth != infoPtr->CaretWidth && infoPtr->ShowingCaret)
2380 {
2381 DestroyCaret();
2382 CreateCaret(hwnd,
2383 NULL,
2384 infoPtr->CaretWidth,
2385 infoPtr->ItemHeight - (2 * CI_TEXT_MARGIN_HEIGHT));
2386 }
2387 break;
2388 }
2389
2390 case WM_SIZE:
2391 {
2392 UpdateControl(infoPtr);
2393 break;
2394 }
2395
2396 case WM_UPDATEUISTATE:
2397 {
2398 DWORD OldUIState = infoPtr->UIState;
2399
2400 switch (LOWORD(wParam))
2401 {
2402 case UIS_SET:
2403 infoPtr->UIState |= HIWORD(wParam);
2404 break;
2405
2406 case UIS_CLEAR:
2407 infoPtr->UIState &= ~(HIWORD(wParam));
2408 break;
2409 }
2410
2411 if (OldUIState != infoPtr->UIState)
2412 {
2413 if (infoPtr->FocusedCheckItem != NULL)
2414 {
2415 UpdateCheckItemBox(infoPtr,
2416 infoPtr->FocusedCheckItem,
2417 infoPtr->FocusedCheckItemBox);
2418 }
2419 }
2420 break;
2421 }
2422
2423 case WM_TIMER:
2424 {
2425 switch (wParam)
2426 {
2427 case TIMER_ID_SETHITFOCUS:
2428 {
2429 /* kill the timer */
2430 KillTimer(hwnd,
2431 wParam);
2432
2433 if (infoPtr->QuickSearchEnabled && infoPtr->QuickSearchHitItem != NULL)
2434 {
2435 /* change the focus to the hit item, this item has to have
2436 at least one enabled checkbox! */
2437 ChangeCheckItemFocus(infoPtr,
2438 infoPtr->QuickSearchHitItem,
2439 ((!(infoPtr->QuickSearchHitItem->State & CIS_ALLOWDISABLED)) ? CLB_ALLOW : CLB_DENY));
2440
2441 /* start the timer to reset quicksearch */
2442 if (infoPtr->QuickSearchResetDelay != 0)
2443 {
2444 SetTimer(hwnd,
2445 TIMER_ID_RESETQUICKSEARCH,
2446 infoPtr->QuickSearchResetDelay,
2447 NULL);
2448 }
2449 }
2450 break;
2451 }
2452 case TIMER_ID_RESETQUICKSEARCH:
2453 {
2454 /* kill the timer */
2455 KillTimer(hwnd,
2456 wParam);
2457
2458 /* escape quick search */
2459 EscapeQuickSearch(infoPtr);
2460 break;
2461 }
2462 }
2463 break;
2464 }
2465
2466 case WM_CREATE:
2467 {
2468 infoPtr = HeapAlloc(GetProcessHeap(),
2469 0,
2470 sizeof(CHECKLISTWND));
2471 if (infoPtr != NULL)
2472 {
2473 RECT rcClient;
2474
2475 infoPtr->hSelf = hwnd;
2476 infoPtr->hNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
2477
2478 SetWindowLongPtr(hwnd,
2479 0,
2480 (DWORD_PTR)infoPtr);
2481
2482 infoPtr->CheckItemListHead = NULL;
2483 infoPtr->CheckItemCount = 0;
2484
2485 if (!SystemParametersInfo(SPI_GETCARETWIDTH,
2486 0,
2487 &infoPtr->CaretWidth,
2488 0))
2489 {
2490 infoPtr->CaretWidth = 2;
2491 }
2492 infoPtr->ItemHeight = 10;
2493 infoPtr->ShowingCaret = FALSE;
2494
2495 infoPtr->HasFocus = FALSE;
2496 infoPtr->FocusedCheckItem = NULL;
2497 infoPtr->FocusedCheckItemBox = 0;
2498 infoPtr->FocusedPushed = FALSE;
2499
2500 infoPtr->TextColor[0] = GetSysColor(COLOR_GRAYTEXT);
2501 infoPtr->TextColor[1] = GetSysColor(COLOR_WINDOWTEXT);
2502
2503 GetClientRect(hwnd,
2504 &rcClient);
2505
2506 infoPtr->CheckBoxLeft[0] = rcClient.right - 30;
2507 infoPtr->CheckBoxLeft[1] = rcClient.right - 15;
2508
2509 infoPtr->QuickSearchEnabled = FALSE;
2510 infoPtr->QuickSearchText[0] = L'\0';
2511
2512 infoPtr->QuickSearchSetFocusDelay = DEFAULT_QUICKSEARCH_SETFOCUS_DELAY;
2513 infoPtr->QuickSearchResetDelay = DEFAULT_QUICKSEARCH_RESET_DELAY;
2514
2515 #if SUPPORT_UXTHEME
2516 infoPtr->HoveredCheckItem = NULL;
2517 infoPtr->HoveredCheckItemBox = 0;
2518 if (!SystemParametersInfo(SPI_GETMOUSEHOVERTIME,
2519 0,
2520 &infoPtr->HoverTime,
2521 0))
2522 {
2523 infoPtr->HoverTime = HOVER_DEFAULT;
2524 }
2525
2526 if (IsAppThemed())
2527 {
2528 infoPtr->ThemeHandle = OpenThemeData(infoPtr->hSelf,
2529 L"BUTTON");
2530 }
2531 else
2532 {
2533 infoPtr->ThemeHandle = NULL;
2534 }
2535 #endif
2536
2537 infoPtr->UIState = SendMessage(hwnd,
2538 WM_QUERYUISTATE,
2539 0,
2540 0);
2541 }
2542 else
2543 {
2544 Ret = -1;
2545 }
2546 break;
2547 }
2548
2549 case WM_DESTROY:
2550 {
2551 if (infoPtr->ShowingCaret)
2552 {
2553 DestroyCaret();
2554 }
2555
2556 ClearCheckItems(infoPtr);
2557
2558 #if SUPPORT_UXTHEME
2559 if (infoPtr->ThemeHandle != NULL)
2560 {
2561 CloseThemeData(infoPtr->ThemeHandle);
2562 }
2563 #endif
2564
2565 HeapFree(GetProcessHeap(),
2566 0,
2567 infoPtr);
2568 SetWindowLongPtr(hwnd,
2569 0,
2570 (DWORD_PTR)NULL);
2571 break;
2572 }
2573
2574 default:
2575 {
2576 HandleDefaultMessage:
2577 Ret = DefWindowProc(hwnd,
2578 uMsg,
2579 wParam,
2580 lParam);
2581 break;
2582 }
2583 }
2584
2585 return Ret;
2586 }
2587
2588 BOOL
2589 RegisterCheckListControl(IN HINSTANCE hInstance)
2590 {
2591 WNDCLASS wc;
2592
2593 wc.style = CS_DBLCLKS;
2594 wc.lpfnWndProc = CheckListWndProc;
2595 wc.cbClsExtra = 0;
2596 wc.cbWndExtra = sizeof(PCHECKLISTWND);
2597 wc.hInstance = hInstance;
2598 wc.hIcon = NULL;
2599 wc.hCursor = LoadCursor(NULL,
2600 (LPWSTR)IDC_ARROW);
2601 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
2602 wc.lpszMenuName = NULL;
2603 wc.lpszClassName = szCheckListWndClass;
2604
2605 return RegisterClass(&wc) != 0;
2606 }
2607
2608 VOID
2609 UnregisterCheckListControl(HINSTANCE hInstance)
2610 {
2611 UnregisterClass(szCheckListWndClass,
2612 hInstance);
2613 }