vbAccelerator - Contents of code file: VSNetListBar.cs

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Runtime.InteropServices;

using vbAccelerator.Components.Drawing;

namespace vbAccelerator.Components.ListBarControl
{
   /// <summary>
   /// A class which subclasses the vbAccelerator .NET <see cref="ListBar"/>
    control 
   /// so it renders like the VS.NET ListBar control.
   /// The primary changes are:
   /// <list type="bullet">To remove the control's border.</list>
   /// <list type="bullet">To place the scroll buttons adjacent to the bar
   /// selection buttons.</list>
   /// <list type="bullet">To provide a method to prevent an item from
   /// begin dragged.</list>
   /// <list type="bullet">To render a popup window showing additional
   /// detail of an item if it is too wide to display (TODO).</list>
   /// </summary>
   public class VSNetListBar : ListBar
   {

      /// <summary>
      /// Creates a new instance of a <see cref="ListBarScrollButton"/> used by 
      /// this control to draw the scroll buttons.  Fired during control
       initialisation
      /// </summary>
      /// <param name="buttonType">The type of scroll button (Up or Down)
      /// to create</param>
      /// <returns>A new <see cref="ListBarScrollButton"/> which is drawn when
       a 
      /// <see cref="ListBarGroup"/> contains more items than can be 
      /// displayed.</returns>
      protected override ListBarScrollButton CreateListBarScrollButton(
         ListBarScrollButton.ListBarScrollButtonType buttonType)
      {
         return new VSNetListBarScrollButton(this, buttonType);
      }

      /// <summary>
      /// Returns a new instance of the object to hold a collection
      /// of groups.
      /// </summary>
      /// <returns>The new <see cref="VSNetListBarGroupCollection"/></returns>
      protected override ListBarGroupCollection CreateListBarGroupCollection()
      {
         return new VSNetListBarGroupCollection(this);
      }

      /// <summary>
      /// Gets the collection of <see cref="VSNetListBarGroup"/> objects
      /// associated with the control.
      /// </summary>      
      public new VSNetListBarGroupCollection Groups
      {
         get
         {
            return (VSNetListBarGroupCollection)base.Groups;
         }
      }
      /// <summary>
      /// Returns the currently selected <see cref="VSNetListBarGroup"/>
      /// object in the control.
      /// </summary>
      public new VSNetListBarGroup SelectedGroup
      {
         get
         {
            return (VSNetListBarGroup)base.SelectedGroup;
         }
      }      

      /// <summary>
      /// Called by the control's internal sizing mechanism.
      /// Returns the width of a <see cref="ListBarGroup" /> button
      /// rectangle.
      /// </summary>
      /// <param name="group">The <see cref="ListBarGroup"/> to get the size of
       the 
      /// button for.</param>
      /// <returns>The width of the button.</returns>
      protected override int GetGroupButtonWidth(ListBarGroup group)
      {

         int ret = base.GetGroupButtonWidth(group);
         ListBarGroup selectedGroup = base.SelectedGroup;
         if (group.Equals(selectedGroup))
         {
            ret -= (group.ButtonHeight + 2);
         }
         else
         {
            ListBarGroup nextGroup = GetNextGroup(selectedGroup);
            if (nextGroup != null)
            {
               if (group.Equals(nextGroup))
               {
                  ret -= (group.ButtonHeight + 2);
               }
            }
         }
         return ret;
      }

      /// <summary>
      /// Called by the control's internal sizing mechanism.
      /// Returns the rectangle for a scroll button.  
      /// </summary>
      /// <param name="buttonType">The scroll button to
      /// get the rectangle for.</param>
      /// <param name="selectedGroup">The Selected Group in the control.</param>
      /// <param name="internalGroupHeight">The internal height of the
      /// selected group</param>
      /// <returns>The Rectangle for the scroll button.</returns>
      protected override Rectangle GetScrollButtonRectangle(
         ListBarScrollButton.ListBarScrollButtonType buttonType,
         ListBarGroup selectedGroup,
         int internalGroupHeight
         )      
      {
         Point location;
         Size size = new Size(
            selectedGroup.ButtonHeight, 
            selectedGroup.ButtonHeight);
         bool rightToLeft = (base.RightToLeft == RightToLeft.Yes);         
         int left = (rightToLeft ? 
            selectedGroup.ButtonLocation.X - size.Width - 2 :
            selectedGroup.ButtonLocation.X + selectedGroup.ButtonWidth + 2
            );
         
         if (buttonType == ListBarScrollButton.ListBarScrollButtonType.Up)
         {   
            location = new Point(
               left,
               selectedGroup.ButtonLocation.Y);
         }
         else
         {
            // is selectedGroup the last visible item in the control?          
              
            ListBarGroup nextGroup = GetNextGroup(selectedGroup);

            location = new Point(
               left,
               (nextGroup == null ? 
                  selectedGroup.ButtonLocation.Y + selectedGroup.ButtonHeight +
                   internalGroupHeight - size.Height :
                  nextGroup.ButtonLocation.Y));               
         }

         return new Rectangle(location, size);
      }

      private ListBarGroup GetNextGroup(ListBarGroup group)
      {
         ListBarGroup nextGroup = null;
         for (int i = base.Groups.IndexOf(group) + 1; i < base.Groups.Count;
          i++)
         {
            if (base.Groups[i].Visible)
            {
               nextGroup = base.Groups[i];
               break;
            }
         }
         return nextGroup;
      }

      /// <summary>
      /// Called by the control's internal sizing mechanism.
      /// Returns the client size excluding the border of the
      /// control.
      /// </summary>
      /// <returns>A <c>Rectangle</c> providing the area to 
      /// draw the control into.</returns>
      protected override Rectangle GetClientRectangleExcludingBorder()
      {
         return base.ClientRectangle;
      }

      /// <summary>
      /// Draw a border around the control.  The default
      /// implementation draws a 1 pixel inset border.
      /// </summary>
      /// <param name="gfx">The graphics object to drawn onto.</param>
      protected override void RenderControlBorder(
         Graphics gfx)
      {
         // intentionally left blank.  This control has no border
      }

      /// <summary>
      /// Creates a new instance of the VSNetListBar control.
      /// </summary>
      public VSNetListBar() : base()
      {
         this.SelectOnMouseDown = true;
      }
   }

   /// <summary>
   /// Maintains the collection of groups within the VS.NET ListBar
   /// control.
   /// </summary>
   public class VSNetListBarGroupCollection : ListBarGroupCollection
   {
      /// <summary>
      /// Adds a new group to the control.
      /// </summary>
      /// <param name="group">The <see cref="VSNetListBarGroup"/> to
       add.</param>
      /// <returns>The 0-based index of the added group.</returns>
      public virtual int Add(VSNetListBarGroup group)
      {
         return base.Add((ListBarGroup)group);
      }

      /// <summary>
      /// Adds a new group with the specified caption and returns a reference
      /// to it.
      /// </summary>
      /// <param name="caption">The caption for the group to create.</param>
      /// <returns>The newly created <see cref="VSNetListBarGroup"/></returns>
      public virtual new VSNetListBarGroup Add(string caption)
      {
         VSNetListBarGroup newGroup = new VSNetListBarGroup(caption);
         Add(newGroup);
         return newGroup;
      }
      
      /// <summary>
      /// Adds a range of groups to the collection.
      /// </summary>
      /// <param name="values">An array of <see cref="VSNetListBarGroup"/>
       objects.</param>
      public virtual void AddRange(VSNetListBarGroup[] values)
      {
         base.AddRange((ListBarGroup[])values);
      }

      /// <summary>
      /// Adds a range of new groups with the specified captions to the
       collection.
      /// </summary>
      /// <param name="captions">Array of captions for the new groups to
       create.</param>
      public override void AddRange(string[] captions)
      {
         foreach (string caption in captions)
         {
            VSNetListBarGroup newGroup = new VSNetListBarGroup(caption);
            Add(newGroup);
         }
      }

      /// <summary>
      /// Gets the <see cref="VSNetListBarGroup" /> at the specified 0-based
       index.
      /// </summary>
      public new VSNetListBarGroup this[int index]
      {
         get
         {
            return (VSNetListBarGroup)base[index];
         }
      }

      /// <summary>
      /// Gets the <see cref="VSNetListBarGroup" /> with the specified key.
      /// </summary>
      public new VSNetListBarGroup this[string key]
      {
         get
         {
            return (VSNetListBarGroup)base[key];
         }
      }

      /// <summary>
      /// Determines whether a <see cref="ListBarGroup"/> element is contained
       within 
      /// the control's collection of groups.
      /// </summary>
      /// <param name="group">The ListBarGroup to check if present.</param>
      /// <returns>True if the ListBarGroup is contained within the control's
      /// collection, False otherwise.</returns>
      public virtual bool Contains ( VSNetListBarGroup group )
      {
         return base.Contains(group);
      }


      /// <summary>
      /// Gets the 0-based index of the specified <see cref="ListBarGroup"/>
       within this
      /// collection.
      /// </summary>
      /// <param name="group">The group to find the index for.</param>
      /// <returns>The 0-based index of the group, if found, otherwise -
       1.</returns>
      [Description("Gets the 0-based index of the specified ListBarGroup within
       this collection.")]
      public virtual int IndexOf ( VSNetListBarGroup group )
      {
         return base.IndexOf(group);
      }

      /// <summary>
      /// Inserts a group at the specified 0-based index in the collection
      /// of groups.
      /// </summary>
      /// <param name="index">The 0-based index to insert the group at.</param>
      /// <param name="group">The ListBarGroup to add.</param>
      [Description("(Inserts a group at the specified 0-based index in the
       collection of groups.")]
      public virtual void Insert ( int index , VSNetListBarGroup group )
      {
         base.Insert(index, group);
      }
      /// <summary>
      /// Inserts a group immediately before the specified <see
       cref="VSNetListBarGroup"/>.
      /// </summary>
      /// <param name="groupBefore"><see cref="VSNetListBarGroup"/> to insert
       before.</param>
      /// <param name="group">Group to insert.</param>
      [Description("(Inserts a group immediately before the specified
       VSNetListBarGroup object.")]
      public virtual void InsertBefore( VSNetListBarGroup groupBefore,
       VSNetListBarGroup group)
      {
         base.InsertBefore(groupBefore, group);
      }

      /// <summary>
      /// Inserts a <see cref="VSNetListBarGroup"/> immediately after the
       specified VSNetListBarGroup.
      /// </summary>
      /// <param name="groupAfter">VSNetListBarGroup to insert after.</param>
      /// <param name="group">Group to insert.</param>
      [Description("(Inserts a group immediately after the specified
       VSNetListBarGroup object.")]
      public virtual void InsertAfter( VSNetListBarGroup groupAfter,
       VSNetListBarGroup group)
      {
         base.InsertAfter(groupAfter, group);
      }


      /// <summary>
      /// Removes the specified <see cref="ListBarGroup"/>.
      /// </summary>
      /// <param name="group">The group to remove.</param>
      [Description("(Removes the specified VSNetListBarGroup object.")]
      public virtual void Remove ( VSNetListBarGroup group )
      {
         this.InnerList.Remove(group);
         NotifyOwner(group, true);
      }



      /// <summary>
      /// Constructs a new instance of this collection.
      /// </summary>
      /// <param name="listBar">The owning <see cref="VSNetListBar"/>
       control.</param>
      public VSNetListBarGroupCollection(VSNetListBar listBar) : base(listBar)
      {
         // intentionally blank
      }
   }

   /// <summary>
   /// An extended ListBarGroup which renders with the VS.NET
   /// ListBar appearance
   /// </summary>
   public class VSNetListBarGroup : ListBarGroup
   {

      /// <summary>
      /// Draws the items within this group bar onto the control.
      /// </summary>
      /// <param name="gfx">The graphics object to draw onto.</param>
      /// <param name="bounds">The bounding rectangle within which
      /// to draw the items.</param>
      /// <param name="ils">The ImageList object to use to draw
      /// the bar.</param>
      /// <param name="defaultFont">The default font to draw the item
       with.</param>
      /// <param name="style">The style to draw the ListBar in.</param>
      /// <param name="controlEnabled">Whether the control is enabled or
       not.</param>
      public override void DrawBar(
         Graphics gfx,
         Rectangle bounds,
         ImageList ils,
         Font defaultFont,
         ListBarDrawStyle style,
         bool controlEnabled
         )
      {   
         if (this.ChildControl != null)
         {
            base.DrawBar(
               gfx, bounds, ils, defaultFont,
               style, controlEnabled);
         }
         else
         {
            this.Items.Draw(
               gfx, bounds, ils, defaultFont,
               style, this.View, controlEnabled,
               this.ScrollOffset + 2 + this.rectangle.Bottom);
         }
      }

      /// <summary>
      /// Draws the button for this group bar onto the control.
      /// </summary>
      /// <param name="gfx">The graphics object to draw onto.</param>
      /// <param name="defaultFont">The default font to use to draw the
       control.</param>
      /// <param name="controlEnabled">Whether the control is enabled or
       not.</param>
      public override void DrawButton(
         Graphics gfx,
         Font defaultFont,
         bool controlEnabled
         )
      {   
         bool rightToLeft = false;
         Font drawFont = this.Font;
         if (this.Owner != null)
         {
            rightToLeft = (this.Owner.RightToLeft == RightToLeft.Yes);
         }
         if (drawFont == null)
         {
            drawFont = defaultFont;
         }

         // Fill background:
         Brush br = new SolidBrush(this.BackColor);
         gfx.FillRectangle(br, this.rectangle);
         br.Dispose();
         
         // Draw the border:
         CustomBorderColor.DrawBorder(
            gfx, this.rectangle, this.BackColor, false, (this.MouseDown &&
             this.MouseOver));

         // Draw the text:
         RectangleF textRect = new RectangleF(
            this.rectangle.Left + 2F, this.rectangle.Top + 1F,
            this.rectangle.Width - 4F, this.rectangle.Height - 2F);
         StringFormat fmt = new StringFormat(StringFormatFlags.LineLimit |
            (rightToLeft ? StringFormatFlags.DirectionRightToLeft : 0));
         fmt.Alignment = StringAlignment.Near;
         fmt.LineAlignment = StringAlignment.Center;
         fmt.Trimming = StringTrimming.EllipsisWord;
         if (controlEnabled)
         {
            br = new SolidBrush(this.ForeColor);
            gfx.DrawString(this.Caption, drawFont, br, textRect, fmt);
            br.Dispose();
         }
         else
         {
            textRect.Offset(1F, 1F);
            gfx.DrawString(this.Caption, drawFont,
             SystemBrushes.ControlLightLight, textRect, fmt);
            textRect.Offset(-1F, -1F);
            gfx.DrawString(this.Caption, drawFont, SystemBrushes.ControlDark,
             textRect, fmt);
         }
         fmt.Dispose();

      }

      /// <summary>
      /// Sets the height of the button for this group.
      /// </summary>
      /// <param name="defaultFont">The default <see
       cref="System.Drawing.Font"/>
      /// to use if this item doesn't already have a font set.</param>
      public override void SetButtonHeight(
         Font defaultFont
         )
      {
         base.SetButtonHeight(defaultFont);
         this.rectangle.Height -= 3;
      }

      private void newDefaults()
      {
         this.rectangle.Height = 17;
         this.View = ListBarGroupView.SmallIcons;
      }

      /// <summary>
      /// Constructs a new, blank instance of a ListBarGroup.
      /// </summary>
      public VSNetListBarGroup() : base()
      {         
         newDefaults();
      }

      /// <summary>
      /// Constructs a new instance of a ListBarGroup with the specified
      /// caption.
      /// </summary>
      /// <param name="caption">Caption for the group's control button.</param>
      public VSNetListBarGroup(
         string caption
         ) : base(caption)
      {
         newDefaults();
      }
      /// <summary>
      /// Constructs a new instance of a ListBarGroup with the specified
      /// caption and items.
      /// </summary>
      /// <param name="caption">Caption for the group's control button.</param>
      /// <param name="subItems">The array of items to add to the group's
      /// collection of items.</param>
      public VSNetListBarGroup(
         string caption,
         ListBarItem[] subItems
         ) : base(caption, subItems)
      {
         newDefaults();
      }

      /// <summary>
      /// Constructs a new instance of a ListBarGroup with the specified
      /// caption and tooltip text.
      /// </summary>
      /// <param name="caption">Caption for the group's control button.</param>
      /// <param name="toolTipText">ToolTip text to show when hovering over
      /// the group.</param>
      public VSNetListBarGroup(
         string caption, 
         string toolTipText
         ) : base(caption, toolTipText)
      {
         newDefaults();
      }

      /// <summary>
      /// Constructs a new instance of a ListBarGroup with the specified
      /// caption, tooltip text and user-defined data.
      /// </summary>
      /// <param name="caption">Caption for the group's control button.</param>
      /// <param name="toolTipText">ToolTip text to show when hovering over
      /// the group.</param>
      /// <param name="tag">User-defined object data which is associated with
      /// the group.</param>
      public VSNetListBarGroup(
         string caption, 
         string toolTipText,
         object tag
         ) : base(caption, toolTipText, tag)
      {
         newDefaults();
      }


   }

   /// <summary>
   /// An extended ListBarItem which renders with an alpha-blended shadow
   /// appearance.
   /// </summary>
   public class VSNetListBarItem : ListBarItem
   {

      /// <summary>
      /// Sets the location and width of this item.  This method
      /// is called by internally by the <see cref="ListBar"/> or
      /// the <see cref="ListBarGroup"/> which owns this item.
      /// </summary>
      /// <remarks>
      /// This member is not intended to be called from client code.
      /// If you do use it, it is likely that a subsequent operation
      /// on the control or group will replace the values.  If you
      /// need more control over placement, override this class
      /// and build the logic into the override for this method
      /// instead.
      /// </remarks>
      /// <param name="location">The new location for the item.</param>
      /// <param name="width">The new width of the item.</param>
      public override void SetLocationAndWidth(Point location, int width)
      {
         if (this.Owner != null)
         {
            //width = this.Owner.Width;
         }
         base.SetLocationAndWidth(location, width);
      }

      /// <summary>
      /// Draws this item into the specified graphics object.
      /// </summary>
      /// <param name="gfx">The graphics object to draw onto.</param>
      /// <param name="ils">The ImageList to source icons from.</param>
      /// <param name="defaultFont">The default font to use to draw the
       button.</param>
      /// <param name="style">The style (Outlook version) to draw using.</param>
      /// <param name="view">The view (large or small icons) to draw
       using.</param>
      /// <param name="scrollOffset">The offset of the first item from the 
      /// (0,0) point in the graphics object.</param>
      /// <param name="skipDrawText">Whether to skip drawing text or not
      /// (the item is being edited)</param>
      /// <param name="controlEnabled">Whether the control is enabled or
       not.</param>
      public override void DrawButton(
         Graphics gfx, 
         ImageList ils,
         Font defaultFont,
         ListBarDrawStyle style, 
         ListBarGroupView view,
         int scrollOffset,
         bool controlEnabled,
         bool skipDrawText
         )
      {
         bool rightToLeft = false;
         Font drawFont = this.Font;
         if (drawFont == null)
         {
            drawFont = defaultFont;
         }
         Color backColor = Color.FromKnownColor(KnownColor.Control);
         if (this.Owner != null)
         {
            if (this.Owner.RightToLeft == RightToLeft.Yes)
            {
               rightToLeft = true;
            }
            backColor = this.Owner.BackColor;
         }

         Rectangle drawRect = new Rectangle(this.Location,
            new Size(this.Width, this.Height));
         drawRect.Offset(0, scrollOffset);
         if (((this.Selected) && (!this.MouseOver)) || (this.MouseOver &&
          this.MouseDown))
         {
            // Draw the background:
            VSNetListBarUtility.DrawSelectedItemBackground(gfx, drawRect);
         }

         Rectangle itemRect = drawRect;
         itemRect.Inflate(-1, -1);

         if ((this.Selected) || (this.MouseDown && this.MouseOver))
         {
            itemRect.Offset(1,1);
         }

         RectangleF textRect;
         // Draw the icon:
         if (rightToLeft)
         {
            this.iconRectangle = new Rectangle(
               itemRect.Right - ils.ImageSize.Width - 4, 
               itemRect.Top + (itemRect.Height - ils.ImageSize.Height) / 2,
               ils.ImageSize.Width, ils.ImageSize.Height);               
            textRect = new RectangleF(
               itemRect.Left, itemRect.Top, 
               itemRect.Width - (ils.ImageSize.Width - 6),
               itemRect.Height);
         }
         else
         {
            this.iconRectangle = new Rectangle(
               itemRect.Left + 4, 
               itemRect.Top + (itemRect.Height - ils.ImageSize.Height) / 2,
               ils.ImageSize.Width, ils.ImageSize.Height);
            textRect = new RectangleF(
               itemRect.Left + ils.ImageSize.Height + 6, itemRect.Top, 
               itemRect.Width - (ils.ImageSize.Width - 6),
               itemRect.Height);
         }
         if (this.IconIndex <= ils.Images.Count)
         {
            if (this.Enabled && controlEnabled)
            {
               ils.Draw(gfx, this.iconRectangle.Left, this.iconRectangle.Top, 
                  this.IconIndex);
            }
            else
            {
               ControlPaint.DrawImageDisabled(gfx, ils.Images[this.IconIndex], 
                  this.iconRectangle.Left, this.iconRectangle.Top, backColor);
            }
         }

         if ((view == ListBarGroupView.SmallIconsOnly) || (view ==
          ListBarGroupView.LargeIconsOnly))
         {
            textRectangle = new Rectangle(0,0,0,0);
            skipDrawText = true;
         }

         if (!skipDrawText)
         {

            // Draw the text:
            StringFormat format = new StringFormat(StringFormatFlags.LineLimit |
               (rightToLeft ? StringFormatFlags.DirectionRightToLeft : 0));
            format.Alignment = StringAlignment.Near;
            format.LineAlignment = StringAlignment.Center;
            format.Trimming = StringTrimming.EllipsisWord;
            if (this.Enabled && controlEnabled)
            {
               Brush br = new SolidBrush(this.ForeColor);
               gfx.DrawString(this.Caption, drawFont, br, textRect, format);
               br.Dispose();
            }
            else
            {
               Brush br = new
                SolidBrush(CustomBorderColor.ColorLightLight(backColor));
               textRect.Offset(1F, 1F);
               gfx.DrawString(this.Caption, drawFont, br, textRect, format);
               br.Dispose();
               textRect.Offset(-1F, -1F);
               br = new SolidBrush(CustomBorderColor.ColorDark(backColor));
               gfx.DrawString(this.Caption, drawFont, br, textRect, format);
               br.Dispose();
            }
            format.Dispose();

            this.textRectangle = new Rectangle((int)textRect.Left,
             (int)textRect.Top,
               (int)textRect.Width, (int)textRect.Height);
            
            // TODO: If mouse over and text too wide show the popup.
            // Currently a problem with my popup code in that it 
            // foils the Framework's WM_NCACTIVATE processing which 
            // results in spurious focus/mouse over events...

         }


         // Draw the border:
         if (((this.MouseOver) || (this.Selected)) && (this.Enabled))
         {
            CustomBorderColor.DrawBorder(gfx, drawRect,
               backColor, true,
               ((this.MouseDown && this.MouseOver) || (this.Selected)));
         }
      }

      /// <summary>
      /// Called to set the height of the item by the owning control.
      /// </summary>
      /// <param name="view">The <see cref="ListBarGroupView"/> in which this
      /// item is being shown.</param>
      /// <param name="defaultFont">The default <see
       cref="System.Drawing.Font"/>
      /// to use when this item does not have a specific font set.</param>
      /// <param name="imageSize">The size of the images in the ImageList
      /// used to render this view.</param>      
      [Description("Called to set the height of an item by the owning
       control.")]
      public override void SetSize ( 
         ListBarGroupView view , 
         Font defaultFont , 
         Size imageSize )
      {
         base.SetSize(view, defaultFont, imageSize);
         this.rectangle.Height -= 3;
      }

      /// <summary>
      /// Incomplete method.  To be used to render an item in the 
      /// popup view.
      /// </summary>
      /// <param name="gfx">Graphics object to draw onto</param>
      public virtual void PopupDraw(Graphics gfx)
      {
         if (this.Owner != null)
         {
            //this.Owner.PopupDraw(gfx, this);
         }

      }

      private void newDefaults()
      {
      }

      /// <summary>
      /// Constructs a new, empty instance of a ListBarItem.
      /// </summary>
      public VSNetListBarItem() : base()
      {         
         newDefaults();
      }
      /// <summary>
      ///  Constructs a new instance of a ListBarItem, specifying
      ///  the caption to display.
      /// </summary>
      /// <param name="caption">The caption for this item.</param>
      public VSNetListBarItem(string caption) : base(caption)
      {
         newDefaults();
      }
      /// <summary>
      ///  Constructs a new instance of a ListBarItem, specifying
      ///  the caption and the index of the icon to display.
      /// </summary>
      /// <param name="caption">The caption for this item.</param>
      /// <param name="iconIndex">The 0-based index of the icon
      /// to display</param>
      public VSNetListBarItem(
         string caption, 
         int iconIndex
         ) : base(caption, iconIndex)
      {
         newDefaults();
      }

      /// <summary>
      ///  Constructs a new instance of a ListBarItem, specifying
      ///  the caption, the index of the icon and the 
      ///  tooltip text.
      /// </summary>
      /// <param name="caption">The caption for this item.</param>
      /// <param name="iconIndex">The 0-based index of the icon
      /// to display</param>
      /// <param name="toolTipText">The tooltip text to show
      /// when the mouse hovers over this item.</param>
      public VSNetListBarItem(
         string caption,
         int iconIndex,
         string toolTipText
         ) : base(caption, iconIndex, toolTipText)
      {
         newDefaults();
      }
      /// <summary>
      ///  Constructs a new instance of a ListBarItem, specifying
      ///  the caption, the index of the icon, the 
      ///  tooltip text and the tag.
      /// </summary>
      /// <param name="caption">The caption for this item.</param>
      /// <param name="iconIndex">The 0-based index of the icon
      /// to display</param>
      /// <param name="toolTipText">The tooltip text to show
      /// when the mouse hovers over this item.</param>
      /// <param name="tag">An object which can be used to 
      /// associate user-defined data with the item.</param>
      public VSNetListBarItem(
         string caption,
         int iconIndex,
         string toolTipText,
         object tag
         ) : base (caption, iconIndex, toolTipText, tag)
      {
         newDefaults();
      }
      /// <summary>
      ///  Constructs a new instance of a ListBarItem, specifying
      ///  the caption, the index of the icon, the 
      ///  tooltip text, the tag and the key.
      /// </summary>
      /// <param name="caption">The caption for this item.</param>
      /// <param name="iconIndex">The 0-based index of the icon
      /// to display</param>
      /// <param name="toolTipText">The tooltip text to show
      /// when the mouse hovers over this item.</param>
      /// <param name="tag">An object which can be used to 
      /// associate user-defined data with the item.</param>
      /// <param name="key">A user-defined string which is 
      /// associated with the item.</param>
      public VSNetListBarItem(
         string caption,
         int iconIndex,
         string toolTipText,
         object tag,
         string key
         ) : base(caption, iconIndex, toolTipText, tag, key)
      {
         newDefaults();
      }


   }

   /// <summary>
   /// A subclassed ListBarScrollButton which renders in the style
   /// of the VS.NET scroll buttons in the ListBar.
   /// </summary>
   public class VSNetListBarScrollButton : ListBarScrollButton
   {
      /// <summary>
      /// The owner control for this scroll button
      /// </summary>
      protected VSNetListBar owner = null;

      /// <summary>
      /// Draws the button onto the specified Graphics object.
      /// </summary>
      /// <param name="gfx">The graphics object to draw on.</param>
      /// <param name="defaultBackColor">The default background
      /// colour to use when drawing the button.</param>
      /// <param name="controlEnabled">Whether the control is enabled or
       not.</param>
      public override void DrawItem(
         Graphics gfx,
         Color defaultBackColor,
         bool controlEnabled
         )
      {
         Color backColor = defaultBackColor;
         if (owner != null)
         {
            VSNetListBarGroup selGroup = this.owner.SelectedGroup;
            if (selGroup != null)
            {
               if (this.ButtonType ==
                ListBarScrollButton.ListBarScrollButtonType.Up)
               {
                  backColor = selGroup.BackColor;
               }
               else
               {
                  VSNetListBarGroup nextGroup = null;
                  for (int i = this.owner.Groups.IndexOf(selGroup) + 1; i <
                   this.owner.Groups.Count; i++)
                  {   
                     if (this.owner.Groups[i].Visible)
                     {
                        nextGroup = this.owner.Groups[i];
                        break;
                     }
                  }
                  if (nextGroup != null)
                  {
                     backColor = nextGroup.BackColor;
                  }
               }
            }
         }
         Brush br = new SolidBrush(backColor);
         gfx.FillRectangle(br, this.Rectangle);
         br.Dispose();
         // in VS.NET bar the item is always drawn.  When
         // it's not "Visible" then it is drawn disabled:
         VSNetListBarUtility.DrawScrollButton(
            gfx, this.Rectangle, backColor,
            (this.MouseDown && this.MouseOver),
            (base.Visible && controlEnabled), 
            (this.ButtonType == ListBarScrollButtonType.Up ? false : true));

      }
      
      /// <summary>
      /// Creates a new instance of this class with the specified
      /// button type (Up or Down)
      /// </summary>
      /// <param name="owner">The owning control.</param>
      /// <param name="buttonType">The scroll button type to create.</param>
      public VSNetListBarScrollButton(
         VSNetListBar owner,
         ListBarScrollButtonType buttonType
         ) : base(buttonType)
      {
         this.owner = owner;
      }

   }

   /// <summary>
   /// Utility methods for drawing the <see cref="VSNetListBar"/>
   /// control.
   /// </summary>
   internal class VSNetListBarUtility
   {

      public static void DrawSelectedItemBackground(
         Graphics gfx,
         Rectangle rect
         )
      {
         Brush br = new SolidBrush(Color.FromArgb(128, Color.White));
         gfx.FillRectangle(br, rect);
         br.Dispose();
      }


      /// <summary>
      /// Draws a scroll button for the <see cref="VSNetListBar"/> control.
      /// </summary>
      /// <param name="gfx"><see cref="Graphics"/> object to draw onto.</param>
      /// <param name="rect"><see cref="System.Drawing.Rectangle"/> to draw
       button in.</param>
      /// <param name="color">The <see cref="System.Drawing.Color"/> of the
       background for the button.</param>
      /// <param name="pressed">Whether the button is pressed or not.</param>
      /// <param name="enabled">Whether the button is enabled or not.</param>
      /// <param name="down">If <c>True</c>, the button is a scroll down button,
      /// otherwise it is a scroll up button.</param>
      public static void DrawScrollButton(
         Graphics gfx,
         Rectangle rect,
         Color color,
         bool pressed,
         bool enabled,
         bool down
         )
      {
         if (!enabled)
         {
            Brush br = new SolidBrush(CustomBorderColor.ColorLightLight(color));
            DrawScrollButtonTriangle(gfx, rect, 
               br, 1, down);
            br.Dispose();
            br = new SolidBrush(CustomBorderColor.ColorDark(color));
            DrawScrollButtonTriangle(gfx, rect, 
               br, 0, down);
            br.Dispose();
         }
         else
         {
            DrawScrollButtonTriangle(gfx, rect, 
               SystemBrushes.WindowText, (pressed ? 1 : 0), down);
         }
         CustomBorderColor.DrawBorder(
            gfx, rect, color, true, pressed);
         
      }

      private static void DrawScrollButtonTriangle(
         Graphics gfx,
         Rectangle rect,
         Brush brush,
         int offset,
         bool down
         )
      {
         // where to put a 9 x 5 triangle in rect:
         PointF location = new PointF(
            (rect.Width - 2F - 9F) / 2F + rect.Left + 1F,
            (rect.Height - 2F - 5F) / 2F + rect.Top + 1F);
         location.X += offset;
         location.Y += offset;
         RectangleF triangleRect = new RectangleF(location, 
            new SizeF(10F, 6F));

         // draw the triangle:
         GraphicsPath glyph = new GraphicsPath();
         if (down)
         {
            glyph.AddLine(triangleRect.Left, triangleRect.Top, 
               triangleRect.Right - 1 , triangleRect.Top);
            glyph.AddLine(triangleRect.Right - 1, triangleRect.Top, 
               triangleRect.Left + 4, triangleRect.Bottom -1 );
            glyph.CloseFigure();
         }
         else
         {
            glyph.AddLine(triangleRect.Left, triangleRect.Bottom - 1, 
               triangleRect.Right - 1 , triangleRect.Bottom - 1);
            glyph.AddLine(triangleRect.Right - 1, triangleRect.Bottom - 1, 
               triangleRect.Left + 4, triangleRect.Top);
            glyph.CloseFigure();
         }
         
         gfx.FillPath(brush, glyph);
         glyph.Dispose();

      }

      /// <summary>
      /// Private constructor, all methods static
      /// </summary>
      private VSNetListBarUtility()
      {
         // intentionally blank
      }

   }

   internal class VSNetListItemPopup : Control
   {
      #region Unmanaged Code
      [DllImport("user32")]
      private static extern int SetParent(
         IntPtr hWndChild,
         IntPtr hWndNewParent);
      [DllImport("user32")]
      private static extern int ShowWindow(
         IntPtr hWnd,
         int nCmdShow);
      
      private const int WS_EX_TOOLWINDOW        = 0x00000080;
      private const int WS_EX_NOACTIVATE        = 0x08000000;
      private const int WS_EX_TOPMOST           = 0x00000008;      
      #endregion

      /// <summary>
      /// The ListBar which owns this popup
      /// </summary>
      private VSNetListBarItem owner = null;

      /// <summary>
      /// Shows the control as a floating Window child 
      /// of the desktop.  To hide the control again,
      /// use the <see cref="System.Windows.Forms.Control.Visible"/> property.
      /// </summary>
      public void ShowFloating(VSNetListBarItem owner)
      {
         this.owner = owner;
         if (this.Handle == IntPtr.Zero)
         {
            base.CreateControl();
         }
         SetParent(base.Handle, IntPtr.Zero);
         ShowWindow(base.Handle, 1);
      }

      /// <summary>
      /// Get the <see cref="System.Windows.Forms.CreateParams"/>
      /// used to create the control.  This override adds the
      /// <code>WS_EX_NOACTIVATE</code>, <code>WS_EX_TOOLWINDOW</code>
      /// and <code>WS_EX_TOPMOST</code> extended styles to make
      /// the Window float on top.
      /// </summary>
      protected override CreateParams CreateParams
      {
         get
         {
            CreateParams p = base.CreateParams;
            p.ExStyle |= (WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST);
            return p;
         }
      }
      
      protected override void OnPaint(PaintEventArgs e)
      {
         this.owner.PopupDraw(e.Graphics);
      }

      /// <summary>
      /// Constructs a new instance of this control.
      /// </summary>
      public VSNetListItemPopup()
      {
      }
   }

}