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