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