initial (not yet complete) implementation of the checklist control for the permissions editor
Modified: trunk/reactos/lib/aclui/aclui.c
Modified: trunk/reactos/lib/aclui/aclui.xml
Modified: trunk/reactos/lib/aclui/aclui_De.rc
Modified: trunk/reactos/lib/aclui/aclui_En.rc
Modified: trunk/reactos/lib/aclui/aclui_Sv.rc
Modified: trunk/reactos/lib/aclui/acluilib.h
Added: trunk/reactos/lib/aclui/checklist.c
Modified: trunk/reactos/lib/aclui/misc.c
Modified: trunk/reactos/lib/aclui/resource.h

Modified: trunk/reactos/lib/aclui/aclui.c
--- trunk/reactos/lib/aclui/aclui.c	2005-07-01 23:26:01 UTC (rev 16373)
+++ trunk/reactos/lib/aclui/aclui.c	2005-07-01 23:33:34 UTC (rev 16374)
@@ -30,32 +30,6 @@
 
 HINSTANCE hDllInstance;
 
-static LPARAM
-ListViewGetSelectedItemData(IN HWND hwnd)
-{
-    int Index;
-    
-    Index = ListView_GetNextItem(hwnd,
-                                 -1,
-                                 LVNI_SELECTED);
-    if (Index != -1)
-    {
-        LVITEM li;
-        
-        li.mask = LVIF_PARAM;
-        li.iItem = Index;
-        li.iSubItem = 0;
-        
-        if (ListView_GetItem(hwnd,
-                             &li))
-        {
-            return li.lParam;
-        }
-    }
-    
-    return 0;
-}
-
 static VOID
 DestroySecurityPage(IN PSECURITY_PAGE sp)
 {
@@ -363,6 +337,38 @@
     }
 }
 
+static INT
+AddAceListEntry(IN PSECURITY_PAGE sp,
+                IN PACE_LISTITEM AceListItem,
+                IN INT Index,
+                IN BOOL Selected)
+{
+    LVITEM li;
+
+    li.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
+    li.iItem = Index;
+    li.iSubItem = 0;
+    li.state = (Selected ? LVIS_SELECTED : 0);
+    li.stateMask = LVIS_SELECTED;
+    li.pszText = (AceListItem->DisplayString != NULL ? AceListItem->DisplayString : AceListItem->AccountName);
+    switch (AceListItem->SidNameUse)
+    {
+        case SidTypeUser:
+            li.iImage = 0;
+            break;
+        case SidTypeGroup:
+            li.iImage = 1;
+            break;
+        default:
+            li.iImage = -1;
+            break;
+    }
+    li.lParam = (LPARAM)AceListItem;
+
+    return ListView_InsertItem(sp->hWndAceList,
+                               &li);
+}
+
 static VOID
 FillUsersGroupsList(IN PSECURITY_PAGE sp)
 {
@@ -382,30 +388,10 @@
          CurItem != NULL;
          CurItem = CurItem->Next)
     {
-        LVITEM li;
-
-        li.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
-        li.iItem = -1;
-        li.iSubItem = 0;
-        li.state = (CurItem == (PACE_LISTITEM)SelLParam ? LVIS_SELECTED : 0);
-        li.stateMask = LVIS_SELECTED;
-        li.pszText = (CurItem->DisplayString != NULL ? CurItem->DisplayString : CurItem->AccountName);
-        switch (CurItem->SidNameUse)
-        {
-            case SidTypeUser:
-                li.iImage = 0;
-                break;
-            case SidTypeGroup:
-                li.iImage = 1;
-                break;
-            default:
-                li.iImage = -1;
-                break;
-        }
-        li.lParam = (LPARAM)CurItem;
-
-        ListView_InsertItem(sp->hWndAceList,
-                            &li);
+        AddAceListEntry(sp,
+                        CurItem,
+                        -1,
+                        (SelLParam == (LPARAM)CurItem));
     }
     
     EnableRedrawWindow(sp->hWndAceList);
@@ -417,7 +403,16 @@
                             rcLvClient.right);
 }
 
+static VOID
+UpdateControlStates(IN PSECURITY_PAGE sp)
+{
+    BOOL UserOrGroupSelected;
+    
+    UserOrGroupSelected = (ListViewGetSelectedItemData(sp->hWndAceList) != 0);
 
+    EnableWindow(sp->hBtnRemove, UserOrGroupSelected);
+}
+
 static UINT CALLBACK
 SecurityPageCallback(IN HWND hwnd,
                      IN UINT uMsg,
@@ -434,6 +429,7 @@
         case PSPCB_RELEASE:
         {
             DestroySecurityPage(sp);
+            UnregisterCheckListControl();
             return FALSE;
         }
     }
@@ -441,7 +437,30 @@
     return FALSE;
 }
 
+static VOID
+SetAceCheckListColumns(IN HWND hAceCheckList,
+                       IN UINT Button,
+                       IN HWND hLabel)
+{
+    POINT pt;
+    RECT rcLabel;
 
+    GetWindowRect(hLabel,
+                  &rcLabel);
+    pt.y = 0;
+    pt.x = (rcLabel.right - rcLabel.left) / 2;
+    MapWindowPoints(hLabel,
+                    hAceCheckList,
+                    &pt,
+                    1);
+
+    SendMessage(hAceCheckList,
+                CLM_SETCHECKBOXCOLUMN,
+                Button,
+                pt.x);
+}
+
+
 static INT_PTR CALLBACK
 SecurityPageProc(IN HWND hwndDlg,
                  IN UINT uMsg,
@@ -452,6 +471,28 @@
 
     switch(uMsg)
     {
+        case WM_NOTIFY:
+        {
+            NMHDR *pnmh = (NMHDR*)lParam;
+            if (pnmh->idFrom == IDC_ACELIST)
+            {
+                sp = (PSECURITY_PAGE)GetWindowLongPtr(hwndDlg,
+                                                      DWL_USER);
+                if (sp != NULL)
+                {
+                    switch(pnmh->code)
+                    {
+                        case LVN_ITEMCHANGED:
+                        {
+                            UpdateControlStates(sp);
+                            break;
+                        }
+                    }
+                }
+            }
+            break;
+        }
+        
         case WM_INITDIALOG:
         {
             sp = (PSECURITY_PAGE)((LPPROPSHEETPAGE)lParam)->lParam;
@@ -462,6 +503,8 @@
                 
                 sp->hWnd = hwndDlg;
                 sp->hWndAceList = GetDlgItem(hwndDlg, IDC_ACELIST);
+                sp->hBtnRemove = GetDlgItem(hwndDlg, IDC_ACELIST_REMOVE);
+                sp->hAceCheckList = GetDlgItem(hwndDlg, IDC_ACE_CHECKLIST);
 
                 /* save the pointer to the structure */
                 SetWindowLongPtr(hwndDlg,
@@ -491,7 +534,18 @@
                 ListView_InsertColumn(sp->hWndAceList, 0, &lvc);
 
                 FillUsersGroupsList(sp);
+                
+                ListViewSelectItem(sp->hWndAceList,
+                                   0);
 
+                /* calculate the columns of the allow/deny checkboxes */
+                SetAceCheckListColumns(sp->hAceCheckList,
+                                       CLB_ALLOW,
+                                       GetDlgItem(hwndDlg, IDC_LABEL_ALLOW));
+                SetAceCheckListColumns(sp->hAceCheckList,
+                                       CLB_DENY,
+                                       GetDlgItem(hwndDlg, IDC_LABEL_DENY));
+
                 /* FIXME - hide controls in case the flags aren't present */
             }
             break;
@@ -530,10 +584,16 @@
     {
         SetLastError(hRet);
 
-        DPRINT("CreateSecurityPage() failed!\n");
+        DPRINT("CreateSecurityPage() failed! Failed to query the object information!\n");
         return NULL;
     }
     
+    if (!RegisterCheckListControl(hDllInstance))
+    {
+        DPRINT("Registering the CHECKLIST_ACLUI class failed!\n");
+        return NULL;
+    }
+    
     sPage = HeapAlloc(GetProcessHeap(),
                       HEAP_ZERO_MEMORY,
                       sizeof(SECURITY_PAGE));
@@ -555,7 +615,7 @@
     psp.lParam = (LPARAM)sPage;
     psp.pfnCallback = SecurityPageCallback;
 
-    if((ObjectInfo.dwFlags & SI_PAGE_TITLE) != 0 &&
+    if((ObjectInfo.dwFlags & SI_PAGE_TITLE) &&
        ObjectInfo.pszPageTitle != NULL && ObjectInfo.pszPageTitle[0] != L'\0')
     {
         /* Set the page title if the flag is present and the string isn't empty */
@@ -618,7 +678,7 @@
     psh.dwFlags = PSH_DEFAULT;
     psh.hwndParent = hwndOwner;
     psh.hInstance = hDllInstance;
-    if((ObjectInfo.dwFlags & SI_PAGE_TITLE) != 0 &&
+    if((ObjectInfo.dwFlags & SI_PAGE_TITLE) &&
        ObjectInfo.pszPageTitle != NULL && ObjectInfo.pszPageTitle[0] != L'\0')
     {
         /* Set the page title if the flag is present and the string isn't empty */

Modified: trunk/reactos/lib/aclui/aclui.xml
--- trunk/reactos/lib/aclui/aclui.xml	2005-07-01 23:26:01 UTC (rev 16373)
+++ trunk/reactos/lib/aclui/aclui.xml	2005-07-01 23:33:34 UTC (rev 16374)
@@ -10,8 +10,10 @@
 	<library>ntdll</library>
 	<library>kernel32</library>
 	<library>user32</library>
+	<library>gdi32</library>
 	<library>comctl32</library>
 	<file>aclui.c</file>
+	<file>checklist.c</file>
 	<file>misc.c</file>
 	<file>aclui.rc</file>
 	<pch>acluilib.h</pch>

Modified: trunk/reactos/lib/aclui/aclui_De.rc
--- trunk/reactos/lib/aclui/aclui_De.rc	2005-07-01 23:26:01 UTC (rev 16373)
+++ trunk/reactos/lib/aclui/aclui_De.rc	2005-07-01 23:33:34 UTC (rev 16374)
@@ -1,7 +1,7 @@
 LANGUAGE LANG_GERMAN, SUBLANG_DEFAULT
 
 IDD_SECPAGE DIALOGEX 0, 0, 227, 215
-STYLE WS_CHILD | WS_VISIBLE | WS_CAPTION
+STYLE DS_NOFAILCREATE | DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE | WS_CAPTION
 CAPTION "Sicherheit"
 FONT 8, "MS Shell Dlg", 0, 0, 0x0
 BEGIN
@@ -9,8 +9,9 @@
   CONTROL "", IDC_ACELIST, "SysListView32", LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP, 7, 17, 213, 66, WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE
   PUSHBUTTON "&Hinzuf³gen...", IDC_ACELIST_ADD, 116, 87, 50, 14
   PUSHBUTTON "&Entfernen", IDC_ACELIST_REMOVE, 170, 87, 50, 14
-  LTEXT "Erlauben", -1, 135, 107, 32, 8, SS_CENTER
-  LTEXT "Verbieten", -1, 176, 107, 32, 8, SS_CENTER
+  LTEXT "Erlauben", IDC_LABEL_ALLOW, 135, 107, 32, 8, SS_CENTER
+  LTEXT "Verbieten", IDC_LABEL_DENY, 176, 107, 32, 8, SS_CENTER
+  CONTROL "", IDC_ACE_CHECKLIST, "CHECKLIST_ACLUI", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 7, 117, 213, 72, WS_EX_CLIENTEDGE
 END
 
 STRINGTABLE DISCARDABLE

Modified: trunk/reactos/lib/aclui/aclui_En.rc
--- trunk/reactos/lib/aclui/aclui_En.rc	2005-07-01 23:26:01 UTC (rev 16373)
+++ trunk/reactos/lib/aclui/aclui_En.rc	2005-07-01 23:33:34 UTC (rev 16374)
@@ -1,7 +1,7 @@
 LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
 
 IDD_SECPAGE DIALOGEX 0, 0, 227, 215
-STYLE WS_CHILD | WS_VISIBLE | WS_CAPTION
+STYLE DS_NOFAILCREATE | DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE | WS_CAPTION
 CAPTION "Security"
 FONT 8, "MS Shell Dlg", 0, 0, 0x0
 BEGIN
@@ -9,8 +9,9 @@
   CONTROL "", IDC_ACELIST, "SysListView32", LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP, 7, 17, 213, 66, WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE
   PUSHBUTTON "A&dd...", IDC_ACELIST_ADD, 116, 87, 50, 14
   PUSHBUTTON "&Remove", IDC_ACELIST_REMOVE, 170, 87, 50, 14
-  LTEXT "Allow", -1, 135, 107, 32, 8, SS_CENTER
-  LTEXT "Deny", -1, 176, 107, 32, 8, SS_CENTER
+  LTEXT "Allow", IDC_LABEL_ALLOW, 135, 107, 32, 8, SS_CENTER
+  LTEXT "Deny", IDC_LABEL_DENY, 176, 107, 32, 8, SS_CENTER
+  CONTROL "", IDC_ACE_CHECKLIST, "CHECKLIST_ACLUI", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 7, 117, 213, 72, WS_EX_CLIENTEDGE
 END
 
 STRINGTABLE DISCARDABLE

Modified: trunk/reactos/lib/aclui/aclui_Sv.rc
--- trunk/reactos/lib/aclui/aclui_Sv.rc	2005-07-01 23:26:01 UTC (rev 16373)
+++ trunk/reactos/lib/aclui/aclui_Sv.rc	2005-07-01 23:33:34 UTC (rev 16374)
@@ -19,7 +19,7 @@
 LANGUAGE LANG_SWEDISH, SUBLANG_DEFAULT
 
 IDD_SECPAGE DIALOGEX 0, 0, 227, 215
-STYLE WS_CHILD | WS_VISIBLE | WS_CAPTION
+STYLE DS_NOFAILCREATE | DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE | WS_CAPTION
 CAPTION "Sõkerhet"
 FONT 8, "MS Shell Dlg", 0, 0, 0x0
 BEGIN
@@ -27,8 +27,9 @@
   CONTROL "", IDC_ACELIST, "SysListView32", LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP, 7, 17, 213, 66, WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE
   PUSHBUTTON "&Lõgg till...", IDC_ACELIST_ADD, 116, 87, 50, 14
   PUSHBUTTON "&Ta bort", IDC_ACELIST_REMOVE, 170, 87, 50, 14
-  LTEXT "TillÕt", -1, 135, 107, 32, 8, SS_CENTER
-  LTEXT "Neka", -1, 176, 107, 32, 8, SS_CENTER
+  LTEXT "TillÕt", IDC_LABEL_ALLOW, 135, 107, 32, 8, SS_CENTER
+  LTEXT "Neka", IDC_LABEL_DENY, 176, 107, 32, 8, SS_CENTER
+  CONTROL "", IDC_ACE_CHECKLIST, "CHECKLIST_ACLUI", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 7, 117, 213, 72, WS_EX_CLIENTEDGE
 END
 
 STRINGTABLE DISCARDABLE

Modified: trunk/reactos/lib/aclui/acluilib.h
--- trunk/reactos/lib/aclui/acluilib.h	2005-07-01 23:26:01 UTC (rev 16373)
+++ trunk/reactos/lib/aclui/acluilib.h	2005-07-01 23:33:34 UTC (rev 16374)
@@ -33,6 +33,8 @@
 typedef struct _SECURITY_PAGE
 {
     HWND hWnd;
+    HWND hBtnRemove;
+    HWND hAceCheckList;
 
     /* Main ACE List */
     HWND hWndAceList;
@@ -44,6 +46,8 @@
     SI_OBJECT_INFO ObjectInfo;
 } SECURITY_PAGE, *PSECURITY_PAGE;
 
+/* MISC ***********************************************************************/
+
 BOOL
 OpenLSAPolicyHandle(IN WCHAR *SystemName,
                     IN ACCESS_MASK DesiredAccess,
@@ -55,4 +59,36 @@
                     OUT LPWSTR *lpTarget,
                     ...);
 
+LPARAM
+ListViewGetSelectedItemData(IN HWND hwnd);
+
+BOOL
+ListViewSelectItem(IN HWND hwnd,
+                   IN INT Index);
+
+/* CHECKLIST CONTROL **********************************************************/
+
+#define CIS_DISABLED    (0x2)
+#define CIS_ENABLED     (0x0)
+#define CIS_ALLOW       (0x1)
+#define CIS_DENY        (0x0)
+
+#define CLB_ALLOW       (0x1)
+#define CLB_DENY        (0x0)
+
+#define CIS_MASK (CIS_DISABLED | CIS_ALLOW | CIS_DENY)
+
+#define CLM_ADDITEM     (WM_USER + 1)
+#define CLM_DELITEM     (WM_USER + 2)
+#define CLM_GETITEMCOUNT        (WM_USER + 3)
+#define CLM_CLEAR       (WM_USER + 4)
+#define CLM_SETCHECKBOXCOLUMN   (WM_USER + 5)
+#define CLM_GETCHECKBOXCOLUMN   (WM_USER + 6)
+
+BOOL
+RegisterCheckListControl(HINSTANCE hInstance);
+
+VOID
+UnregisterCheckListControl(VOID);
+
 /* EOF */

Added: trunk/reactos/lib/aclui/checklist.c
--- trunk/reactos/lib/aclui/checklist.c	2005-07-01 23:26:01 UTC (rev 16373)
+++ trunk/reactos/lib/aclui/checklist.c	2005-07-01 23:33:34 UTC (rev 16374)
@@ -0,0 +1,785 @@
+/*
+ * ReactOS Access Control List Editor
+ * Copyright (C) 2004 ReactOS Team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* $Id$
+ *
+ * PROJECT:         ReactOS Access Control List Editor
+ * FILE:            lib/aclui/checklist.c
+ * PURPOSE:         Access Control List Editor
+ * PROGRAMMER:      Thomas Weidenmueller <w3seek@reactos.com>
+ *
+ * UPDATE HISTORY:
+ *      08/10/2004  Created
+ */
+#include "acluilib.h"
+
+typedef struct _CHECKITEM
+{
+    struct _CHECKITEM *Next;
+    DWORD State;
+    WCHAR Name[1];
+} CHECKITEM, *PCHECKITEM;
+
+typedef struct _CHECKLISTWND
+{
+   HWND hSelf;
+   HWND hNotify;
+   DWORD Style;
+   HFONT hFont;
+   
+   PCHECKITEM CheckItemListHead;
+   UINT CheckItemCount;
+   
+   INT ItemHeight;
+   BOOL HasFocus;
+   
+   COLORREF TextColor[2];
+   UINT CheckBoxLeft[2];
+} CHECKLISTWND, *PCHECKLISTWND;
+
+static PCHECKITEM
+FindCheckItemByIndex(IN PCHECKLISTWND infoPtr,
+                     IN UINT Index)
+{
+    PCHECKITEM Item, Found = NULL;
+    
+    for (Item = infoPtr->CheckItemListHead;
+         Item != NULL;
+         Item = Item->Next)
+    {
+        if (Index == 0)
+        {
+            Found = Item;
+            break;
+        }
+        
+        Index--;
+    }
+    
+    return Found;
+}
+
+static VOID
+ClearCheckItems(IN PCHECKLISTWND infoPtr)
+{
+    PCHECKITEM CurItem, NextItem;
+
+    CurItem = infoPtr->CheckItemListHead;
+    while (CurItem != NULL)
+    {
+        NextItem = CurItem->Next;
+        HeapFree(GetProcessHeap(),
+                 0,
+                 CurItem);
+        CurItem = NextItem;
+    }
+
+    infoPtr->CheckItemListHead = NULL;
+    infoPtr->CheckItemCount = 0;
+}
+
+static BOOL
+DeleteCheckItem(IN PCHECKLISTWND infoPtr,
+                IN PCHECKITEM Item)
+{
+    PCHECKITEM CurItem;
+    PCHECKITEM *PrevPtr = &infoPtr->CheckItemListHead;
+    
+    for (CurItem = infoPtr->CheckItemListHead;
+         CurItem != NULL;
+         CurItem = CurItem->Next)
+    {
+        if (CurItem == Item)
+        {
+            *PrevPtr = CurItem->Next;
+            HeapFree(GetProcessHeap(),
+                     0,
+                     CurItem);
+            infoPtr->CheckItemCount--;
+            return TRUE;
+        }
+        
+        PrevPtr = &CurItem->Next;
+    }
+    
+    return FALSE;
+}
+
+static PCHECKITEM
+AddCheckItem(IN PCHECKLISTWND infoPtr,
+             IN LPWSTR Name,
+             IN DWORD State)
+{
+    PCHECKITEM CurItem;
+    PCHECKITEM *PrevPtr = &infoPtr->CheckItemListHead;
+    PCHECKITEM Item = HeapAlloc(GetProcessHeap(),
+                                0,
+                                sizeof(CHECKITEM) + (wcslen(Name) * sizeof(WCHAR)));
+    if (Item != NULL)
+    {
+        for (CurItem = infoPtr->CheckItemListHead;
+             CurItem != NULL;
+             CurItem = CurItem->Next)
+        {
+            PrevPtr = &CurItem->Next;
+        }
+        
+        Item->Next = NULL;
+        Item->State = State & CIS_MASK;
+        wcscpy(Item->Name,
+               Name);
+        
+        *PrevPtr = Item;
+        infoPtr->CheckItemCount++;
+    }
+    
+    return Item;
+}
+
+static VOID
+UpdateControl(IN PCHECKLISTWND infoPtr,
+              IN BOOL AllowChangeStyle)
+{
+    RECT rcClient;
+    SCROLLINFO ScrollInfo;
+    
+    GetClientRect(infoPtr->hSelf,
+                  &rcClient);
+
+    ScrollInfo.cbSize = sizeof(ScrollInfo);
+    ScrollInfo.fMask = SIF_PAGE | SIF_RANGE;
+    ScrollInfo.nMin = 0;
+    ScrollInfo.nMax = infoPtr->CheckItemCount;
+    ScrollInfo.nPage = ((rcClient.bottom - rcClient.top) + infoPtr->ItemHeight - 1) / infoPtr->ItemHeight;
+    ScrollInfo.nPos = 0;
+    ScrollInfo.nTrackPos = 0;
+
+    if (AllowChangeStyle)
+    {
+        /* determine whether the vertical scrollbar has to be visible or not */
+        if (ScrollInfo.nMax > ScrollInfo.nPage &&
+            !(infoPtr->Style & WS_VSCROLL))
+        {
+            SetWindowLong(infoPtr->hSelf,
+                          GWL_STYLE,
+                          infoPtr->Style | WS_VSCROLL);
+        }
+        else if (ScrollInfo.nMax < ScrollInfo.nPage &&
+                 infoPtr->Style & WS_VSCROLL)
+        {
+            SetWindowLong(infoPtr->hSelf,
+                          GWL_STYLE,
+                          infoPtr->Style & ~WS_VSCROLL);
+        }
+    }
+    
+    SetScrollInfo(infoPtr->hSelf,
+                  SB_VERT,
+                  &ScrollInfo,
+                  TRUE);
+
+    RedrawWindow(infoPtr->hSelf,
+                 NULL,
+                 NULL,
+                 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN);
+}
+
+static UINT
+GetIdealItemHeight(IN PCHECKLISTWND infoPtr)
+{
+    HDC hdc = GetDC(infoPtr->hSelf);
+    if(hdc != NULL)
+    {
+        UINT height;
+        TEXTMETRIC tm;
+        HGDIOBJ hOldFont = SelectObject(hdc,
+                                        infoPtr->hFont);
+
+        if(GetTextMetrics(hdc,
+                          &tm))
+        {
+            height = tm.tmHeight;
+        }
+        else
+        {
+            height = 0;
+        }
+
+        SelectObject(hdc,
+                     hOldFont);
+
+        ReleaseDC(infoPtr->hSelf,
+                  hdc);
+
+        return height;
+    }
+    return 0;
+}
+
+static HFONT
+RetChangeControlFont(IN PCHECKLISTWND infoPtr,
+                     IN HFONT hFont,
+                     IN BOOL Redraw)
+{
+    HFONT hOldFont = infoPtr->hFont;
+    infoPtr->hFont = hFont;
+    
+    if (hOldFont != hFont)
+    {
+        infoPtr->ItemHeight = 4 + GetIdealItemHeight(infoPtr);
+    }
+
+    UpdateControl(infoPtr,
+                  TRUE);
+
+    return hOldFont;
+}
+
+static VOID
+PaintControl(IN PCHECKLISTWND infoPtr,
+             IN HDC hDC,
+             IN PRECT rcUpdate)
+{
+    INT ScrollPos;
+    PCHECKITEM FirstItem, Item;
+    RECT rcClient;
+    UINT VisibleFirstIndex = rcUpdate->top / infoPtr->ItemHeight;
+    UINT LastTouchedIndex = rcUpdate->bottom / infoPtr->ItemHeight;
+    
+    FillRect(hDC,
+             rcUpdate,
+             (HBRUSH)(COLOR_WINDOW + 1));
+    
+    GetClientRect(infoPtr->hSelf, &rcClient);
+    
+    if (infoPtr->Style & WS_VSCROLL)
+    {
+        ScrollPos = GetScrollPos(infoPtr->hSelf,
+                                 SB_VERT);
+    }
+    else
+    {
+        ScrollPos = 0;
+    }
+    
+    FirstItem = FindCheckItemByIndex(infoPtr,
+                                     ScrollPos + VisibleFirstIndex);
+    if (FirstItem != NULL)
+    {
+        RECT TextRect, ItemRect;
+        HFONT hOldFont;
+        DWORD CurrentIndex;
+        COLORREF OldTextColor;
+        BOOL Enabled, PrevEnabled;
+        POINT ptOld;
+        
+        Enabled = IsWindowEnabled(infoPtr->hSelf);
+        PrevEnabled = Enabled;
+        
+        ItemRect.left = 0;
+        ItemRect.right = rcClient.right;
+        ItemRect.top = VisibleFirstIndex * infoPtr->ItemHeight;
+        
+        TextRect.left = ItemRect.left + 6;
+        TextRect.right = ItemRect.right - 6;
+        TextRect.top = ItemRect.top + 2;
+        
+        MoveToEx(hDC,
+                 infoPtr->CheckBoxLeft[CLB_ALLOW],
+                 ItemRect.top,
+                 &ptOld);
+        
+        OldTextColor = SetTextColor(hDC,
+                                    infoPtr->TextColor[Enabled]);
+
+        hOldFont = SelectObject(hDC,
+                                infoPtr->hFont);
+        
+        for (Item = FirstItem, CurrentIndex = VisibleFirstIndex;
+             Item != NULL && CurrentIndex <= LastTouchedIndex;
+             Item = Item->Next, CurrentIndex++)
+        {
+            TextRect.bottom = TextRect.top + infoPtr->ItemHeight;
+            
+            if (Enabled && PrevEnabled != ((Item->State & CIS_DISABLED) == 0))
+            {
+                PrevEnabled = ((Item->State & CIS_DISABLED) == 0);
+                
+                SetTextColor(hDC,
+                             infoPtr->TextColor[PrevEnabled]);
+            }
+            
+            DrawText(hDC,
+                     Item->Name,
+                     -1,
+                     &TextRect,
+                     DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
+            
+            MoveToEx(hDC,
+                     infoPtr->CheckBoxLeft[CLB_ALLOW],
+                     TextRect.top - 6,
+                     NULL);
+            LineTo(hDC,
+                   infoPtr->CheckBoxLeft[CLB_ALLOW],
+                   TextRect.bottom - 6);
+            MoveToEx(hDC,
+                     infoPtr->CheckBoxLeft[CLB_DENY],
+                     TextRect.top - 6,
+                     NULL);
+            LineTo(hDC,
+                   infoPtr->CheckBoxLeft[CLB_DENY],
+                   TextRect.bottom - 6);
+
+            TextRect.top += infoPtr->ItemHeight;
+        }
+
+        SelectObject(hDC,
+                     hOldFont);
+
+        SetTextColor(hDC,
+                     OldTextColor);
+
+        MoveToEx(hDC,
+                 ptOld.x,
+                 ptOld.y,
+                 NULL);
+    }
+}
+
+LRESULT CALLBACK
+CheckListWndProc(IN HWND hwnd,
+                 IN UINT uMsg,
+                 IN WPARAM wParam,
+                 IN LPARAM lParam)
+{
+    PCHECKLISTWND infoPtr;
+    LRESULT Ret;
+    
+    infoPtr = (PCHECKLISTWND)GetWindowLongPtr(hwnd,
+                                              0);
+    
+    if (infoPtr == NULL && uMsg != WM_CREATE)
+    {
+        return DefWindowProc(hwnd,
+                             uMsg,
+                             wParam,
+                             lParam);
+    }
+    
+    Ret = 0;
+    
+    switch (uMsg)
+    {
+        case WM_PAINT:
+        {
+            HDC hdc;
+            RECT rcUpdate;
+            PAINTSTRUCT ps;
+            
+            if (GetUpdateRect(hwnd,
+                              &rcUpdate,
+                              FALSE))
+            {
+                hdc = (wParam != 0 ? (HDC)wParam : BeginPaint(hwnd, &ps));
+
+                PaintControl(infoPtr,
+                             hdc,
+                             &rcUpdate);
+
+                if (wParam == 0)
+                {
+                    EndPaint(hwnd,
+                             &ps);
+                }
+            }
+            break;
+        }
+        
+        case WM_VSCROLL:
+        {
+            SCROLLINFO ScrollInfo;
+            
+            ScrollInfo.cbSize = sizeof(ScrollInfo);
+            ScrollInfo.fMask = SIF_PAGE | SIF_RANGE | SIF_POS | SIF_TRACKPOS;
+
+            if (GetScrollInfo(hwnd,
+                              SB_VERT,
+                              &ScrollInfo))
+            {
+                INT OldPos = ScrollInfo.nPos;
+                
+                switch (LOWORD(wParam))
+                {
+                    case SB_BOTTOM:
+                        ScrollInfo.nPos = ScrollInfo.nMax;
+                        break;
+
+                    case SB_LINEDOWN:
+                        if (ScrollInfo.nPos < ScrollInfo.nMax)
+                        {
+                            ScrollInfo.nPos++;
+                        }
+                        break;
+
+                    case SB_LINEUP:
+                        if (ScrollInfo.nPos > 0)
+                        {
+                            ScrollInfo.nPos--;
+                        }
+                        break;
+
+                    case SB_PAGEDOWN:
+                        if (ScrollInfo.nPos + ScrollInfo.nPage <= ScrollInfo.nMax)
+                        {
+                            ScrollInfo.nPos += ScrollInfo.nPage;
+                        }
+                        else
+                        {
+                            ScrollInfo.nPos = ScrollInfo.nMax;
+                        }
+                        break;
+
+                    case SB_PAGEUP:
+                        if (ScrollInfo.nPos >= ScrollInfo.nPage)
+                        {
+                            ScrollInfo.nPos -= ScrollInfo.nPage;
+                        }
+                        else
+                        {
+                            ScrollInfo.nPos = 0;
+                        }
+                        break;
+
+                    case SB_THUMBPOSITION:
+                    {
+                        ScrollInfo.nPos = HIWORD(wParam);
+                        break;
+                    }
+                    
+                    case SB_THUMBTRACK:
+                    {
+                        ScrollInfo.nPos = ScrollInfo.nTrackPos;
+                        break;
+                    }
+
+                    case SB_TOP:
+                        ScrollInfo.nPos = 0;
+                        break;
+                }
+                
+                if (OldPos != ScrollInfo.nPos)
+                {
+                    ScrollInfo.fMask = SIF_POS;
+                    
+                    ScrollInfo.nPos = SetScrollInfo(hwnd,
+                                                    SB_VERT,
+                                                    &ScrollInfo,
+                                                    TRUE);
+                    
+                    if (OldPos != ScrollInfo.nPos)
+                    {
+                        ScrollWindowEx(hwnd,
+                                       0,
+                                       (OldPos - ScrollInfo.nPos) * infoPtr->ItemHeight,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       SW_INVALIDATE);
+
+                        RedrawWindow(hwnd,
+                                     NULL,
+                                     NULL,
+                                     RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN);
+                    }
+                }
+            }
+            break;
+        }
+        
+        case CLM_ADDITEM:
+        {
+            Ret = (AddCheckItem(infoPtr,
+                                (LPWSTR)lParam,
+                                (DWORD)wParam) != NULL);
+            if (Ret)
+            {
+                UpdateControl(infoPtr,
+                              TRUE);
+            }
+            break;
+        }
+        
+        case CLM_DELITEM:
+        {
+            PCHECKITEM Item = FindCheckItemByIndex(infoPtr,
+                                                   wParam);
+            if (Item != NULL)
+            {
+                Ret = DeleteCheckItem(infoPtr,
+                                      Item);
+                if (Ret)
+                {
+                    UpdateControl(infoPtr,
+                                  TRUE);
+                }
+            }
+            else
+            {
+                Ret = FALSE;
+            }
+            break;
+        }
+        
+        case CLM_GETITEMCOUNT:
+        {
+            Ret = infoPtr->CheckItemCount;
+            break;
+        }
+        
+        case CLM_CLEAR:
+        {
+            ClearCheckItems(infoPtr);
+            UpdateControl(infoPtr,
+                          TRUE);
+            break;
+        }
+        
+        case CLM_SETCHECKBOXCOLUMN:
+        {
+            infoPtr->CheckBoxLeft[wParam != CLB_DENY] = (UINT)lParam;
+            Ret = 1;
+            break;
+        }
+        
+        case CLM_GETCHECKBOXCOLUMN:
+        {
+            Ret = (LRESULT)infoPtr->CheckBoxLeft[wParam != CLB_DENY];
+            break;
+        }
+        
+        case WM_SETFONT:
+        {
+            Ret = (LRESULT)RetChangeControlFont(infoPtr,
+                                                (HFONT)wParam,
+                                                (BOOL)lParam);
+            break;
+        }
+        
+        case WM_GETFONT:
+        {
+            Ret = (LRESULT)infoPtr->hFont;
[truncated at 1000 lines; 270 more skipped]