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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 2614 UnregisterCheckListControl(HINSTANCE hInstance) 2615 { 2616 UnregisterClass(szCheckListWndClass, 2617 hInstance); 2618 } 2619