add a quick search feature to the checklist control
Modified: trunk/reactos/lib/aclui/aclui.c
Modified: trunk/reactos/lib/aclui/acluilib.h
Modified: trunk/reactos/lib/aclui/checklist.c

Modified: trunk/reactos/lib/aclui/aclui.c
--- trunk/reactos/lib/aclui/aclui.c	2005-07-04 13:37:02 UTC (rev 16409)
+++ trunk/reactos/lib/aclui/aclui.c	2005-07-04 17:22:18 UTC (rev 16410)
@@ -705,6 +705,12 @@
                     ShowWindow(GetDlgItem(hwndDlg, IDC_LABEL_ADVANCED),
                                SW_HIDE);
                 }
+                
+                /* enable quicksearch for the permissions checklist control */
+                SendMessage(sp->hAceCheckList,
+                            CLM_ENABLEQUICKSEARCH,
+                            TRUE,
+                            0);
             }
             break;
         }

Modified: trunk/reactos/lib/aclui/acluilib.h
--- trunk/reactos/lib/aclui/acluilib.h	2005-07-04 13:37:02 UTC (rev 16409)
+++ trunk/reactos/lib/aclui/acluilib.h	2005-07-04 17:22:18 UTC (rev 16410)
@@ -102,6 +102,9 @@
 #define CLM_GETCHECKBOXCOLUMN   (WM_USER + 6)
 #define CLM_CLEARCHECKBOXES     (WM_USER + 7)
 #define CLM_SETITEMSTATE        (WM_USER + 8)
+#define CLM_ENABLEQUICKSEARCH   (WM_USER + 9)
+#define CLM_SETQUICKSEARCH_TIMEOUT_RESET        (WM_USER + 10)
+#define CLM_SETQUICKSEARCH_TIMEOUT_SETFOCUS     (WM_USER + 11)
 
 BOOL
 RegisterCheckListControl(HINSTANCE hInstance);

Modified: trunk/reactos/lib/aclui/checklist.c
--- trunk/reactos/lib/aclui/checklist.c	2005-07-04 13:37:02 UTC (rev 16409)
+++ trunk/reactos/lib/aclui/checklist.c	2005-07-04 17:22:18 UTC (rev 16410)
@@ -28,6 +28,16 @@
  */
 #include "acluilib.h"
 
+#define CI_TEXT_MARGIN_WIDTH    (8)
+#define CI_TEXT_MARGIN_HEIGHT   (3)
+#define CI_TEXT_SELECTIONMARGIN (1)
+
+#define TIMER_ID_SETHITFOCUS    (1)
+#define TIMER_ID_RESETQUICKSEARCH       (2)
+
+#define DEFAULT_QUICKSEARCH_SETFOCUS_DELAY      (2000)
+#define DEFAULT_QUICKSEARCH_RESET_DELAY (3000)
+
 typedef struct _CHECKITEM
 {
     struct _CHECKITEM *Next;
@@ -54,6 +64,15 @@
    
    COLORREF TextColor[2];
    UINT CheckBoxLeft[2];
+   
+   BOOL QuickSearchEnabled;
+   PCHECKITEM QuickSearchHitItem;
+   WCHAR QuickSearchText[65];
+   UINT QuickSearchSetFocusDelay;
+   UINT QuickSearchResetDelay;
+   
+   DWORD CaretWidth;
+   BOOL ShowingCaret;
 
 #if SUPPORT_UXTHEME
    PCHECKITEM HoveredCheckItem;
@@ -64,9 +83,18 @@
 #endif
 } CHECKLISTWND, *PCHECKLISTWND;
 
-#define CI_TEXT_MARGIN_WIDTH    (8)
-#define CI_TEXT_MARGIN_HEIGHT   (3)
+static VOID EscapeQuickSearch(IN PCHECKLISTWND infoPtr);
+#if SUPPORT_UXTHEME
+static VOID ChangeCheckItemHotTrack(IN PCHECKLISTWND infoPtr,
+                                    IN PCHECKITEM NewHotTrack,
+                                    IN UINT NewHotTrackBox);
+#endif
+static VOID ChangeCheckItemFocus(IN PCHECKLISTWND infoPtr,
+                                 IN PCHECKITEM NewFocus,
+                                 IN UINT NewFocusBox);
 
+/******************************************************************************/
+
 static PCHECKITEM
 FindCheckItemByIndex(IN PCHECKLISTWND infoPtr,
                      IN UINT Index)
@@ -113,6 +141,27 @@
 }
 
 static PCHECKITEM
+FindCheckItem(IN PCHECKLISTWND infoPtr,
+              IN LPWSTR SearchText)
+{
+    PCHECKITEM CurItem;
+    SIZE_T Count = wcslen(SearchText);
+    
+    for (CurItem = infoPtr->CheckItemListHead;
+         CurItem != NULL;
+         CurItem = CurItem->Next)
+    {
+        if ((CurItem->State & CIS_DISABLED) != CIS_DISABLED &&
+            !wcsnicmp(CurItem->Name, SearchText, Count))
+        {
+            break;
+        }
+    }
+    
+    return CurItem;
+}
+
+static PCHECKITEM
 FindFirstEnabledCheckBox(IN PCHECKLISTWND infoPtr,
                          OUT UINT *CheckBox)
 {
@@ -366,6 +415,27 @@
     {
         if (CurItem == Item)
         {
+            if (Item == infoPtr->QuickSearchHitItem && infoPtr->QuickSearchEnabled)
+            {
+                EscapeQuickSearch(infoPtr);
+            }
+            
+#if SUPPORT_UXTHEME
+            if (Item == infoPtr->HoveredCheckItem)
+            {
+                ChangeCheckItemHotTrack(infoPtr,
+                                        NULL,
+                                        0);
+            }
+#endif
+            
+            if (Item == infoPtr->FocusedCheckItem)
+            {
+                ChangeCheckItemFocus(infoPtr,
+                                     NULL,
+                                     0);
+            }
+            
             *PrevPtr = CurItem->Next;
             HeapFree(GetProcessHeap(),
                      0,
@@ -608,7 +678,7 @@
                                    NULL,
                                    NULL,
                                    NULL,
-                                   SW_INVALIDATE);
+                                   SW_INVALIDATE | SW_SCROLLCHILDREN);
 
                     RedrawWindow(infoPtr->hSelf,
                                  NULL,
@@ -664,7 +734,16 @@
     {
         infoPtr->ItemHeight = (2 * CI_TEXT_MARGIN_HEIGHT) + GetIdealItemHeight(infoPtr);
     }
-
+    
+    if (infoPtr->ShowingCaret)
+    {
+        DestroyCaret();
+        CreateCaret(infoPtr->hSelf,
+                    NULL,
+                    0,
+                    infoPtr->ItemHeight - (2 * CI_TEXT_MARGIN_HEIGHT));
+    }
+    
     UpdateControl(infoPtr,
                   TRUE);
 
@@ -788,14 +867,73 @@
 #if SUPPORT_UXTHEME
             ItemHovered = (Enabled && infoPtr->HoveredCheckItem == Item);
 #endif
+
+            if (infoPtr->QuickSearchHitItem == Item)
+            {
+                COLORREF OldBkColor, OldFgColor;
+                SIZE TextSize;
+                SIZE_T TextLen, HighlightLen = wcslen(infoPtr->QuickSearchText);
+
+                /* highlight the quicksearch text */
+                if (GetTextExtentPoint32(hDC,
+                                         Item->Name,
+                                         HighlightLen,
+                                         &TextSize))
+                {
+                    COLORREF HighlightTextColor, HighlightBackground;
+                    RECT rcHighlight = TextRect;
+                    
+                    HighlightTextColor = GetSysColor(COLOR_HIGHLIGHTTEXT);
+                    HighlightBackground = GetSysColor(COLOR_HIGHLIGHT);
+
+                    rcHighlight.right = rcHighlight.left + TextSize.cx;
+                    
+                    InflateRect(&rcHighlight,
+                                0,
+                                CI_TEXT_SELECTIONMARGIN);
+                    
+                    OldBkColor = SetBkColor(hDC,
+                                            HighlightBackground);
+                    OldFgColor = SetTextColor(hDC,
+                                              HighlightTextColor);
+
+                    /* draw the highlighted text */
+                    DrawText(hDC,
+                             Item->Name,
+                             HighlightLen,
+                             &rcHighlight,
+                             DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
+
+                    SetBkColor(hDC,
+                               OldBkColor);
+                    SetTextColor(hDC,
+                                 OldFgColor);
+
+                    /* draw the remaining part of the text */
+                    TextLen = wcslen(Item->Name);
+                    if (HighlightLen < TextLen)
+                    {
+                        rcHighlight.left = rcHighlight.right;
+                        rcHighlight.right = TextRect.right;
+                        
+                        DrawText(hDC,
+                                 Item->Name + HighlightLen,
+                                 -1,
+                                 &rcHighlight,
+                                 DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
+                    }
+                }
+            }
+            else
+            {
+                /* draw the text */
+                DrawText(hDC,
+                         Item->Name,
+                         -1,
+                         &TextRect,
+                         DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
+            }
             
-            /* draw the text */
-            DrawText(hDC,
-                     Item->Name,
-                     -1,
-                     &TextRect,
-                     DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
-
             CheckBox.top = TextRect.top;
             CheckBox.bottom = TextRect.bottom;
 
@@ -1059,6 +1197,295 @@
     CheckItem->State = NewState;
 }
 
+static VOID
+DisplayCaret(IN PCHECKLISTWND infoPtr)
+{
+    if (IsWindowEnabled(infoPtr->hSelf) && !infoPtr->ShowingCaret)
+    {
+        infoPtr->ShowingCaret = TRUE;
+        
+        CreateCaret(infoPtr->hSelf,
+                    NULL,
+                    infoPtr->CaretWidth,
+                    infoPtr->ItemHeight - (2 * CI_TEXT_MARGIN_HEIGHT));
+
+        ShowCaret(infoPtr->hSelf);
+    }
+}
+
+static VOID
+RemoveCaret(IN PCHECKLISTWND infoPtr)
+{
+    if (IsWindowEnabled(infoPtr->hSelf) && infoPtr->ShowingCaret)
+    {
+        infoPtr->ShowingCaret = FALSE;
+        
+        HideCaret(infoPtr->hSelf);
+        DestroyCaret();
+    }
+}
+
+static VOID
+KillQuickSearchTimers(IN PCHECKLISTWND infoPtr)
+{
+    KillTimer(infoPtr->hSelf,
+              TIMER_ID_SETHITFOCUS);
+    KillTimer(infoPtr->hSelf,
+              TIMER_ID_RESETQUICKSEARCH);
+}
+
+static VOID
+MapItemToRect(IN PCHECKLISTWND infoPtr,
+              IN PCHECKITEM CheckItem,
+              OUT RECT *prcItem)
+{
+    INT Index = CheckItemToIndex(infoPtr,
+                                 CheckItem);
+    if (Index != -1)
+    {
+        RECT rcClient;
+        LONG Style;
+        INT VisibleFirst;
+        
+        GetClientRect(infoPtr->hSelf, &rcClient);
+        
+        Style = GetWindowLong(infoPtr->hSelf,
+                              GWL_STYLE);
+
+        if (Style & WS_VSCROLL)
+        {
+            VisibleFirst = GetScrollPos(infoPtr->hSelf,
+                                        SB_VERT);
+        }
+        else
+        {
+            VisibleFirst = 0;
+        }
+        
+        prcItem->left = rcClient.left;
+        prcItem->right = rcClient.right;
+        prcItem->top = (Index - VisibleFirst) * infoPtr->ItemHeight;
+        prcItem->bottom = prcItem->top + infoPtr->ItemHeight;
+    }
+    else
+    {
+        prcItem->left = 0;
+        prcItem->top = 0;
+        prcItem->right = 0;
+        prcItem->bottom = 0;
+    }
+}
+
+static VOID
+UpdateCaretPos(IN PCHECKLISTWND infoPtr)
+{
+    if (infoPtr->ShowingCaret && infoPtr->QuickSearchHitItem != NULL)
+    {
+        HDC hDC = GetDC(infoPtr->hSelf);
+        if (hDC != NULL)
+        {
+            SIZE TextSize;
+            HGDIOBJ hOldFont = SelectObject(hDC,
+                                            infoPtr->hFont);
+
+            TextSize.cx = 0;
+            TextSize.cy = 0;
+            
+            if (infoPtr->QuickSearchText[0] == L'\0' ||
+                GetTextExtentPoint32(hDC,
+                                     infoPtr->QuickSearchHitItem->Name,
+                                     wcslen(infoPtr->QuickSearchText),
+                                     &TextSize))
+            {
+                RECT rcItem;
+                
+                MapItemToRect(infoPtr,
+                              infoPtr->QuickSearchHitItem,
+                              &rcItem);
+
+                /* actually change the caret position */
+                SetCaretPos(rcItem.left + CI_TEXT_MARGIN_WIDTH + TextSize.cx,
+                            rcItem.top + CI_TEXT_MARGIN_HEIGHT);
+            }
+
+            SelectObject(hDC,
+                         hOldFont);
+
+            ReleaseDC(infoPtr->hSelf,
+                      hDC);
+        }
+    }
+}
+
+static VOID
+EscapeQuickSearch(IN PCHECKLISTWND infoPtr)
+{
+    if (infoPtr->QuickSearchEnabled && infoPtr->QuickSearchHitItem != NULL)
+    {
+        PCHECKITEM OldHit = infoPtr->QuickSearchHitItem;
+        
+        infoPtr->QuickSearchHitItem = NULL;
+        infoPtr->QuickSearchText[0] = L'\0';
+        
+        /* scroll back to the focused item */
+        if (infoPtr->FocusedCheckItem != NULL)
+        {
+            MakeCheckItemVisible(infoPtr,
+                                 infoPtr->FocusedCheckItem);
+        }
+        
+        /* repaint the old search hit item if it's still visible */
+        UpdateCheckItem(infoPtr,
+                        OldHit);
+
+        KillQuickSearchTimers(infoPtr);
+        
+        RemoveCaret(infoPtr);
+    }
+}
+
+static VOID
+ChangeSearchHit(IN PCHECKLISTWND infoPtr,
+                IN PCHECKITEM NewHit)
+{
+    PCHECKITEM OldHit = infoPtr->QuickSearchHitItem;
+    
+    infoPtr->QuickSearchHitItem = NewHit;
+    
+    if (OldHit != NewHit)
+    {
+        /* scroll to the new search hit */
+        MakeCheckItemVisible(infoPtr,
+                             NewHit);
+        
+        /* repaint the old hit if present and visible */
+        if (OldHit != NULL)
+        {
+            UpdateCheckItem(infoPtr,
+                            OldHit);
+        }
+        else
+        {
+            /* show the caret the first time we find an item */
+             DisplayCaret(infoPtr);
+        }
+    }
+    
+    UpdateCaretPos(infoPtr);
+    
+    UpdateCheckItem(infoPtr,
+                    NewHit);
+    
+    /* kill the reset timer and restart the set hit focus timer */
+    KillTimer(infoPtr->hSelf,
+              TIMER_ID_RESETQUICKSEARCH);
+    if (infoPtr->QuickSearchSetFocusDelay != 0)
+    {
+        SetTimer(infoPtr->hSelf,
+                 TIMER_ID_SETHITFOCUS,
+                 infoPtr->QuickSearchSetFocusDelay,
+                 NULL);
+    }
+}
+
+static BOOL
+QuickSearchFindHit(IN PCHECKLISTWND infoPtr,
+                   IN WCHAR c)
+{
+    if (infoPtr->QuickSearchEnabled)
+    {
+        BOOL Ret = FALSE;
+        PCHECKITEM NewHit;
+        
+        switch (c)
+        {
+            case '\r':
+            case '\n':
+            {
+                Ret = infoPtr->QuickSearchHitItem != NULL;
+                if (Ret)
+                {
+                    /* NOTE: QuickSearchHitItem definitely has at least one
+                             enabled check box, the user can't search for disabled
+                             check items */
+
+                    ChangeCheckItemFocus(infoPtr,
+                                         infoPtr->QuickSearchHitItem,
+                                         ((!(infoPtr->QuickSearchHitItem->State & CIS_ALLOWDISABLED)) ? CLB_ALLOW : CLB_DENY));
+
+                    EscapeQuickSearch(infoPtr);
+                }
+                break;
+            }
+            
+            case VK_BACK:
+            {
+                if (infoPtr->QuickSearchHitItem != NULL)
+                {
+                    INT SearchLen = wcslen(infoPtr->QuickSearchText);
+                    if (SearchLen > 0)
+                    {
+                        /* delete the last character */
+                        infoPtr->QuickSearchText[--SearchLen] = L'\0';
+                        
+                        if (SearchLen > 0)
+                        {
+                            /* search again */
+                            NewHit = FindCheckItem(infoPtr,
+                                                   infoPtr->QuickSearchText);
+
+                            if (NewHit != NULL)
+                            {
+                                /* change the search hit */
+                                ChangeSearchHit(infoPtr,
+                                                NewHit);
+
+                                Ret = TRUE;
+                            }
+                        }
+                    }
+
+                    if (!Ret)
+                    {
+                        EscapeQuickSearch(infoPtr);
+                    }
+                }
+                break;
+            }
+            
+            default:
+            {
+                INT SearchLen = wcslen(infoPtr->QuickSearchText);
+                if (SearchLen < (sizeof(infoPtr->QuickSearchText) / sizeof(infoPtr->QuickSearchText[0])) - 1)
+                {
+                    infoPtr->QuickSearchText[SearchLen++] = c;
+                    infoPtr->QuickSearchText[SearchLen] = L'\0';
+                    
+                    NewHit = FindCheckItem(infoPtr,
+                                           infoPtr->QuickSearchText);
+                    if (NewHit != NULL)
+                    {
+                        /* change the search hit */
+                        ChangeSearchHit(infoPtr,
+                                        NewHit);
+                        
+                        Ret = TRUE;
+                    }
+                    else
+                    {
+                        /* reset the input */
+                        infoPtr->QuickSearchText[--SearchLen] = L'\0';
+                    }
+                }
+                break;
+            }
+        }
+        return Ret;
+    }
+    
+    return FALSE;
+}
+
 static LRESULT CALLBACK
 CheckListWndProc(IN HWND hwnd,
                  IN UINT uMsg,
@@ -1292,7 +1719,7 @@
                                        NULL,
                                        NULL,
                                        NULL,
-                                       SW_INVALIDATE);
+                                       SW_INVALIDATE | SW_SCROLLCHILDREN);
 
                         RedrawWindow(hwnd,
                                      NULL,
@@ -1427,6 +1854,28 @@
             break;
         }
         
+        case CLM_ENABLEQUICKSEARCH:
+        {
+            if (wParam == 0)
+            {
+                EscapeQuickSearch(infoPtr);
+            }
+            infoPtr->QuickSearchEnabled = (wParam != 0);
+            break;
+        }
+        
+        case CLM_SETQUICKSEARCH_TIMEOUT_RESET:
+        {
+            infoPtr->QuickSearchResetDelay = (UINT)wParam;
+            break;
+        }
+        
+        case CLM_SETQUICKSEARCH_TIMEOUT_SETFOCUS:
+        {
+            infoPtr->QuickSearchSetFocusDelay = (UINT)wParam;
+            break;
+        }
+        
         case WM_SETFONT:
         {
             Ret = (LRESULT)RetChangeControlFont(infoPtr,
@@ -1461,6 +1910,8 @@
         
         case WM_ENABLE:
         {
+            EscapeQuickSearch(infoPtr);
+            
             UpdateControl(infoPtr,
                           TRUE);
             break;
@@ -1515,7 +1966,7 @@
                                            NULL,
                                            NULL,
                                            NULL,
-                                           SW_INVALIDATE);
+                                           SW_INVALIDATE | SW_SCROLLCHILDREN);
 
                             RedrawWindow(hwnd,
                                          NULL,
@@ -1552,6 +2003,8 @@
         
         case WM_KILLFOCUS:
         {
+            EscapeQuickSearch(infoPtr);
+            
             infoPtr->HasFocus = FALSE;
             if (infoPtr->FocusedCheckItem != NULL)
             {
@@ -1617,6 +2070,12 @@
                 
                 if (ChangeFocus)
                 {
+                    if (infoPtr->QuickSearchEnabled && infoPtr->QuickSearchHitItem != NULL &&
+                        infoPtr->QuickSearchHitItem != NewFocus)
+                    {
+                        EscapeQuickSearch(infoPtr);
+                    }
+                    
                     ChangeCheckItemFocus(infoPtr,
                                          NewFocus,
                                          NewFocusBox);
@@ -1680,19 +2139,26 @@
             {
                 case VK_SPACE:
                 {
-                    if (infoPtr->FocusedCheckItem != NULL && GetCapture() == NULL)
+                    if (GetCapture() == NULL &&
+                        !QuickSearchFindHit(infoPtr,
+                                            L' '))
                     {
-                        BOOL OldPushed = infoPtr->FocusedPushed;
-                        infoPtr->FocusedPushed = TRUE;
-
-                        if (infoPtr->FocusedPushed != OldPushed)
+                        if (infoPtr->FocusedCheckItem != NULL &&
+                            (infoPtr->QuickSearchHitItem == NULL ||
+                             infoPtr->QuickSearchHitItem == infoPtr->FocusedCheckItem))
                         {
-                            MakeCheckItemVisible(infoPtr,
-                                                 infoPtr->FocusedCheckItem);
+                            BOOL OldPushed = infoPtr->FocusedPushed;
+                            infoPtr->FocusedPushed = TRUE;
 
-                            UpdateCheckItemBox(infoPtr,
-                                               infoPtr->FocusedCheckItem,
-                                               infoPtr->FocusedCheckItemBox);
+                            if (infoPtr->FocusedPushed != OldPushed)
+                            {
+                                MakeCheckItemVisible(infoPtr,
+                                                     infoPtr->FocusedCheckItem);
+
+                                UpdateCheckItemBox(infoPtr,
+                                                   infoPtr->FocusedCheckItem,
+                                                   infoPtr->FocusedCheckItemBox);
+                            }
                         }
                     }
                     break;
@@ -1700,18 +2166,24 @@
                 
                 case VK_RETURN:
                 {
-                    if (infoPtr->FocusedCheckItem != NULL && GetCapture() == NULL)
+                    if (GetCapture() == NULL &&
+                        !QuickSearchFindHit(infoPtr,
+                                            L'\n'))
                     {
-                        MakeCheckItemVisible(infoPtr,
-                                             infoPtr->FocusedCheckItem);
+                        if (infoPtr->FocusedCheckItem != NULL &&
+                            infoPtr->QuickSearchHitItem == NULL)
+                        {
+                            MakeCheckItemVisible(infoPtr,
+                                                 infoPtr->FocusedCheckItem);
 
-                        ChangeCheckBox(infoPtr,
-                                       infoPtr->FocusedCheckItem,
-                                       infoPtr->FocusedCheckItemBox);
-
-                        UpdateCheckItemBox(infoPtr,
+                            ChangeCheckBox(infoPtr,
                                            infoPtr->FocusedCheckItem,
                                            infoPtr->FocusedCheckItemBox);
+
+                            UpdateCheckItemBox(infoPtr,
+                                               infoPtr->FocusedCheckItem,
+                                               infoPtr->FocusedCheckItemBox);
+                        }
                     }
                     break;
                 }
@@ -1723,6 +2195,8 @@
                         PCHECKITEM NewFocus;
                         UINT NewFocusBox = 0;
                         BOOL Shift = GetKeyState(VK_SHIFT) & 0x8000;
+                        
+                        EscapeQuickSearch(infoPtr);
 
                         NewFocus = FindEnabledCheckBox(infoPtr,
                                                        Shift,
@@ -1779,16 +2253,23 @@
         {
             INT virtKey;
             
-            Ret = DLGC_HASSETSEL;
+            Ret = 0;
             virtKey = (lParam != 0 ? (INT)((LPMSG)lParam)->wParam : 0);
             switch (virtKey)
             {
                 case VK_RETURN:
                 {
-                    Ret |= DLGC_WANTMESSAGE;
+                    if (infoPtr->QuickSearchEnabled && infoPtr->QuickSearchHitItem != NULL)
+                    {
+                        Ret |= DLGC_WANTCHARS | DLGC_WANTMESSAGE;
+                    }
+                    else
+                    {
+                        Ret |= DLGC_WANTMESSAGE;
+                    }
                     break;
                 }
-                
+
                 case VK_TAB:
                 {
                     INT CheckBox;
@@ -1801,10 +2282,26 @@
                     Ret |= (EnabledBox ? DLGC_WANTTAB : DLGC_WANTCHARS);
                     break;
                 }
+                
+                default:
+                {
+                    if (infoPtr->QuickSearchEnabled)
+                    {
+                        Ret |= DLGC_WANTCHARS;
+                    }
+                    break;
+                }
             }
             break;
         }
         
+        case WM_CHAR:
+        {
+            QuickSearchFindHit(infoPtr,
+                               (WCHAR)wParam);
+            break;
+        }
+        
         case WM_SYSCOLORCHANGE:
         {
             infoPtr->TextColor[0] = GetSysColor(COLOR_GRAYTEXT);
@@ -1843,6 +2340,8 @@
         
         case WM_SETTINGCHANGE:
         {
+            DWORD OldCaretWidth = infoPtr->CaretWidth;
+            
 #if SUPPORT_UXTHEME
             /* update the hover time */
             if (!SystemParametersInfo(SPI_GETMOUSEHOVERTIME,
@@ -1853,6 +2352,23 @@
                 infoPtr->HoverTime = HOVER_DEFAULT;
             }
 #endif
+
+            /* update the caret */
+            if (!SystemParametersInfo(SPI_GETCARETWIDTH,
+                                      0,
+                                      &infoPtr->CaretWidth,
+                                      0))
+            {
+                infoPtr->CaretWidth = 2;
+            }
+            if (OldCaretWidth != infoPtr->CaretWidth && infoPtr->ShowingCaret)
+            {
+                DestroyCaret();
+                CreateCaret(hwnd,
+                            NULL,
+                            infoPtr->CaretWidth,
+                            infoPtr->ItemHeight - (2 * CI_TEXT_MARGIN_HEIGHT));
+            }
             break;
         }
         
@@ -1881,7 +2397,50 @@
             }
             break;
         }
+        
+        case WM_TIMER:
+        {
+            switch (wParam)
+            {
+                case TIMER_ID_SETHITFOCUS:
+                {
+                    /* kill the timer */
+                    KillTimer(hwnd,
+                              wParam);
 
+                    if (infoPtr->QuickSearchEnabled && infoPtr->QuickSearchHitItem != NULL)
+                    {
+                        /* change the focus to the hit item, this item has to have
+                           at least one enabled checkbox! */
+                        ChangeCheckItemFocus(infoPtr,
+                                             infoPtr->QuickSearchHitItem,
+                                             ((!(infoPtr->QuickSearchHitItem->State & CIS_ALLOWDISABLED)) ? CLB_ALLOW : CLB_DENY));
+
+                        /* start the timer to reset quicksearch */
+                        if (infoPtr->QuickSearchResetDelay != 0)
+                        {
+                            SetTimer(hwnd,
+                                     TIMER_ID_RESETQUICKSEARCH,
+                                     infoPtr->QuickSearchResetDelay,
+                                     NULL);
+                        }
+                    }
+                    break;
+                }
+                case TIMER_ID_RESETQUICKSEARCH:
+                {
+                    /* kill the timer */
+                    KillTimer(hwnd,
+                              wParam);
+
+                    /* escape quick search */
+                    EscapeQuickSearch(infoPtr);
+                    break;
+                }
+            }
+            break;
+        }
+
         case WM_CREATE:
         {
             infoPtr = HeapAlloc(GetProcessHeap(),
@@ -1901,7 +2460,15 @@
                 infoPtr->CheckItemListHead = NULL;
                 infoPtr->CheckItemCount = 0;
                 
+                if (!SystemParametersInfo(SPI_GETCARETWIDTH,
+                                          0,
+                                          &infoPtr->CaretWidth,
+                                          0))
+                {
+                    infoPtr->CaretWidth = 2;
+                }
                 infoPtr->ItemHeight = 10;
+                infoPtr->ShowingCaret = FALSE;
                 
                 infoPtr->HasFocus = FALSE;
                 infoPtr->FocusedCheckItem = NULL;
@@ -1915,7 +2482,13 @@
                 
                 infoPtr->CheckBoxLeft[0] = rcClient.right - 30;
                 infoPtr->CheckBoxLeft[1] = rcClient.right - 15;
+                
+                infoPtr->QuickSearchEnabled = FALSE;
+                infoPtr->QuickSearchText[0] = L'\0';
 
+                infoPtr->QuickSearchSetFocusDelay = DEFAULT_QUICKSEARCH_SETFOCUS_DELAY;
+                infoPtr->QuickSearchResetDelay = DEFAULT_QUICKSEARCH_RESET_DELAY;
+
 #if SUPPORT_UXTHEME
                 infoPtr->HoveredCheckItem = NULL;
                 infoPtr->HoveredCheckItemBox = 0;
@@ -1952,6 +2525,11 @@
         
         case WM_DESTROY:
         {
+            if (infoPtr->ShowingCaret)
+            {
+                DestroyCaret();
+            }
+
             ClearCheckItems(infoPtr);
             
 #if SUPPORT_UXTHEME