File: winforms\Managed\System\WinForms\ToolStripItemCollection.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="ToolStripItemCollection.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
namespace System.Windows.Forms {
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Drawing.Design;
    using System.Diagnostics;
    using System.Windows.Forms.Layout;
    using System.Drawing;
    using System.Security;
    using System.Security.Permissions;
    
    /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection"]/*' />
    /// <summary>
    /// Summary description for ToolStripItemCollection.
    /// </summary>
    [
    Editor("System.Windows.Forms.Design.ToolStripCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)),
    ListBindable(false),
    UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)
    ]
    public class ToolStripItemCollection : ArrangedElementCollection, IList {
        
        private ToolStrip owner;
        private bool itemsCollection;
        private bool isReadOnly = false;
        /// A caching mechanism for key accessor
        /// We use an index here rather than control so that we don't have lifetime
        /// issues by holding on to extra references.
        /// Note this is not Thread Safe - but Microsoft has to be run in a STA anyways.
        private int lastAccessedIndex = -1;
 
 
        internal ToolStripItemCollection(ToolStrip owner, bool itemsCollection) : this(owner, itemsCollection, /*isReadOnly=*/false){
        }
 
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        internal ToolStripItemCollection(ToolStrip owner, bool itemsCollection, bool isReadOnly) {
            this.owner = owner;
            this.itemsCollection = itemsCollection;
            this.isReadOnly = isReadOnly;
        }
 
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.ToolStripItemCollection"]/*' />
        /// <devdoc>
        /// <para>[To be supplied.]</para>
        /// </devdoc>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
        public ToolStripItemCollection(ToolStrip owner, ToolStripItem[] value) {
            if (owner == null) {
                throw new ArgumentNullException("owner");
            }
           
            this.owner = owner;
            AddRange(value);
        }
 
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.this"]/*' />
        /// <devdoc>
        /// <para></para>
        /// </devdoc>
        public new virtual ToolStripItem this[int index] {
            get {
                return (ToolStripItem)(InnerList[index]);
            }
        }
        
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.this1"]/*' />
        /// <devdoc>
        /// <para>Retrieves the child control with the specified key.</para>
        /// </devdoc>
        public virtual ToolStripItem this[string key] {
            get {
                // We do not support null and empty string as valid keys.
                if ((key == null) || (key.Length == 0)){
                    return null;
                }
 
                // Search for the key in our collection
                int index = IndexOfKey(key);
                if (IsValidIndex(index)) {
                    return (ToolStripItem)InnerList[index];
                }
                else {
                    return null;
                }
 
            }
        }
 
        
        public ToolStripItem Add(string text) {
            return Add(text,null,null);
        }
        public ToolStripItem Add(Image image) {
            return Add(null,image,null);
        }
        public ToolStripItem Add(string text, Image image) {
            return Add(text,image,null);
        }
        public ToolStripItem Add(string text, Image image, EventHandler onClick) {
            ToolStripItem item = owner.CreateDefaultItem(text,image,onClick);
            Add(item);
            return item;
        }
            
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.Add"]/*' />
        /// <devdoc>
        /// <para>[To be supplied.]</para>
        /// </devdoc>
        public int Add(ToolStripItem value) {
            CheckCanAddOrInsertItem(value);
            
            SetOwner(value);
            int retVal =  InnerList.Add(value);   
            if (itemsCollection &&  owner != null) {
                owner.OnItemAddedInternal(value);
                owner.OnItemAdded(new ToolStripItemEventArgs(value));                
            }           
            return retVal;
            
        }
 
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.AddRange"]/*' />
        /// <devdoc>
        /// <para>[To be supplied.]</para>
        /// </devdoc>
        public void AddRange(ToolStripItem[] toolStripItems) {        
            if (toolStripItems == null) {
                throw new ArgumentNullException("toolStripItems");
            }
            if (IsReadOnly) {
                throw new NotSupportedException(SR.GetString(SR.ToolStripItemCollectionIsReadOnly));
            }
 
            // ToolStripDropDown will look for PropertyNames.Items to determine if it needs
            // to resize itself.
            using (new LayoutTransaction(this.owner, this.owner, PropertyNames.Items)) {
                for (int i = 0; i < toolStripItems.Length; i++) {
                    this.Add(toolStripItems[i]);
                }
            }
        }
 
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.AddRange1"]/*' />
        /// <devdoc>
        /// <para>[To be supplied.]</para>
        /// </devdoc>
        public void AddRange(ToolStripItemCollection toolStripItems) {
            if (toolStripItems == null) {
                throw new ArgumentNullException("toolStripItems");
            }
            if (IsReadOnly) {
                throw new NotSupportedException(SR.GetString(SR.ToolStripItemCollectionIsReadOnly));
            }
 
            // ToolStripDropDown will look for PropertyNames.Items to determine if it needs
            // to resize itself.
            using (new LayoutTransaction(this.owner, this.owner, PropertyNames.Items)) {
                int currentCount = toolStripItems.Count;
                for (int i = 0; i < currentCount; i++) {
                    this.Add(toolStripItems[i]);
                }
            }
          
        }
 
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.Contains"]/*' />
        /// <devdoc>
        /// <para>[To be supplied.]</para>
        /// </devdoc>
        public bool Contains(ToolStripItem value) {
            return InnerList.Contains(value);
        }
 
        
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.Clear"]/*' />
        /// <devdoc>
        /// <para>[To be supplied.]</para>
        /// </devdoc>
        public virtual void Clear() {
            if (IsReadOnly) {
               throw new NotSupportedException(SR.GetString(SR.ToolStripItemCollectionIsReadOnly));
            }
            if (Count == 0) {
                return;
            }
            ToolStripOverflow overflow = null;
 
            if (owner != null && !owner.IsDisposingItems) {
                owner.SuspendLayout();
                overflow = owner.GetOverflow();
                if (overflow != null) {
                    overflow.SuspendLayout();
                }
            }
            try {
                while (Count != 0) {
                    RemoveAt(Count - 1);
                }
            }
            finally {
                if (overflow != null) {
                    overflow.ResumeLayout(false);
                }
                if (owner != null && !owner.IsDisposingItems) {
                    owner.ResumeLayout();
                }
            }
        }
   
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.ContainsKey"]/*' />
        /// <devdoc>
        /// <para>Returns true if the collection contains an item with the specified key, false otherwise.</para>
        /// </devdoc>
        public virtual bool ContainsKey(string key) {
            return IsValidIndex(IndexOfKey(key)); 
        }
 
        private void CheckCanAddOrInsertItem(ToolStripItem value) {
            if (value == null) {
                throw new ArgumentNullException("value");
            }
            if (IsReadOnly) {
                throw new NotSupportedException(SR.GetString(SR.ToolStripItemCollectionIsReadOnly));
            }
            
           
            ToolStripDropDown dropDown = owner as ToolStripDropDown;
            if (dropDown != null) {
                // VSWhidbey 436973: if we're on a dropdown, we can only add non-control host items
                // as we dont want anything on a dropdown to get keyboard messages in the Internet.
 
                if (dropDown.OwnerItem == value) {
                   throw new NotSupportedException(SR.GetString(SR.ToolStripItemCircularReference)); 
                }
                
                // VSWhidbey 496526: ScrollButton is the only allowed control host as it correctly eats key messages.
                if (value is ToolStripControlHost && !(value is System.Windows.Forms.ToolStripScrollButton)) {
                    if (dropDown.IsRestrictedWindow) {
                        IntSecurity.AllWindows.Demand();
                    }
                }
            }
           
        }
 
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.Find"]/*' />
        /// <devdoc>
        /// <para>Searches for Items by their Name property, builds up an array 
        /// of all the controls that match. 
        /// </para>
        /// </devdoc>
        public ToolStripItem[] Find(string key, bool searchAllChildren) {            
            if ((key == null) || (key.Length == 0)) {
              throw new System.ArgumentNullException("key", SR.GetString(SR.FindKeyMayNotBeEmptyOrNull));
            }
 
            ArrayList foundItems =  FindInternal(key, searchAllChildren, this, new ArrayList());
 
            // Make this a stongly typed collection.
            ToolStripItem[] stronglyTypedFoundItems = new ToolStripItem[foundItems.Count]; 
            foundItems.CopyTo(stronglyTypedFoundItems, 0);
 
            return stronglyTypedFoundItems;
        }
      
        /// <devdoc>
        ///     <para>Searches for Items by their Name property, builds up an array list
        ///           of all the items that match. 
        ///     </para>
        /// </devdoc>
        /// <internalonly/> 
        private ArrayList FindInternal(string key, bool searchAllChildren, ToolStripItemCollection itemsToLookIn, ArrayList foundItems)
        {
            if ((itemsToLookIn == null) || (foundItems == null)) {
                return null;  // 
            }
 
            try
            {
                for (int i = 0; i < itemsToLookIn.Count; i++)
                {
                    if (itemsToLookIn[i] == null)
                    {
                        continue;
                    }
 
                    if (WindowsFormsUtils.SafeCompareStrings(itemsToLookIn[i].Name, key, /* ignoreCase = */ true))
                    {
                        foundItems.Add(itemsToLookIn[i]);
                    }
                }
 
 
                // Optional recurive search for controls in child collections.
 
                if (searchAllChildren)
                {
                    for (int j = 0; j < itemsToLookIn.Count; j++)
                    {
                        ToolStripDropDownItem item = itemsToLookIn[j] as ToolStripDropDownItem;
                        if (item == null)
                        {
                            continue;
                        }
                        if (item.HasDropDownItems)
                        {
                            // if it has a valid child collecion, append those results to our collection
                            foundItems = FindInternal(key, searchAllChildren, item.DropDownItems, foundItems);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                // VSWHIDBEY 80122 make sure we deal with non-critical failures gracefully.
                if (ClientUtils.IsCriticalException(e))
                {
                    throw;
                }
            }
      
            return foundItems;
        }
 
 
        
        public override bool IsReadOnly { get { return this.isReadOnly; }}
 
        void IList.Clear() { Clear(); }
        bool IList.IsFixedSize { get { return InnerList.IsFixedSize; }}
        bool IList.Contains(object value) { return InnerList.Contains(value); }
        void IList.RemoveAt(int index) { RemoveAt(index); }
        void IList.Remove(object value) { Remove(value as ToolStripItem); }
        int IList.Add(object value) { return Add(value as ToolStripItem); }
        int IList.IndexOf(object value) { return IndexOf(value as ToolStripItem); }
        void IList.Insert(int index, object value) { Insert(index, value as ToolStripItem);  }
 
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.IList.this"]/*' />
        /// <internalonly/>
        object IList.this[int index] {
            get { return InnerList[index]; }            
            set { throw new NotSupportedException(SR.GetString(SR.ToolStripCollectionMustInsertAndRemove)); /* InnerList[index] = value; */ }
        }
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.Insert"]/*' />
        /// <devdoc>
        /// <para>[To be supplied.]</para>
        /// </devdoc>
        public void Insert(int index, ToolStripItem value) {
            CheckCanAddOrInsertItem(value);
            SetOwner(value);
            InnerList.Insert(index, value);
            if (itemsCollection && owner != null) {
                if (owner.IsHandleCreated){
                    LayoutTransaction.DoLayout(owner, value, PropertyNames.Parent);
                }
                else {
                    // next time we fetch the preferred size, recalc it.
                    CommonProperties.xClearPreferredSizeCache(owner);
                }
                owner.OnItemAddedInternal(value);
                owner.OnItemAdded(new ToolStripItemEventArgs(value));                  
            }           
 
        }
			
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.IndexOf"]/*' />
        /// <devdoc>
        /// <para>[To be supplied.]</para>
        /// </devdoc>
        public int IndexOf(ToolStripItem value) {
            return InnerList.IndexOf(value);
        }
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.IndexOfKey"]/*' />
        /// <devdoc>
        /// <para>The zero-based index of the first occurrence of value within the entire CollectionBase, if found; otherwise, -1.</para>
        /// </devdoc>
        public virtual int IndexOfKey(String key) {
            // Step 0 - Arg validation
            if ((key == null) || (key.Length == 0)){
                return -1; // we dont support empty or null keys.
            }
 
            // step 1 - check the last cached item
            if (IsValidIndex(lastAccessedIndex)) {
                if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) {
                    return lastAccessedIndex;
                }
            }
 
            // step 2 - search for the item
            for (int i = 0; i < this.Count; i ++) {
                if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) {
                    lastAccessedIndex = i;
                    return i;
                }
            }
 
            // step 3 - we didn't find it.  Invalidate the last accessed index and return -1.
            lastAccessedIndex = -1;
            return -1;
        }
 
        /// <devdoc>
        ///     <para>Determines if the index is valid for the collection.</para>
        /// </devdoc>
        /// <internalonly/> 
        private bool IsValidIndex(int index) {
            return ((index >= 0) && (index < this.Count));
        }
 
        /// <devdoc>
        ///  Do proper cleanup of ownership, etc.
        /// </devdoc>
        private void OnAfterRemove(ToolStripItem item) {
            if (itemsCollection) {
                ToolStrip parent = null; 
                if (item != null) {
                    parent = item.ParentInternal;
                    item.SetOwner(null);
                }
 
                if (owner != null) {
                    owner.OnItemRemovedInternal(item);
 
                    if (!owner.IsDisposingItems) {
                        ToolStripItemEventArgs e = new ToolStripItemEventArgs(item);
                        owner.OnItemRemoved(e);
 
                        // VSWhidbey 505129
                        // dont fire the ItemRemoved event for Overflow
                        // it would fire constantly.... instead clear any state if the item
                        // is really being removed from the master collection.
                        if (parent != null && parent != owner) {
                            parent.OnItemVisibleChanged(e, /*performLayout*/false);
                        }
                    }
                }                
            }
        }
        
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.Remove"]/*' />
        /// <devdoc>
        /// <para>[To be supplied.]</para>
        /// </devdoc>
        public void Remove(ToolStripItem value) {
            if (IsReadOnly) {
               throw new NotSupportedException(SR.GetString(SR.ToolStripItemCollectionIsReadOnly));
            }
            InnerList.Remove(value);
            OnAfterRemove(value);
        }
 
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.RemoveAt"]/*' />
        /// <devdoc>
        /// <para>[To be supplied.]</para>
        /// </devdoc>
        public void RemoveAt(int index) {
            if (IsReadOnly) {
               throw new NotSupportedException(SR.GetString(SR.ToolStripItemCollectionIsReadOnly));
            }
            ToolStripItem item = null;
            if (index < Count && index >= 0)  {
                item = (ToolStripItem)(InnerList[index]);
            }
            InnerList.RemoveAt(index);
            OnAfterRemove(item);
        }
      
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.RemoveByKey"]/*' />
        /// <devdoc>
        /// <para>Removes the child item with the specified key.</para>
        /// </devdoc>
        public virtual void RemoveByKey(string key) {
            if (IsReadOnly) {
               throw new NotSupportedException(SR.GetString(SR.ToolStripItemCollectionIsReadOnly));
            }
            int index = IndexOfKey(key);
            if (IsValidIndex(index)) {
                RemoveAt(index); 
            }
        }
 
        /// <include file='doc\ToolStripItemCollection.uex' path='docs/doc[@for="ToolStripItemCollection.CopyTo"]/*' />
        /// <devdoc>
        /// <para>[To be supplied.]</para>
        /// </devdoc>
        public void CopyTo(ToolStripItem[] array, int index) {
            InnerList.CopyTo(array, index);
        }
            
        // 
        internal void MoveItem(ToolStripItem value) {
            if (value.ParentInternal != null) {
                int indexOfItem = value.ParentInternal.Items.IndexOf(value);
                if (indexOfItem >= 0) {
                    value.ParentInternal.Items.RemoveAt(indexOfItem);
                }
            }
            Add(value);
 
        }
 
        internal void MoveItem(int index, ToolStripItem value) {
           
            // if moving to the end - call add instead.
            if (index == this.Count) {
                MoveItem(value);
                return;
            }
			
            if (value.ParentInternal != null) {
                int indexOfItem = value.ParentInternal.Items.IndexOf(value);
				
                if (indexOfItem >= 0) {
                    value.ParentInternal.Items.RemoveAt(indexOfItem);
				
                    if ((value.ParentInternal == owner) && (index > indexOfItem)) {
                        index--;
                    }
                }
            }
            Insert(index,value);
 
        }
 
        private void SetOwner(ToolStripItem item) {
 
            if (itemsCollection) {
              
                if (item != null) {
                    if (item.Owner != null) {
                        item.Owner.Items.Remove(item);
                    }
 
                    item.SetOwner(owner);
                    if (item.Renderer != null) {
                        item.Renderer.InitializeItem(item);
                    }
                }
            }
        }
        
    }
    
 
}