context: using System;
  using System.ComponentModel;
  using System.Drawing;
  using System.Windows.Forms;
  using System.Windows.Forms.VisualStyles;
  
  
  /* Get the latest version of SplitButton at: http://wyday.com/splitbutton/
   * 
   * Copyright (c) 2010, wyDay
   * All rights reserved.
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are met:
   * 
   *  * Redistributions of source code must retain the above copyright notice,
   *    this list of conditions and the following disclaimer.
   *  * Redistributions in binary form must reproduce the above copyright notice,
   *    this list of conditions and the following disclaimer in the documentation
   *    and/or other materials provided with the distribution.
   * 
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  
  namespace GitUI.Script
  {
      public class SplitButton : Button
      {
          PushButtonState _state;
  
  
          const int SplitSectionWidth = 18;
  
          static int BorderSize = SystemInformation.Border3DSize.Width * 2;
          bool skipNextOpen;
          Rectangle dropDownRectangle;
          bool showSplit;
  
          bool isSplitMenuVisible;
  
  
          ContextMenuStrip m_SplitMenuStrip;
          ContextMenu m_SplitMenu;
  
          TextFormatFlags textFormatFlags = TextFormatFlags.Default;
  
          public SplitButton()
          {
              AutoSize = true;
          }
  
          #region Properties
  
          [Browsable(false)]
          public override ContextMenuStrip ContextMenuStrip
          {
              get
              {
                  return SplitMenuStrip;
              }
              set
              {
                  SplitMenuStrip = value;
              }
          }
  
          [DefaultValue(null)]
          public ContextMenu SplitMenu
          {
              get { return m_SplitMenu; }
              set
              {
                  //remove the event handlers for the old SplitMenu
                  if (m_SplitMenu != null)
                  {
                      m_SplitMenu.Popup -= SplitMenu_Popup;
                  }
  
                  //add the event handlers for the new SplitMenu
                  if (value != null)
                  {
                      ShowSplit = true;
                      value.Popup += SplitMenu_Popup;
                  }
                  else
                      ShowSplit = false;
  
                  m_SplitMenu = value;
              }
          }
  
          [DefaultValue(null)]
          public ContextMenuStrip SplitMenuStrip
          {
              get
              {
                  return m_SplitMenuStrip;
              }
              set
              {
                  //remove the event handlers for the old SplitMenuStrip
                  if (m_SplitMenuStrip != null)
                  {
                      m_SplitMenuStrip.Closing -= SplitMenuStrip_Closing;
                      m_SplitMenuStrip.Opening -= SplitMenuStrip_Opening;
                  }
  
                  //add the event handlers for the new SplitMenuStrip
                  if (value != null)
                  {
                      ShowSplit = true;
                      value.Closing += SplitMenuStrip_Closing;
                      value.Opening += SplitMenuStrip_Opening;
                  }
                  else
                      ShowSplit = false;
  
  
                  m_SplitMenuStrip = value;
              }
          }
  
          [DefaultValue(false)]
          public bool ShowSplit
          {
              set
              {
                  if (value != showSplit)
                  {
                      showSplit = value;
                      Invalidate();
  
                      if (Parent != null)
                          Parent.PerformLayout();
                  }
              }
          }
  
          [DefaultValue(false)]
          public bool WholeButtonDropdown { get; set; }
  
          private PushButtonState State
          {
              get
              {
                  return _state;
              }
              set
              {
                  if (!_state.Equals(value))
                  {
                      _state = value;
                      Invalidate();
                  }
              }
          }
  
          #endregion Properties
  
          protected override bool IsInputKey(Keys keyData)
          {
              if (keyData.Equals(Keys.Down) && showSplit)
                  return true;
  
              return base.IsInputKey(keyData);
          }
  
          protected override void OnGotFocus(EventArgs e)
          {
              if (!showSplit)
              {
                  base.OnGotFocus(e);
                  return;
              }
  
              if (!State.Equals(PushButtonState.Pressed) && !State.Equals(PushButtonState.Disabled))
              {
                  State = PushButtonState.Default;
              }
          }
  
          protected override void OnKeyDown(KeyEventArgs kevent)
          {
              if (showSplit)
              {
                  if (kevent.KeyCode.Equals(Keys.Down) && !isSplitMenuVisible)
                  {
                      ShowContextMenuStrip();
                  }
  
                  else if (kevent.KeyCode.Equals(Keys.Space) && kevent.Modifiers == Keys.None)
                  {
                      State = PushButtonState.Pressed;
                  }
              }
  
              base.OnKeyDown(kevent);
          }
  
          protected override void OnKeyUp(KeyEventArgs kevent)
          {
              if (kevent.KeyCode.Equals(Keys.Space))
              {
                  if (MouseButtons == MouseButtons.None)
                  {
                      State = PushButtonState.Normal;
                  }
              }
              else if (kevent.KeyCode.Equals(Keys.Apps))
              {
                  if (MouseButtons == MouseButtons.None && !isSplitMenuVisible)
                  {
                      ShowContextMenuStrip();
                  }
              }
  
              base.OnKeyUp(kevent);
          }
  
          protected override void OnEnabledChanged(EventArgs e)
          {
              State = Enabled ? PushButtonState.Normal : PushButtonState.Disabled;
  
              base.OnEnabledChanged(e);
          }
  
          protected override void OnLostFocus(EventArgs e)
          {
              if (!showSplit)
              {
                  base.OnLostFocus(e);
                  return;
              }
  
              if (!State.Equals(PushButtonState.Pressed) && !State.Equals(PushButtonState.Disabled))
              {
                  State = PushButtonState.Normal;
              }
          }
  
          bool isMouseEntered;
  
          protected override void OnMouseEnter(EventArgs e)
          {
              if (!showSplit)
              {
                  base.OnMouseEnter(e);
                  return;
              }
  
              isMouseEntered = true;
  
              if (!State.Equals(PushButtonState.Pressed) && !State.Equals(PushButtonState.Disabled))
              {
                  State = PushButtonState.Hot;
              }
                 
          }
  
          protected override void OnMouseLeave(EventArgs e)
          {
              if (!showSplit)
              {
                  base.OnMouseLeave(e);
                  return;
              }
  
              isMouseEntered = false;
  
              if (!State.Equals(PushButtonState.Pressed) && !State.Equals(PushButtonState.Disabled))
              {
                  State = Focused ? PushButtonState.Default : PushButtonState.Normal;
              }
          }
  
          protected override void OnMouseDown(MouseEventArgs e)
          {
              if (!showSplit)
              {
                  base.OnMouseDown(e);
                  return;
              }
  
              //handle ContextMenu re-clicking the drop-down region to close the menu
              if (m_SplitMenu != null && e.Button == MouseButtons.Left && !isMouseEntered)
                  skipNextOpen = true;
  
              if ((dropDownRectangle.Contains(e.Location) || this.WholeButtonDropdown) && 
                  !isSplitMenuVisible && e.Button == MouseButtons.Left)
              {
                  ShowContextMenuStrip();
              }
              else
              {
                  State = PushButtonState.Pressed;
              }
          }
  
          protected override void OnMouseUp(MouseEventArgs mevent)
          {
              if (!showSplit)
              {
                  base.OnMouseUp(mevent);
                  return;
              }
  
              // if the right button was released inside the button
              if (mevent.Button == MouseButtons.Right && ClientRectangle.Contains(mevent.Location) && !isSplitMenuVisible)
              {
                  ShowContextMenuStrip();
              }
              else if (m_SplitMenuStrip == null && m_SplitMenu == null || !isSplitMenuVisible)
              {
                  SetButtonDrawState();
  
                  if (ClientRectangle.Contains(mevent.Location) && !dropDownRectangle.Contains(mevent.Location))
                  {
                      OnClick(new EventArgs());
                  }
              }
          }
  
          protected override void OnPaint(PaintEventArgs pevent)
          {
              base.OnPaint(pevent);
  
              if (!showSplit)
                  return;
  
              Graphics g = pevent.Graphics;
              Rectangle bounds = ClientRectangle;
  
              // draw the button background as according to the current state.
              if (State != PushButtonState.Pressed && IsDefault && !Application.RenderWithVisualStyles)
              {
                  Rectangle backgroundBounds = bounds;
                  backgroundBounds.Inflate(-1, -1);
                  ButtonRenderer.DrawButton(g, backgroundBounds, State);
  
                  // button renderer doesnt draw the black frame when themes are off
                  g.DrawRectangle(SystemPens.WindowFrame, 0, 0, bounds.Width - 1, bounds.Height - 1);
              }
              else
              {
                  ButtonRenderer.DrawButton(g, bounds, State);
              }
  
              // calculate the current dropdown rectangle.
              dropDownRectangle = new Rectangle(bounds.Right - SplitSectionWidth, 0, SplitSectionWidth, bounds.Height);
  
              int internalBorder = BorderSize;
              Rectangle focusRect =
                  new Rectangle(internalBorder - 1,
                                internalBorder - 1,
                                bounds.Width - dropDownRectangle.Width - internalBorder,
                                bounds.Height - (internalBorder * 2) + 2);
  
              bool drawSplitLine = (State == PushButtonState.Hot || State == PushButtonState.Pressed || !Application.RenderWithVisualStyles);
  
  
              if (RightToLeft == RightToLeft.Yes)
              {
                  dropDownRectangle.X = bounds.Left + 1;
                  focusRect.X = dropDownRectangle.Right;
  
                  if (drawSplitLine)
                  {
                      // draw two lines at the edge of the dropdown button
                      g.DrawLine(SystemPens.ButtonShadow, bounds.Left + SplitSectionWidth, BorderSize, bounds.Left + SplitSectionWidth, bounds.Bottom - BorderSize);
                      g.DrawLine(SystemPens.ButtonFace, bounds.Left + SplitSectionWidth + 1, BorderSize, bounds.Left + SplitSectionWidth + 1, bounds.Bottom - BorderSize);
                  }
              }
              else
              {
                  if (drawSplitLine)
                  {
                      // draw two lines at the edge of the dropdown button
                      g.DrawLine(SystemPens.ButtonShadow, bounds.Right - SplitSectionWidth, BorderSize, bounds.Right - SplitSectionWidth, bounds.Bottom - BorderSize);
                      g.DrawLine(SystemPens.ButtonFace, bounds.Right - SplitSectionWidth - 1, BorderSize, bounds.Right - SplitSectionWidth - 1, bounds.Bottom - BorderSize);
                  }
              }
  
              // Draw an arrow in the correct location
              PaintArrow(g, dropDownRectangle);
  
              //paint the image and text in the "button" part of the splitButton
              PaintTextandImage(g, new Rectangle(0, 0, ClientRectangle.Width - SplitSectionWidth, ClientRectangle.Height));
  
              // draw the focus rectangle.
              if (State != PushButtonState.Pressed && Focused && ShowFocusCues)
              {
                  ControlPaint.DrawFocusRectangle(g, focusRect);
              }
          }
  
          private void PaintTextandImage(Graphics g, Rectangle bounds)
          {
              // Figure out where our text and image should go
              Rectangle text_rectangle;
              Rectangle image_rectangle;
  
              CalculateButtonTextAndImageLayout(ref bounds, out text_rectangle, out image_rectangle);
  
              //draw the image
              if (Image != null)
              {
                  if (Enabled)
                      g.DrawImage(Image, image_rectangle.X, image_rectangle.Y, Image.Width, Image.Height);
                  else
                      ControlPaint.DrawImageDisabled(g, Image, image_rectangle.X, image_rectangle.Y, BackColor);
              }
  
              // If we dont' use mnemonic, set formatFlag to NoPrefix as this will show ampersand.
              if (!UseMnemonic)
                  textFormatFlags = textFormatFlags | TextFormatFlags.NoPrefix;
              else if (!ShowKeyboardCues)
                  textFormatFlags = textFormatFlags | TextFormatFlags.HidePrefix;
  
              //draw the text
              if (!string.IsNullOrEmpty(Text))
              {
                  if (Enabled)
                      TextRenderer.DrawText(g, Text, Font, text_rectangle, ForeColor, textFormatFlags);
                  else
                      ControlPaint.DrawStringDisabled(g, Text, Font, BackColor, text_rectangle, textFormatFlags);
              }
          }
  
          private void PaintArrow(Graphics g, Rectangle dropDownRect)
          {
              Point middle = new Point(Convert.ToInt32(dropDownRect.Left + dropDownRect.Width / 2), Convert.ToInt32(dropDownRect.Top + dropDownRect.Height / 2));
  
              //if the width is odd - favor pushing it over one pixel right.
              middle.X += (dropDownRect.Width % 2);
  
              Point[] arrow = new[] { new Point(middle.X - 2, middle.Y - 1), new Point(middle.X + 3, middle.Y - 1), new Point(middle.X, middle.Y + 2) };
  
              if (Enabled)
                  g.FillPolygon(SystemBrushes.ControlText, arrow);
              else
                  g.FillPolygon(SystemBrushes.ButtonShadow, arrow);
          }
  
          public override Size GetPreferredSize(Size proposedSize)
          {
              Size preferredSize = base.GetPreferredSize(proposedSize);
  
              //autosize correctly for splitbuttons
              if (showSplit)
              {
                  if (AutoSize)
                      return CalculateButtonAutoSize();
                  
                  if (!string.IsNullOrEmpty(Text) && TextRenderer.MeasureText(Text, Font).Width + SplitSectionWidth > preferredSize.Width)
                      return preferredSize + new Size(SplitSectionWidth + BorderSize * 2, 0);
              }
  
              return preferredSize;
          }
  
          private Size CalculateButtonAutoSize()
          {
              Size ret_size = Size.Empty;
              Size text_size = TextRenderer.MeasureText(Text, Font);
              Size image_size = Image == null ? Size.Empty : Image.Size;
  
              // Pad the text size
              if (Text.Length != 0)
              {
                  text_size.Height += 4;
                  text_size.Width += 4;
              }
  
              switch (TextImageRelation)
              {
                  case TextImageRelation.Overlay:
                      ret_size.Height = Math.Max(Text.Length == 0 ? 0 : text_size.Height, image_size.Height);
                      ret_size.Width = Math.Max(text_size.Width, image_size.Width);
                      break;
                  case TextImageRelation.ImageAboveText:
                  case TextImageRelation.TextAboveImage:
                      ret_size.Height = text_size.Height + image_size.Height;
                      ret_size.Width = Math.Max(text_size.Width, image_size.Width);
                      break;
                  case TextImageRelation.ImageBeforeText:
                  case TextImageRelation.TextBeforeImage:
                      ret_size.Height = Math.Max(text_size.Height, image_size.Height);
                      ret_size.Width = text_size.Width + image_size.Width;
                      break;
              }
  
              // Pad the result
  
gt:             ret_size.Height += (Padding.Vertical + 6);