/// <include file='doc\SelectionService.uex' path='docs/doc[@for="SelectionService.ValidatePrimarySelection"]/*' />
        /// <devdoc>
        ///      Ensures that a valid primary selection exists.
        /// </devdoc>
        private void ValidatePrimarySelection()
        {
            if (primarySelection == null)
            {
                IDictionaryEnumerator selections = (IDictionaryEnumerator)selectionsByComponent.GetEnumerator();
                bool valueFound = selections.MoveNext();

                if (valueFound)
                {
                    primarySelection         = (SelectionItem)selections.Value;
                    primarySelection.Primary = true;
                }
            }
        }
        /// <include file='doc\SelectionService.uex' path='docs/doc[@for="SelectionService.SetPrimarySelection"]/*' />
        /// <devdoc>
        ///     Sets the given selection object to be the primary selection.
        /// </devdoc>
        internal void SetPrimarySelection(SelectionItem sel)
        {
            if (sel != primarySelection)
            {
                if (null != primarySelection)
                {
                    primarySelection.Primary = false;
                }

                primarySelection = sel;
            }

            if (null != primarySelection)
            {
                primarySelection.Primary = true;
            }
        }
        /// <include file='doc\SelectionService.uex' path='docs/doc[@for="SelectionService.Dispose"]/*' />
        /// <devdoc>
        ///     Disposes the entire selection manager.
        /// </devdoc>
        public void Dispose()
        {
            // We've got to be careful here.  We're one of the last things to go away when
            // a form is being torn down, and we've got to be wary if things have pulled out
            // already.
            //
            host.RemoveService(typeof(ISelectionService));
            host.TransactionOpened -= new EventHandler(this.OnTransactionOpened);
            host.TransactionClosed -= new DesignerTransactionCloseEventHandler(this.OnTransactionClosed);
            if (host.InTransaction)
            {
                OnTransactionClosed(host, new DesignerTransactionCloseEventArgs(true));
            }
            host.LoadComplete -= new EventHandler(this.OnLoadComplete);

            // And configure the events we want to listen to.
            //
            IComponentChangeService cs = (IComponentChangeService)host.GetService(typeof(IComponentChangeService));

            Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || cs != null, "IComponentChangeService not found");
            if (cs != null)
            {
                cs.ComponentAdded   -= new ComponentEventHandler(this.OnComponentAdd);
                cs.ComponentRemoved -= new ComponentEventHandler(this.OnComponentRemove);
                cs.ComponentChanged -= new ComponentChangedEventHandler(this.OnComponentChanged);
            }

            SelectionItem[] sels = new SelectionItem[selectionsByComponent.Values.Count];
            selectionsByComponent.Values.CopyTo(sels, 0);

            for (int i = 0; i < sels.Length; i++)
            {
                sels[i].Dispose();
            }

            selectionsByComponent.Clear();
            primarySelection = null;
        }
        /// <include file='doc\SelectionService.uex' path='docs/doc[@for="SelectionService.SetSelectedComponents1"]/*' />
        /// <devdoc>
        ///     Changes the user's current set of selected components to the components
        ///     in the given array.  If the array is null or doesn't contain any
        ///     components, this will select the top level component in the designer.
        /// </devdoc>
        public void SetSelectedComponents(ICollection components, SelectionTypes selectionType)
        {
            bool fToggle  = false;
            bool fControl = false;
            bool fClick   = false;
            bool fChanged = false;  // did we actually change something?

            // We always want to allow NULL arrays coming in.
            //
            if (components == null)
            {
                components = new object[0];
            }

#if DEBUG
            foreach (object comp in components)
            {
                Debug.Assert(comp != null, "NULL component pushed into SetSelectedComponents");
                if (comp is IComponent)
                {
                    Debug.Assert(((IComponent)comp).Site != null, "Component of type " + comp.GetType().Name + " doesn't have a site!");
                }
            }
#endif

            if ((selectionType & SelectionTypes.Normal) == SelectionTypes.Normal ||
                (selectionType & SelectionTypes.Click) == SelectionTypes.Click)
            {
                fControl = ((Control.ModifierKeys & Keys.Control) == Keys.Control);

                // Only toggle when we are affecting a single control, and
                // when we are handling the "mouse" state events (i.e. up/down
                // used to show/hide the selection).
                //
                fToggle = ((Control.ModifierKeys & Keys.Control) != 0 || (Control.ModifierKeys & Keys.Shift) != 0) &&
                          components.Count == 1 &&
                          (selectionType & SelectionTypes.MouseUp) != SelectionTypes.MouseUp;
            }

            if ((selectionType & SelectionTypes.Click) == SelectionTypes.Click)
            {
                fClick = true;
            }

#if DEBUG
            if (Switches.SETSELECT.TraceVerbose)
            {
                Debug.Write("SETSELECT: [");
                bool first = true;
                foreach (object comp in components)
                {
                    if (first)
                    {
                        first = false;
                        Debug.Write(" ");
                    }
                    Debug.Write(((IComponent)comp).Site.Name);
                }
                Debug.Write("]  ");
                Debug.Write(" fToggle=" + fToggle.ToString());
                Debug.Write(" fControl=" + fControl.ToString());
                Debug.Write(" fClick=" + fClick.ToString());
                Debug.WriteLine("");
                Debug.WriteLine(Environment.StackTrace);
            }
#endif

            // If we are replacing the selection, only remove the ones that are not in our new list.
            // We also handle the special case here of having a singular component selected that's
            // already selected.  In this case we just move it to the primary selection.
            //
            if (!fToggle && !fControl)
            {
                object firstSelection = null;
                foreach (object o in components)
                {
                    firstSelection = o;
                    break;
                }

                if (fClick && 1 == components.Count && GetComponentSelected(firstSelection))
                {
                    SelectionItem oldPrimary = primarySelection;
                    SetPrimarySelection((SelectionItem)selectionsByComponent[firstSelection]);
                    if (oldPrimary != primarySelection)
                    {
                        fChanged = true;
                    }
                }
                else
                {
                    SelectionItem[] selections = new SelectionItem[selectionsByComponent.Values.Count];
                    selectionsByComponent.Values.CopyTo(selections, 0);

                    // Yucky and N^2, but even with several hundred components this should be fairly fast
                    //
                    foreach (SelectionItem item in selections)
                    {
                        bool remove = true;

                        foreach (object comp in components)
                        {
                            if (comp == item.component)
                            {
                                remove = false;
                                break;
                            }
                        }

                        if (remove)
                        {
                            Debug.WriteLineIf(Switches.SETSELECT.TraceVerbose, "Removing selection due to replace selection");
                            RemoveSelection(item);
                            fChanged = true;
                        }
                    }
                }
            }

            SelectionItem primarySel    = null;
            int           selectedCount = selectionsByComponent.Count;

            // Now do the selection!
            //
            foreach (object comp in components)
            {
                if (comp != null)
                {
                    SelectionItem s = (SelectionItem)selectionsByComponent[comp];

                    if (null == s)
                    {
                        Debug.WriteLineIf(Switches.SETSELECT.TraceVerbose, "Adding selection due to no selection existing");
                        s = new SelectionItem(this, comp);
                        AddSelection(s);

                        if (fControl || fToggle)
                        {
                            primarySel = s;
                        }

                        fChanged = true;
                    }
                    else
                    {
                        if (fToggle)
                        {
                            // remove the selection from our list
                            //
                            Debug.WriteLineIf(Switches.SETSELECT.TraceVerbose, "Removing selection due to toggle");
                            RemoveSelection(s);
                            fChanged = true;
                        }
                    }
                }
            }

            if (primarySel != null)
            {
                SetPrimarySelection(primarySel);
            }

            // Now notify that our selection has changed
            //
            if (fChanged)
            {
                Debug.WriteLineIf(Switches.SETSELECT.TraceVerbose, "Selection changed, calling OnSelectionChanged");
                OnSelectionChanged();
            }
        }
 /// <include file='doc\SelectionService.uex' path='docs/doc[@for="SelectionService.RemoveSelection"]/*' />
 /// <devdoc>
 ///     Removes the given selection from our selection list, keeping everything nicely in
 ///     ssync.
 /// </devdoc>
 internal void RemoveSelection(SelectionItem s)
 {
     selectionsByComponent.Remove(s.component);
     s.Dispose();
 }
        /// <include file='doc\SelectionService.uex' path='docs/doc[@for="SelectionService.OnComponentRemove"]/*' />
        /// <devdoc>
        ///     called by the formcore when someone has removed a component.  This will
        ///     remove any selection on the component without disturbing the rest of
        ///     the selection
        /// </devdoc>
        private void OnComponentRemove(object sender, ComponentEventArgs ce)
        {
            SelectionItem sel = (SelectionItem)selectionsByComponent[ce.Component];

            if (sel != null)
            {
                RemoveSelection(sel);


                // IF allowNoSelection has been set, then we need to return before
                // we change the selection to something we shouldn't
                //
                if (AllowNoSelection)
                {
                    return;
                }

                // If we removed the last selection and we have a designer host, then select the base
                // component of the host.  Otherwise, it is OK to have an empty selection.
                //
                if (selectionsByComponent.Count == 0 && host != null)
                {
                    // now we have to run through all the components and pick
                    // the control with the highest zorder.  For v.next we should
                    // consider providing a service that allows a designer to decide
                    // a new selection to pick when the existing one dies.
                    //
                    IComponent[] comps = new IComponent[host.Container.Components.Count];
                    host.Container.Components.CopyTo(comps, 0);

                    if (comps == null)
                    {
                        return;
                    }

                    int    maxZOrder    = -1;
                    int    defaultIndex = -1;
                    object maxIndexComp = null;
                    object baseComp     = host.RootComponent;

                    if (baseComp == null)
                    {
                        return;
                    }

                    for (int i = comps.Length - 1; i >= 0; i--)
                    {
                        if (comps[i] == baseComp)
                        {
                            continue;
                        }
                        else if (defaultIndex == -1)
                        {
                            defaultIndex = i;
                        }

                        if (comps[i] is Control)
                        {
                            int zorder = ((Control)comps[i]).TabIndex;
                            if (zorder > maxZOrder)
                            {
                                maxZOrder    = zorder;
                                maxIndexComp = (object)comps[i];
                            }
                        }
                    }

                    if (maxIndexComp == null)
                    {
                        if (defaultIndex != -1)
                        {
                            maxIndexComp = comps[defaultIndex];
                        }
                        else
                        {
                            maxIndexComp = baseComp;
                        }
                    }

                    SetSelectedComponents(new object[] { maxIndexComp }, SelectionTypes.Replace);
                }
                else
                {
                    OnSelectionChanged();
                }
            }
            else
            {
                // Component isn't selected, but our list of selectable components is
                // different.
                //
                OnSelectionContentsChanged();
            }
        }
 /// <include file='doc\SelectionService.uex' path='docs/doc[@for="SelectionService.AddSelection"]/*' />
 /// <devdoc>
 ///     Adds the given selection to our selection list.
 /// </devdoc>
 internal void AddSelection(SelectionItem sel)
 {
     selectionsByComponent[sel.component] = sel;
 }
        /// <include file='doc\SelectionService.uex' path='docs/doc[@for="SelectionService.UpdateHelpContext"]/*' />
        /// <devdoc>
        ///     Pushes the help context into the help service for the
        ///     current set of selected objects.
        /// </devdoc>
        private void UpdateHelpContext()
        {
            IHelpService helpService = (IHelpService)host.GetService(typeof(IHelpService));
            IEnumerator  e;

            if (helpService == null)
            {
                return;
            }

            // If there is an old set of context attributes, remove them.
            //
            if (contextAttributes != null && contextAttributes.Keys.Count > 0)
            {
                e = contextAttributes.Keys.GetEnumerator();

                while (e.MoveNext())
                {
                    helpService.RemoveContextAttribute("Keyword", (string)e.Current);
                }
                contextAttributes.Clear();
            }

            IComponent rootComponent = host.RootComponent;
            IDesigner  baseDesigner  = null;

            if (rootComponent != null)
            {
                baseDesigner = host.GetDesigner(rootComponent);
                if (baseDesigner != null)
                {
                    helpService.RemoveContextAttribute("Keyword", "Designer_" + baseDesigner.GetType().FullName);
                }
            }

            // Clear the selection keyword
            //
            helpService.RemoveContextAttribute("Selection", selectionKeywords[contextKeyword]);

            if (contextAttributes == null)
            {
                contextAttributes = new Hashtable();
            }

            // Get a list of unique class names.
            //
            e = selectionsByComponent.Values.GetEnumerator();

            bool baseComponentSelected = false;

            if (rootComponent != null && (selectionsByComponent.Count == 0 || (selectionsByComponent.Count == 1 && selectionsByComponent[rootComponent] != null)))
            {
                baseComponentSelected = true;
            }

            while (e.MoveNext())
            {
                SelectionItem s = (SelectionItem)e.Current;
                contextAttributes[s.component.GetType().FullName] = null;
            }

            // And push them into the help context as keywords.
            //
            e = contextAttributes.Keys.GetEnumerator();
            HelpKeywordType selectionType = baseComponentSelected ? HelpKeywordType.GeneralKeyword : HelpKeywordType.F1Keyword;

            while (e.MoveNext())
            {
                helpService.AddContextAttribute("Keyword", (string)e.Current, selectionType);
            }

            // Now add the form designer context as well
            //
            if (baseDesigner != null)
            {
                helpService.AddContextAttribute("Keyword", "Designer_" + baseDesigner.GetType().FullName,
                                                baseComponentSelected ? HelpKeywordType.F1Keyword : HelpKeywordType.GeneralKeyword);
            }

            // Now add the appropriate selection keyword.  Note that we do not count
            // the base component as being selected if it is the only thing selected.
            //
            int count = SelectionCount;

            if (rootComponent != null && count == 1 && selectionsByComponent[rootComponent] != null)
            {
                count--;
            }
            contextKeyword = (short)Math.Min(count, selectionKeywords.Length - 1);
            helpService.AddContextAttribute("Selection", selectionKeywords[contextKeyword], HelpKeywordType.FilterKeyword);
        }