Click here to Skip to main content
15,896,154 members
Articles / Containers / Docker

SVG Artiste - An SVG Editor

Rate me:
Please Sign up or sign in to vote.
4.69/5 (23 votes)
3 Aug 2010CPOL14 min read 96.3K   15.4K   93  
A Vector based tool to create and edit SVG images
/***************************************************************************
 *   CopyRight (C) 2009 by Cristinel Mazarine                              *
 *   Author:   Cristinel Mazarine                                          *
 *   Contact:  cristinel@osec.ro                                           *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the Crom Free License as published by           *
 *   the SC Crom-Osec SRL; version 1 of the License                        *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   Crom Free License for more details.                                   *
 *                                                                         *
 *   You should have received a copy of the Crom Free License along with   *
 *   this program; if not, write to the contact@osec.ro                    *
 ***************************************************************************/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace Crom.Controls.TabbedDocument
{
   /// <summary>
   /// Implementation of buttons panel object
   /// </summary>
   public abstract partial class ButtonsPanel : UserControl
   {
      #region Fields

      private const int                ButtonMargin                     = 16;
      private const int                ButtonHeight                     = 16;

      private Timer                    _scrollMoveTimer                 = new Timer();

      private int                      _selectedIndex                   = -1;
      private List<TabButton>          _buttons                         = new List<TabButton>();

      private TabButtonRenderer        _buttonsRenderer                 = null;

      private Color                    _backGradient1                   = SystemColors.Control;
      private Color                    _backGradient2                   = SystemColors.ControlLightLight;
      private LinearGradientMode       _backGradientMode                = LinearGradientMode.Horizontal;

      private bool                     _cutRoundRect                    = true;

      private bool                     _isMouseDownInScrollBackButton   = false;
      private bool                     _isMouseDownInScrollNextButton   = false;
      private bool                     _isMouseOverScrollBackButton     = false;
      private bool                     _isMouseOverScrollNextButton     = false;

      private bool                     _isMouseDownInTabButton          = false;
      private bool                     _isMovingTabButton               = false;
      private bool                     _isDraggingTabButton             = false;
      private TabButton                _buttonDisplaced                 = null;

      private Rectangle                _clientBounds                    = new Rectangle();
      private Rectangle                _buttonsPanelBounds              = new Rectangle();
      private Rectangle                _pagesPanelBounds                = new Rectangle();
      private bool                     _updatePositionsOnDraw           = false;
      private bool                     _hasScrolls                      = false;
      private bool                     _canScrollNext                   = false;
      private int                      _firstShownButtonIndex           = 0;

      private Rectangle                _scrollBackBounds                = new Rectangle();
      private Rectangle                _scrollNextBounds                = new Rectangle();

      private Rectangle                _captionButtonsBounds            = new Rectangle();
      private int                      _captionButtonIndexUnderMouse    = -1;

      private bool                     _showOneTabButton                = true;

      #endregion Fields

      #region Instance

      /// <summary>
      /// Default constructor
      /// </summary>
      public ButtonsPanel()
      {
         InitializeComponent();

         SetStyle(ControlStyles.AllPaintingInWmPaint,  true);
         SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
         SetStyle(ControlStyles.ResizeRedraw,          true);
         SetStyle(ControlStyles.Selectable,            true);
         SetStyle(ControlStyles.UserPaint,             true);

         //_scrollMoveTimer.Tick    += OnScrollMoveTimeOccurence;
         //_scrollMoveTimer.Interval = 500;
         //_scrollMoveTimer.Enabled  = true;
      }

      #endregion Instance

      #region Public section

      /// <summary>
      /// Buttons renderer
      /// </summary>
      public TabButtonRenderer ButtonsRenderer
      {
         get 
         {
            if (_buttonsRenderer == null)
            {
               _buttonsRenderer  = new TopTabButtonRenderer();
               OnTabButonRendererChanged();
            }

            return _buttonsRenderer; 
         }
         set 
         {
            if (_buttonsRenderer != value)
            {
               _buttonsRenderer = value;
               OnTabButonRendererChanged();
               Invalidate();
            }
         }
      }

      /// <summary>
      /// Back gradient color 1
      /// </summary>
      public Color BackGradient1
      {
         get { return _backGradient1; }
         set { _backGradient1 = value; }
      }

      /// <summary>
      /// Back gradient color 2
      /// </summary>
      public Color BackGradient2
      {
         get { return _backGradient2; }
         set { _backGradient2 = value; }
      }

      /// <summary>
      /// Back gradient mode
      /// </summary>
      public LinearGradientMode BackGradientMode
      {
         get { return _backGradientMode; }
         set { _backGradientMode = value; }
      }


      /// <summary>
      /// Set this true to show tab buttons when only one button exists
      /// </summary>
      public bool ShowOneTabButton
      {
         get { return _showOneTabButton; }
         set 
         {
            if (_showOneTabButton != value)
            {
               _showOneTabButton      = value;
               _updatePositionsOnDraw = true;
            }
         }
      }


      /// <summary>
      /// Zero based selected button index
      /// </summary>
      public int SelectedIndex
      {
         get { return _selectedIndex; }
         set 
         {
            if (_selectedIndex != value)
            {
               if ((value < 0 || value >= _buttons.Count) && _buttons.Count > 0)
               {
                  throw new IndexOutOfRangeException("Valid values are between 0 and " + _buttons.Count.ToString() + ".");
               }

               _selectedIndex = value;

               Invalidate();
            }

            OnSelectedIndexSet(EventArgs.Empty);
         }
      }

      #endregion Public section

      #region Protected section

      /// <summary> 
      /// Clean up any resources being used.
      /// </summary>
      /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
      protected override void Dispose(bool disposing)
      {
         if (disposing)
         {
            if (_scrollMoveTimer != null)
            {
               _scrollMoveTimer.Enabled = false;
               _scrollMoveTimer.Tick   -= OnScrollMoveTimeOccurence;
               _scrollMoveTimer.Dispose();
               _scrollMoveTimer = null;
            }
         }

         base.Dispose(disposing);
      }

      /// <summary>
      /// Occurs when paint is required
      /// </summary>
      /// <param name="e">event argument</param>
      protected override void OnPaint(PaintEventArgs e)
      {
         if (_clientBounds != ClientRectangle || _updatePositionsOnDraw)
         {
            _clientBounds = ClientRectangle;
            _updatePositionsOnDraw = false;

            UpdatePositions(e.Graphics, false);
         }

         OnPaintPanelBackground(e);


         if (CaptionButtonsCount > 0)
         {
            OnDrawCaptionButtons(_captionButtonsBounds, e.Graphics);
         }

         DrawRoundBorder(e);

         int buttonsCount = _buttons.Count;
         if (buttonsCount == 1 && ShowOneTabButton == false)
         {
            buttonsCount = 0;
         }

         if (buttonsCount > 0)
         {
            DrawButtonsLine(e);

            RectangleF clip = e.Graphics.ClipBounds;

            e.Graphics.SetClip(_buttonsPanelBounds);

            for (int index = buttonsCount - 1; index >= 0; index--)
            {
               if (index != SelectedIndex)
               {
                  _buttons[index].Draw(ButtonsRenderer, false, Font, e.Graphics);
               }
            }

            _buttons[SelectedIndex].Draw(ButtonsRenderer, true, Font, e.Graphics);

            e.Graphics.SetClip(clip);
         }


         if (_hasScrolls && buttonsCount > 1)
         {
            zButtonState stateBack = zButtonState.Normal;
            zButtonState stateNext = zButtonState.Normal;
            if (_firstShownButtonIndex == 0)
            {
               stateBack = zButtonState.Disabled;
            }
            else if (IsMouseDownInScrollBackButton)
            {
               stateBack = zButtonState.Pressed | zButtonState.UnderMouseCursor;
            }
            else if (IsMouseOverScrollBackButton)
            {
               stateBack = zButtonState.UnderMouseCursor;
            }
            else if (IsMouseDownInScrollNextButton)
            {
               stateNext = zButtonState.Pressed | zButtonState.UnderMouseCursor;
            }
            else if (IsMouseOverScrollNextButton)
            {
               stateNext = zButtonState.UnderMouseCursor;
            }

            if (_canScrollNext == false)
            {
               stateNext = zButtonState.Disabled;
            }

            ButtonsRenderer.DrawScrollBackButton(_scrollBackBounds, stateBack, e.Graphics);
            ButtonsRenderer.DrawScrollNextButton(_scrollNextBounds, stateNext, e.Graphics);
         }
     
         base.OnPaint(e);
      }

      /// <summary>
      /// Draw the round border
      /// </summary>
      /// <param name="e">event args</param>
      protected virtual void DrawRoundBorder(PaintEventArgs e)
      {
         int round  = RoundRadius;
         int width  = Width - 1;
         int height = Height - 1;

         using (GraphicsPath roundRectPath = GraphicsUtility.CreateRoundRectPath(0, 0, width, height, round))
         {
            using (Brush backBrush = new SolidBrush(ButtonsRenderer.SelectedBackGradient2))
            {
               e.Graphics.FillPath(backBrush, roundRectPath);
            }

            using (Pen borderPen = new Pen(ButtonsRenderer.SelectedBorder2))
            {
               e.Graphics.DrawPath(borderPen, roundRectPath);

               if (CutRoundRect)
               {
                  e.Graphics.DrawLine(borderPen, 0, height - 1, width, height - 1);

                  using (GraphicsPath roundRect = GraphicsUtility.CreateRoundRectPath(0, 0, width + 1, height, round))
                  {
                     SetControlRegion(roundRect, this);
                  }
               }
               else
               {
                  SetControlRegion(null, this);
               }
            }
         }
      }

      /// <summary>
      /// Draw buttons line
      /// </summary>
      /// <param name="e">event arg</param>
      protected virtual void DrawButtonsLine(PaintEventArgs e)
      {
         ButtonsRenderer.DrawButtonsLine(e.Graphics, _clientBounds, _buttonsPanelBounds);
      }

      /// <summary>
      /// On paint panel background
      /// </summary>
      /// <param name="e">event arg</param>
      protected virtual void OnPaintPanelBackground(PaintEventArgs e)
      {
         using (LinearGradientBrush backBrush = new LinearGradientBrush(_clientBounds, BackGradient1, BackGradient2, BackGradientMode))
         {
            e.Graphics.FillRectangle(backBrush, _clientBounds);
         }
      }

      /// <summary>
      /// Occurs when size was changed on this control
      /// </summary>
      /// <param name="e">event argument</param>
      protected override void OnSizeChanged(EventArgs e)
      {
         if (ClientRectangle != _clientBounds)
         {
            _clientBounds = ClientRectangle;

            UpdateSize();
         }

         base.OnSizeChanged(e);
      }

      /// <summary>
      /// Occurs when the mouse id down
      /// </summary>
      /// <param name="e">event argument</param>
      protected override void OnMouseDown(MouseEventArgs e)
      {
         _isMouseDownInTabButton = false;
         _isMovingTabButton      = false;

         if (IsMouseOverScrollBackButton)
         {
            IsMouseDownInScrollBackButton = true;
            ScrollBack();
         }
         else if (IsMouseOverScrollNextButton)
         {
            IsMouseDownInScrollNextButton = true;
            ScrollNext();
         }
         else if (_captionButtonsBounds.Contains(e.Location))
         {
            int captionIndex = ButtonsRenderer.GetCaptionButtonIndex(_captionButtonsBounds, e.Location);
            OnMouseDownInCaptionButton(captionIndex, e);
         }
         else
         {
            int index = 0;
            TabButton button = GetButtonFromPoint(e.Location, out index);
            if (button != null)
            {
               SelectedIndex = index;

               Invalidate();

               _isMouseDownInTabButton = e.Button == MouseButtons.Left;

               OnMouseDownInTabButton(button);
            }
         }


         base.OnMouseDown(e);
      }

      /// <summary>
      /// Occurs when mouse cursor is moved over the panel
      /// </summary>
      /// <param name="e">event argument</param>
      protected override void OnMouseMove(MouseEventArgs e)
      {
         Cursor cursor = Cursors.Default;

         if (_isMouseDownInTabButton && e.Button == MouseButtons.Left)
         {
            cursor = Cursors.Hand;

            if (_isMovingTabButton)
            {
               TabButton selected = _buttons[SelectedIndex];
               if (selected.Contains(e.Location) == false)
               {
                  if (_isDraggingTabButton)
                  {
                     ContinueButtonDrag(e.Location, ref cursor);
                  }
                  else
                  {
                     int index = -1;
                     TabButton underMouse = GetButtonFromPoint(e.Location, true, out index);
                     if (underMouse != null)
                     {
                        bool displace  = false;
                        if (underMouse == _buttonDisplaced)
                        {
                           if (index < SelectedIndex)
                           {
                              displace = ButtonsRenderer.CanUndoDisplaceBack(underMouse, selected, e.Location);
                           }
                           else if (index > SelectedIndex)
                           {
                              displace = ButtonsRenderer.CanUndoDisplaceNext(underMouse, selected, e.Location);
                           }
                        }

                        _buttonDisplaced = underMouse;

                        if (displace)
                        {
                           _buttons[index]         = selected;
                           _buttons[SelectedIndex] = underMouse;
                           _updatePositionsOnDraw  = true;
                           SelectedIndex           = index;
                        }
                     }
                     else
                     {
                        _isDraggingTabButton = BeginButtonDrag(selected, e.Location, ref cursor);
                     }
                  }
               }
            }
            else
            {
               _isMovingTabButton   = _buttons[SelectedIndex].Contains(e.Location);
               _isDraggingTabButton = false;
            }
         }
         else if (GetButtonFromPoint(e.Location) != null)
         {
            cursor = Cursors.Hand;
         }

         CheckIfIsMouseOverScrollButtons(e.Location);
         if (IsMouseOverScrollBackButton && _firstShownButtonIndex > 0)
         {
            cursor = Cursors.Hand;
         }
         else if (IsMouseOverScrollNextButton && _canScrollNext)
         {
            cursor = Cursors.Hand;
         }

         int captionIndex = ButtonsRenderer.GetCaptionButtonIndex(_captionButtonsBounds, e.Location);
         if (captionIndex < 0 || captionIndex >= ButtonsCount)
         {
            captionIndex = -1;
         }
         else
         {
            cursor = Cursors.Hand;
         }

         if (_captionButtonIndexUnderMouse != captionIndex)
         {
            _captionButtonIndexUnderMouse = captionIndex;
            OnMouseMoveOverCaptionButton(captionIndex, e);

            Invalidate();
         }

         int buttonIndex = -1;
         TabButton buttonUnderMouse = GetButtonFromPoint(e.Location, false, out buttonIndex);
         if (buttonUnderMouse != null)
         {
            OnMouseMoveOverTabButton(buttonUnderMouse);
         }


         Cursor = cursor;

         base.OnMouseMove(e);
      }

      /// <summary>
      /// Occurs when mouse cursor is released
      /// </summary>
      /// <param name="e">event argument</param>
      protected override void OnMouseUp(MouseEventArgs e)
      {
         EndMouseAction(false);

         base.OnMouseUp(e);
      }

      /// <summary>
      /// ProcessDialogKey
      /// </summary>
      /// <param name="keyData">keyData</param>
      /// <returns>processing result</returns>
      protected override bool ProcessDialogKey(Keys keyData)
      {
         if (keyData == Keys.Escape)
         {
            EndMouseAction(true);
         }

         return base.ProcessDialogKey(keyData);
      }

      /// <summary>
      /// Occurs when key was pressed inside this control
      /// </summary>
      /// <param name="e">event arg</param>
      protected override void OnKeyDown(KeyEventArgs e)
      {
         if (e.KeyCode == Keys.Escape)
         {
            EndMouseAction(true);
         }

         base.OnKeyDown(e);
      }

      /// <summary>
      /// Occurs after pages panel size was changed
      /// </summary>
      /// <param name="bounds">bounds</param>
      protected virtual void OnPagesPanelBoundsChanged(Rectangle bounds)
      {
      }

      /// <summary>
      /// Occurs when selected index was set
      /// </summary>
      /// <param name="e">event argument</param>
      protected virtual void OnSelectedIndexSet(EventArgs e)
      {
      }

      /// <summary>
      /// Occurs when mouse is moved over caption button
      /// </summary>
      /// <param name="captionIndex">zero-based caption button index</param>
      /// <param name="e">mouse event argument</param>
      protected virtual void OnMouseMoveOverCaptionButton(int captionIndex, MouseEventArgs e)
      {
      }

      /// <summary>
      /// Occurs when mouse button was down inside caption button
      /// </summary>
      /// <param name="captionIndex">zero-based caption button index</param>
      /// <param name="e">mouse event argument</param>
      protected virtual void OnMouseDownInCaptionButton(int captionIndex, MouseEventArgs e)
      {
      }

      /// <summary>
      /// Occurs when mouse button was moved over a tab button
      /// </summary>
      /// <param name="buttonUnderMouse">button under mouse</param>
      protected virtual void OnMouseMoveOverTabButton(TabButton buttonUnderMouse)
      {
      }

      /// <summary>
      /// Occurs when mouse button was pressed while the cursor was over a tab button
      /// </summary>
      /// <param name="buttonUnderMouse">button under mouse</param>
      protected virtual void OnMouseDownInTabButton(TabButton buttonUnderMouse)
      {
      }

      /// <summary>
      /// Occurs when tab button is removed
      /// </summary>
      /// <param name="button">button removed</param>
      protected virtual void OnButtonRemoved(TabButton button)
      {
      }

      /// <summary>
      /// Occurs when tab button is added
      /// </summary>
      /// <param name="button">button added</param>
      protected virtual void OnButtonAdded(TabButton button)
      {
      }

      /// <summary>
      /// Called when tab button renderer is changed
      /// </summary>
      protected virtual void OnTabButonRendererChanged()
      {
      }

      /// <summary>
      /// Gets the margins for pages panel
      /// </summary>
      protected virtual int PagesPanelMargins
      {
         get { return 4; }
      }

      /// <summary>
      /// Gets the radius for round rect
      /// </summary>
      protected virtual int RoundRadius
      {
         get { return 3; }
      }

      /// <summary>
      /// Count of caption buttons
      /// </summary>
      protected virtual int CaptionButtonsCount
      {
         get { return 0; }
      }

      /// <summary>
      /// Draw caption buttons
      /// </summary>
      /// <param name="captionButtonsBounds">caption buttons bounds</param>
      /// <param name="graphics">graphics object</param>
      protected virtual void OnDrawCaptionButtons(Rectangle captionButtonsBounds, Graphics graphics)
      {
      }

      /// <summary>
      /// Update the positions on draw
      /// </summary>
      protected void UpdatePositions()
      {
         _updatePositionsOnDraw = true;
         Invalidate();
      }

      /// <summary>
      /// Gets the count of buttons
      /// </summary>
      protected int ButtonsCount
      {
         get { return _buttons.Count; }
      }

      /// <summary>
      /// Add button
      /// </summary>
      /// <param name="button">button</param>
      protected void AddButton(TabButton button)
      {
         InsertButton(button, _buttons.Count);
      }

      /// <summary>
      /// Insert a button 
      /// </summary>
      /// <param name="button">button to insert</param>
      /// <param name="insertIndex">zero based insert index</param>
      protected void InsertButton(TabButton button, int insertIndex)
      {
         if (button != null && insertIndex >= 0 && insertIndex <= _buttons.Count)
         {
            _buttons.Insert(insertIndex, button);
            OnButtonAdded(button);

            button.TextChanged         += OnButtonTextChanged;
            button.ExplicitDisposing   += OnButtonDisposing;

            SelectedIndex = insertIndex;

            if (_buttons.Count == 1)
            {
               using (Graphics graphics = CreateGraphics())
               {
                  UpdatePositions(graphics, true);
               }
            }
            else
            {
               _updatePositionsOnDraw = true;
            }

            UpdateSize();
            Invalidate();
         }
      }

      /// <summary>
      /// Remove button
      /// </summary>
      /// <param name="button">button</param>
      /// <returns>true if button was removed</returns>
      protected bool RemoveButton(TabButton button)
      {
         if (_buttons.Contains(button) == false)
         {
            return false;
         }

         // If the selected index is the last button index, must decrement the selected index
         // to prevent index out of range exception after the count of buttons is updated.
         if (SelectedIndex > 0 && SelectedIndex == ButtonsCount - 1)
         {
            SelectedIndex--;
         }

         _buttons.Remove(button);
         OnButtonRemoved(button);

         button.TextChanged       -= OnButtonTextChanged;
         button.ExplicitDisposing -= OnButtonDisposing;

         if (_buttons.Count == 0)
         {
            if (Disposing == false && IsDisposed == false)
            {
               using (Graphics graphics = CreateGraphics())
               {
                  UpdatePositions(graphics, false);
               }
            }
         }
         else
         {
            _updatePositionsOnDraw = true;
         }

         if (Disposing == false && IsDisposed == false)
         {
            UpdateSize();
            Invalidate();
         }

         return true;
      }

      /// <summary>
      /// Get the button at given index
      /// </summary>
      /// <param name="buttonIndex">zero based button index</param>
      /// <returns>button at given index</returns>
      protected TabButton GetButtonAt(int buttonIndex)
      {
         if (buttonIndex == -1)
         {
            return null;
         }

         return _buttons[buttonIndex];
      }

      /// <summary>
      /// Selected button
      /// </summary>
      protected TabButton SelectedButton
      {
         get { return GetButtonAt(SelectedIndex); }
         set { SelectedIndex = _buttons.IndexOf(value); }
      }

      /// <summary>
      /// Pages bounds
      /// </summary>
      protected Rectangle PagesBounds
      {
         get { return _pagesPanelBounds; }
      }

      /// <summary>
      /// Validates that current instance is not disposed
      /// </summary>
      protected void ValidateNotDisposed()
      {
         if (IsDisposed)
         {
            throw new ObjectDisposedException(GetType().Name);
         }
      }

      /// <summary>
      /// Set the region of the control
      /// </summary>
      /// <param name="newRegionPath">new region path</param>
      /// <param name="control">control</param>
      protected static void SetControlRegion(GraphicsPath newRegionPath, Control control)
      {
         Region oldRegion = control.Region;
         if (newRegionPath != null)
         {
            control.Region = new Region(newRegionPath);
         }
         else
         {
            if (control.Region == null)
            {
               return;
            }
            control.Region = null;
         }
         if (oldRegion != null)
         {
            oldRegion.Dispose();
         }
      }

      /// <summary>
      /// Cut round rect
      /// </summary>
      protected bool CutRoundRect
      {
         get { return _cutRoundRect; }
         set
         {
            if (_cutRoundRect != value)
            {
               _cutRoundRect = value;
               Invalidate();
            }
         }
      }

      /// <summary>
      /// Update the panel size
      /// </summary>
      protected void UpdateSize()
      {
         if (IsDisposed)
         {
            return;
         }

         if (CutRoundRect)
         {
            using (GraphicsPath roundRect = GraphicsUtility.CreateRoundRectPath(0, 0, Width, Height - 1, RoundRadius))
            {
               SetControlRegion(roundRect, this);
            }
         }
         else
         {
            SetControlRegion(null, this);
         }

         using (Graphics graphics = CreateGraphics())
         {
            UpdatePositions(graphics, false);
         }
      }


      /// <summary>
      /// Begin button drag
      /// </summary>
      /// <param name="selected">selected button</param>
      /// <param name="mousePosition">mouse position</param>
      /// <param name="cursor">cursor</param>
      /// <returns>true if button drag is started</returns>
      protected virtual bool BeginButtonDrag(TabButton selected, Point mousePosition, ref Cursor cursor)
      {
         return false;
      }

      /// <summary>
      /// Continue button drag
      /// </summary>
      /// <param name="mousePosition">mouse position</param>
      /// <param name="cursor">cursor</param>
      protected virtual void ContinueButtonDrag(Point mousePosition, ref Cursor cursor)
      {
      }

      /// <summary>
      /// End button drag
      /// </summary>
      /// <param name="cancel">cancel</param>
      protected virtual void EndButtonDrag(bool cancel)
      {
      }


      #endregion Protected section

      #region Private section
      #region Received events

      /// <summary>
      /// Event received when button is disposing
      /// </summary>
      /// <param name="sender">sender</param>
      /// <param name="e">event args</param>
      private void OnButtonDisposing(object sender, EventArgs e)
      {
         RemoveButton((TabButton)sender);
      }

      /// <summary>
      /// Event received when button text changed
      /// </summary>
      /// <param name="sender">sender</param>
      /// <param name="e">event args</param>
      private void OnButtonTextChanged(object sender, EventArgs e)
      {
         _updatePositionsOnDraw = true;
         Invalidate();
      }

      /// <summary>
      /// Event received when scroll move time occurs
      /// </summary>
      /// <param name="sender">sender</param>
      /// <param name="e">event args</param>
      private void OnScrollMoveTimeOccurence(object sender, EventArgs e)
      {
         if (_hasScrolls == false)
         {
            return;
         }

         
         if (IsMouseDownInScrollBackButton && _firstShownButtonIndex > 0)
         {
            _firstShownButtonIndex--;
            Invalidate();
         }
         else if (IsMouseDownInScrollNextButton && _canScrollNext)
         {
            _firstShownButtonIndex++;
            Invalidate();
         }
      }

      #endregion Received events

      /// <summary>
      /// Get the button at given point
      /// </summary>
      /// <param name="point">point on which the button is searched</param>
      /// <param name="permisive">permisive search</param>
      /// <param name="buttonIndex">zero based button index if found, -1 if not found</param>
      /// <returns>button at given point or null</returns>
      private TabButton GetButtonFromPoint(Point point, bool permisive, out int buttonIndex)
      {
         if (_buttonsPanelBounds.Contains(point))
         {
            if (permisive)
            {
               int x = _buttonsPanelBounds.Left;
               if (_buttons.Count > 0)
               {
                  x = _buttons[0].Left - 4;
               }

               Rectangle bounds = new Rectangle();
               bounds.Y         = _buttonsPanelBounds.Y - 4;
               bounds.Height    = _buttonsPanelBounds.Height + 8;
               for (int index   = 0; index < _buttons.Count; index++)
               {
                  bounds.X      = x;
                  bounds.Width  = _buttons[index].Width + 4;

                  if (bounds.Contains(point))
                  {
                     buttonIndex = index;
                     return _buttons[index];
                  }

                  x = bounds.Right;
               }
            }
            else
            {
               for (int index = 0; index < _buttons.Count; index++)
               {
                  if (_buttons[index].Contains(point))
                  {
                     buttonIndex = index;
                     return _buttons[index];
                  }
               }
            }
         }

         buttonIndex = -1;
         return null;
      }

      /// <summary>
      /// Get the button at given point
      /// </summary>
      /// <param name="point">point on which the button is searched</param>
      /// <param name="buttonIndex">zero based button index if found, -1 if not found</param>
      /// <returns>button at given point or null</returns>
      private TabButton GetButtonFromPoint(Point point, out int buttonIndex)
      {
         return GetButtonFromPoint(point, false, out buttonIndex);
      }

      /// <summary>
      /// Get the button at given point
      /// </summary>
      /// <param name="point">point on which the button is searched</param>
      /// <returns>button at given point or null</returns>
      private TabButton GetButtonFromPoint(Point point)
      {
         int buttonIndex = -1;
         return GetButtonFromPoint(point, out buttonIndex);
      }

      /// <summary>
      /// Check if the mouse is over scroll button
      /// </summary>
      /// <param name="location">mouse location</param>
      private void CheckIfIsMouseOverScrollButtons(Point location)
      {
         if (_scrollBackBounds.Contains(location))
         {
            IsMouseOverScrollBackButton = true;
            IsMouseOverScrollNextButton = false;
         }
         else if (_scrollNextBounds.Contains(location))
         {
            IsMouseOverScrollBackButton = false;
            IsMouseOverScrollNextButton = true;
         }
         else
         {
            IsMouseOverScrollBackButton = false;
            IsMouseOverScrollNextButton = false;
         }
      }

      /// <summary>
      /// Update positions on panel
      /// </summary>
      /// <param name="graphics">graphics object</param>
      /// <param name="forceChangeNotification">force change notification</param>
      private void UpdatePositions(Graphics graphics, bool forceChangeNotification)
      {
         Rectangle bounds           = new Rectangle();
         Rectangle pagesPanelBounds = _clientBounds;

         int buttonsCount = _buttons.Count;
         if (buttonsCount == 1 && ShowOneTabButton == false)
         {
            buttonsCount  = 0;
         }

         if (buttonsCount != 0)
         {
            bounds = ButtonsRenderer.GetFirstButtonBounds(_clientBounds, false, 0, _buttons[0].Text, Font, _buttons[0].PageIcon, graphics);
            _buttons[0].SetBounds(bounds);

            for (int index = 1; index < _buttons.Count; index++)
            {
               bounds = ButtonsRenderer.GetNextButtonBounds(bounds, _buttons[index].Text, Font, _buttons[index].PageIcon, graphics);
               _buttons[index].SetBounds(bounds);
            }

            _buttonsPanelBounds = ButtonsRenderer.GetButtonsClipRectangle(_clientBounds, false, CaptionButtonsCount);

            _hasScrolls = ButtonsRenderer.HasScroll(_buttonsPanelBounds, bounds);
            if (_hasScrolls)
            {
               _buttonsPanelBounds = ButtonsRenderer.GetButtonsClipRectangle(_clientBounds, true, CaptionButtonsCount);
            }

            if (_hasScrolls)
            {
               int scrollPos = ButtonsRenderer.GetScrollPos(_buttons, _firstShownButtonIndex);

               bounds = ButtonsRenderer.GetFirstButtonBounds(_clientBounds, true, scrollPos, _buttons[0].Text, Font, _buttons[0].PageIcon, graphics);
               _buttons[0].SetBounds(bounds);

               for (int index = 1; index < _buttons.Count; index++)
               {
                  bounds = ButtonsRenderer.GetNextButtonBounds(bounds, _buttons[index].Text, Font, _buttons[index].PageIcon, graphics);
                  _buttons[index].SetBounds(bounds);
               }

               _scrollBackBounds = ButtonsRenderer.GetScrollBackButtonBounds(_buttonsPanelBounds);
               _scrollNextBounds = ButtonsRenderer.GetScrollNextButtonBounds(_buttonsPanelBounds);

               _canScrollNext    = ButtonsRenderer.CanScrollNext(_buttons[ButtonsCount - 1], _scrollNextBounds) && _firstShownButtonIndex < ButtonsCount - 1;
            }

            pagesPanelBounds = ButtonsRenderer.GetClientRectangle(pagesPanelBounds);
         }

         _captionButtonsBounds = ButtonsRenderer.GetCaptionButtonsRectangle(_clientBounds, CaptionButtonsCount);

         if (_pagesPanelBounds != pagesPanelBounds || forceChangeNotification)
         {
            _pagesPanelBounds   = pagesPanelBounds;
            pagesPanelBounds.Height++;

            //pagesPanelBounds.Inflate(-PagesPanelMargins, -PagesPanelMargins);

            OnPagesPanelBoundsChanged(pagesPanelBounds);
         }
      }

      /// <summary>
      /// Accessor of flag indicating if the mouse cursor is over scroll back button
      /// </summary>
      private bool IsMouseOverScrollBackButton
      {
         get { return _isMouseOverScrollBackButton; }
         set
         {
            if (_isMouseOverScrollBackButton != value)
            {
               _isMouseOverScrollBackButton = value;

               if (IsMouseOverScrollBackButton == false)
               {
                  IsMouseDownInScrollBackButton = false;
               }

               Invalidate();
            }
         }
      }

      /// <summary>
      /// Accessor of flag indicating if the mouse cursor is over scroll next button
      /// </summary>
      private bool IsMouseOverScrollNextButton
      {
         get { return _isMouseOverScrollNextButton; }
         set
         {
            if (_isMouseOverScrollNextButton != value)
            {
               _isMouseOverScrollNextButton = value;

               if (IsMouseOverScrollNextButton == false)
               {
                  IsMouseDownInScrollNextButton = false;
               }

               Invalidate();
            }
         }
      }

      /// <summary>
      /// Accessor of flag indicating if the mouse left button is down when the cursor is over scroll back button
      /// </summary>
      private bool IsMouseDownInScrollBackButton
      {
         get { return _isMouseDownInScrollBackButton; }
         set
         {
            if (_isMouseDownInScrollBackButton != value)
            {
               _isMouseDownInScrollBackButton = value;
               Invalidate();
            }
         }
      }

      /// <summary>
      /// Accessor of flag indicating if the mouse left button is down when the cursor is over scroll next button
      /// </summary>
      private bool IsMouseDownInScrollNextButton
      {
         get { return _isMouseDownInScrollNextButton; }
         set
         {
            if (_isMouseDownInScrollNextButton != value)
            {
               _isMouseDownInScrollNextButton = value;
               Invalidate();
            }
         }
      }

      /// <summary>
      /// Scroll next
      /// </summary>
      private void ScrollNext()
      {
         if (_hasScrolls == false)
         {
            return;
         }

         if (IsMouseDownInScrollNextButton && _canScrollNext)
         {
            _firstShownButtonIndex++;
            _updatePositionsOnDraw = true;
            Invalidate();
         }
      }

      /// <summary>
      /// Scroll back
      /// </summary>
      private void ScrollBack()
      {
         if (_hasScrolls == false)
         {
            return;
         }


         if (IsMouseDownInScrollBackButton && _firstShownButtonIndex > 0)
         {
            _firstShownButtonIndex--;
            _updatePositionsOnDraw = true;
            Invalidate();
         }
      }

      /// <summary>
      /// End mouse action
      /// </summary>
      /// <param name="cancel">cancel</param>
      private void EndMouseAction(bool cancel)
      {
         _isMouseDownInTabButton = false;

         if (_isMovingTabButton)
         {
            if (_isDraggingTabButton)
            {
               EndButtonDrag(cancel);
            }

            _isMovingTabButton = false;
            _buttonDisplaced = null;
         }

         IsMouseDownInScrollBackButton = false;
         IsMouseDownInScrollNextButton = false;
      }

      #endregion Private section
   }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer
Singapore Singapore
He is a Microsoft technology enthusiast, who wish to create applications which others find useful.He loves making small tools and getting involved in architecting bigger systems.

He is currently working as a professional developer in a software development firm in .Net technologies.

He likes reading technical blogs, contributing to opensource and most importantly, enjoying life.

His ambition is to be an impressive software maker.

Comments and Discussions