// Is focus set to the specified item
            protected override bool IsFocused ()
            {
                NativeMethods.LVGROUP_V6 groupInfo = new NativeMethods.LVGROUP_V6();
                groupInfo.Init(Marshal.SizeOf(typeof(NativeMethods.LVGROUP_V6)));
                groupInfo.iGroupID = _groupId;
                groupInfo.mask = NativeMethods.LVGF_STATE;
                groupInfo.stateMask = NativeMethods.LVGS_SUBSETLINKFOCUSED;
                
                // Note: return code of GetGroupInfo() is not reliable.
                XSendMessage.GetGroupInfo(_hwnd, ref groupInfo); // ignore return code.

                return (groupInfo.state & NativeMethods.LVGS_SUBSETLINKFOCUSED) != 0;
            }
        private static unsafe GroupManager InitializeManager(IntPtr hwnd)
        {
            bool isComctrlV6OnOsVerV6orHigher = Misc.IsComctrlV6OnOsVerV6orHigher(hwnd);
           
            int itemCount = WindowsListView.GetItemCount(hwnd);
            NativeMethods.LVITEM_V6 item = new NativeMethods.LVITEM_V6();
            item.mask = NativeMethods.LVIF_GROUPID;

            // The only place where the GroupManager gets constructed
            GroupManager manager = new GroupManager(itemCount, hwnd, isComctrlV6OnOsVerV6orHigher);
            
            if (isComctrlV6OnOsVerV6orHigher)
            {
                NativeMethods.LVITEMINDEX ii = new NativeMethods.LVITEMINDEX(-1, -1);

                int flags = NativeMethods.LVNI_VISIBLEONLY | NativeMethods.LVNI_VISIBLEORDER;

                // When a listview is being "grouped by" an item may be in more than one group.  The itemCount
                // is the number of unique items.  This loop may iterate for more than the unique items in the group.
                // We are taking advantage of that fact the the array list will expand if there are alot of duplicates.
                while (XSendMessage.XSend (hwnd, NativeMethods.LVM_GETNEXTITEMINDEX, new IntPtr(&ii), flags, Marshal.SizeOf(ii.GetType())))
                {
                    // need to convert the item id to a group id
                    NativeMethods.LVGROUP_V6 groupInfo = new NativeMethods.LVGROUP_V6();
                    groupInfo.Init(Marshal.SizeOf(typeof(NativeMethods.LVGROUP_V6)));
                    groupInfo.mask = NativeMethods.LVGF_GROUPID;
                    
                    bool lresult  = XSendMessage.XSend(hwnd, NativeMethods.LVM_GETGROUPINFOBYINDEX, new IntPtr(ii.iGroup), new IntPtr(&groupInfo), Marshal.SizeOf(typeof(NativeMethods.LVGROUP_V6)));
                    if (!lresult)
                    {
                        if (groupInfo.iGroupID == -1)
                        {
                            // A -1 here means that there are no duplicates in this grouped listview so
                            // we have to get the group the old way.  This is done for performance reasons.
                            break;
                        }
                        // no group for this item should never happen.  
                        // If it ever does the other items might ok so just keep going.
                        continue;
                    }
                    
                    if (!manager.Add(groupInfo.iGroupID, ii.iItem))
                    {
                        // we had problem adding item to the needed group at this point it makes no
                        // sense to continue
                        System.Diagnostics.Debug.Assert(false, "Cannot add item to the needed group");
                        return null;
                    }
                }
            }

            bool sortNeeded = false;
            // If the code above did not yield anything try this way.  This will work for 
            // listviews pre Vista and grouped listviews in vista that don't have duplicate items.
            if (manager.GroupCount() == 0)
            {
                // if we get the groups this way they need to be sorted.  The code above brings them in sorted.
                sortNeeded = true;
                int current = 0;
                while (current < itemCount)
                {
                    item.iItem = current;
                    if (XSendMessage.GetItem(hwnd, ref item) && manager.Add(item.iGroupID, item.iItem))
                    {
                        current++;
                    }
                    else
                    {
                        // we had problem adding item to the needed group at this point it makes no
                        // sense to continue
                        System.Diagnostics.Debug.Assert(false, "Cannot add item to the needed group");
                        return null;
                    }
                }
            }

            // Sort items within the group
            int groupsCount = manager.GroupCount();
            for (int i = 0; i < groupsCount; i++)
            {
                Group group = (Group)manager._groups[i];
                Array.Sort(group.Items, 0, group.Count, new SortGroupItems(hwnd));
            }


            // Depending on how we got the group info we may need to sort it.
            // In vista the the listview can put the list items in the correct order.
            // Pre vista or old ui (v5) will always need to be sorted.
            if (sortNeeded)
            {
                // Sort groups
                manager._groups.Sort(new SortGroups(hwnd));
            }
            
            return manager;
        }
        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------

        #region Internal Methods

        // get focused element
        internal static ProxySimple GetFocusInGroup (IntPtr hwnd, ProxyFragment parent)
        {
            int index = WindowsListView.GetItemNext(hwnd, -1, NativeMethods.LVNI_FOCUSED);

            if (index != -1)
            {
                // get id of the group to which item belongs
                NativeMethods.LVITEM_V6 item = new NativeMethods.LVITEM_V6 ();

                item.mask = NativeMethods.LVIF_GROUPID;
                item.iItem = index;
                if (XSendMessage.GetItem(hwnd, ref item))
                {
                    WindowsListViewGroup group = new WindowsListViewGroup (hwnd, parent, item.iGroupID);

                    return new ListViewItem (hwnd, group, index);
                }
            }
            else
            {
                // if none of the items have focus see if the focus is on the subset link
                // this only exists in v6 comctrl on vista or later.
                if (Misc.IsComctrlV6OnOsVerV6orHigher(hwnd))
                {
                    int groupIndex = (int)Misc.ProxySendMessage(hwnd, NativeMethods.LVM_GETFOCUSEDGROUP, IntPtr.Zero, IntPtr.Zero);

                    // need to convert the item id to a group id
                    NativeMethods.LVGROUP_V6 groupInfo = new NativeMethods.LVGROUP_V6();
                    groupInfo.Init(Marshal.SizeOf(typeof(NativeMethods.LVGROUP_V6)));
                    groupInfo.mask = NativeMethods.LVGF_GROUPID;

                    unsafe
                    {
                        bool lresult  = XSendMessage.XSend(hwnd, NativeMethods.LVM_GETGROUPINFOBYINDEX, new IntPtr(groupIndex), new IntPtr(&groupInfo), Marshal.SizeOf(typeof(NativeMethods.LVGROUP_V6)));
                        if (!lresult)
                        {
                            // no group for this item should never happen.  
                            return null;
                        }
                    }
                    
                    int groupId = groupInfo.iGroupID;
                    groupInfo.Init(Marshal.SizeOf(typeof(NativeMethods.LVGROUP_V6)));
                    groupInfo.iGroupID = groupId;
                    groupInfo.mask = NativeMethods.LVGF_STATE;
                    groupInfo.stateMask = NativeMethods.LVGS_SUBSETLINKFOCUSED;
                    
                    // Note: return code of GetGroupInfo() is not reliable.
                    XSendMessage.GetGroupInfo(hwnd, ref groupInfo); // ignore return code.

                    if ((groupInfo.state & NativeMethods.LVGS_SUBSETLINKFOCUSED) != 0)
                    {
                        GroupManager.GroupInfo groupManagerInfo = GetGroupInfo (hwnd, groupId);
                        int [] items = groupManagerInfo._items;
                        if (groupManagerInfo._count <= 0 || groupManagerInfo._count >= items.Length)
                        {
                            return null;
                        }
                        
                        int sslIndex = items [groupManagerInfo._count - 1];

                        // The items array holds the list items in this group.  If we have a subset link we 
                        // don't store it with the list items because it isn't really a list item.  Instead we just
                        // create the subset link proxy with an item index one more than the last index.
                        WindowsListViewGroup group = new WindowsListViewGroup (hwnd, parent, groupId);
                        return group.CreateGroupSubsetLink(sslIndex + 1);
                    }
                    else
                    {
                        return new WindowsListViewGroup (hwnd, parent, groupId);
                    }
                }
            }

            return null;
        }
        protected override bool IsFocused ()
        {
            int groupIndex = (int)Misc.ProxySendMessage(_hwnd, NativeMethods.LVM_GETFOCUSEDGROUP, IntPtr.Zero, IntPtr.Zero);

            // need to convert the item id to a group id
            NativeMethods.LVGROUP_V6 groupInfo = new NativeMethods.LVGROUP_V6();
            groupInfo.Init(Marshal.SizeOf(typeof(NativeMethods.LVGROUP_V6)));
            groupInfo.mask = NativeMethods.LVGF_GROUPID;

            unsafe
            {
                bool lresult  = XSendMessage.XSend(_hwnd, NativeMethods.LVM_GETGROUPINFOBYINDEX, new IntPtr(groupIndex), new IntPtr(&groupInfo), Marshal.SizeOf(typeof(NativeMethods.LVGROUP_V6)));
                if (!lresult)
                {
                    // no group for this item should never happen.  
                    return false;
                }
            }      
            
            if (groupInfo.iGroupID == _groupID)
            {   
                return true;
            }

            return false;
        }
        // Returns the last child element in the raw hierarchy.
        internal override ProxySimple GetLastChild ()
        {
            // return last item in this group
            GroupManager.GroupInfo groupInfo = GetGroupInfo (_hwnd, ID);

            // List view groups in vista can have an extra link at the end that says
            // something like "show all 11 items...".  If one exists expose it as the last child.
            if (_isComctrlV6OnOsVerV6orHigher)
            {
                NativeMethods.LVGROUP_V6 group = new NativeMethods.LVGROUP_V6();
                group.Init(Marshal.SizeOf(typeof(NativeMethods.LVGROUP_V6)));
                group.iGroupID = _groupID;
                group.mask = NativeMethods.LVGF_STATE;
                group.stateMask = NativeMethods.LVGS_SUBSETED;
                
                // Note: return code of GetGroupInfo() is not reliable.
                XSendMessage.GetGroupInfo(_hwnd, ref group); // ignore return code.

                // if we are not subseted then the last item is a regular listitem so
                // it is ok to fall through and let that be created.  Otherwise we need to 
                // create the subset link proxy.
                if ((group.state & NativeMethods.LVGS_SUBSETED) != 0)
                {
                    int [] items = groupInfo._items;
                    if (groupInfo._count <= 0 || groupInfo._count > items.Length)
                    {
                        return null;
                    }
                    
                    int index = items [groupInfo._count - 1];
                    return CreateGroupSubsetLink(index + 1);
                }
            }


            if (groupInfo)
            {
                int [] items = groupInfo._items;
                if (groupInfo._count <= 0 || groupInfo._count > items.Length)
                {
                    return null;
                }
                
                int index = items [groupInfo._count - 1];

                return CreateListViewItem (index);
            }

            return null;
        }
        // Returns the next sibling element in the raw hierarchy.
        // Peripheral controls have always negative values.
        // Returns null if no next child
        internal override ProxySimple GetNextSibling (ProxySimple child)
        {
            GroupManager.GroupInfo groupInfo = GetGroupInfo (_hwnd, ID);

            if (groupInfo)
            {
                int [] items = groupInfo._items;
                int current = child._item;

                // find the index of the current lvitem
                int nextLocation = groupInfo.IndexOf (current) + 1; //Array.IndexOf(items, current) + 1;

                if (nextLocation <= 0)
                {
                    // No more siblings
                    return null;
                }

                if (nextLocation < groupInfo._count)
                {
                    return CreateListViewItem (items [nextLocation]);
                }

                // List view groups in vista can have an extra link at the end that says
                // somthing like "show all 11 items..."
                if (_isComctrlV6OnOsVerV6orHigher && nextLocation == groupInfo._count)
                {
                    NativeMethods.LVGROUP_V6 group = new NativeMethods.LVGROUP_V6();
                    group.Init(Marshal.SizeOf(typeof(NativeMethods.LVGROUP_V6)));
                    group.iGroupID = _groupID;
                    group.mask = NativeMethods.LVGF_STATE;
                    group.stateMask = NativeMethods.LVGS_SUBSETED;
                
                    // Note: return code of GetGroupInfo() is not reliable.
                    XSendMessage.GetGroupInfo(_hwnd, ref group); // ignore return code.

                    // The items array holds the list items in this group.  If we have a subset link we 
                    // don't store it with the list items because it isn't really a list item.  Instead we just
                    // create the subset link proxy with an item index one more than the last index.
                    if ((group.state & NativeMethods.LVGS_SUBSETED) != 0)
                    {
                        return CreateGroupSubsetLink(items [groupInfo._count - 1] + 1);
                    }
                }

            }

            return null;
        }