- allow changing the focus of the check boxes in the checklist control
authorThomas Bluemel <thomas@reactsoft.com>
Sun, 3 Jul 2005 01:44:25 +0000 (01:44 +0000)
committerThomas Bluemel <thomas@reactsoft.com>
Sun, 3 Jul 2005 01:44:25 +0000 (01:44 +0000)
- minor fixes

svn path=/trunk/; revision=16384

reactos/lib/aclui/aclui.c
reactos/lib/aclui/aclui_De.rc
reactos/lib/aclui/aclui_En.rc
reactos/lib/aclui/aclui_Sv.rc
reactos/lib/aclui/acluilib.h
reactos/lib/aclui/checklist.c
reactos/lib/aclui/resource.h

index 473addb..c7121c0 100644 (file)
@@ -416,22 +416,31 @@ UpdateControlStates(IN PSECURITY_PAGE sp)
     {
         LPWSTR szLabel;
 
-        LoadAndFormatString(hDllInstance,
-                            IDS_PERMISSIONS_FOR,
-                            &szLabel,
-                            Selected->AccountName);
-
-        SetWindowText(sp->hPermissionsForLabel,
-                      szLabel);
-
-        LocalFree((HLOCAL)szLabel);
+        if (LoadAndFormatString(hDllInstance,
+                                IDS_PERMISSIONS_FOR,
+                                &szLabel,
+                                Selected->AccountName))
+        {
+            SetWindowText(sp->hPermissionsForLabel,
+                          szLabel);
 
+            LocalFree((HLOCAL)szLabel);
+        }
+        
         /* FIXME - update the checkboxes */
     }
     else
     {
-        SetWindowText(sp->hPermissionsForLabel,
-                      NULL);
+        WCHAR szPermissions[255];
+        
+        if (LoadString(hDllInstance,
+                       IDS_PERMISSIONS,
+                       szPermissions,
+                       sizeof(szPermissions) / sizeof(szPermissions[0])))
+        {
+            SetWindowText(sp->hPermissionsForLabel,
+                          szPermissions);
+        }
 
         SendMessage(sp->hAceCheckList,
                     CLM_CLEARCHECKBOXES,
@@ -552,7 +561,7 @@ LoadPermissionsList(IN PSECURITY_PAGE sp,
 
                 SendMessage(sp->hAceCheckList,
                             CLM_ADDITEM,
-                            CIS_DISABLED | CIS_NONE,
+                            CIS_NONE,
                             (LPARAM)NameStr);
             }
         }
@@ -685,7 +694,7 @@ SecurityPageProc(IN HWND hwndDlg,
                         /* add the special permissions check item */
                         sp->SpecialPermCheckIndex = (INT)SendMessage(sp->hAceCheckList,
                                                                      CLM_ADDITEM,
-                                                                     CIS_DISABLED | CIS_NONE,
+                                                                     CIS_ALLOWDISABLED | CIS_DENYDISABLED | CIS_NONE,
                                                                      (LPARAM)szSpecialPermissions);
                     }
                 }
index eac32e4..49b2dc8 100644 (file)
@@ -23,4 +23,5 @@ STRINGTABLE DISCARDABLE
   IDS_UNKNOWN "Unbekannt"
   IDS_SPECIAL_PERMISSIONS "Spezielle Berechtigungen"
   IDS_PERMISSIONS_FOR "Berechtigungen für %1"
+  IDS_PERMISSIONS "Berechtigungen"
 }
index cc38178..f4aa27f 100644 (file)
@@ -23,5 +23,6 @@ STRINGTABLE DISCARDABLE
   IDS_UNKNOWN "Unknown"
   IDS_SPECIAL_PERMISSIONS "Special Permissions"
   IDS_PERMISSIONS_FOR "Permissions for %1"
+  IDS_PERMISSIONS "Permissions"
 }
 
index 7b05e68..7d34ab9 100644 (file)
@@ -41,5 +41,6 @@ STRINGTABLE DISCARDABLE
   IDS_UNKNOWN "Unknown"\r
   IDS_SPECIAL_PERMISSIONS "Special Permissions"\r
   IDS_PERMISSIONS_FOR "Permissions for %1"\r
+  IDS_PERMISSIONS "Permissions"\r
 }\r
 \r
index e4f923c..d03f624 100644 (file)
@@ -76,11 +76,14 @@ ListViewSelectItem(IN HWND hwnd,
 
 /* CHECKLIST CONTROL **********************************************************/
 
-#define CIS_DISABLED    (0x4)
-#define CIS_ENABLED     (0x0)
+#define CIS_DENYDISABLED        (0x8)
+#define CIS_DENYENABLED (0x0)
+#define CIS_ALLOWDISABLED       (0x4)
+#define CIS_ALLOWENABLED        (0x0)
 #define CIS_ALLOW       (0x2)
 #define CIS_DENY        (0x1)
 #define CIS_NONE        (0x0)
+#define CIS_DISABLED    (CIS_ALLOWDISABLED | CIS_DENYDISABLED)
 
 #define CLB_ALLOW       (0x1)
 #define CLB_DENY        (0x0)
index cf38a1a..7649475 100644 (file)
@@ -24,7 +24,7 @@
  * PROGRAMMER:      Thomas Weidenmueller <w3seek@reactos.com>
  *
  * UPDATE HISTORY:
- *      08/10/2004  Created
+ *      07/01/2005  Created
  */
 #include "acluilib.h"
 
@@ -39,19 +39,24 @@ typedef struct _CHECKLISTWND
 {
    HWND hSelf;
    HWND hNotify;
-   DWORD Style;
    HFONT hFont;
    
    PCHECKITEM CheckItemListHead;
    UINT CheckItemCount;
    
    INT ItemHeight;
+   
    BOOL HasFocus;
+   PCHECKITEM FocusedCheckItem;
+   UINT FocusedCheckItemBox;
    
    COLORREF TextColor[2];
    UINT CheckBoxLeft[2];
 } CHECKLISTWND, *PCHECKLISTWND;
 
+#define CI_TEXT_MARGIN_WIDTH    (6)
+#define CI_TEXT_MARGIN_HEIGHT   (2)
+
 static PCHECKITEM
 FindCheckItemByIndex(IN PCHECKLISTWND infoPtr,
                      IN UINT Index)
@@ -74,6 +79,248 @@ FindCheckItemByIndex(IN PCHECKLISTWND infoPtr,
     return Found;
 }
 
+static INT
+CheckItemToIndex(IN PCHECKLISTWND infoPtr,
+                 IN PCHECKITEM Item)
+{
+    PCHECKITEM CurItem;
+    INT Index;
+    
+    for (CurItem = infoPtr->CheckItemListHead, Index = 0;
+         CurItem != NULL;
+         CurItem = CurItem->Next, Index++)
+    {
+        if (CurItem == Item)
+        {
+            return Index;
+        }
+    }
+    
+    return -1;
+}
+
+static PCHECKITEM
+FindFirstEnabledCheckBox(IN PCHECKLISTWND infoPtr,
+                         OUT UINT *CheckBox)
+{
+    PCHECKITEM CurItem;
+
+    for (CurItem = infoPtr->CheckItemListHead;
+         CurItem != NULL;
+         CurItem = CurItem->Next)
+    {
+        if ((CurItem->State & CIS_DISABLED) != CIS_DISABLED)
+        {
+            /* return the Allow checkbox in case both check boxes are enabled! */
+            *CheckBox = ((!(CurItem->State & CIS_ALLOWDISABLED)) ? CLB_ALLOW : CLB_DENY);
+            return CurItem;
+        }
+    }
+    
+    return NULL;
+}
+
+static PCHECKITEM
+FindLastEnabledCheckBox(IN PCHECKLISTWND infoPtr,
+                        OUT UINT *CheckBox)
+{
+    PCHECKITEM CurItem;
+    PCHECKITEM LastEnabledItem = NULL;
+
+    for (CurItem = infoPtr->CheckItemListHead;
+         CurItem != NULL;
+         CurItem = CurItem->Next)
+    {
+        if ((CurItem->State & CIS_DISABLED) != CIS_DISABLED)
+        {
+            LastEnabledItem = CurItem;
+        }
+    }
+    
+    if (LastEnabledItem != NULL)
+    {
+        /* return the Deny checkbox in case both check boxes are enabled! */
+        *CheckBox = ((!(LastEnabledItem->State & CIS_DENYDISABLED)) ? CLB_DENY : CLB_ALLOW);
+        return LastEnabledItem;
+    }
+
+    return NULL;
+}
+
+static PCHECKITEM
+FindPreviousEnabledCheckBox(IN PCHECKLISTWND infoPtr,
+                            OUT UINT *CheckBox)
+{
+    PCHECKITEM Item;
+
+    if (infoPtr->FocusedCheckItem != NULL)
+    {
+        Item = infoPtr->FocusedCheckItem;
+
+        if (infoPtr->FocusedCheckItemBox == CLB_DENY &&
+            !(Item->State & CIS_ALLOWDISABLED))
+        {
+            /* currently an Deny checkbox is focused. return the Allow checkbox
+               if it's enabled */
+            *CheckBox = CLB_ALLOW;
+        }
+        else
+        {
+            PCHECKITEM CurItem;
+
+            Item = NULL;
+
+            for (CurItem = infoPtr->CheckItemListHead;
+                 CurItem != infoPtr->FocusedCheckItem;
+                 CurItem = CurItem->Next)
+            {
+                if ((CurItem->State & CIS_DISABLED) != CIS_DISABLED)
+                {
+                    Item = CurItem;
+                }
+            }
+            
+            if (Item != NULL)
+            {
+                /* return the Deny checkbox in case both check boxes are enabled! */
+                *CheckBox = ((!(Item->State & CIS_DENYDISABLED)) ? CLB_DENY : CLB_ALLOW);
+            }
+        }
+    }
+    else
+    {
+        Item = FindLastEnabledCheckBox(infoPtr,
+                                       CheckBox);
+    }
+
+    return Item;
+}
+
+static PCHECKITEM
+FindNextEnabledCheckBox(IN PCHECKLISTWND infoPtr,
+                        OUT UINT *CheckBox)
+{
+    PCHECKITEM Item;
+    
+    if (infoPtr->FocusedCheckItem != NULL)
+    {
+        Item = infoPtr->FocusedCheckItem;
+        
+        if (infoPtr->FocusedCheckItemBox != CLB_DENY &&
+            !(Item->State & CIS_DENYDISABLED))
+        {
+            /* currently an Allow checkbox is focused. return the Deny checkbox
+               if it's enabled */
+            *CheckBox = CLB_DENY;
+        }
+        else
+        {
+            Item = Item->Next;
+            
+            while (Item != NULL)
+            {
+                if ((Item->State & CIS_DISABLED) != CIS_DISABLED)
+                {
+                    /* return the Allow checkbox in case both check boxes are enabled! */
+                    *CheckBox = ((!(Item->State & CIS_ALLOWDISABLED)) ? CLB_ALLOW : CLB_DENY);
+                    break;
+                }
+
+                Item = Item->Next;
+            }
+        }
+    }
+    else
+    {
+        Item = FindFirstEnabledCheckBox(infoPtr,
+                                        CheckBox);
+    }
+    
+    return Item;
+}
+
+static PCHECKITEM
+FindEnabledCheckBox(IN PCHECKLISTWND infoPtr,
+                    IN BOOL ReverseSearch,
+                    OUT UINT *CheckBox)
+{
+    PCHECKITEM Item;
+    
+    if (ReverseSearch)
+    {
+        Item = FindPreviousEnabledCheckBox(infoPtr,
+                                           CheckBox);
+    }
+    else
+    {
+        Item = FindNextEnabledCheckBox(infoPtr,
+                                       CheckBox);
+    }
+    
+    return Item;
+}
+
+static PCHECKITEM
+PtToCheckItemBox(IN PCHECKLISTWND infoPtr,
+                 IN PPOINT ppt,
+                 OUT UINT *CheckBox,
+                 OUT BOOL *DirectlyInCheckBox)
+{
+    LONG Style;
+    INT FirstVisible, Index;
+    PCHECKITEM Item;
+    
+    Style = GetWindowLong(infoPtr->hSelf,
+                          GWL_STYLE);
+    
+    if (Style & WS_VSCROLL)
+    {
+        FirstVisible = GetScrollPos(infoPtr->hSelf,
+                                    SB_VERT);
+    }
+    else
+    {
+        FirstVisible = 0;
+    }
+    
+    Index = FirstVisible + (ppt->y / infoPtr->ItemHeight);
+    
+    Item = FindCheckItemByIndex(infoPtr,
+                                Index);
+    if (Item != NULL)
+    {
+        INT cx;
+        
+        cx = infoPtr->CheckBoxLeft[CLB_ALLOW] +
+             ((infoPtr->CheckBoxLeft[CLB_DENY] - infoPtr->CheckBoxLeft[CLB_ALLOW]) / 2);
+
+        *CheckBox = ((ppt->x <= cx) ? CLB_ALLOW : CLB_DENY);
+        
+        if (DirectlyInCheckBox != NULL)
+        {
+            INT y = ppt->y % infoPtr->ItemHeight;
+            
+            if ((y >= CI_TEXT_MARGIN_HEIGHT &&
+                 y <= infoPtr->ItemHeight - CI_TEXT_MARGIN_HEIGHT) &&
+
+                (((ppt->x >= (infoPtr->CheckBoxLeft[CLB_ALLOW] - (infoPtr->ItemHeight / 2))) &&
+                  (ppt->x <= (infoPtr->CheckBoxLeft[CLB_ALLOW] - (infoPtr->ItemHeight / 2) + infoPtr->ItemHeight)))
+                 ||
+                 ((ppt->x >= (infoPtr->CheckBoxLeft[CLB_DENY] - (infoPtr->ItemHeight / 2))) &&
+                  (ppt->x <= (infoPtr->CheckBoxLeft[CLB_DENY] - (infoPtr->ItemHeight / 2) + infoPtr->ItemHeight)))))
+            {
+                *DirectlyInCheckBox = TRUE;
+            }
+            else
+            {
+                *DirectlyInCheckBox = FALSE;
+            }
+        }
+    }
+    
+    return Item;
+}
+
 static VOID
 ClearCheckItems(IN PCHECKLISTWND infoPtr)
 {
@@ -185,6 +432,7 @@ UpdateControl(IN PCHECKLISTWND infoPtr,
 {
     RECT rcClient;
     SCROLLINFO ScrollInfo;
+    LONG Style;
     
     GetClientRect(infoPtr->hSelf,
                   &rcClient);
@@ -199,20 +447,23 @@ UpdateControl(IN PCHECKLISTWND infoPtr,
 
     if (AllowChangeStyle)
     {
+        Style = GetWindowLong(infoPtr->hSelf,
+                              GWL_STYLE);
+
         /* determine whether the vertical scrollbar has to be visible or not */
         if (ScrollInfo.nMax > ScrollInfo.nPage &&
-            !(infoPtr->Style & WS_VSCROLL))
+            !(Style & WS_VSCROLL))
         {
             SetWindowLong(infoPtr->hSelf,
                           GWL_STYLE,
-                          infoPtr->Style | WS_VSCROLL);
+                          Style | WS_VSCROLL);
         }
         else if (ScrollInfo.nMax < ScrollInfo.nPage &&
-                 infoPtr->Style & WS_VSCROLL)
+                 Style & WS_VSCROLL)
         {
             SetWindowLong(infoPtr->hSelf,
                           GWL_STYLE,
-                          infoPtr->Style & ~WS_VSCROLL);
+                          Style & ~WS_VSCROLL);
         }
     }
     
@@ -227,6 +478,127 @@ UpdateControl(IN PCHECKLISTWND infoPtr,
                  RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN);
 }
 
+static VOID
+UpdateCheckItem(IN PCHECKLISTWND infoPtr,
+                IN PCHECKITEM Item)
+{
+    LONG Style;
+    RECT rcClient;
+    INT VisibleFirst, VisibleItems;
+    INT Index = CheckItemToIndex(infoPtr,
+                                 Item);
+    if (Index != -1)
+    {
+        Style = GetWindowLong(infoPtr->hSelf,
+                              GWL_STYLE);
+
+        if (Style & WS_VSCROLL)
+        {
+            VisibleFirst = GetScrollPos(infoPtr->hSelf,
+                                        SB_VERT);
+        }
+        else
+        {
+            VisibleFirst = 0;
+        }
+        
+        if (Index >= VisibleFirst)
+        {
+            GetClientRect(infoPtr->hSelf,
+                          &rcClient);
+
+            VisibleItems = ((rcClient.bottom - rcClient.top) + infoPtr->ItemHeight - 1) / infoPtr->ItemHeight;
+            
+            if (Index < VisibleFirst + VisibleItems)
+            {
+                RECT rcUpdate;
+                
+                rcUpdate.left = rcClient.left;
+                rcUpdate.right = rcClient.right;
+                rcUpdate.top = (Index - VisibleFirst) * infoPtr->ItemHeight;
+                rcUpdate.bottom = rcUpdate.top + infoPtr->ItemHeight;
+
+                RedrawWindow(infoPtr->hSelf,
+                             &rcUpdate,
+                             NULL,
+                             RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN);
+            }
+        }
+    }
+}
+
+static VOID
+MakeCheckItemVisible(IN PCHECKLISTWND infoPtr,
+                     IN PCHECKITEM Item)
+{
+    LONG Style;
+    RECT rcClient;
+    INT VisibleFirst, VisibleItems, NewPos;
+    INT Index = CheckItemToIndex(infoPtr,
+                                 Item);
+    if (Index != -1)
+    {
+        Style = GetWindowLong(infoPtr->hSelf,
+                              GWL_STYLE);
+
+        if (Style & WS_VSCROLL)
+        {
+            VisibleFirst = GetScrollPos(infoPtr->hSelf,
+                                        SB_VERT);
+        
+            if (Index <= VisibleFirst)
+            {
+                NewPos = Index;
+            }
+            else
+            {
+                GetClientRect(infoPtr->hSelf,
+                              &rcClient);
+
+                VisibleItems = (rcClient.bottom - rcClient.top) / infoPtr->ItemHeight;
+                if (Index - VisibleItems + 1 > VisibleFirst)
+                {
+                    NewPos = Index - VisibleItems + 1;
+                }
+                else
+                {
+                    NewPos = VisibleFirst;
+                }
+            }
+            
+            if (VisibleFirst != NewPos)
+            {
+                SCROLLINFO ScrollInfo;
+                
+                ScrollInfo.cbSize = sizeof(ScrollInfo);
+                ScrollInfo.fMask = SIF_POS;
+                ScrollInfo.nPos = NewPos;
+                NewPos = SetScrollInfo(infoPtr->hSelf,
+                                       SB_VERT,
+                                       &ScrollInfo,
+                                       TRUE);
+
+                if (VisibleFirst != NewPos)
+                {
+                    ScrollWindowEx(infoPtr->hSelf,
+                                   0,
+                                   (NewPos - VisibleFirst) * infoPtr->ItemHeight,
+                                   NULL,
+                                   NULL,
+                                   NULL,
+                                   NULL,
+                                   SW_INVALIDATE);
+
+                    RedrawWindow(infoPtr->hSelf,
+                                 NULL,
+                                 NULL,
+                                 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN);
+                }
+            }
+        }
+    }
+}
+
 static UINT
 GetIdealItemHeight(IN PCHECKLISTWND infoPtr)
 {
@@ -286,6 +658,7 @@ PaintControl(IN PCHECKLISTWND infoPtr,
     INT ScrollPos;
     PCHECKITEM FirstItem, Item;
     RECT rcClient;
+    LONG Style;
     UINT VisibleFirstIndex = rcUpdate->top / infoPtr->ItemHeight;
     UINT LastTouchedIndex = rcUpdate->bottom / infoPtr->ItemHeight;
     
@@ -295,7 +668,10 @@ PaintControl(IN PCHECKLISTWND infoPtr,
     
     GetClientRect(infoPtr->hSelf, &rcClient);
     
-    if (infoPtr->Style & WS_VSCROLL)
+    Style = GetWindowLong(infoPtr->hSelf,
+                          GWL_STYLE);
+    
+    if (Style & WS_VSCROLL)
     {
         ScrollPos = GetScrollPos(infoPtr->hSelf,
                                  SB_VERT);
@@ -314,6 +690,7 @@ PaintControl(IN PCHECKLISTWND infoPtr,
         DWORD CurrentIndex;
         COLORREF OldTextColor;
         BOOL Enabled, PrevEnabled;
+        POINT hOldBrushOrg;
         
         Enabled = IsWindowEnabled(infoPtr->hSelf);
         PrevEnabled = Enabled;
@@ -322,9 +699,14 @@ PaintControl(IN PCHECKLISTWND infoPtr,
         ItemRect.right = rcClient.right;
         ItemRect.top = VisibleFirstIndex * infoPtr->ItemHeight;
         
-        TextRect.left = ItemRect.left + 6;
-        TextRect.right = ItemRect.right - 6;
-        TextRect.top = ItemRect.top + 2;
+        TextRect.left = ItemRect.left + CI_TEXT_MARGIN_WIDTH;
+        TextRect.right = ItemRect.right - CI_TEXT_MARGIN_WIDTH;
+        TextRect.top = ItemRect.top + CI_TEXT_MARGIN_HEIGHT;
+        
+        SetBrushOrgEx(hDC,
+                      ItemRect.left,
+                      ItemRect.top,
+                      &hOldBrushOrg);
         
         OldTextColor = SetTextColor(hDC,
                                     infoPtr->TextColor[Enabled]);
@@ -337,10 +719,16 @@ PaintControl(IN PCHECKLISTWND infoPtr,
              Item = Item->Next, CurrentIndex++)
         {
             TextRect.bottom = TextRect.top + infoPtr->ItemHeight;
+            ItemRect.bottom = ItemRect.top + infoPtr->ItemHeight;
+            
+            SetBrushOrgEx(hDC,
+                          ItemRect.left,
+                          ItemRect.top,
+                          NULL);
             
-            if (Enabled && PrevEnabled != ((Item->State & CIS_DISABLED) == 0))
+            if (Enabled && PrevEnabled != ((Item->State & CIS_DISABLED) != CIS_DISABLED))
             {
-                PrevEnabled = ((Item->State & CIS_DISABLED) == 0);
+                PrevEnabled = ((Item->State & CIS_DISABLED) != CIS_DISABLED);
                 
                 SetTextColor(hDC,
                              infoPtr->TextColor[PrevEnabled]);
@@ -355,27 +743,54 @@ PaintControl(IN PCHECKLISTWND infoPtr,
             
             /* draw the Allow checkbox */
             CheckBox.left = infoPtr->CheckBoxLeft[CLB_ALLOW] - ((TextRect.bottom - TextRect.top) / 2);
-            CheckBox.right = CheckBox.left + (TextRect.bottom - TextRect.top) - 4;
+            CheckBox.right = CheckBox.left + (TextRect.bottom - TextRect.top) - (2 * CI_TEXT_MARGIN_HEIGHT);
             CheckBox.top = TextRect.top;
-            CheckBox.bottom = CheckBox.top + (TextRect.bottom - TextRect.top) - 4;
+            CheckBox.bottom = CheckBox.top + (TextRect.bottom - TextRect.top) - (2 * CI_TEXT_MARGIN_HEIGHT);
             DrawFrameControl(hDC,
                              &CheckBox,
                              DFC_BUTTON,
                              DFCS_BUTTONCHECK | DFCS_FLAT |
-                             ((Item->State & CIS_DISABLED) && Enabled ? DFCS_INACTIVE : 0) |
+                             ((Item->State & CIS_ALLOWDISABLED) || !Enabled ? DFCS_INACTIVE : 0) |
                              ((Item->State & CIS_ALLOW) ? DFCS_CHECKED : 0));
+            if (infoPtr->HasFocus &&
+                Item == infoPtr->FocusedCheckItem &&
+                infoPtr->FocusedCheckItemBox != CLB_DENY)
+            {
+                RECT rcFocus = CheckBox;
+                
+                InflateRect (&rcFocus,
+                             CI_TEXT_MARGIN_HEIGHT,
+                             CI_TEXT_MARGIN_HEIGHT);
+
+                DrawFocusRect(hDC,
+                              &rcFocus);
+            }
 
             /* draw the Deny checkbox */
             CheckBox.left = infoPtr->CheckBoxLeft[CLB_DENY] - ((TextRect.bottom - TextRect.top) / 2);
-            CheckBox.right = CheckBox.left + (TextRect.bottom - TextRect.top) - 4;
+            CheckBox.right = CheckBox.left + (TextRect.bottom - TextRect.top) - (2 * CI_TEXT_MARGIN_HEIGHT);
             DrawFrameControl(hDC,
                              &CheckBox,
                              DFC_BUTTON,
                              DFCS_BUTTONCHECK | DFCS_FLAT |
-                             ((Item->State & CIS_DISABLED) && Enabled ? DFCS_INACTIVE : 0) |
+                             ((Item->State & CIS_DENYDISABLED) || !Enabled ? DFCS_INACTIVE : 0) |
                              ((Item->State & CIS_DENY) ? DFCS_CHECKED : 0));
+            if (infoPtr->HasFocus &&
+                Item == infoPtr->FocusedCheckItem &&
+                infoPtr->FocusedCheckItemBox == CLB_DENY)
+            {
+                RECT rcFocus = CheckBox;
+
+                InflateRect (&rcFocus,
+                             CI_TEXT_MARGIN_HEIGHT,
+                             CI_TEXT_MARGIN_HEIGHT);
+
+                DrawFocusRect(hDC,
+                              &rcFocus);
+            }
 
             TextRect.top += infoPtr->ItemHeight;
+            ItemRect.top += infoPtr->ItemHeight;
         }
 
         SelectObject(hDC,
@@ -383,10 +798,46 @@ PaintControl(IN PCHECKLISTWND infoPtr,
 
         SetTextColor(hDC,
                      OldTextColor);
+
+        SetBrushOrgEx(hDC,
+                      hOldBrushOrg.x,
+                      hOldBrushOrg.y,
+                      NULL);
     }
 }
 
-LRESULT CALLBACK
+static VOID
+ChangeCheckItemFocus(IN PCHECKLISTWND infoPtr,
+                     IN PCHECKITEM NewFocus,
+                     IN INT NewFocusBox)
+{
+    if (NewFocus != infoPtr->FocusedCheckItem)
+    {
+        PCHECKITEM OldFocus = infoPtr->FocusedCheckItem;
+        infoPtr->FocusedCheckItem = NewFocus;
+        infoPtr->FocusedCheckItemBox = NewFocusBox;
+
+        if (OldFocus != NULL)
+        {
+            UpdateCheckItem(infoPtr,
+                            OldFocus);
+        }
+    }
+    else
+    {
+        infoPtr->FocusedCheckItemBox = NewFocusBox;
+    }
+
+    if (NewFocus != NULL)
+    {
+        MakeCheckItemVisible(infoPtr,
+                             NewFocus);
+        UpdateCheckItem(infoPtr,
+                        NewFocus);
+    }
+}
+
+static LRESULT CALLBACK
 CheckListWndProc(IN HWND hwnd,
                  IN UINT uMsg,
                  IN WPARAM wParam,
@@ -440,7 +891,7 @@ CheckListWndProc(IN HWND hwnd,
             SCROLLINFO ScrollInfo;
             
             ScrollInfo.cbSize = sizeof(ScrollInfo);
-            ScrollInfo.fMask = SIF_PAGE | SIF_RANGE | SIF_POS | SIF_TRACKPOS;
+            ScrollInfo.fMask = SIF_RANGE | SIF_POS;
 
             if (GetScrollInfo(hwnd,
                               SB_VERT,
@@ -469,36 +920,57 @@ CheckListWndProc(IN HWND hwnd,
                         break;
 
                     case SB_PAGEDOWN:
-                        if (ScrollInfo.nPos + ScrollInfo.nPage <= ScrollInfo.nMax)
+                    {
+                        RECT rcClient;
+                        INT ScrollLines;
+                        
+                        /* don't use ScrollInfo.nPage because we should only scroll
+                           down by the number of completely visible list entries.
+                           nPage however also includes the partly cropped list
+                           item at the bottom of the control */
+
+                        GetClientRect(hwnd, &rcClient);
+                        ScrollLines = max(1, (rcClient.bottom - rcClient.top) / infoPtr->ItemHeight);
+                        
+                        if (ScrollInfo.nPos + ScrollLines <= ScrollInfo.nMax)
                         {
-                            ScrollInfo.nPos += ScrollInfo.nPage;
+                            ScrollInfo.nPos += ScrollLines;
                         }
                         else
                         {
                             ScrollInfo.nPos = ScrollInfo.nMax;
                         }
                         break;
+                    }
 
                     case SB_PAGEUP:
-                        if (ScrollInfo.nPos >= ScrollInfo.nPage)
+                    {
+                        RECT rcClient;
+                        INT ScrollLines;
+
+                        /* don't use ScrollInfo.nPage because we should only scroll
+                           down by the number of completely visible list entries.
+                           nPage however also includes the partly cropped list
+                           item at the bottom of the control */
+
+                        GetClientRect(hwnd, &rcClient);
+                        ScrollLines = max(1, (rcClient.bottom - rcClient.top) / infoPtr->ItemHeight);
+                        
+                        if (ScrollInfo.nPos >= ScrollLines)
                         {
-                            ScrollInfo.nPos -= ScrollInfo.nPage;
+                            ScrollInfo.nPos -= ScrollLines;
                         }
                         else
                         {
                             ScrollInfo.nPos = 0;
                         }
                         break;
+                    }
 
                     case SB_THUMBPOSITION:
-                    {
-                        ScrollInfo.nPos = HIWORD(wParam);
-                        break;
-                    }
-                    
                     case SB_THUMBTRACK:
                     {
-                        ScrollInfo.nPos = ScrollInfo.nTrackPos;
+                        ScrollInfo.nPos = HIWORD(wParam);
                         break;
                     }
 
@@ -589,6 +1061,27 @@ CheckListWndProc(IN HWND hwnd,
                 
                 if (Item->State != OldState)
                 {
+                    /* revert the focus if the currently focused item is about
+                       to be disabled */
+                    if (Item == infoPtr->FocusedCheckItem &&
+                        (Item->State & CIS_DISABLED))
+                    {
+                        if (infoPtr->FocusedCheckItemBox == CLB_DENY)
+                        {
+                            if (Item->State & CIS_DENYDISABLED)
+                            {
+                                infoPtr->FocusedCheckItem = NULL;
+                            }
+                        }
+                        else
+                        {
+                            if (Item->State & CIS_ALLOWDISABLED)
+                            {
+                                infoPtr->FocusedCheckItem = NULL;
+                            }
+                        }
+                    }
+
                     UpdateControl(infoPtr,
                                   TRUE);
                 }
@@ -659,13 +1152,25 @@ CheckListWndProc(IN HWND hwnd,
             
             if (wParam == GWL_STYLE)
             {
-                infoPtr->Style = Style->styleNew;
+                BOOL AllowChangeStyle;
+
+                /* don't allow the control to enable/disable the vertical scrollbar
+                   if this message was invoked due to such a window style change! */
+                AllowChangeStyle = ((Style->styleNew & WS_VSCROLL) == (Style->styleOld & WS_VSCROLL));
+
                 UpdateControl(infoPtr,
-                              FALSE);
+                              AllowChangeStyle);
             }
             break;
         }
         
+        case WM_ENABLE:
+        {
+            UpdateControl(infoPtr,
+                          TRUE);
+            break;
+        }
+        
         case WM_MOUSEWHEEL:
         {
             SHORT ScrollDelta;
@@ -731,12 +1236,33 @@ CheckListWndProc(IN HWND hwnd,
         case WM_SETFOCUS:
         {
             infoPtr->HasFocus = TRUE;
+
+            if (infoPtr->FocusedCheckItem == NULL)
+            {
+                BOOL Shift = GetKeyState(VK_SHIFT) & 0x8000;
+                infoPtr->FocusedCheckItem = FindEnabledCheckBox(infoPtr,
+                                                                Shift,
+                                                                &infoPtr->FocusedCheckItemBox);
+            }
+            if (infoPtr->FocusedCheckItem != NULL)
+            {
+                MakeCheckItemVisible(infoPtr,
+                                     infoPtr->FocusedCheckItem);
+
+                UpdateCheckItem(infoPtr,
+                                infoPtr->FocusedCheckItem);
+            }
             break;
         }
         
         case WM_KILLFOCUS:
         {
             infoPtr->HasFocus = FALSE;
+            if (infoPtr->FocusedCheckItem != NULL)
+            {
+                UpdateCheckItem(infoPtr,
+                                infoPtr->FocusedCheckItem);
+            }
             break;
         }
         
@@ -744,9 +1270,125 @@ CheckListWndProc(IN HWND hwnd,
         case WM_MBUTTONDOWN:
         case WM_RBUTTONDOWN:
         {
-            if (!infoPtr->HasFocus && IsWindowEnabled(hwnd))
+            if (IsWindowEnabled(hwnd))
             {
-                SetFocus(hwnd);
+                PCHECKITEM NewFocus;
+                INT NewFocusBox = 0;
+                BOOL InCheckBox;
+                POINT pt;
+                BOOL ChangeFocus;
+                
+                pt.x = (LONG)LOWORD(lParam);
+                pt.y = (LONG)HIWORD(lParam);
+                
+                if (!infoPtr->HasFocus)
+                {
+                    SetFocus(hwnd);
+                }
+                
+                NewFocus = PtToCheckItemBox(infoPtr,
+                                            &pt,
+                                            &NewFocusBox,
+                                            &InCheckBox);
+                if (NewFocus != NULL)
+                {
+                    if (NewFocus->State & ((NewFocusBox != CLB_DENY) ? CIS_ALLOWDISABLED : CIS_DENYDISABLED))
+                    {
+                        /* the user clicked on a disabled checkbox, try to set
+                           the focus to the other one or not change it at all */
+
+                        InCheckBox = FALSE;
+                        
+                        ChangeFocus = ((NewFocus->State & CIS_DISABLED) != CIS_DISABLED);
+                        if (ChangeFocus)
+                        {
+                            NewFocusBox = ((NewFocusBox != CLB_DENY) ? CLB_DENY : CLB_ALLOW);
+                        }
+                    }
+                    else
+                    {
+                        ChangeFocus = TRUE;
+                    }
+                }
+                else
+                {
+                    ChangeFocus = TRUE;
+                }
+                
+                if (ChangeFocus)
+                {
+                    ChangeCheckItemFocus(infoPtr,
+                                         NewFocus,
+                                         NewFocusBox);
+                }
+            }
+            break;
+        }
+        
+        case WM_KEYDOWN:
+        {
+            switch (wParam)
+            {
+                case VK_RETURN:
+                {
+                    /* FIXME */
+                    break;
+                }
+                
+                case VK_TAB:
+                {
+                    PCHECKITEM NewFocus;
+                    UINT NewFocusBox = 0;
+                    BOOL Shift = GetKeyState(VK_SHIFT) & 0x8000;
+                    
+                    NewFocus = FindEnabledCheckBox(infoPtr,
+                                                   Shift,
+                                                   &NewFocusBox);
+
+                    ChangeCheckItemFocus(infoPtr,
+                                         NewFocus,
+                                         NewFocusBox);
+                    break;
+                }
+                
+                default:
+                {
+                    Ret = DefWindowProc(hwnd,
+                                        uMsg,
+                                        wParam,
+                                        lParam);
+                    break;
+                }
+            }
+            break;
+        }
+        
+        case WM_GETDLGCODE:
+        {
+            INT virtKey;
+            
+            Ret = DLGC_HASSETSEL;
+            virtKey = (lParam != 0 ? (INT)((LPMSG)lParam)->wParam : 0);
+            switch (virtKey)
+            {
+                case VK_RETURN:
+                {
+                    Ret |= DLGC_WANTMESSAGE;
+                    break;
+                }
+                
+                case VK_TAB:
+                {
+                    INT CheckBox;
+                    BOOL EnabledBox;
+                    BOOL Shift = GetKeyState(VK_SHIFT) & 0x8000;
+                    
+                    EnabledBox = FindEnabledCheckBox(infoPtr,
+                                                     Shift,
+                                                     &CheckBox) != NULL;
+                    Ret |= (EnabledBox ? DLGC_WANTTAB : DLGC_WANTCHARS);
+                    break;
+                }
             }
             break;
         }
@@ -769,7 +1411,6 @@ CheckListWndProc(IN HWND hwnd,
                 
                 infoPtr->hSelf = hwnd;
                 infoPtr->hNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
-                infoPtr->Style = ((LPCREATESTRUCTW)lParam)->style;
                 
                 SetWindowLongPtr(hwnd,
                                  0,
@@ -781,6 +1422,8 @@ CheckListWndProc(IN HWND hwnd,
                 infoPtr->ItemHeight = 10;
                 
                 infoPtr->HasFocus = FALSE;
+                infoPtr->FocusedCheckItem = NULL;
+                infoPtr->FocusedCheckItemBox = 0;
                 
                 infoPtr->TextColor[0] = GetSysColor(COLOR_GRAYTEXT);
                 infoPtr->TextColor[1] = GetSysColor(COLOR_WINDOWTEXT);
index 314f29b..bd5b99a 100644 (file)
 #define IDC_LABEL_ADVANCED      1008
 #define IDC_LABEL_PERMISSIONS_FOR       1009
 
-#define IDI_DEVMGR     100
-
 #define IDS_PSP_TITLE  1001
 #define IDS_UNKNOWN     1002
 #define IDS_SPECIAL_PERMISSIONS 1003
 #define IDS_PERMISSIONS_FOR     1004
+#define IDS_PERMISSIONS 1005
 
 #define IDB_USRGRPIMAGES       1001