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