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