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