//============================================================================
//ZedGraph Class Library - A Flexible Line Graph/Bar Graph Library in C#
//Copyright ?2004  John Champion
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library 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 GNU
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//=============================================================================
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.Data;
using System.Globalization;
using System.IO;
using System.Resources;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace DrawGraph
{
    /*
        /// 
        /// 
        /// 
        public struct DrawingThreadData
        {
            /// 
            /// 
            /// 
            public Graphics _g;
            /// 
            /// 
            /// 
            public MasterPane _masterPane;
    //		public DrawingThread( Graphics g, MasterPane masterPane )
    //		{
    //			_g = g;
    //			_masterPane = masterPane;
    //		}
        }
    */
    /// 
    /// The ZedGraphControl class provides a UserControl interface to the
    ///  class library.  This allows ZedGraph to be installed
    /// as a control in the Visual Studio toolbox.  You can use the control by simply
    /// dragging it onto a form in the Visual Studio form editor.  All graph
    /// attributes are accessible via the 
    /// property.
    /// 
    ///  John Champion revised by Jerry Vos 
    ///  $Revision: 3.82 $ $Date: 2007/04/16 00:03:06 $ 
    public partial class ZedGraphControl : UserControl
    {
        #region Private Fields
        /// 
        /// This private field contains the instance for the MasterPane object of this control.
        /// You can access the MasterPane object through the public property
        /// . This is nulled when this Control is
        /// disposed.
        /// 
        private MasterPane _masterPane;
        /// 
        /// private field that determines if anti-aliased drawing will be forced on.  Use the
        /// public property  to access this value.
        /// 
        private bool _isAntiAlias = false;
        /// 
        /// private field that determines whether or not tooltips will be displayed
        /// when the mouse hovers over data values.  Use the public property
        ///  to access this value.
        /// 
        private bool _isShowPointValues = false;
        /// 
        /// private field that determines whether or not tooltips will be displayed
        /// showing the scale values while the mouse is located within the ChartRect.
        /// Use the public property  to access this value.
        /// 
        private bool _isShowCursorValues = false;
        /// 
        /// private field that determines the format for displaying tooltip values.
        /// This format is passed to .
        /// Use the public property  to access this
        /// value.
        /// 
        private string _pointValueFormat = PointPair.DefaultFormat;
        /// 
        /// private field that determines whether or not the context menu will be available.  Use the
        /// public property  to access this value.
        /// 
        private bool _isShowContextMenu = true;
        /// 
        /// private field that determines whether or not a message box will be shown in response to
        /// a context menu "Copy" command.  Use the
        /// public property  to access this value.
        /// 
        /// 
        /// Note that, if this value is set to false, the user will receive no indicative feedback
        /// in response to a Copy action.
        /// 
        private bool _isShowCopyMessage = true;
        private SaveFileDialog _saveFileDialog = new SaveFileDialog();
        /// 
        /// private field that determines whether the settings of
        ///  and 
        /// will be overridden to true during printing operations.
        /// 
        /// 
        /// Printing involves pixel maps that are typically of a dramatically different dimension
        /// than on-screen pixel maps.  Therefore, it becomes more important to scale the fonts and
        /// lines to give a printed image that looks like what is shown on-screen.  The default
        /// setting for  is true, but the default
        /// setting for  is false.
        /// 
        /// 
        /// A value of true will cause both  and
        ///  to be temporarily set to true during
        /// printing operations.
        /// 
        private bool _isPrintScaleAll = true;
        /// 
        /// private field that determines whether or not the visible aspect ratio of the
        ///   will be preserved
        /// when printing this .
        /// 
        private bool _isPrintKeepAspectRatio = true;
        /// 
        /// private field that determines whether or not the 
        ///  dimensions will be expanded to fill the
        /// available space when printing this .
        /// 
        /// 
        /// If  is also true, then the 
        ///  dimensions will be expanded to fit as large
        /// a space as possible while still honoring the visible aspect ratio.
        /// 
        private bool _isPrintFillPage = true;
        /// 
        /// private field that determines the format for displaying tooltip date values.
        /// This format is passed to .
        /// Use the public property  to access this
        /// value.
        /// 
        private string _pointDateFormat = XDate.DefaultFormatStr;
        /// 
        /// private value that determines whether or not zooming is enabled for the control in the
        /// vertical direction.  Use the public property  to access this
        /// value.
        /// 
        private bool _isEnableVZoom = true;
        /// 
        /// private value that determines whether or not zooming is enabled for the control in the
        /// horizontal direction.  Use the public property  to access this
        /// value.
        /// 
        private bool _isEnableHZoom = true;
        /// 
        /// private value that determines whether or not point editing is enabled in the
        /// vertical direction.  Use the public property  to access this
        /// value.
        /// 
        private bool _isEnableVEdit = false;
        /// 
        /// private value that determines whether or not point editing is enabled in the
        /// horizontal direction.  Use the public property  to access this
        /// value.
        /// 
        private bool _isEnableHEdit = false;
        /// 
        /// private value that determines whether or not panning is allowed for the control in the
        /// horizontal direction.  Use the
        /// public property  to access this value.
        /// 
        private bool _isEnableHPan = true;
        /// 
        /// private value that determines whether or not panning is allowed for the control in the
        /// vertical direction.  Use the
        /// public property  to access this value.
        /// 
        private bool _isEnableVPan = true;
        // Revision: JCarpenter 10/06
        /// 
        /// Internal variable that indicates if the control can manage selections. 
        /// 
        private bool _isEnableSelection = false;
        private double _zoomStepFraction = 0.1;
        private ScrollRange _xScrollRange;
        private ScrollRangeList _yScrollRangeList;
        private ScrollRangeList _y2ScrollRangeList;
        private bool _isShowHScrollBar = false;
        private bool _isShowVScrollBar = false;
        //private bool		isScrollY2 = false;
        private bool _isAutoScrollRange = false;
        private double _scrollGrace = 0.00; //0.05;
        private bool _isSynchronizeXAxes = false;
        private bool _isSynchronizeYAxes = false;
        //private System.Windows.Forms.HScrollBar hScrollBar1;
        //private System.Windows.Forms.VScrollBar vScrollBar1;
        // The range of values to use the scroll control bars
        private const int _ScrollControlSpan = int.MaxValue;
        // The ratio of the largeChange to the smallChange for the scroll bars
        private const int _ScrollSmallRatio = 10;
        private bool _isZoomOnMouseCenter = false;
        private ResourceManager _resourceManager;
        /// 
        /// private field that stores a  instance, which maintains
        /// a persistent selection of printer options.
        /// 
        /// 
        /// This is needed so that a "Print" action utilizes the settings from a prior
        /// "Page Setup" action.
        private PrintDocument _pdSave = null;
        //private PrinterSettings printSave = null;
        //private PageSettings pageSave = null;
        /// 
        /// This private field contains a list of selected CurveItems.
        /// 
        //private List _selection = new List();
        private Selection _selection = new Selection();
        #endregion
        #region Fields: Buttons & Keys Properties
        /// 
        /// Gets or sets a value that determines which Mouse button will be used to click on
        /// linkable objects
        /// 
        /// 
        private MouseButtons _linkButtons = MouseButtons.Left;
        /// 
        /// Gets or sets a value that determines which modifier keys will be used to click
        /// on linkable objects
        /// 
        /// 
        private Keys _linkModifierKeys = Keys.Alt;
        /// 
        /// Gets or sets a value that determines which Mouse button will be used to edit point
        /// data values
        /// 
        /// 
        /// This setting only applies if  and/or
        ///  are true.
        /// 
        /// 
        private MouseButtons _editButtons = MouseButtons.Right;
        /// 
        /// Gets or sets a value that determines which modifier keys will be used to edit point
        /// data values
        /// 
        /// 
        /// This setting only applies if  and/or
        ///  are true.
        /// 
        /// 
        private Keys _editModifierKeys = Keys.Alt;
        /// 
        /// Gets or sets a value that determines which mouse button will be used to select
        /// 's.
        /// 
        /// 
        /// This setting only applies if  is true.
        /// 
        /// 
        private MouseButtons _selectButtons = MouseButtons.Left;
        /// 
        /// Gets or sets a value that determines which modifier keys will be used to select
        /// 's.
        /// 
        /// 
        /// This setting only applies if  is true.
        /// 
        /// 
        private Keys _selectModifierKeys = Keys.Shift;
        private Keys _selectAppendModifierKeys = Keys.Shift | Keys.Control;
        /// 
        /// Gets or sets a value that determines which Mouse button will be used to perform
        /// zoom operations
        /// 
        /// 
        /// This setting only applies if  and/or
        ///  are true.
        /// 
        /// 
        /// 
        /// 
        private MouseButtons _zoomButtons = MouseButtons.Left;
        /// 
        /// Gets or sets a value that determines which modifier keys will be used to perform
        /// zoom operations
        /// 
        /// 
        /// This setting only applies if  and/or
        ///  are true.
        /// 
        /// 
        /// 
        /// 
        private Keys _zoomModifierKeys = Keys.None;
        /// 
        /// Gets or sets a value that determines which Mouse button will be used as a
        /// secondary option to perform zoom operations
        /// 
        /// 
        /// This setting only applies if  and/or
        ///  are true.
        /// 
        /// 
        /// 
        /// 
        private MouseButtons _zoomButtons2 = MouseButtons.None;
        /// 
        /// Gets or sets a value that determines which modifier keys will be used as a
        /// secondary option to perform zoom operations
        /// 
        /// 
        /// This setting only applies if  and/or
        ///  are true.
        /// 
        /// 
        /// 
        /// 
        private Keys _zoomModifierKeys2 = Keys.None;
        /// 
        /// Gets or sets a value that determines which Mouse button will be used to perform
        /// panning operations
        /// 
        /// 
        /// This setting only applies if  and/or
        ///  are true.  A Pan operation (dragging the graph with
        /// the mouse) should not be confused with a scroll operation (using a scroll bar to
        /// move the graph).
        /// 
        /// 
        /// 
        /// 
        private MouseButtons _panButtons = MouseButtons.Left;
        // Setting this field to Keys.Shift here
        // causes an apparent bug to crop up in VS 2003, by which it will have the value:
        // "System.Windows.Forms.Keys.Shift+None", which won't compile
        /// 
        /// Gets or sets a value that determines which modifier keys will be used to perform
        /// panning operations
        /// 
        /// 
        /// This setting only applies if  and/or
        ///  are true.  A Pan operation (dragging the graph with
        /// the mouse) should not be confused with a scroll operation (using a scroll bar to
        /// move the graph).
        /// 
        /// 
        /// 
        /// 
        private Keys _panModifierKeys = Keys.Control;
        /// 
        /// Gets or sets a value that determines which Mouse button will be used as a
        /// secondary option to perform panning operations
        /// 
        /// 
        /// This setting only applies if  and/or
        ///  are true.  A Pan operation (dragging the graph with
        /// the mouse) should not be confused with a scroll operation (using a scroll bar to
        /// move the graph).
        /// 
        /// 
        /// 
        /// 
        private MouseButtons _panButtons2 = MouseButtons.Middle;
        // Setting this field to Keys.Shift here
        // causes an apparent bug to crop up in VS 2003, by which it will have the value:
        // "System.Windows.Forms.Keys.Shift+None", which won't compile
        /// 
        /// Gets or sets a value that determines which modifier keys will be used as a
        /// secondary option to perform panning operations
        /// 
        /// 
        /// This setting only applies if  and/or
        ///  are true.  A Pan operation (dragging the graph with
        /// the mouse) should not be confused with a scroll operation (using a scroll bar to
        /// move the graph).
        /// 
        /// 
        /// 
        /// 
        private Keys _panModifierKeys2 = Keys.None;
        #endregion
        #region Button and Key Properties
        /// 
        /// Gets or sets a value that determines which mouse button will be used as a primary option
        /// to trigger a zoom event.
        /// 
        /// 
        /// This value is combined with  to determine the actual zoom combination.
        /// A secondary zoom button/key combination option is available via  and
        /// .  To not use this button/key combination, set the value
        /// of  to .
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(MouseButtons.Left),
            Description("Determines which mouse button is used as the primary for zooming")]
        public MouseButtons ZoomButtons
        {
            get { return _zoomButtons; }
            set { _zoomButtons = value; }
        }
        /// 
        /// Gets or sets a value that determines which mouse button will be used as the secondary option
        /// to trigger a zoom event.
        /// 
        /// 
        /// This value is combined with  to determine the actual zoom combination.
        /// The primary zoom button/key combination option is available via  and
        /// .  To not use this button/key combination, set the value
        /// of  to .
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(MouseButtons.None),
            Description("Determines which mouse button is used as the secondary for zooming")]
        public MouseButtons ZoomButtons2
        {
            get { return _zoomButtons2; }
            set { _zoomButtons2 = value; }
        }
        /// 
        /// Gets or sets a value that determines which modifier keys will be used as a primary option
        /// to trigger a zoom event.
        /// 
        /// 
        /// This value is combined with  to determine the actual zoom combination.
        /// A secondary zoom button/key combination option is available via  and
        /// .  To not use this button/key combination, set the value
        /// of  to .
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(Keys.None),
            Description("Determines which modifier key used as the primary for zooming")]
        public Keys ZoomModifierKeys
        {
            get { return _zoomModifierKeys; }
            set { _zoomModifierKeys = value; }
        }
        /// 
        /// Gets or sets a value that determines which modifier keys will be used as a secondary option
        /// to trigger a zoom event.
        /// 
        /// 
        /// This value is combined with  to determine the actual zoom combination.
        /// A primary zoom button/key combination option is available via  and
        /// .  To not use this button/key combination, set the value
        /// of  to .
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(Keys.None),
            Description("Determines which modifier key used as the secondary for zooming")]
        public Keys ZoomModifierKeys2
        {
            get { return _zoomModifierKeys2; }
            set { _zoomModifierKeys2 = value; }
        }
        /// 
        /// Gets or sets a value that determines which mouse button will be used as a primary option
        /// to trigger a pan event.
        /// 
        /// 
        /// This value is combined with  to determine the actual pan combination.
        /// A secondary pan button/key combination option is available via  and
        /// .  To not use this button/key combination, set the value
        /// of  to .
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(MouseButtons.Left),
            Description("Determines which mouse button is used as the primary for panning")]
        public MouseButtons PanButtons
        {
            get { return _panButtons; }
            set { _panButtons = value; }
        }
        /// 
        /// Gets or sets a value that determines which mouse button will be used as the secondary option
        /// to trigger a pan event.
        /// 
        /// 
        /// This value is combined with  to determine the actual pan combination.
        /// The primary pan button/key combination option is available via  and
        /// .  To not use this button/key combination, set the value
        /// of  to .
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(MouseButtons.Middle),
            Description("Determines which mouse button is used as the secondary for panning")]
        public MouseButtons PanButtons2
        {
            get { return _panButtons2; }
            set { _panButtons2 = value; }
        }
        // NOTE: The default value of PanModifierKeys is Keys.Shift. Because of an apparent bug in
        // VS 2003, the initial value set in InitializeComponent by the code wizard is "Keys.Shift+None"
        // which will not compile.  As a temporary workaround, I've hidden the value so that it won't
        // have compile errors.  This problem does not exist in VS 2005.
        /// 
        /// Gets or sets a value that determines which modifier keys will be used as a primary option
        /// to trigger a pan event.
        /// 
        /// 
        /// This value is combined with  to determine the actual pan combination.
        /// A secondary pan button/key combination option is available via  and
        /// .  To not use this button/key combination, set the value
        /// of  to .
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(Keys.Control),
            Description("Determines which modifier key is used as the primary for panning")]
        public Keys PanModifierKeys
        {
            get { return _panModifierKeys; }
            set { _panModifierKeys = value; }
        }
        /// 
        /// Gets or sets a value that determines which modifier keys will be used as a secondary option
        /// to trigger a pan event.
        /// 
        /// 
        /// This value is combined with  to determine the actual pan combination.
        /// A primary pan button/key combination option is available via  and
        /// .  To not use this button/key combination, set the value
        /// of  to .
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(Keys.None),
            Description("Determines which modifier key is used as the secondary for panning")]
        public Keys PanModifierKeys2
        {
            get { return _panModifierKeys2; }
            set { _panModifierKeys2 = value; }
        }
        /// 
        /// Gets or sets a value that determines which Mouse button will be used to edit point
        /// data values
        /// 
        /// 
        /// This setting only applies if  and/or
        ///  are true.
        /// 
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
         DefaultValue(MouseButtons.Right),
         Description("Specify mouse button for point editing")]
        public MouseButtons EditButtons
        {
            get { return _editButtons; }
            set { _editButtons = value; }
        }
        /// 
        /// Gets or sets a value that determines which modifier keys will be used to edit point
        /// data values
        /// 
        /// 
        /// This setting only applies if  and/or
        ///  are true.
        /// 
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
         DefaultValue(Keys.Alt),
         Description("Specify modifier key for point editing")]
        public Keys EditModifierKeys
        {
            get { return _editModifierKeys; }
            set { _editModifierKeys = value; }
        }
        /// 
        /// Gets or sets a value that determines which Mouse button will be used to 
        /// select 's.
        /// 
        /// 
        /// This setting only applies if  is true.
        /// 
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
         DefaultValue(MouseButtons.Left),
         Description("Specify mouse button for curve selection")]
        public MouseButtons SelectButtons
        {
            get { return _selectButtons; }
            set { _selectButtons = value; }
        }
        /// 
        /// Gets or sets a value that determines which Modifier keys will be used to 
        /// select 's.
        /// 
        /// 
        /// This setting only applies if  is true.
        /// 
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
         DefaultValue(Keys.Shift),
         Description("Specify modifier key for curve selection")]
        public Keys SelectModifierKeys
        {
            get { return _selectModifierKeys; }
            set { _selectModifierKeys = value; }
        }
        /// 
        /// Gets or sets a value that determines which Modifier keys will be used to 
        /// append a  to the selection list.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
         DefaultValue(Keys.Shift | Keys.Alt),
         Description("Specify modifier key for append curve selection")]
        public Keys SelectAppendModifierKeys
        {
            get { return _selectAppendModifierKeys; }
        }
        /// 
        /// Gets or sets a value that determines which Mouse button will be used to click
        /// on linkable objects
        /// 
        /// 
        /// 
        // /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(MouseButtons.Left),
            Description("Specify mouse button for clicking on linkable objects")]
        public MouseButtons LinkButtons
        {
            get { return _linkButtons; }
            set { _linkButtons = value; }
        }
        /// 
        /// Gets or sets a value that determines which modifier keys will be used to click
        /// on linkable objects
        /// 
        /// 
        /// 
        // /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(Keys.Alt),
            Description("Specify modifier key for clicking on linkable objects")]
        public Keys LinkModifierKeys
        {
            get { return _linkModifierKeys; }
            set { _linkModifierKeys = value; }
        }
        #endregion
        #region Fields: Temporary state variables
        /// 
        /// Internal variable that indicates the control is currently being zoomed. 
        /// 
        private bool _isZooming = false;
        /// 
        /// Internal variable that indicates the control is currently being panned.
        /// 
        private bool _isPanning = false;
        /// 
        /// Internal variable that indicates a point value is currently being edited.
        /// 
        private bool _isEditing = false;
        // Revision: JCarpenter 10/06
        /// 
        /// Internal variable that indicates the control is currently using selection. 
        /// 
        private bool _isSelecting = false;
        /// 
        /// Internal variable that stores the  reference for the Pane that is
        /// currently being zoomed or panned.
        /// 
        private GraphPane _dragPane = null;
        /// 
        /// Internal variable that stores a rectangle which is either the zoom rectangle, or the incremental
        /// pan amount since the last mousemove event.
        /// 
        private Point _dragStartPt;
        private Point _dragEndPt;
        private int _dragIndex;
        private CurveItem _dragCurve;
        private PointPair _dragStartPair;
        /// 
        /// private field that stores the state of the scale ranges prior to starting a panning action.
        /// 
        private ZoomState _zoomState;
        private ZoomStateStack _zoomStateStack;
        //temporarily save the location of a context menu click so we can use it for reference
        // Note that Control.MousePosition ends up returning the position after the mouse has
        // moved to the menu item within the context menu.  Therefore, this point is saved so
        // that we have the point at which the context menu was first right-clicked
        internal Point _menuClickPt;
        #endregion
        #region Events
        /// 
        /// A delegate that allows subscribing methods to append or modify the context menu.
        /// 
        /// The source  object
        /// A reference to the  object
        /// that contains the context menu.
        /// 
        /// The point at which the mouse was clicked
        /// The current context menu state
        /// 
        public delegate void ContextMenuBuilderEventHandler(ZedGraphControl sender,
            ContextMenuStrip menuStrip, Point mousePt, ContextMenuObjectState objState);
        /// 
        /// Subscribe to this event to be able to modify the ZedGraph context menu.
        /// 
        /// 
        /// The context menu is built on the fly after a right mouse click.  You can add menu items
        /// to this menu by simply modifying the  parameter.
        /// 
        [Bindable(true), Category("Events"),
            Description("Subscribe to this event to be able to modify the ZedGraph context menu")]
        public event ContextMenuBuilderEventHandler ContextMenuBuilder;
        /// 
        /// A delegate that allows notification of zoom and pan events.
        /// 
        /// The source  object
        /// A  object that corresponds to the state of the
        ///  before the zoom or pan event.
        /// A  object that corresponds to the state of the
        ///  after the zoom or pan event
        /// 
        public delegate void ZoomEventHandler(ZedGraphControl sender, ZoomState oldState,
            ZoomState newState);
        /// 
        /// Subscribe to this event to be notified when the  is zoomed or panned by the user,
        /// either via a mouse drag operation or by the context menu commands.
        /// 
        [Bindable(true), Category("Events"),
            Description("Subscribe to this event to be notified when the graph is zoomed or panned")]
        public event ZoomEventHandler ZoomEvent;
        /// 
        /// A delegate that allows notification of scroll events.
        /// 
        /// The source  object
        /// The source  object
        /// A  object that corresponds to the state of the
        ///  before the scroll event.
        /// A  object that corresponds to the state of the
        ///  after the scroll event
        /// 
        public delegate void ScrollDoneHandler(ZedGraphControl sender, ScrollBar scrollBar,
            ZoomState oldState, ZoomState newState);
        /// 
        /// Subscribe to this event to be notified when the  is scrolled by the user
        /// using the scrollbars.
        /// 
        [Bindable(true), Category("Events"),
            Description("Subscribe this event to be notified when a scroll operation using the scrollbars is completed")]
        public event ScrollDoneHandler ScrollDoneEvent;
        /// 
        /// A delegate that allows notification of scroll events.
        /// 
        /// The source  object
        /// The source  object
        /// A  object that corresponds to the state of the
        ///  before the scroll event.
        /// A  object that corresponds to the state of the
        ///  after the scroll event
        /// 
        public delegate void ScrollProgressHandler(ZedGraphControl sender, ScrollBar scrollBar,
            ZoomState oldState, ZoomState newState);
        /// 
        /// Subscribe to this event to be notified when the  is scrolled by the user
        /// using the scrollbars.
        /// 
        [Bindable(true), Category("Events"),
            Description("Subscribe this event to be notified continuously as a scroll operation is taking place")]
        public event ScrollProgressHandler ScrollProgressEvent;
        /// 
        /// A delegate that receives notification after a point-edit operation is completed.
        /// 
        /// The source  object
        /// The  object that contains the
        /// point that has been edited
        /// The  object that contains the point
        /// that has been edited
        /// The integer index of the edited  within the
        ///  of the selected 
        /// 
        /// 
        public delegate string PointEditHandler(ZedGraphControl sender, GraphPane pane,
            CurveItem curve, int iPt);
        /// 
        /// Subscribe to this event to receive notifcation and/or respond after a data
        /// point has been edited via  and .
        /// 
        /// 
        /// To subscribe to this event, use the following in your Form_Load method:
        /// zedGraphControl1.PointEditEvent +=
        /// new ZedGraphControl.PointEditHandler( MyPointEditHandler );
        /// Add this method to your Form1.cs:
        /// 
        ///    private string MyPointEditHandler( object sender, GraphPane pane, CurveItem curve, int iPt )
        ///    {
        ///        PointPair pt = curve[iPt];
        ///        return "This value is " + pt.Y.ToString("f2") + " gallons";
        ///    }
        /// 
        [Bindable(true), Category("Events"),
            Description("Subscribe to this event to respond to data point edit actions")]
        public event PointEditHandler PointEditEvent;
        /// 
        /// A delegate that allows custom formatting of the point value tooltips
        /// 
        /// The source  object
        /// The  object that contains the point value of interest
        /// The  object that contains the point value of interest
        /// The integer index of the selected  within the
        ///  of the selected 
        /// 
        public delegate string PointValueHandler(ZedGraphControl sender, GraphPane pane,
            CurveItem curve, int iPt);
        /// 
        /// Subscribe to this event to provide custom formatting for the tooltips
        /// 
        /// 
        /// To subscribe to this event, use the following in your FormLoad method:
        /// zedGraphControl1.PointValueEvent +=
        /// new ZedGraphControl.PointValueHandler( MyPointValueHandler );
        /// Add this method to your Form1.cs:
        /// 
        ///    private string MyPointValueHandler( object sender, GraphPane pane, CurveItem curve, int iPt )
        ///    {
        ///    #region
        ///        PointPair pt = curve[iPt];
        ///        return "This value is " + pt.Y.ToString("f2") + " gallons";
        ///    #endregion
        ///    }
        /// 
        [Bindable(true), Category("Events"),
            Description("Subscribe to this event to provide custom-formatting for data point tooltips")]
        public event PointValueHandler PointValueEvent;
        /// 
        /// A delegate that allows notification of mouse events on Graph objects.
        /// 
        /// The source  object
        /// A  corresponding to this event
        /// 
        /// 
        /// Return true if you have handled the mouse event entirely, and you do not
        /// want the  to do any further action (e.g., starting
        /// a zoom operation).  Return false if ZedGraph should go ahead and process the
        /// mouse event.
        /// 
        public delegate bool ZedMouseEventHandler(ZedGraphControl sender, MouseEventArgs e);
        /// 
        /// Subscribe to this event to provide notification of MouseDown clicks on graph
        /// objects
        /// 
        /// 
        /// This event provides for a notification when the mouse is clicked on an object
        /// within any  of the  associated
        /// with this .  This event will use the
        ///  method to determine which object
        /// was clicked.  The boolean value that you return from this handler determines whether
        /// or not the  will do any further handling of the
        /// MouseDown event (see ).  Return true if you have
        /// handled the MouseDown event entirely, and you do not
        /// want the  to do any further action (e.g., starting
        /// a zoom operation).  Return false if ZedGraph should go ahead and process the
        /// MouseDown event.
        /// 
        [Bindable(true), Category("Events"),
            Description("Subscribe to be notified when the left mouse button is clicked down")]
        public event ZedMouseEventHandler MouseDownEvent;
        /// 
        /// Hide the standard control MouseDown event so that the ZedGraphControl.MouseDownEvent
        /// can be used.  This is so that the user must return true/false in order to indicate
        /// whether or not we should respond to the event.
        /// 
        [Bindable(false), Browsable(false)]
        public new event MouseEventHandler MouseDown;
        /// 
        /// Hide the standard control MouseUp event so that the ZedGraphControl.MouseUpEvent
        /// can be used.  This is so that the user must return true/false in order to indicate
        /// whether or not we should respond to the event.
        /// 
        [Bindable(false), Browsable(false)]
        public new event MouseEventHandler MouseUp;
        /// 
        /// Hide the standard control MouseMove event so that the ZedGraphControl.MouseMoveEvent
        /// can be used.  This is so that the user must return true/false in order to indicate
        /// whether or not we should respond to the event.
        /// 
        [Bindable(false), Browsable(false)]
        private new event MouseEventHandler MouseMove;
        /// 
        /// Subscribe to this event to provide notification of MouseUp clicks on graph
        /// objects
        /// 
        /// 
        /// This event provides for a notification when the mouse is clicked on an object
        /// within any  of the  associated
        /// with this .  This event will use the
        ///  method to determine which object
        /// was clicked.  The boolean value that you return from this handler determines whether
        /// or not the  will do any further handling of the
        /// MouseUp event (see ).  Return true if you have
        /// handled the MouseUp event entirely, and you do not
        /// want the  to do any further action (e.g., starting
        /// a zoom operation).  Return false if ZedGraph should go ahead and process the
        /// MouseUp event.
        /// 
        [Bindable(true), Category("Events"),
            Description("Subscribe to be notified when the left mouse button is released")]
        public event ZedMouseEventHandler MouseUpEvent;
        /// 
        /// Subscribe to this event to provide notification of MouseMove events over graph
        /// objects
        /// 
        /// 
        /// This event provides for a notification when the mouse is moving over on the control.
        /// The boolean value that you return from this handler determines whether
        /// or not the  will do any further handling of the
        /// MouseMove event (see ).  Return true if you
        /// have handled the MouseMove event entirely, and you do not
        /// want the  to do any further action.
        /// Return false if ZedGraph should go ahead and process the MouseMove event.
        /// 
        [Bindable(true), Category("Events"),
            Description("Subscribe to be notified when the mouse is moved inside the control")]
        public event ZedMouseEventHandler MouseMoveEvent;
        /// 
        /// Subscribe to this event to provide notification of Double Clicks on graph
        /// objects
        /// 
        /// 
        /// This event provides for a notification when the mouse is double-clicked on an object
        /// within any  of the  associated
        /// with this .  This event will use the
        ///  method to determine which object
        /// was clicked.  The boolean value that you return from this handler determines whether
        /// or not the  will do any further handling of the
        /// DoubleClick event (see ).  Return true if you have
        /// handled the DoubleClick event entirely, and you do not
        /// want the  to do any further action. 
        /// Return false if ZedGraph should go ahead and process the
        /// DoubleClick event.
        /// 
        [Bindable(true), Category("Events"),
            Description("Subscribe to be notified when the left mouse button is double-clicked")]
        public event ZedMouseEventHandler DoubleClickEvent;
        /// 
        /// A delegate that allows notification of clicks on ZedGraph objects that have
        /// active links enabled
        /// 
        /// The source  object
        /// The source  in which the click
        /// occurred.
        /// 
        /// The source object which was clicked.  This is typically
        /// a type of  if a curve point was clicked, or
        /// a type of  if a graph object was clicked.
        /// 
        /// The  object, belonging to
        /// , that contains the link information
        /// 
        /// An index value, typically used if a 
        /// was clicked, indicating the ordinal value of the actual point that was clicked.
        /// 
        /// 
        /// Return true if you have handled the LinkEvent entirely, and you do not
        /// want the  to do any further action.
        /// Return false if ZedGraph should go ahead and process the LinkEvent.
        /// 
        public delegate bool LinkEventHandler(ZedGraphControl sender, GraphPane pane,
            object source, Link link, int index);
        /// 
        /// Subscribe to this event to be able to respond to mouse clicks within linked
        /// objects.
        /// 
        /// 
        /// Linked objects are typically either  type objects or
        ///  type objects.  These object types can include
        /// hyperlink information allowing for "drill-down" type operation.  
        /// 
        /// 
        /// 
        /// CurveItem.Link
        /// GraphObj.Link
        // /// 
        [Bindable(true), Category("Events"),
            Description("Subscribe to be notified when a link-enabled item is clicked")]
        public event LinkEventHandler LinkEvent;
        #endregion
        #region Constructors
        /// 
        /// Default Constructor
        /// 
        public ZedGraphControl()
        {
            InitializeComponent();
            // These commands do nothing, but they get rid of the compiler warnings for
            // unused events
            bool b = MouseDown == null || MouseUp == null || MouseMove == null;
            // Link in these events from the base class, since we disable them from this class.
            base.MouseDown += new System.Windows.Forms.MouseEventHandler(this.ZedGraphControl_MouseDown);
            base.MouseUp += new System.Windows.Forms.MouseEventHandler(this.ZedGraphControl_MouseUp);
            base.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ZedGraphControl_MouseMove);
            //this.MouseWheel += new System.Windows.Forms.MouseEventHandler( this.ZedGraphControl_MouseWheel );
            // Use double-buffering for flicker-free updating:
            SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint
                | ControlStyles.DoubleBuffer | ControlStyles.ResizeRedraw, true);
            //isTransparentBackground = false;
            //SetStyle( ControlStyles.Opaque, false );
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            //this.BackColor = Color.Transparent;
            _resourceManager = new ResourceManager("DrawGraph.ZedGraphLocale",
                Assembly.GetExecutingAssembly());
            Rectangle rect = new Rectangle(0, 0, this.Size.Width, this.Size.Height);
            _masterPane = new MasterPane("", rect);
            _masterPane.Margin.All = 0;
            _masterPane.Title.IsVisible = false;
            string titleStr = _resourceManager.GetString("title_def");
            string xStr = _resourceManager.GetString("x_title_def");
            string yStr = _resourceManager.GetString("y_title_def");
            //GraphPane graphPane = new GraphPane( rect, "Title", "X Axis", "Y Axis" );
            GraphPane graphPane = new GraphPane(rect, titleStr, xStr, yStr);
            using (Graphics g = this.CreateGraphics())
            {
                graphPane.AxisChange(g);
                //g.Dispose();
            }
            _masterPane.Add(graphPane);
            this.hScrollBar1.Minimum = 0;
            this.hScrollBar1.Maximum = 100;
            this.hScrollBar1.Value = 0;
            this.vScrollBar1.Minimum = 0;
            this.vScrollBar1.Maximum = 100;
            this.vScrollBar1.Value = 0;
            _xScrollRange = new ScrollRange(true);
            _yScrollRangeList = new ScrollRangeList();
            _y2ScrollRangeList = new ScrollRangeList();
            _yScrollRangeList.Add(new ScrollRange(true));
            _y2ScrollRangeList.Add(new ScrollRange(false));
            _zoomState = null;
            _zoomStateStack = new ZoomStateStack();
        }
        /// 
        /// Clean up any resources being used.
        /// 
        /// true if the components should be
        /// disposed, false otherwise
        protected override void Dispose(bool disposing)
        {
            lock (this)
            {
                if (disposing)
                {
                    if (components != null)
                        components.Dispose();
                }
                base.Dispose(disposing);
                _masterPane = null;
            }
        }
        #endregion
        #region Properties
        /// 
        /// Gets or sets the  property for the control
        /// 
        [Bindable(false), Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public MasterPane MasterPane
        {
            get { lock (this) return _masterPane; }
            set { lock (this) _masterPane = value; }
        }
        // Testing for Designer attribute
        /*
		Class1 _class1 = null;
		[ Bindable( true ), Browsable( true ), Category( "Data" ), NotifyParentProperty( true ),
			DesignerSerializationVisibility( DesignerSerializationVisibility.Content ),
			Description( "My Class1 Test" )]
		public Class1 Class1
		{
			get { if ( _class1 == null ) _class1 = new Class1(); return _class1; }
			set { _class1 = value; }
		}
		*/
        /// 
        /// Gets or sets the  property for the control
        /// 
        /// 
        ///  actually uses a  object
        /// to hold a list of  objects.  This property really only
        /// accesses the first  in the list.  If there is more
        /// than one , use the 
        /// indexer property to access any of the  objects.
        [
            Bindable(false), Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
        ]
        //[
        //	Bindable( true ), Browsable( true ), Category( "Data" ), NotifyParentProperty( true ),
        //	AttributeProvider( typeof( GraphPane ) ),
        //	Description("Access to the primary GraphPane object associated with this control")
        //]
        public GraphPane GraphPane
        {
            get
            {
                // Just return the first GraphPane in the list
                lock (this)
                {
                    if (_masterPane != null && _masterPane.PaneList.Count > 0)
                        return _masterPane[0];
                    else
                        return null;
                }
            }
            set
            {
                lock (this)
                {
                    //Clear the list, and replace it with the specified Graphpane
                    if (_masterPane != null)
                    {
                        _masterPane.PaneList.Clear();
                        _masterPane.Add(value);
                    }
                }
            }
        }
        /// 
        /// Gets or sets a value that determines if all drawing operations for this control
        /// will be forced to operate in Anti-alias mode.  Note that if this value is set to
        /// "true", it overrides the setting for sub-objects.  Otherwise, the sub-object settings
        /// (such as )
        /// will be honored.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
         DefaultValue(false),
         Description("true to force all objects to be draw in anti-alias mode")]
        public bool IsAntiAlias
        {
            get { return _isAntiAlias; }
            set { _isAntiAlias = value; }
        }
        /// 
        /// Gets or sets a value that determines whether or not tooltips will be displayed
        /// when the mouse hovers over data values.
        /// 
        /// The displayed values are taken from 
        /// if it is a  type, or 
        /// otherwise (using the  as a format string).
        /// Additionally, the user can custom format the values using the
        ///  event.  Note that 
        /// may be overridden by .
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(false),
            Description("true to display tooltips when the mouse hovers over data points")]
        public bool IsShowPointValues
        {
            get { return _isShowPointValues; }
            set { _isShowPointValues = value; }
        }
        /// 
        /// Gets or sets a value that determines whether or not tooltips will be displayed
        /// showing the current scale values when the mouse is within the
        /// .
        /// 
        /// The displayed values are taken from the current mouse position, and formatted
        /// according to  and/or .  If this
        /// value is set to true, it overrides the  setting.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(false),
            Description("true to display tooltips showing the current mouse position within the Chart area")]
        public bool IsShowCursorValues
        {
            get { return _isShowCursorValues; }
            set { _isShowCursorValues = value; }
        }
        /// 
        /// Gets or sets a value that determines whether or not editing of point data is allowed in
        /// the horizontal direction.
        /// 
        /// 
        /// Editing is done by holding down the Alt key, and left-clicking on an individual point of
        /// a given  to drag it to a new location.  The Mouse and Key
        /// combination for this mode are modifiable using  and
        /// .
        /// 
        /// 
        /// 
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(false),
            Description("true to allow horizontal editing by alt-left-click-drag")]
        public bool IsEnableHEdit
        {
            get { return _isEnableHEdit; }
            set { _isEnableHEdit = value; }
        }
        /// 
        /// Gets or sets a value that determines whether or not editing of point data is allowed in
        /// the vertical direction.
        /// 
        /// 
        /// Editing is done by holding down the Alt key, and left-clicking on an individual point of
        /// a given  to drag it to a new location.  The Mouse and Key
        /// combination for this mode are modifiable using  and
        /// .
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(false),
            Description("true to allow vertical editing by alt-left-click-drag")]
        public bool IsEnableVEdit
        {
            get { return _isEnableVEdit; }
            set { _isEnableVEdit = value; }
        }
        /// 
        /// Gets or sets a value that determines whether or not zooming is allowed for the control.
        /// 
        /// 
        /// Zooming is done by left-clicking inside the  to drag
        /// out a rectangle, indicating the new scale ranges that will be part of the graph.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(true),
            Description("true to allow horizontal and vertical zooming by left-click-drag")]
        public bool IsEnableZoom
        {
            set { _isEnableHZoom = value; _isEnableVZoom = value; }
        }
        /// 
        /// Gets or sets a value that determines whether or not zooming is allowed for the control in
        /// the horizontal direction.
        /// 
        /// 
        /// Zooming is done by left-clicking inside the  to drag
        /// out a rectangle, indicating the new scale ranges that will be part of the graph.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(true),
            Description("true to allow horizontal zooming by left-click-drag")]
        public bool IsEnableHZoom
        {
            get { return _isEnableHZoom; }
            set { _isEnableHZoom = value; }
        }
        /// 
        /// Gets or sets a value that determines whether or not zooming is allowed for the control in
        /// the vertical direction.
        /// 
        /// 
        /// Zooming is done by left-clicking inside the  to drag
        /// out a rectangle, indicating the new scale ranges that will be part of the graph.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(true),
            Description("true to allow vertical zooming by left-click-drag")]
        public bool IsEnableVZoom
        {
            get { return _isEnableVZoom; }
            set { _isEnableVZoom = value; }
        }
        /// 
        /// Gets or sets a value that determines whether or not panning is allowed for the control in
        /// the horizontal direction.
        /// 
        /// 
        /// Panning is done by clicking the middle mouse button (or holding down the shift key
        /// while clicking the left mouse button) inside the  and
        /// dragging the mouse around to shift the scale ranges as desired.
        /// 
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(true),
            Description("true to allow horizontal panning by middle-mouse-drag or shift-left-drag")]
        public bool IsEnableHPan
        {
            get { return _isEnableHPan; }
            set { _isEnableHPan = value; }
        }
        /// 
        /// Gets or sets a value that determines whether or not panning is allowed for the control in
        /// the vertical direction.
        /// 
        /// 
        /// Panning is done by clicking the middle mouse button (or holding down the shift key
        /// while clicking the left mouse button) inside the  and
        /// dragging the mouse around to shift the scale ranges as desired.
        /// 
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(true),
            Description("true to allow vertical panning by middle-mouse-drag or shift-left-drag")]
        public bool IsEnableVPan
        {
            get { return _isEnableVPan; }
            set { _isEnableVPan = value; }
        }
        /// 
        /// Gets or sets a value that determines whether or not the context menu will be available.
        /// 
        /// The context menu is a menu that appears when you right-click on the
        /// .  It provides options for Zoom, Pan, AutoScale, Clipboard
        /// Copy, and toggle .
        /// 
        /// true to allow the context menu, false to disable it
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(true),
            Description("true to enable the right mouse button context menu")]
        public bool IsShowContextMenu
        {
            get { return _isShowContextMenu; }
            set { _isShowContextMenu = value; }
        }
        /// 
        /// Gets or sets a value that determines whether or not a message box will be shown
        /// in response to a context menu "Copy" command.
        /// 
        /// 
        /// Note that, if this property is set to false, the user will receive no
        /// indicative feedback in response to a Copy action.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
         DefaultValue(true),
         Description("true to show a message box after a 'Copy' context menu action completes")]
        public bool IsShowCopyMessage
        {
            get { return _isShowCopyMessage; }
            set { _isShowCopyMessage = value; }
        }
        /// 
        /// Gets or sets the  instance that will be used
        /// by the "Save As..." context menu item.
        /// 
        /// 
        /// This provides the opportunity to modify the dialog, such as setting the
        ///  property.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
         DefaultValue(true),
         Description("Provides access to the SaveFileDialog for the 'Save As' menu item")]
        public SaveFileDialog SaveFileDialog
        {
            get { return _saveFileDialog; }
            set { _saveFileDialog = value; }
        }
        /// 
        /// Gets or sets a value that determines whether or not the visible aspect ratio of the
        ///   will be preserved
        /// when printing this .
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(true),
            Description("true to preserve the displayed aspect ratio when printing")]
        public bool IsPrintKeepAspectRatio
        {
            get { return _isPrintKeepAspectRatio; }
            set { _isPrintKeepAspectRatio = value; }
        }
        /// 
        /// Gets or sets a value that determines whether or not the 
        ///  dimensions will be expanded to fill the
        /// available space when printing this .
        /// 
        /// 
        /// If  is also true, then the 
        ///  dimensions will be expanded to fit as large
        /// a space as possible while still honoring the visible aspect ratio.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(true),
            Description("true to resize to fill the page when printing")]
        public bool IsPrintFillPage
        {
            get { return _isPrintFillPage; }
            set { _isPrintFillPage = value; }
        }
        /// 
        /// Gets or sets a value that determines whether the settings of
        ///  and 
        /// will be overridden to true during printing operations.
        /// 
        /// 
        /// Printing involves pixel maps that are typically of a dramatically different dimension
        /// than on-screen pixel maps.  Therefore, it becomes more important to scale the fonts and
        /// lines to give a printed image that looks like what is shown on-screen.  The default
        /// setting for  is true, but the default
        /// setting for  is false.
        /// 
        /// 
        /// A value of true will cause both  and
        ///  to be temporarily set to true during
        /// printing operations.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(true),
            Description("true to force font and pen width scaling when printing")]
        public bool IsPrintScaleAll
        {
            get { return _isPrintScaleAll; }
            set { _isPrintScaleAll = value; }
        }
        /// 
        /// Gets or sets a value that controls whether or not the axis value range for the scroll
        /// bars will be set automatically.
        /// 
        /// 
        /// If this value is set to true, then the range of the scroll bars will be set automatically
        /// to the actual range of the data as returned by  at the
        /// time that  was last called.  Note that a value of true
        /// can override any setting of , ,
        /// , , 
        /// , and .  Note also that you must
        /// call  from the  for this to
        /// work properly (e.g., don't call it directly from the .
        /// Alternatively, you can call  at anytime to set
        /// the scroll bar range.
        /// In most cases, you will probably want to disable
        ///  before activating this option.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(false),
            Description("true to automatically set the scroll bar range to the actual data range")]
        public bool IsAutoScrollRange
        {
            get { return _isAutoScrollRange; }
            set { _isAutoScrollRange = value; }
        }
        /// 
        /// Set a "grace" value that leaves a buffer area around the data when
        ///  is true.
        /// 
        /// 
        /// This value represents a fraction of the total range around each axis.  For example, if the
        /// axis ranges from 0 to 100, then a 0.05 value for ScrollGrace would set the scroll range
        /// to -5 to 105.
        /// 
        public double ScrollGrace
        {
            get { return _scrollGrace; }
            set { _scrollGrace = value; }
        }
        /// 
        /// Gets or sets a value that determines if the horizontal scroll bar will be visible.
        /// 
        /// This scroll bar allows the display to be scrolled in the horizontal direction.
        /// Another option is display panning, in which the user can move the display around by
        /// clicking directly on it and dragging (see  and ).
        /// You can control the available range of scrolling with the  and
        ///  properties.  Note that the scroll range can be set automatically by
        /// .
        /// In most cases, you will probably want to disable
        ///  before activating this option.
        /// 
        /// A boolean value.  true to display a horizontal scrollbar, false otherwise.
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(false),
            Description("true to display the horizontal scroll bar")]
        public bool IsShowHScrollBar
        {
            get { return _isShowHScrollBar; }
            set { _isShowHScrollBar = value; ZedGraphControl_ReSize(this, new EventArgs()); }
        }
        /// 
        /// Gets or sets a value that determines if the vertical scroll bar will be visible.
        /// 
        /// This scroll bar allows the display to be scrolled in the vertical direction.
        /// Another option is display panning, in which the user can move the display around by
        /// clicking directly on it and dragging (see  and ).
        /// You can control the available range of scrolling with the  and
        ///  properties.
        /// Note that the vertical scroll bar only affects the ; it has no impact on
        /// the .  The panning options affect both the  and
        /// .  Note also that the scroll range can be set automatically by
        /// .
        /// In most cases, you will probably want to disable
        ///  before activating this option.
        /// 
        /// A boolean value.  true to display a vertical scrollbar, false otherwise.
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(false),
            Description("true to display the vertical scroll bar")]
        public bool IsShowVScrollBar
        {
            get { return _isShowVScrollBar; }
            set { _isShowVScrollBar = value; ZedGraphControl_ReSize(this, new EventArgs()); }
        }
        /// 
        /// Gets or sets a value that determines if the  
        /// ranges for all  objects in the  will
        /// be forced to match.
        /// 
        /// 
        /// If set to true (default is false), then all of the  objects
        /// in the  associated with this 
        /// will be forced to have matching scale ranges for the x axis.  That is, zoom, pan,
        /// and scroll operations will result in zoom/pan/scroll for all graphpanes simultaneously.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(false),
            Description("true to force the X axis ranges for all GraphPanes to match")]
        public bool IsSynchronizeXAxes
        {
            get { return _isSynchronizeXAxes; }
            set
            {
                if (_isSynchronizeXAxes != value)
                    ZoomStatePurge();
                _isSynchronizeXAxes = value;
            }
        }
        /// 
        /// Gets or sets a value that determines if the  
        /// ranges for all  objects in the  will
        /// be forced to match.
        /// 
        /// 
        /// If set to true (default is false), then all of the  objects
        /// in the  associated with this 
        /// will be forced to have matching scale ranges for the y axis.  That is, zoom, pan,
        /// and scroll operations will result in zoom/pan/scroll for all graphpanes simultaneously.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(false),
            Description("true to force the Y axis ranges for all GraphPanes to match")]
        public bool IsSynchronizeYAxes
        {
            get { return _isSynchronizeYAxes; }
            set
            {
                if (_isSynchronizeYAxes != value)
                    ZoomStatePurge();
                _isSynchronizeYAxes = value;
            }
        }
        /// 
        /// Gets or sets a value that determines if the vertical scroll bar will affect the Y2 axis.
        /// 
        /// 
        /// The vertical scroll bar is automatically associated with the Y axis.  With this value, you
        /// can choose to include or exclude the Y2 axis with the scrolling.  Note that the Y2 axis
        /// scrolling is handled as a secondary.  The vertical scroll bar position always reflects
        /// the status of the Y axis.  This can cause the Y2 axis to "jump" when first scrolled if
        /// the  and  values are not set to the
        /// same proportions as  and  with respect
        /// to the actual  and .  Also note that
        /// this property is actually just an alias to the 
        /// property of the first element of .
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(false),
            Description("true to scroll the Y2 axis along with the Y axis")]
        public bool IsScrollY2
        {
            get
            {
                if (_y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0)
                    return _y2ScrollRangeList[0].IsScrollable;
                else
                    return false;
            }
            set
            {
                if (_y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0)
                {
                    ScrollRange tmp = _y2ScrollRangeList[0];
                    tmp.IsScrollable = value;
                    _y2ScrollRangeList[0] = tmp;
                }
            }
        }
        /// 
        /// Access the  for the Y axes.
        /// 
        /// 
        /// This list maintains the user scale ranges for the scroll bars for each axis
        /// in the .  Each ordinal location in
        ///  corresponds to an equivalent ordinal location
        /// in .
        /// 
        /// 
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true)]
        [Description("Sets the manual scroll bar ranges for the collection of Y axes")]
        public ScrollRangeList YScrollRangeList
        {
            get { return _yScrollRangeList; }
        }
        /// 
        /// Access the  for the Y2 axes.
        /// 
        /// 
        /// This list maintains the user scale ranges for the scroll bars for each axis
        /// in the .  Each ordinal location in
        ///  corresponds to an equivalent ordinal location
        /// in .
        /// 
        /// 
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true)]
        [Description("Sets the manual scroll bar ranges for the collection of Y2 axes")]
        public ScrollRangeList Y2ScrollRangeList
        {
            get { return _y2ScrollRangeList; }
        }
        /// 
        /// The minimum value for the X axis scroll range.
        /// 
        /// 
        /// Effectively, the minimum endpoint of the scroll range will cause the
        ///  value to be set to .  Note that this
        /// value applies only to the scroll bar settings.  Axis panning (see )
        /// is not affected by this value.  Note that this value can be overridden by
        ///  and .
        /// 
        /// A double value indicating the minimum axis value
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(0),
            Description("Sets the manual scroll minimum value for the X axis")]
        public double ScrollMinX
        {
            get { return _xScrollRange.Min; }
            set { _xScrollRange.Min = value; }
        }
        /// 
        /// The maximum value for the X axis scroll range.
        /// 
        /// 
        /// Effectively, the maximum endpoint of the scroll range will cause the
        ///  value to be set to .  Note that this
        /// value applies only to the scroll bar settings.  Axis panning (see )
        /// is not affected by this value.  Note that this value can be overridden by
        ///  and .
        /// 
        /// A double value indicating the maximum axis value
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(0),
            Description("Sets the manual scroll maximum value for the X axis")]
        public double ScrollMaxX
        {
            get { return _xScrollRange.Max; }
            set { _xScrollRange.Max = value; }
        }
        /// 
        /// The minimum value for the Y axis scroll range.
        /// 
        /// 
        /// Effectively, the minimum endpoint of the scroll range will cause the
        ///  value to be set to .  Note that this
        /// value applies only to the scroll bar settings.  Axis panning (see )
        /// is not affected by this value.  Note that this value can be overridden by
        ///  and .  Also note that
        /// this property is actually just an alias to the 
        /// property of the first element of .
        /// 
        /// A double value indicating the minimum axis value
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(0),
            Description("Sets the manual scroll minimum value for the Y axis")]
        public double ScrollMinY
        {
            get
            {
                if (_yScrollRangeList != null && _yScrollRangeList.Count > 0)
                    return _yScrollRangeList[0].Min;
                else
                    return double.NaN;
            }
            set
            {
                if (_yScrollRangeList != null && _yScrollRangeList.Count > 0)
                {
                    ScrollRange tmp = _yScrollRangeList[0];
                    tmp.Min = value;
                    _yScrollRangeList[0] = tmp;
                }
            }
        }
        /// 
        /// The maximum value for the Y axis scroll range.
        /// 
        /// 
        /// Effectively, the maximum endpoint of the scroll range will cause the
        ///  value to be set to .  Note that this
        /// value applies only to the scroll bar settings.  Axis panning (see )
        /// is not affected by this value.  Note that this value can be overridden by
        ///  and .  Also note that
        /// this property is actually just an alias to the 
        /// property of the first element of .
        /// 
        /// A double value indicating the maximum axis value
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(0),
            Description("Sets the manual scroll maximum value for the Y axis")]
        public double ScrollMaxY
        {
            get
            {
                if (_yScrollRangeList != null && _yScrollRangeList.Count > 0)
                    return _yScrollRangeList[0].Max;
                else
                    return double.NaN;
            }
            set
            {
                if (_yScrollRangeList != null && _yScrollRangeList.Count > 0)
                {
                    ScrollRange tmp = _yScrollRangeList[0];
                    tmp.Max = value;
                    _yScrollRangeList[0] = tmp;
                }
            }
        }
        /// 
        /// The minimum value for the Y2 axis scroll range.
        /// 
        /// 
        /// Effectively, the minimum endpoint of the scroll range will cause the
        ///  value to be set to .  Note that this
        /// value applies only to the scroll bar settings.  Axis panning (see )
        /// is not affected by this value.  Note that this value can be overridden by
        ///  and .  Also note that
        /// this property is actually just an alias to the 
        /// property of the first element of .
        /// 
        /// A double value indicating the minimum axis value
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(0),
            Description("Sets the manual scroll minimum value for the Y2 axis")]
        public double ScrollMinY2
        {
            get
            {
                if (_y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0)
                    return _y2ScrollRangeList[0].Min;
                else
                    return double.NaN;
            }
            set
            {
                if (_y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0)
                {
                    ScrollRange tmp = _y2ScrollRangeList[0];
                    tmp.Min = value;
                    _y2ScrollRangeList[0] = tmp;
                }
            }
        }
        /// 
        /// The maximum value for the Y2 axis scroll range.
        /// 
        /// 
        /// Effectively, the maximum endpoint of the scroll range will cause the
        ///  value to be set to .  Note that this
        /// value applies only to the scroll bar settings.  Axis panning (see )
        /// is not affected by this value.  Note that this value can be overridden by
        ///  and .  Also note that
        /// this property is actually just an alias to the 
        /// property of the first element of .
        /// 
        /// A double value indicating the maximum axis value
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(0),
            Description("Sets the manual scroll maximum value for the Y2 axis")]
        public double ScrollMaxY2
        {
            get
            {
                if (_y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0)
                    return _y2ScrollRangeList[0].Max;
                else
                    return double.NaN;
            }
            set
            {
                if (_y2ScrollRangeList != null && _y2ScrollRangeList.Count > 0)
                {
                    ScrollRange tmp = _y2ScrollRangeList[0];
                    tmp.Max = value;
                    _y2ScrollRangeList[0] = tmp;
                }
            }
        }
        /// 
        /// Returns true if the user is currently scrolling via the scrollbar, or
        /// false if no scrolling is taking place.
        /// 
        /// 
        /// This method just tests ScrollBar.Capture to see if the
        /// mouse has been captured by the scroll bar.  If so, scrolling is active.
        /// 
        public Boolean IsScrolling
        {
            get
            {
                if (hScrollBar1 != null && vScrollBar1 != null)
                    return hScrollBar1.Capture || vScrollBar1.Capture;
                else
                    return false;
            }
        }
        /// 
        /// Gets or sets the format for displaying tooltip values.
        /// This format is passed to .
        /// 
        /// 
        /// Use the  type
        /// to determine the format strings.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(PointPair.DefaultFormat),
            Description("Sets the numeric display format string for the point value tooltips")]
        public string PointValueFormat
        {
            get { return _pointValueFormat; }
            set { _pointValueFormat = value; }
        }
        /// 
        /// Gets or sets the format for displaying tooltip values.
        /// This format is passed to .
        /// 
        /// 
        /// Use the  type
        /// to determine the format strings.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(XDate.DefaultFormatStr),
            Description("Sets the date display format for the point value tooltips")]
        public string PointDateFormat
        {
            get { return _pointDateFormat; }
            set { _pointDateFormat = value; }
        }
        /// 
        /// Gets or sets the step size fraction for zooming with the mouse wheel.
        /// A value of 0.1 will result in a 10% zoom step for each mouse wheel movement.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(0.1),
            Description("Sets the step size fraction for zooming with the mouse wheel")]
        public double ZoomStepFraction
        {
            get { return _zoomStepFraction; }
            set { _zoomStepFraction = value; }
        }
        /// 
        /// Gets or sets a boolean value that determines if zooming with the wheel mouse
        /// is centered on the mouse location, or centered on the existing graph.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(false),
            Description("true to center the mouse wheel zoom at the current mouse location")]
        public bool IsZoomOnMouseCenter
        {
            get { return _isZoomOnMouseCenter; }
            set { _isZoomOnMouseCenter = value; }
        }
        /// 
        /// Gets the graph pane's current image.
        /// 
        /// 
        /// 
        /// When the control has been disposed before this call.
        /// 
        [Bindable(false), Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Image GetImage()
        {
            lock (this)
            {
                if (BeenDisposed || _masterPane == null || _masterPane[0] == null)
                    throw new ZException("The control has been disposed");
                return _masterPane.GetImage();
            }
        }
        /// 
        /// This checks if the control has been disposed.  This is synonymous with
        /// the graph pane having been nulled or disposed.  Therefore this is the
        /// same as ZedGraphControl.GraphPane == null.
        /// 
        [Bindable(false), Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool BeenDisposed
        {
            get
            {
                lock (this) return _masterPane == null;
            }
        }
        // Revision: JCarpenter 10/06
        /// 
        /// Readonly property that gets the list of selected CurveItems
        /// 
        public Selection Selection
        {
            get { return _selection; }
        }
        /// 
        /// Gets or sets a value that determines whether or not selection is allowed for the control.
        /// 
        [Bindable(true), Category("Display"), NotifyParentProperty(true),
            DefaultValue(false),
            Description("true to allow selecting Curves")]
        public bool IsEnableSelection
        {
            get { return _isEnableSelection; }
            set
            {
                _isEnableSelection = value;
                /*
				if ( value )
				{
					this.Cursor = Cursors.Default;
					this.IsEnableZoom = false;
				}
				else
				{
					this.Cursor = Cursors.Cross;
					this.IsEnableZoom = true;
				}
				*/
            }
        }
        #endregion
        #region Methods
        /// 
        /// Called by the system to update the control on-screen
        /// 
        /// 
        /// A PaintEventArgs object containing the Graphics specifications
        /// for this Paint event.
        /// 
        protected override void OnPaint(PaintEventArgs e)
        {
            lock (this)
            {
                if (BeenDisposed || _masterPane == null || this.GraphPane == null)
                    return;
                if (hScrollBar1 != null && this.GraphPane != null &&
                    vScrollBar1 != null && _yScrollRangeList != null)
                {
                    SetScroll(hScrollBar1, this.GraphPane.XAxis, _xScrollRange.Min, _xScrollRange.Max);
                    SetScroll(vScrollBar1, this.GraphPane.YAxis, _yScrollRangeList[0].Min,
                        _yScrollRangeList[0].Max);
                }
                SmoothingMode sModeSave = e.Graphics.SmoothingMode;
                TextRenderingHint sHintSave = e.Graphics.TextRenderingHint;
                if (_isAntiAlias)
                {
                    e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
                    e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
                }
                base.OnPaint(e);
                // Add a try/catch pair since the users of the control can't catch this one
                try { _masterPane.Draw(e.Graphics); }
                catch { }
                e.Graphics.SmoothingMode = sModeSave;
                e.Graphics.TextRenderingHint = sHintSave;
            }
            /*
                        // first, see if an old thread is still running
                        if ( t != null && t.IsAlive )
                        {
                            t.Abort();
                        }
                        //dt = new DrawingThread( e.Graphics, _masterPane );
                        //g = e.Graphics;
                        // Fire off the new thread
                        t = new Thread( new ParameterizedThreadStart( DoDrawingThread ) );
                        //ct.ApartmentState = ApartmentState.STA;
                        //ct.SetApartmentState( ApartmentState.STA );
                        DrawingThreadData dtd;
                        dtd._g = e.Graphics;
                        dtd._masterPane = _masterPane;
                        t.Start( dtd );
                        //ct.Join();
            */
        }
        //		Thread t = null;
        //DrawingThread dt = null;
        /*
                /// 
                /// 
                /// 
                /// 
                public void DoDrawingThread( object dtdobj )
                {
                    try
                    {
                        DrawingThreadData dtd = (DrawingThreadData) dtdobj;
                        if ( dtd._g != null && dtd._masterPane != null )
                            dtd._masterPane.Draw( dtd._g );
                        //				else
                        //				{
                        //					using ( Graphics g2 = CreateGraphics() )
                        //						_masterPane.Draw( g2 );
                        //				}
                    }
                    catch
                    {
                    }
                }
        */
        /// 
        /// Called when the control has been resized.
        /// 
        /// 
        /// A reference to the control that has been resized.
        /// 
        /// 
        /// An EventArgs object.
        /// 
        protected void ZedGraphControl_ReSize(object sender, System.EventArgs e)
        {
            lock (this)
            {
                if (BeenDisposed || _masterPane == null)
                    return;
                Size newSize = this.Size;
                if (_isShowHScrollBar)
                {
                    hScrollBar1.Visible = true;
                    newSize.Height -= this.hScrollBar1.Size.Height;
                    hScrollBar1.Location = new Point(0, newSize.Height);
                    hScrollBar1.Size = new Size(newSize.Width, hScrollBar1.Height);
                }
                else
                    hScrollBar1.Visible = false;
                if (_isShowVScrollBar)
                {
                    vScrollBar1.Visible = true;
                    newSize.Width -= this.vScrollBar1.Size.Width;
                    vScrollBar1.Location = new Point(newSize.Width, 0);
                    vScrollBar1.Size = new Size(vScrollBar1.Width, newSize.Height);
                }
                else
                    vScrollBar1.Visible = false;
                using (Graphics g = this.CreateGraphics())
                {
                    _masterPane.ReSize(g, new RectangleF(0, 0, newSize.Width, newSize.Height));
                    //g.Dispose();
                }
                this.Invalidate();
            }
        }
        /// This performs an axis change command on the graphPane.
        /// 
        /// 
        /// This is the same as
        /// ZedGraphControl.GraphPane.AxisChange( ZedGraphControl.CreateGraphics() ), however,
        /// this method also calls  if 
        /// is true.
        /// 
        public virtual void AxisChange()
        {
            lock (this)
            {
                if (BeenDisposed || _masterPane == null)
                    return;
                using (Graphics g = this.CreateGraphics())
                {
                    _masterPane.AxisChange(g);
                    //g.Dispose();
                }
                if (_isAutoScrollRange)
                    SetScrollRangeFromData();
            }
        }
        #endregion
        #region Mouse Events
        /// 
        /// Handle a MouseDown event in the 
        /// 
        /// A reference to the 
        /// A  instance
        protected void ZedGraphControl_MouseDown(object sender, MouseEventArgs e)
        {
            _isPanning = false;
            _isZooming = false;
            _isEditing = false;
            _isSelecting = false;
            _dragPane = null;
            Point mousePt = new Point(e.X, e.Y);
            // Callback for doubleclick events
            if (_masterPane != null && e.Clicks > 1 && this.DoubleClickEvent != null)
            {
                if (this.DoubleClickEvent(this, e))
                    return;
            }
            // Provide Callback for MouseDown events
            if (_masterPane != null && this.MouseDownEvent != null)
            {
                if (this.MouseDownEvent(this, e))
                    return;
            }
            if (e.Clicks > 1 || _masterPane == null)
                return;
            // First, see if the click is within a Linkable object within any GraphPane
            GraphPane pane = this.MasterPane.FindPane(mousePt);
            if (pane != null &&
                    e.Button == _linkButtons && Control.ModifierKeys == _linkModifierKeys)
            {
                object source;
                Link link;
                int index;
                using (Graphics g = this.CreateGraphics())
                {
                    float scaleFactor = pane.CalcScaleFactor();
                    if (pane.FindLinkableObject(mousePt, g, scaleFactor, out source, out link, out index))
                    {
                        if (LinkEvent != null && LinkEvent(this, pane, source, link, index))
                            return;
                        string url;
                        CurveItem curve = source as CurveItem;
                        if (curve != null)
                            url = link.MakeCurveItemUrl(pane, curve, index);
                        else
                            url = link._url;
                        if (url != string.Empty)
                        {
                            System.Diagnostics.Process.Start(url);
                            // linkable objects override any other actions with mouse
                            return;
                        }
                    }
                    //g.Dispose();
                }
            }
            // Second, Check to see if it's within a Chart Rect
            pane = this.MasterPane.FindChartRect(mousePt);
            //Rectangle rect = new Rectangle( mousePt, new Size( 1, 1 ) );
            if (pane != null &&
                (_isEnableHPan || _isEnableVPan) &&
                ((e.Button == _panButtons && Control.ModifierKeys == _panModifierKeys) ||
                (e.Button == _panButtons2 && Control.ModifierKeys == _panModifierKeys2)))
            {
                _isPanning = true;
                _dragStartPt = mousePt;
                _dragPane = pane;
                //_zoomState = new ZoomState( _dragPane, ZoomState.StateType.Pan );
                ZoomStateSave(_dragPane, ZoomState.StateType.Pan);
            }
            else if (pane != null && (_isEnableHZoom || _isEnableVZoom) &&
                ((e.Button == _zoomButtons && Control.ModifierKeys == _zoomModifierKeys) ||
                (e.Button == _zoomButtons2 && Control.ModifierKeys == _zoomModifierKeys2)))
            {
                _isZooming = true;
                _dragStartPt = mousePt;
                _dragEndPt = mousePt;
                _dragEndPt.Offset(1, 1);
                _dragPane = pane;
                ZoomStateSave(_dragPane, ZoomState.StateType.Zoom);
            }
            //Revision: JCarpenter 10/06
            else if (pane != null && _isEnableSelection && e.Button == _selectButtons &&
                (Control.ModifierKeys == _selectModifierKeys ||
                    Control.ModifierKeys == _selectAppendModifierKeys))
            {
                _isSelecting = true;
                _dragStartPt = mousePt;
                _dragEndPt = mousePt;
                _dragEndPt.Offset(1, 1);
                _dragPane = pane;
            }
            else if (pane != null && (_isEnableHEdit || _isEnableVEdit) &&
                 (e.Button == EditButtons && Control.ModifierKeys == EditModifierKeys))
            {
                // find the point that was clicked, and make sure the point list is editable
                // and that it's a primary Y axis (the first Y or Y2 axis)
                if (pane.FindNearestPoint(mousePt, out _dragCurve, out _dragIndex) &&
                            _dragCurve.Points is IPointListEdit)
                {
                    _isEditing = true;
                    _dragPane = pane;
                    _dragStartPt = mousePt;
                    _dragStartPair = _dragCurve[_dragIndex];
                }
            }
        }
        /// 
        /// Set the cursor according to the current mouse location.
        /// 
        protected void SetCursor()
        {
            SetCursor(this.PointToClient(Control.MousePosition));
        }
        /// 
        /// Set the cursor according to the current mouse location.
        /// 
        protected void SetCursor(Point mousePt)
        {
            if (_masterPane != null)
            {
                GraphPane pane = _masterPane.FindChartRect(mousePt);
                if ((_isEnableHPan || _isEnableVPan) && (Control.ModifierKeys == Keys.Shift || _isPanning) &&
                    (pane != null || _isPanning))
                    this.Cursor = Cursors.Hand;
                else if ((_isEnableVZoom || _isEnableHZoom) && (pane != null || _isZooming))
                    this.Cursor = Cursors.Cross;
                else if (_isEnableSelection && (pane != null || _isSelecting))
                    this.Cursor = Cursors.Cross;
                else
                    this.Cursor = Cursors.Default;
                //			else if ( isZoomMode || isPanMode )
                //				this.Cursor = Cursors.No;
            }
        }
        /// 
        /// Handle a KeyUp event
        /// 
        /// The  in which the KeyUp occurred.
        /// A  instance.
        protected void ZedGraphControl_KeyUp(object sender, KeyEventArgs e)
        {
            SetCursor();
        }
        /// 
        /// Handle the Key Events so ZedGraph can Escape out of a panning or zooming operation.
        /// 
        /// 
        /// 
        protected void ZedGraphControl_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
        {
            SetCursor();
            if (e.KeyCode == Keys.Escape)
            {
                if (_isPanning)
                    HandlePanCancel();
                if (_isZooming)
                    HandleZoomCancel();
                if (_isEditing)
                    HandleEditCancel();
                //if ( _isSelecting )
                // Esc always cancels the selection
                HandleSelectionCancel();
                _isZooming = false;
                _isPanning = false;
                _isEditing = false;
                _isSelecting = false;
                Refresh();
            }
        }
        /// 
        /// Handle a MouseUp event in the 
        /// 
        /// A reference to the 
        /// A  instance
        protected void ZedGraphControl_MouseUp(object sender, MouseEventArgs e)
        {
            // Provide Callback for MouseUp events
            if (_masterPane != null && this.MouseUpEvent != null)
            {
                if (this.MouseUpEvent(this, e))
                    return;
            }
            if (_masterPane != null && _dragPane != null)
            {
                // If the MouseUp event occurs, the user is done dragging.
                if (_isZooming)
                    HandleZoomFinish(sender, e);
                else if (_isPanning)
                    HandlePanFinish();
                else if (_isEditing)
                    HandleEditFinish();
                //Revision: JCarpenter 10/06
                else if (_isSelecting)
                    HandleSelectionFinish(sender, e);
            }
            // Reset the rectangle.
            //dragStartPt = new Rectangle( 0, 0, 0, 0 );
            _dragPane = null;
            _isZooming = false;
            _isPanning = false;
            _isEditing = false;
            _isSelecting = false;
            Cursor.Current = Cursors.Default;
        }
        /// 
        /// Make a string label that corresponds to a user scale value.
        /// 
        /// The axis from which to obtain the scale value.  This determines
        /// if it's a date value, linear, log, etc.
        /// The value to be made into a label
        /// The ordinal position of the value
        /// true to override the ordinal settings of the axis,
        /// and prefer the actual value instead.
        /// The string label.
        protected string MakeValueLabel(Axis axis, double val, int iPt, bool isOverrideOrdinal)
        {
            if (axis != null)
            {
                if (axis.Scale.IsDate || axis.Scale.Type == AxisType.DateAsOrdinal)
                {
                    return XDate.ToString(val, _pointDateFormat);
                }
                else if (axis._scale.IsText && axis._scale._textLabels != null)
                {
                    int i = iPt;
                    if (isOverrideOrdinal)
                        i = (int)(val - 0.5);
                    if (i >= 0 && i < axis._scale._textLabels.Length)
                        return axis._scale._textLabels[i];
                    else
                        return (i + 1).ToString();
                }
                else if (axis.Scale.IsAnyOrdinal && axis.Scale.Type != AxisType.LinearAsOrdinal
                                && !isOverrideOrdinal)
                {
                    return iPt.ToString(_pointValueFormat);
                }
                else
                    return val.ToString(_pointValueFormat);
            }
            else
                return "";
        }
        /// 
        /// protected method for handling MouseMove events to display tooltips over
        /// individual datapoints.
        /// 
        /// 
        /// A reference to the control that has the MouseMove event.
        /// 
        /// 
        /// A MouseEventArgs object.
        /// 
        protected void ZedGraphControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (_masterPane != null)
            {
                // Provide Callback for MouseMove events
                if (this.MouseMoveEvent != null && this.MouseMoveEvent(this, e))
                    return;
                Point mousePt = new Point(e.X, e.Y);
                //Point tempPt = this.PointToClient( Control.MousePosition );
                SetCursor(mousePt);
                // If the mouse is being dragged,
                // undraw and redraw the rectangle as the mouse moves.
                if (_isZooming)
                    HandleZoomDrag(mousePt);
                else if (_isPanning)
                    HandlePanDrag(mousePt);
                else if (_isEditing)
                    HandleEditDrag(mousePt);
                else if (_isShowCursorValues)
                    HandleCursorValues(mousePt);
                else if (_isShowPointValues)
                    HandlePointValues(mousePt);
                //Revision: JCarpenter 10/06
                else if (_isSelecting)
                    HandleZoomDrag(mousePt);
            }
        }
        private Point HandlePointValues(Point mousePt)
        {
            int iPt;
            GraphPane pane;
            object nearestObj;
            using (Graphics g = this.CreateGraphics())
            {
                if (_masterPane.FindNearestPaneObject(mousePt,
                    g, out pane, out nearestObj, out iPt))
                {
                    if (nearestObj is CurveItem && iPt >= 0)
                    {
                        CurveItem curve = (CurveItem)nearestObj;
                        // Provide Callback for User to customize the tooltips
                        if (this.PointValueEvent != null)
                        {
                            string label = this.PointValueEvent(this, pane, curve, iPt);
                            if (label != null && label.Length > 0)
                            {
                                this.pointToolTip.SetToolTip(this, label);
                                this.pointToolTip.Active = true;
                            }
                            else
                                this.pointToolTip.Active = false;
                        }
                        else
                        {
                            if (curve is PieItem)
                            {
                                this.pointToolTip.SetToolTip(this,
                                    ((PieItem)curve).Value.ToString(_pointValueFormat));
                            }
                            //							else if ( curve is OHLCBarItem || curve is JapaneseCandleStickItem )
                            //							{
                            //								StockPt spt = (StockPt)curve.Points[iPt];
                            //								this.pointToolTip.SetToolTip( this, ( (XDate) spt.Date ).ToString( "MM/dd/yyyy" ) + "\nOpen: $" +
                            //								spt.Open.ToString( "N2" ) +
                            //								"\nHigh: $" +
                            //								spt.High.ToString( "N2" ) + "\nLow: $" +
                            //								spt.Low.ToString( "N2" ) + "\nClose: $" +
                            //								spt.Close.ToString
                            //								( "N2" ) );
                            //							}
                            else
                            {
                                PointPair pt = curve.Points[iPt];
                                if (pt.Tag is string)
                                    this.pointToolTip.SetToolTip(this, (string)pt.Tag);
                                else
                                {
                                    double xVal, yVal, lowVal;
                                    ValueHandler valueHandler = new ValueHandler(pane, false);
                                    if ((curve is BarItem || curve is ErrorBarItem || curve is HiLowBarItem)
                                            && pane.BarSettings.Base != BarBase.X)
                                        valueHandler.GetValues(curve, iPt, out yVal, out lowVal, out xVal);
                                    else
                                        valueHandler.GetValues(curve, iPt, out xVal, out lowVal, out yVal);
                                    string xStr = MakeValueLabel(curve.GetXAxis(pane), xVal, iPt,
                                        curve.IsOverrideOrdinal);
                                    string yStr = MakeValueLabel(curve.GetYAxis(pane), yVal, iPt,
                                        curve.IsOverrideOrdinal);
                                    this.pointToolTip.SetToolTip(this, "( " + xStr + ", " + yStr + " )");
                                    //this.pointToolTip.SetToolTip( this,
                                    //	curve.Points[iPt].ToString( this.pointValueFormat ) );
                                }
                            }
                            this.pointToolTip.Active = true;
                        }
                    }
                    else
                        this.pointToolTip.Active = false;
                }
                else
                    this.pointToolTip.Active = false;
                //g.Dispose();
            }
            return mousePt;
        }
        private Point HandleCursorValues(Point mousePt)
        {
            GraphPane pane = _masterPane.FindPane(mousePt);
            if (pane != null && pane.Chart._rect.Contains(mousePt))
            {
                double x, x2, y, y2;
                pane.ReverseTransform(mousePt, out x, out x2, out y, out y2);
                string xStr = MakeValueLabel(pane.XAxis, x, -1, true);
                string yStr = MakeValueLabel(pane.YAxis, y, -1, true);
                string y2Str = MakeValueLabel(pane.Y2Axis, y2, -1, true);
                this.pointToolTip.SetToolTip(this, "( " + xStr + ", " + yStr + ", " + y2Str + " )");
                this.pointToolTip.Active = true;
            }
            else
                this.pointToolTip.Active = false;
            return mousePt;
        }
        #endregion
        #region Mouse Wheel Zoom Events
        /// 
        /// Handle a MouseWheel event in the 
        /// 
        /// A reference to the 
        /// A  instance
        protected void ZedGraphControl_MouseWheel(object sender, MouseEventArgs e)
        {
            if ((_isEnableVZoom || _isEnableHZoom) && _masterPane != null)
            {
                GraphPane pane = this.MasterPane.FindChartRect(new PointF(e.X, e.Y));
                if (pane != null && e.Delta != 0)
                {
                    ZoomState oldState = ZoomStateSave(pane, ZoomState.StateType.WheelZoom);
                    //ZoomState oldState = pane.ZoomStack.Push( pane, ZoomState.StateType.Zoom );
                    PointF centerPoint = new PointF(e.X, e.Y);
                    double zoomFraction = (1 + (e.Delta < 0 ? 1.0 : -1.0) * ZoomStepFraction);
                    ZoomPane(pane, zoomFraction, centerPoint, _isZoomOnMouseCenter, false);
                    ApplyToAllPanes(pane);
                    using (Graphics g = this.CreateGraphics())
                    {
                        // always AxisChange() the dragPane
                        pane.AxisChange(g);
                        foreach (GraphPane tempPane in _masterPane._paneList)
                        {
                            if (tempPane != pane && (_isSynchronizeXAxes || _isSynchronizeYAxes))
                                tempPane.AxisChange(g);
                        }
                    }
                    ZoomStatePush(pane);
                    // Provide Callback to notify the user of zoom events
                    if (this.ZoomEvent != null)
                        this.ZoomEvent(this, oldState, new ZoomState(pane, ZoomState.StateType.WheelZoom));
                    this.Refresh();
                }
            }
        }
        /// 
        /// Zoom a specified pane in or out according to the specified zoom fraction.
        /// 
        /// 
        /// The zoom will occur on the , , and
        ///  only if the corresponding flag,  or
        /// , is true.  Note that if there are multiple Y or Y2 axes, all of
        /// them will be zoomed.
        /// 
        /// The  instance to be zoomed.
        /// The fraction by which to zoom, less than 1 to zoom in, greater than
        /// 1 to zoom out.  For example, 0.9 will zoom in such that the scale is 90% of what it was
        /// originally.
        /// The screen position about which the zoom will be centered.  This
        /// value is only used if  is true.
        /// 
        /// true to cause the zoom to be centered on the point
        /// , false to center on the .
        /// 
        /// true to force a refresh of the control, false to leave it unrefreshed
        protected void ZoomPane(GraphPane pane, double zoomFraction, PointF centerPt,
                    bool isZoomOnCenter, bool isRefresh)
        {
            double x;
            double x2;
            double[] y;
            double[] y2;
            pane.ReverseTransform(centerPt, out x, out x2, out y, out y2);
            if (_isEnableHZoom)
            {
                ZoomScale(pane.XAxis, zoomFraction, x, isZoomOnCenter);
                ZoomScale(pane.X2Axis, zoomFraction, x2, isZoomOnCenter);
            }
            if (_isEnableVZoom)
            {
                for (int i = 0; i < pane.YAxisList.Count; i++)
                    ZoomScale(pane.YAxisList[i], zoomFraction, y[i], isZoomOnCenter);
                for (int i = 0; i < pane.Y2AxisList.Count; i++)
                    ZoomScale(pane.Y2AxisList[i], zoomFraction, y2[i], isZoomOnCenter);
            }
            using (Graphics g = this.CreateGraphics())
            {
                pane.AxisChange(g);
                //g.Dispose();
            }
            this.SetScroll(this.hScrollBar1, pane.XAxis, _xScrollRange.Min, _xScrollRange.Max);
            this.SetScroll(this.vScrollBar1, pane.YAxis, _yScrollRangeList[0].Min,
                _yScrollRangeList[0].Max);
            if (isRefresh)
                Refresh();
        }
        /// 
        /// Zoom a specified pane in or out according to the specified zoom fraction.
        /// 
        /// 
        /// The zoom will occur on the , , and
        ///  only if the corresponding flag,  or
        /// , is true.  Note that if there are multiple Y or Y2 axes, all of
        /// them will be zoomed.
        /// 
        /// The  instance to be zoomed.
        /// The fraction by which to zoom, less than 1 to zoom in, greater than
        /// 1 to zoom out.  For example, 0.9 will zoom in such that the scale is 90% of what it was
        /// originally.
        /// The screen position about which the zoom will be centered.  This
        /// value is only used if  is true.
        /// 
        /// true to cause the zoom to be centered on the point
        /// , false to center on the .
        /// 
        public void ZoomPane(GraphPane pane, double zoomFraction, PointF centerPt, bool isZoomOnCenter)
        {
            ZoomPane(pane, zoomFraction, centerPt, isZoomOnCenter, true);
        }
        /// 
        /// Zoom the specified axis by the specified amount, with the center of the zoom at the
        /// (optionally) specified point.
        /// 
        /// 
        /// This method is used for MouseWheel zoom operations
        /// The  to be zoomed.
        /// The zoom fraction, less than 1.0 to zoom in, greater than 1.0 to
        /// zoom out.  That is, a value of 0.9 will zoom in such that the scale length is 90% of what
        /// it previously was.
        /// The location for the center of the zoom.  This is only used if
        ///  is true.
        /// true if the zoom is to be centered at the
        ///  screen position, false for the zoom to be centered within
        /// the .
        /// 
        protected void ZoomScale(Axis axis, double zoomFraction, double centerVal, bool isZoomOnCenter)
        {
            if (axis != null && zoomFraction > 0.0001 && zoomFraction < 1000.0)
            {
                Scale scale = axis._scale;
                /*
                                if ( axis.Scale.IsLog )
                                {
                                    double ratio = Math.Sqrt( axis._scale._max / axis._scale._min * zoomFraction );
                                    if ( !isZoomOnCenter )
                                        centerVal = Math.Sqrt( axis._scale._max * axis._scale._min );
                                    axis._scale._min = centerVal / ratio;
                                    axis._scale._max = centerVal * ratio;
                                }
                                else
                                {
                */
                double minLin = axis._scale._minLinearized;
                double maxLin = axis._scale._maxLinearized;
                double range = (maxLin - minLin) * zoomFraction / 2.0;
                if (!isZoomOnCenter)
                    centerVal = (maxLin + minLin) / 2.0;
                axis._scale._minLinearized = centerVal - range;
                axis._scale._maxLinearized = centerVal + range;
                //				}
                axis._scale._minAuto = false;
                axis._scale._maxAuto = false;
            }
        }
        #endregion
        #region Pan Events
        private Point HandlePanDrag(Point mousePt)
        {
            double x1, x2, xx1, xx2;
            double[] y1, y2, yy1, yy2;
            //PointF endPoint = mousePt;
            //PointF startPoint = ( (Control)sender ).PointToClient( this.dragRect.Location );
            _dragPane.ReverseTransform(_dragStartPt, out x1, out xx1, out y1, out yy1);
            _dragPane.ReverseTransform(mousePt, out x2, out xx2, out y2, out yy2);
            if (_isEnableHPan)
            {
                PanScale(_dragPane.XAxis, x1, x2);
                PanScale(_dragPane.X2Axis, xx1, xx2);
                this.SetScroll(this.hScrollBar1, _dragPane.XAxis, _xScrollRange.Min, _xScrollRange.Max);
            }
            if (_isEnableVPan)
            {
                for (int i = 0; i < y1.Length; i++)
                    PanScale(_dragPane.YAxisList[i], y1[i], y2[i]);
                for (int i = 0; i < yy1.Length; i++)
                    PanScale(_dragPane.Y2AxisList[i], yy1[i], yy2[i]);
                this.SetScroll(this.vScrollBar1, _dragPane.YAxis, _yScrollRangeList[0].Min,
                    _yScrollRangeList[0].Max);
            }
            ApplyToAllPanes(_dragPane);
            Refresh();
            _dragStartPt = mousePt;
            return mousePt;
        }
        private void HandlePanFinish()
        {
            // push the prior saved zoomstate, since the scale ranges have already been changed on
            // the fly during the panning operation
            if (_zoomState != null && _zoomState.IsChanged(_dragPane))
            {
                //_dragPane.ZoomStack.Push( _zoomState );
                ZoomStatePush(_dragPane);
                // Provide Callback to notify the user of pan events
                if (this.ZoomEvent != null)
                    this.ZoomEvent(this, _zoomState,
                        new ZoomState(_dragPane, ZoomState.StateType.Pan));
                _zoomState = null;
            }
        }
        private void HandlePanCancel()
        {
            if (_isPanning)
            {
                if (_zoomState != null && _zoomState.IsChanged(_dragPane))
                {
                    ZoomStateRestore(_dragPane);
                    //_zoomState.ApplyState( _dragPane );
                    //_zoomState = null;
                }
                _isPanning = false;
                Refresh();
                ZoomStateClear();
            }
        }
        /// 
        /// Handle a panning operation for the specified .
        /// 
        /// The  to be panned
        /// The value where the pan started.  The scale range
        /// will be shifted by the difference between  and
        /// .
        /// 
        /// The value where the pan ended.  The scale range
        /// will be shifted by the difference between  and
        /// .
        /// 
        protected void PanScale(Axis axis, double startVal, double endVal)
        {
            if (axis != null)
            {
                Scale scale = axis._scale;
                double delta = scale.Linearize(startVal) - scale.Linearize(endVal);
                scale._minLinearized += delta;
                scale._maxLinearized += delta;
                scale._minAuto = false;
                scale._maxAuto = false;
                /*
                                if ( axis.Type == AxisType.Log )
                                {
                                    axis._scale._min *= startVal / endVal;
                                    axis._scale._max *= startVal / endVal;
                                }
                                else
                                {
                                    axis._scale._min += startVal - endVal;
                                    axis._scale._max += startVal - endVal;
                                }
                */
            }
        }
        #endregion
        #region Edit Point Events
        private void HandleEditDrag(Point mousePt)
        {
            // get the scale values that correspond to the current point
            double curX, curY;
            _dragPane.ReverseTransform(mousePt, _dragCurve.IsX2Axis, _dragCurve.IsY2Axis,
                    _dragCurve.YAxisIndex, out curX, out curY);
            double startX, startY;
            _dragPane.ReverseTransform(_dragStartPt, _dragCurve.IsX2Axis, _dragCurve.IsY2Axis,
                    _dragCurve.YAxisIndex, out startX, out startY);
            // calculate the new scale values for the point
            PointPair newPt = new PointPair(_dragStartPair);
            Scale xScale = _dragCurve.GetXAxis(_dragPane)._scale;
            if (_isEnableHEdit)
                newPt.X = xScale.DeLinearize(xScale.Linearize(newPt.X) +
                            xScale.Linearize(curX) - xScale.Linearize(startX));
            Scale yScale = _dragCurve.GetYAxis(_dragPane)._scale;
            if (_isEnableVEdit)
                newPt.Y = yScale.DeLinearize(yScale.Linearize(newPt.Y) +
                            yScale.Linearize(curY) - yScale.Linearize(startY));
            // save the data back to the point list
            IPointListEdit list = _dragCurve.Points as IPointListEdit;
            if (list != null)
                list[_dragIndex] = newPt;
            // force a redraw
            Refresh();
        }
        private void HandleEditFinish()
        {
            if (this.PointEditEvent != null)
                this.PointEditEvent(this, _dragPane, _dragCurve, _dragIndex);
        }
        private void HandleEditCancel()
        {
            if (_isEditing)
            {
                IPointListEdit list = _dragCurve.Points as IPointListEdit;
                if (list != null)
                    list[_dragIndex] = _dragStartPair;
                _isEditing = false;
                Refresh();
            }
        }
        #endregion
        #region Zoom Events
        private void HandleZoomDrag(Point mousePt)
        {
            // Hide the previous rectangle by calling the
            // DrawReversibleFrame method with the same parameters.
            Rectangle rect = CalcScreenRect(_dragStartPt, _dragEndPt);
            ControlPaint.DrawReversibleFrame(rect, this.BackColor, FrameStyle.Dashed);
            // Bound the zoom to the ChartRect
            _dragEndPt = Point.Round(BoundPointToRect(mousePt, _dragPane.Chart._rect));
            rect = CalcScreenRect(_dragStartPt, _dragEndPt);
            // Draw the new rectangle by calling DrawReversibleFrame again.
            ControlPaint.DrawReversibleFrame(rect, this.BackColor, FrameStyle.Dashed);
        }
        private void HandleZoomFinish(object sender, MouseEventArgs e)
        {
            PointF mousePtF = BoundPointToRect(new Point(e.X, e.Y), _dragPane.Chart._rect);
            // Only accept a drag if it covers at least 5 pixels in each direction
            //Point curPt = ( (Control)sender ).PointToScreen( Point.Round( mousePt ) );
            if ((Math.Abs(mousePtF.X - _dragStartPt.X) > 4 || !_isEnableHZoom) &&
                    (Math.Abs(mousePtF.Y - _dragStartPt.Y) > 4 || !_isEnableVZoom))
            {
                // Draw the rectangle to be evaluated. Set a dashed frame style
                // using the FrameStyle enumeration.
                //ControlPaint.DrawReversibleFrame( this.dragRect,
                //	this.BackColor, FrameStyle.Dashed );
                double x1, x2, xx1, xx2;
                double[] y1, y2, yy1, yy2;
                //PointF startPoint = ( (Control)sender ).PointToClient( this.dragRect.Location );
                _dragPane.ReverseTransform(_dragStartPt, out x1, out xx1, out y1, out yy1);
                _dragPane.ReverseTransform(mousePtF, out x2, out xx2, out y2, out yy2);
                ZoomStatePush(_dragPane);
                //ZoomState oldState = _dragPane.ZoomStack.Push( _dragPane,
                //			ZoomState.StateType.Zoom );
                if (_isEnableHZoom)
                {
                    _dragPane.XAxis._scale._min = Math.Min(x1, x2);
                    _dragPane.XAxis._scale._minAuto = false;
                    _dragPane.XAxis._scale._max = Math.Max(x1, x2);
                    _dragPane.XAxis._scale._maxAuto = false;
                    _dragPane.X2Axis._scale._min = Math.Min(xx1, xx2);
                    _dragPane.X2Axis._scale._minAuto = false;
                    _dragPane.X2Axis._scale._max = Math.Max(xx1, xx2);
                    _dragPane.X2Axis._scale._maxAuto = false;
                }
                if (_isEnableVZoom)
                {
                    for (int i = 0; i < y1.Length; i++)
                    {
                        _dragPane.YAxisList[i]._scale._min = Math.Min(y1[i], y2[i]);
                        _dragPane.YAxisList[i]._scale._max = Math.Max(y1[i], y2[i]);
                        _dragPane.YAxisList[i]._scale._minAuto = false;
                        _dragPane.YAxisList[i]._scale._maxAuto = false;
                    }
                    for (int i = 0; i < yy1.Length; i++)
                    {
                        _dragPane.Y2AxisList[i]._scale._min = Math.Min(yy1[i], yy2[i]);
                        _dragPane.Y2AxisList[i]._scale._max = Math.Max(yy1[i], yy2[i]);
                        _dragPane.Y2AxisList[i]._scale._minAuto = false;
                        _dragPane.Y2AxisList[i]._scale._maxAuto = false;
                    }
                }
                this.SetScroll(this.hScrollBar1, _dragPane.XAxis, _xScrollRange.Min, _xScrollRange.Max);
                this.SetScroll(this.vScrollBar1, _dragPane.YAxis, _yScrollRangeList[0].Min,
                    _yScrollRangeList[0].Max);
                ApplyToAllPanes(_dragPane);
                // Provide Callback to notify the user of zoom events
                if (this.ZoomEvent != null)
                    this.ZoomEvent(this, _zoomState, //oldState,
                        new ZoomState(_dragPane, ZoomState.StateType.Zoom));
                using (Graphics g = this.CreateGraphics())
                {
                    // always AxisChange() the dragPane
                    _dragPane.AxisChange(g);
                    foreach (GraphPane pane in _masterPane._paneList)
                    {
                        if (pane != _dragPane && (_isSynchronizeXAxes || _isSynchronizeYAxes))
                            pane.AxisChange(g);
                    }
                }
            }
            Refresh();
        }
        private void HandleZoomCancel()
        {
            if (_isZooming)
            {
                _isZooming = false;
                Refresh();
                ZoomStateClear();
            }
        }
        private PointF BoundPointToRect(Point mousePt, RectangleF rect)
        {
            PointF newPt = new PointF(mousePt.X, mousePt.Y);
            if (mousePt.X < rect.X) newPt.X = rect.X;
            if (mousePt.X > rect.Right) newPt.X = rect.Right;
            if (mousePt.Y < rect.Y) newPt.Y = rect.Y;
            if (mousePt.Y > rect.Bottom) newPt.Y = rect.Bottom;
            return newPt;
        }
        private Rectangle CalcScreenRect(Point mousePt1, Point mousePt2)
        {
            Point screenPt = PointToScreen(mousePt1);
            Size size = new Size(mousePt2.X - mousePt1.X, mousePt2.Y - mousePt1.Y);
            Rectangle rect = new Rectangle(screenPt, size);
            if (_isZooming)
            {
                Rectangle chartRect = Rectangle.Round(_dragPane.Chart._rect);
                Point chartPt = PointToScreen(chartRect.Location);
                if (!_isEnableVZoom)
                {
                    rect.Y = chartPt.Y;
                    rect.Height = chartRect.Height + 1;
                }
                else if (!_isEnableHZoom)
                {
                    rect.X = chartPt.X;
                    rect.Width = chartRect.Width + 1;
                }
            }
            return rect;
        }
        #endregion
        #region Selection Events
        // Revision: JCarpenter 10/06
        /// 
        /// Perform selection on curves within the drag pane, or under the mouse click.
        /// 
        /// 
        /// 
        private void HandleSelectionFinish(object sender, MouseEventArgs e)
        {
            if (e.Button != _selectButtons)
            {
                Refresh();
                return;
            }
            PointF mousePtF = BoundPointToRect(new Point(e.X, e.Y), _dragPane.Chart._rect);
            PointF mousePt = BoundPointToRect(new Point(e.X, e.Y), _dragPane.Rect);
            Point curPt = ((Control)sender).PointToScreen(Point.Round(mousePt));
            // Only accept a drag if it covers at least 5 pixels in each direction
            //Point curPt = ( (Control)sender ).PointToScreen( Point.Round( mousePt ) );
            if ((Math.Abs(mousePtF.X - _dragStartPt.X) > 4) &&
                      (Math.Abs(mousePtF.Y - _dragStartPt.Y) > 4))
            {
                #region New Code to Select on Rubber Band
                double x1, x2, xx1, xx2;
                double[] y1, y2, yy1, yy2;
                PointF startPoint = ((Control)sender).PointToClient(new Point(Convert.ToInt32(this._dragPane.Rect.X), Convert.ToInt32(this._dragPane.Rect.Y)));
                _dragPane.ReverseTransform(_dragStartPt, out x1, out xx1, out y1, out yy1);
                _dragPane.ReverseTransform(mousePtF, out x2, out xx2, out y2, out yy2);
                CurveList objects = new CurveList();
                double left = Math.Min(x1, x2);
                double right = Math.Max(x1, x2);
                double top = 0;
                double bottom = 0;
                for (int i = 0; i < y1.Length; i++)
                {
                    bottom = Math.Min(y1[i], y2[i]);
                    top = Math.Max(y1[i], y2[i]);
                }
                for (int i = 0; i < yy1.Length; i++)
                {
                    bottom = Math.Min(bottom, yy2[i]);
                    bottom = Math.Min(yy1[i], bottom);
                    top = Math.Max(top, yy2[i]);
                    top = Math.Max(yy1[i], top);
                }
                double w = right - left;
                double h = bottom - top;
                RectangleF rF = new RectangleF((float)left, (float)top, (float)w, (float)h);
                _dragPane.FindContainedObjects(rF, this.CreateGraphics(), out objects);
                if (Control.ModifierKeys == _selectAppendModifierKeys)
                    _selection.AddToSelection(_masterPane, objects);
                else
                    _selection.Select(_masterPane, objects);
                //				this.Select( objects );
                //Graphics g = this.CreateGraphics();
                //this._dragPane.AxisChange( g );
                //g.Dispose();
                #endregion
            }
            else   // It's a single-select
            {
                #region New Code to Single Select
                //Point mousePt = new Point( e.X, e.Y );
                int iPt;
                GraphPane pane;
                object nearestObj;
                using (Graphics g = this.CreateGraphics())
                {
                    if (this.MasterPane.FindNearestPaneObject(mousePt, g, out pane,
                                out nearestObj, out iPt))
                    {
                        if (nearestObj is CurveItem && iPt >= 0)
                        {
                            if (Control.ModifierKeys == _selectAppendModifierKeys)
                                _selection.AddToSelection(_masterPane, nearestObj as CurveItem);
                            else
                                _selection.Select(_masterPane, nearestObj as CurveItem);
                        }
                        else
                            _selection.ClearSelection(_masterPane);
                        Refresh();
                    }
                    else
                    {
                        _selection.ClearSelection(_masterPane);
                    }
                }
                #endregion New Code to Single Select
            }
            using (Graphics g = this.CreateGraphics())
            {
                // always AxisChange() the dragPane
                _dragPane.AxisChange(g);
                foreach (GraphPane pane in _masterPane._paneList)
                {
                    if (pane != _dragPane && (_isSynchronizeXAxes || _isSynchronizeYAxes))
                        pane.AxisChange(g);
                }
            }
            Refresh();
        }
        private void HandleSelectionCancel()
        {
            _isSelecting = false;
            _selection.ClearSelection(_masterPane);
            Refresh();
        }
        #endregion
        #region ScrollBars
        private void vScrollBar1_Scroll(object sender, ScrollEventArgs e)
        {
            if (this.GraphPane != null)
            {
                /*
                                ScrollBar scrollBar = sender as ScrollBar;
                                bool clearZoomState = false;
                                //ZoomState curState = new ZoomState( this.GraphPane, ZoomState.StateType.Scroll );
                                if ( _zoomState == null ) //&& !scrollBar.Capture )
                                {
                                    clearZoomState = true;
                                    ZoomStateSave( this.GraphPane, ZoomState.StateType.Scroll );
                                }
                */
                for (int i = 0; i < this.GraphPane.YAxisList.Count; i++)
                {
                    ScrollRange scroll = _yScrollRangeList[i];
                    if (scroll.IsScrollable)
                    {
                        Axis axis = this.GraphPane.YAxisList[i];
                        HandleScroll(axis, e.NewValue, scroll.Min, scroll.Max, vScrollBar1.LargeChange,
                                        !axis.Scale.IsReverse);
                    }
                }
                for (int i = 0; i < this.GraphPane.Y2AxisList.Count; i++)
                {
                    ScrollRange scroll = _y2ScrollRangeList[i];
                    if (scroll.IsScrollable)
                    {
                        Axis axis = this.GraphPane.Y2AxisList[i];
                        HandleScroll(axis, e.NewValue, scroll.Min, scroll.Max, vScrollBar1.LargeChange,
                                        !axis.Scale.IsReverse);
                    }
                }
                ApplyToAllPanes(this.GraphPane);
                if (_zoomState != null) // && ! clearZoomState && scrollBar.Capture )
                {
                    // Provide Callback to notify the user of scroll events
                    if (this.ScrollProgressEvent != null)
                        this.ScrollProgressEvent(this, vScrollBar1, _zoomState,
                                    new ZoomState(this.GraphPane, ZoomState.StateType.Scroll));
                }
                /*				else if ( clearZoomState && _zoomState != null && // !scrollBar.Capture &&
                                                _zoomState.IsChanged( this.GraphPane ) )
                                {
                                    //this.GraphPane.ZoomStack.Push( _zoomState );
                                    ZoomStatePush( this.GraphPane );
                                    // Provide Callback to notify the user of pan events
                                    if ( this.ScrollDoneEvent != null )
                                        this.ScrollDoneEvent( this, scrollBar, _zoomState,
                                                    new ZoomState( this.GraphPane, ZoomState.StateType.Scroll ) );
                                }
                                if ( clearZoomState )
                                    _zoomState = null;
                */
            }
        }
        private void ApplyToAllPanes(GraphPane primaryPane)
        {
            foreach (GraphPane pane in _masterPane._paneList)
            {
                if (pane != primaryPane)
                {
                    if (_isSynchronizeXAxes)
                        Synchronize(primaryPane.XAxis, pane.XAxis);
                    if (_isSynchronizeYAxes)
                        Synchronize(primaryPane.YAxis, pane.YAxis);
                }
            }
        }
        private void Synchronize(Axis source, Axis dest)
        {
            dest._scale._min = source._scale._min;
            dest._scale._max = source._scale._max;
            dest._scale._majorStep = source._scale._majorStep;
            dest._scale._minorStep = source._scale._minorStep;
            dest._scale._minAuto = source._scale._minAuto;
            dest._scale._maxAuto = source._scale._maxAuto;
            dest._scale._majorStepAuto = source._scale._majorStepAuto;
            dest._scale._minorStepAuto = source._scale._minorStepAuto;
        }
        private void hScrollBar1_Scroll(object sender, ScrollEventArgs e)
        {
            if (this.GraphPane != null)
            {
                HandleScroll(this.GraphPane.XAxis, e.NewValue, _xScrollRange.Min, _xScrollRange.Max,
                            hScrollBar1.LargeChange, this.GraphPane.XAxis.Scale.IsReverse);
            }
            ApplyToAllPanes(this.GraphPane);
            if (_zoomState != null && this.GraphPane != null)
            {
                // Provide Callback to notify the user of pan events
                if (this.ScrollProgressEvent != null)
                    this.ScrollProgressEvent(this, hScrollBar1, _zoomState,
                                new ZoomState(this.GraphPane, ZoomState.StateType.Scroll));
            }
        }
        /// 
        /// Use the MouseCaptureChanged as an indicator for the start and end of a scrolling operation
        /// 
        private void ScrollBarMouseCaptureChanged(object sender, EventArgs e)
        {
            ScrollBar scrollBar = sender as ScrollBar;
            if (scrollBar != null)
            {
                // If this is the start of a new scroll, then Capture will be true
                if (scrollBar.Capture)
                {
                    // save the original zoomstate
                    //_zoomState = new ZoomState( this.GraphPane, ZoomState.StateType.Scroll );
                    ZoomStateSave(this.GraphPane, ZoomState.StateType.Scroll);
                }
                else
                {
                    // push the prior saved zoomstate, since the scale ranges have already been changed on
                    // the fly during the scrolling operation
                    if (_zoomState != null && _zoomState.IsChanged(this.GraphPane))
                    {
                        //this.GraphPane.ZoomStack.Push( _zoomState );
                        ZoomStatePush(this.GraphPane);
                        // Provide Callback to notify the user of pan events
                        if (this.ScrollDoneEvent != null)
                            this.ScrollDoneEvent(this, scrollBar, _zoomState,
                                        new ZoomState(this.GraphPane, ZoomState.StateType.Scroll));
                        _zoomState = null;
                    }
                }
            }
        }
        private void HandleScroll(Axis axis, int newValue, double scrollMin, double scrollMax,
                                    int largeChange, bool reverse)
        {
            if (axis != null)
            {
                if (scrollMin > axis._scale._min)
                    scrollMin = axis._scale._min;
                if (scrollMax < axis._scale._max)
                    scrollMax = axis._scale._max;
                int span = _ScrollControlSpan - largeChange;
                if (span <= 0)
                    return;
                if (reverse)
                    newValue = span - newValue;
                Scale scale = axis._scale;
                double delta = scale._maxLinearized - scale._minLinearized;
                double scrollMin2 = scale.Linearize(scrollMax) - delta;
                scrollMin = scale.Linearize(scrollMin);
                //scrollMax = scale.Linearize( scrollMax );
                double val = scrollMin + (double)newValue / (double)span *
                        (scrollMin2 - scrollMin);
                scale._minLinearized = val;
                scale._maxLinearized = val + delta;
                /*
                                if ( axis.Scale.IsLog )
                                {
                                    double ratio = axis._scale._max / axis._scale._min;
                                    double scrollMin2 = scrollMax / ratio;
                                    double val = scrollMin * Math.Exp( (double)newValue / (double)span *
                                                ( Math.Log( scrollMin2 ) - Math.Log( scrollMin ) ) );
                                    axis._scale._min = val;
                                    axis._scale._max = val * ratio;
                                }
                                else
                                {
                                    double delta = axis._scale._max - axis._scale._min;
                                    double scrollMin2 = scrollMax - delta;
                                    double val = scrollMin + (double)newValue / (double)span *
                                                ( scrollMin2 - scrollMin );
                                    axis._scale._min = val;
                                    axis._scale._max = val + delta;
                                }
                */
                this.Invalidate();
            }
        }
        /// 
        /// Sets the value of the scroll range properties (see ,
        /// , , and 
        ///  based on the actual range of the data for
        /// each corresponding .
        /// 
        /// 
        /// This method is called automatically by  if
        /// 
        /// is true.  Note that this will not be called if you call AxisChange directly from the
        /// .  For example, zedGraphControl1.AxisChange() works properly, but
        /// zedGraphControl1.GraphPane.AxisChange() does not.
        public void SetScrollRangeFromData()
        {
            if (this.GraphPane != null)
            {
                double grace = CalcScrollGrace(this.GraphPane.XAxis.Scale._rangeMin,
                            this.GraphPane.XAxis.Scale._rangeMax);
                _xScrollRange.Min = this.GraphPane.XAxis.Scale._rangeMin - grace;
                _xScrollRange.Max = this.GraphPane.XAxis.Scale._rangeMax + grace;
                _xScrollRange.IsScrollable = true;
                for (int i = 0; i < this.GraphPane.YAxisList.Count; i++)
                {
                    Axis axis = this.GraphPane.YAxisList[i];
                    grace = CalcScrollGrace(axis.Scale._rangeMin, axis.Scale._rangeMax);
                    ScrollRange range = new ScrollRange(axis.Scale._rangeMin - grace,
                        axis.Scale._rangeMax + grace, _yScrollRangeList[i].IsScrollable);
                    if (i >= _yScrollRangeList.Count)
                        _yScrollRangeList.Add(range);
                    else
                        _yScrollRangeList[i] = range;
                }
                for (int i = 0; i < this.GraphPane.Y2AxisList.Count; i++)
                {
                    Axis axis = this.GraphPane.Y2AxisList[i];
                    grace = CalcScrollGrace(axis.Scale._rangeMin, axis.Scale._rangeMax);
                    ScrollRange range = new ScrollRange(axis.Scale._rangeMin - grace,
                            axis.Scale._rangeMax + grace, _y2ScrollRangeList[i].IsScrollable);
                    if (i >= _y2ScrollRangeList.Count)
                        _y2ScrollRangeList.Add(range);
                    else
                        _y2ScrollRangeList[i] = range;
                }
                //this.GraphPane.CurveList.GetRange( out scrollMinX, out scrollMaxX,
                //		out scrollMinY, out scrollMaxY, out scrollMinY2, out scrollMaxY2, false, false,
                //		this.GraphPane );
            }
        }
        private double CalcScrollGrace(double min, double max)
        {
            if (Math.Abs(max - min) < 1e-30)
            {
                if (Math.Abs(max) < 1e-30)
                    return _scrollGrace;
                else
                    return max * _scrollGrace;
            }
            else
                return (max - min) * _scrollGrace;
        }
        private void SetScroll(ScrollBar scrollBar, Axis axis, double scrollMin, double scrollMax)
        {
            if (scrollBar != null && axis != null)
            {
                scrollBar.Minimum = 0;
                scrollBar.Maximum = _ScrollControlSpan - 1;
                if (scrollMin > axis._scale._min)
                    scrollMin = axis._scale._min;
                if (scrollMax < axis._scale._max)
                    scrollMax = axis._scale._max;
                int val = 0;
                Scale scale = axis._scale;
                double minLinearized = scale._minLinearized;
                double maxLinearized = scale._maxLinearized;
                scrollMin = scale.Linearize(scrollMin);
                scrollMax = scale.Linearize(scrollMax);
                double scrollMin2 = scrollMax - (maxLinearized - minLinearized);
                /*
				if ( axis.Scale.IsLog )
					scrollMin2 = scrollMax / ( axis._scale._max / axis._scale._min );
				else
					scrollMin2 = scrollMax - ( axis._scale._max - axis._scale._min );
				*/
                if (scrollMin >= scrollMin2)
                {
                    //scrollBar.Visible = false;
                    scrollBar.Enabled = false;
                    scrollBar.Value = 0;
                }
                else
                {
                    double ratio = (maxLinearized - minLinearized) / (scrollMax - scrollMin);
                    /*
					if ( axis.Scale.IsLog )
						ratio = ( Math.Log( axis._scale._max ) - Math.Log( axis._scale._min ) ) /
									( Math.Log( scrollMax ) - Math.Log( scrollMin ) );
					else
						ratio = ( axis._scale._max - axis._scale._min ) / ( scrollMax - scrollMin );
					*/
                    int largeChange = (int)(ratio * _ScrollControlSpan + 0.5);
                    if (largeChange < 1)
                        largeChange = 1;
                    scrollBar.LargeChange = largeChange;
                    int smallChange = largeChange / _ScrollSmallRatio;
                    if (smallChange < 1)
                        smallChange = 1;
                    scrollBar.SmallChange = smallChange;
                    int span = _ScrollControlSpan - largeChange;
                    val = (int)((minLinearized - scrollMin) / (scrollMin2 - scrollMin) *
                                    span + 0.5);
                    /*
					if ( axis.Scale.IsLog )
						val = (int)( ( Math.Log( axis._scale._min ) - Math.Log( scrollMin ) ) /
								( Math.Log( scrollMin2 ) - Math.Log( scrollMin ) ) * span + 0.5 );
					else
						val = (int)( ( axis._scale._min - scrollMin ) / ( scrollMin2 - scrollMin ) *
								span + 0.5 );
					*/
                    if (val < 0)
                        val = 0;
                    else if (val > span)
                        val = span;
                    //if ( ( axis is XAxis && axis.IsReverse ) || ( ( ! axis is XAxis ) && ! axis.IsReverse ) )
                    if ((axis is XAxis) == axis.Scale.IsReverse)
                        val = span - val;
                    if (val < scrollBar.Minimum)
                        val = scrollBar.Minimum;
                    if (val > scrollBar.Maximum)
                        val = scrollBar.Maximum;
                    scrollBar.Value = val;
                    scrollBar.Enabled = true;
                    //scrollBar.Visible = true;
                }
            }
        }
        #endregion
        #region ContextMenu
        // Revision: JCarpenter 10/06
        /// 
        /// Public enumeration that specifies the type of 
        /// object present at the Context Menu's mouse location
        /// 
        public enum ContextMenuObjectState
        {
            /// 
            /// The object is an Inactive Curve Item at the Context Menu's mouse position
            /// 
            InactiveSelection,
            /// 
            /// The object is an active Curve Item at the Context Menu's mouse position
            /// 
            ActiveSelection,
            /// 
            /// There is no selectable object present at the Context Menu's mouse position
            /// 
            Background
        }
        //Revision: JCarpenter 10/06
        /// 
        /// Find the object currently under the mouse cursor, and return its state.
        /// 
        private ContextMenuObjectState GetObjectState()
        {
            ContextMenuObjectState objState = ContextMenuObjectState.Background;
            // Determine object state
            Point mousePt = this.PointToClient(Control.MousePosition);
            int iPt;
            GraphPane pane;
            object nearestObj;
            using (Graphics g = this.CreateGraphics())
            {
                if (this.MasterPane.FindNearestPaneObject(mousePt, g, out pane,
                        out nearestObj, out iPt))
                {
                    CurveItem item = nearestObj as CurveItem;
                    if (item != null && iPt >= 0)
                    {
                        if (item.IsSelected)
                            objState = ContextMenuObjectState.ActiveSelection;
                        else
                            objState = ContextMenuObjectState.InactiveSelection;
                    }
                }
            }
            return objState;
        }
        /// 
        /// protected method to handle the popup context menu in the .
        /// 
        /// 
        /// 
        private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
        {
            // disable context menu by default
            e.Cancel = true;
            ContextMenuStrip menuStrip = sender as ContextMenuStrip;
            //Revision: JCarpenter 10/06
            ContextMenuObjectState objState = GetObjectState();
            if (_masterPane != null && menuStrip != null)
            {
                menuStrip.Items.Clear();
                _isZooming = false;
                _isPanning = false;
                Cursor.Current = Cursors.Default;
                _menuClickPt = this.PointToClient(Control.MousePosition);
                GraphPane pane = _masterPane.FindPane(_menuClickPt);
                if (_isShowContextMenu)
                {
                    string menuStr = string.Empty;
                    //ToolStripMenuItem item = new ToolStripMenuItem();
                    //item.Name = "copy";
                    //item.Tag = "copy";
                    //item.Text = _resourceManager.GetString( "copy" );
                    //item.Click += new System.EventHandler( this.MenuClick_Copy );
                    //menuStrip.Items.Add( item );
                    //ToolStripMenuItem item = new ToolStripMenuItem();
                    //item.Name = "±£´æÍ¼Æ¬";
                    //item.Tag = "±£´æÍ¼Æ¬";
                    //item.Text = _resourceManager.GetString("save_as");
                    //item.Click += new System.EventHandler(this.MenuClick_SaveAs);
                    //menuStrip.Items.Add(item);
                    //item = new ToolStripMenuItem();
                    //item.Name = "page_setup";
                    //item.Tag = "page_setup";
                    //item.Text = _resourceManager.GetString( "page_setup" );
                    //item.Click += new System.EventHandler( this.MenuClick_PageSetup );
                    //menuStrip.Items.Add( item );
                    //item = new ToolStripMenuItem();
                    //item.Name = "print";
                    //item.Tag = "print";
                    //item.Text = _resourceManager.GetString( "print" );
                    //item.Click += new System.EventHandler( this.MenuClick_Print );
                    //menuStrip.Items.Add( item );
                    //item = new ToolStripMenuItem();
                    //item.Name = "show_val";
                    //item.Tag = "show_val";
                    //item.Text = _resourceManager.GetString( "show_val" );
                    //item.Click += new System.EventHandler( this.MenuClick_ShowValues );
                    //item.Checked = this.IsShowPointValues;
                    //menuStrip.Items.Add( item );
                    //item = new ToolStripMenuItem();
                    //item.Name = "unzoom";
                    //item.Tag = "unzoom";
                    //if ( pane == null || pane.ZoomStack.IsEmpty )
                    //	menuStr = _resourceManager.GetString( "unzoom" );
                    //else
                    //{
                    //	switch ( pane.ZoomStack.Top.Type )
                    //	{
                    //		case ZoomState.StateType.Zoom:
                    //		case ZoomState.StateType.WheelZoom:
                    //			menuStr = _resourceManager.GetString( "unzoom" );
                    //			break;
                    //		case ZoomState.StateType.Pan:
                    //			menuStr = _resourceManager.GetString( "unpan" );
                    //			break;
                    //		case ZoomState.StateType.Scroll:
                    //			menuStr = _resourceManager.GetString( "unscroll" );
                    //			break;
                    //	}
                    //}
                    ////menuItem.Text = "Un-" + ( ( pane == null || pane.zoomStack.IsEmpty ) ?
                    ////	"Zoom" : pane.zoomStack.Top.TypeString );
                    //item.Text = menuStr;
                    //item.Click += new EventHandler( this.MenuClick_ZoomOut );
                    //if ( pane == null || pane.ZoomStack.IsEmpty )
                    //	item.Enabled = false;
                    //menuStrip.Items.Add( item );
                    //item = new ToolStripMenuItem();
                    //item.Name = "undo_all";
                    //item.Tag = "undo_all";
                    //menuStr = _resourceManager.GetString( "undo_all" );
                    //item.Text = menuStr;
                    //item.Click += new EventHandler( this.MenuClick_ZoomOutAll );
                    //if ( pane == null || pane.ZoomStack.IsEmpty )
                    //	item.Enabled = false;
                    //menuStrip.Items.Add( item );
                    //item = new ToolStripMenuItem();
                    //item.Name = "set_default";
                    //item.Tag = "set_default";
                    //menuStr = _resourceManager.GetString( "set_default" );
                    //item.Text = menuStr;
                    //item.Click += new EventHandler( this.MenuClick_RestoreScale );
                    //if ( pane == null )
                    //	item.Enabled = false;
                    //menuStrip.Items.Add( item );
                    // if e.Cancel is set to false, the context menu does not display
                    // it is initially set to false because the context menu has no items
                    e.Cancel = false;
                    // Provide Callback for User to edit the context menu
                    //Revision: JCarpenter 10/06 - add ContextMenuObjectState objState 
                    if (this.ContextMenuBuilder != null)
                        this.ContextMenuBuilder(this, menuStrip, _menuClickPt, objState);
                }
            }
        }
        /// 
        /// Handler for the "Copy" context menu item.  Copies the current image to a bitmap on the
        /// clipboard.
        /// 
        /// 
        /// 
        protected void MenuClick_Copy(System.Object sender, System.EventArgs e)
        {
            Copy(_isShowCopyMessage);
        }
        /// 
        /// Handler for the "Copy" context menu item.  Copies the current image to a bitmap on the
        /// clipboard.
        /// 
        /// boolean value that determines whether or not a prompt will be
        /// displayed.  true to show a message of "Image Copied to ClipBoard".
        public void Copy(bool isShowMessage)
        {
            if (_masterPane != null)
            {
                //Clipboard.SetDataObject( _masterPane.GetImage(), true );
                // Threaded copy mode to avoid crash with MTA
                // Contributed by Dave Moor
                Thread ct = new Thread(new ThreadStart(this.ClipboardCopyThread));
                //ct.ApartmentState = ApartmentState.STA;
                ct.SetApartmentState(ApartmentState.STA);
                ct.Start();
                ct.Join();
                if (isShowMessage)
                {
                    string str = _resourceManager.GetString("copied_to_clip");
                    //MessageBox.Show( "Image Copied to ClipBoard" );
                    MessageBox.Show(str);
                }
            }
        }
        /// 
        /// A threaded version of the copy method to avoid crash with MTA
        /// 
        private void ClipboardCopyThread()
        {
            Clipboard.SetDataObject(_masterPane.GetImage(), true);
        }
        /// 
        /// Handler for the "Save Image As" context menu item.  Copies the current image to the selected
        /// file.
        /// 
        /// 
        /// 
        protected void MenuClick_SaveAs(System.Object sender, System.EventArgs e)
        {
            SaveAs();
        }
        /// 
        /// Handler for the "Save Image As" context menu item.  Copies the current image to the selected
        /// file in either the Emf (vector), or a variety of Bitmap formats.
        /// 
        /// 
        /// Note that  and  methods are provided
        /// which allow for Bitmap-only or Emf-only handling of the "Save As" context menu item.
        /// 
        public void SaveAs()
        {
            if (_masterPane != null)
            {
                _saveFileDialog.Filter =
                    "Emf Format (*.emf)|*.emf|" +
                    "PNG Format (*.png)|*.png|" +
                    "Gif Format (*.gif)|*.gif|" +
                    "Jpeg Format (*.jpg)|*.jpg|" +
                    "Tiff Format (*.tif)|*.tif|" +
                    "Bmp Format (*.bmp)|*.bmp";
                if (_saveFileDialog.ShowDialog() == DialogResult.OK)
                {
                    Stream myStream = _saveFileDialog.OpenFile();
                    if (myStream != null)
                    {
                        if (_saveFileDialog.FilterIndex == 1)
                        {
                            myStream.Close();
                            _masterPane.GetMetafile().Save(_saveFileDialog.FileName);
                        }
                        else
                        {
                            ImageFormat format = ImageFormat.Png;
                            if (_saveFileDialog.FilterIndex == 3)
                                format = ImageFormat.Gif;
                            else if (_saveFileDialog.FilterIndex == 4)
                                format = ImageFormat.Jpeg;
                            else if (_saveFileDialog.FilterIndex == 5)
                                format = ImageFormat.Tiff;
                            else if (_saveFileDialog.FilterIndex == 6)
                                format = ImageFormat.Bmp;
                            _masterPane.GetImage().Save(myStream, format);
                            myStream.Close();
                        }
                    }
                }
            }
        }
        /// 
        /// Handler for the "Save Image As" context menu item.  Copies the current image to the selected
        /// Bitmap file.
        /// 
        /// 
        /// Note that this handler saves as a bitmap only.  The default handler is
        /// , which allows for Bitmap or EMF formats
        /// 
        public void SaveAsBitmap()
        {
            if (_masterPane != null)
            {
                _saveFileDialog.Filter =
                    "PNG Format (*.png)|*.png|" +
                    "Gif Format (*.gif)|*.gif|" +
                    "Jpeg Format (*.jpg)|*.jpg|" +
                    "Tiff Format (*.tif)|*.tif|" +
                    "Bmp Format (*.bmp)|*.bmp";
                if (_saveFileDialog.ShowDialog() == DialogResult.OK)
                {
                    ImageFormat format = ImageFormat.Png;
                    if (_saveFileDialog.FilterIndex == 2)
                        format = ImageFormat.Gif;
                    else if (_saveFileDialog.FilterIndex == 3)
                        format = ImageFormat.Jpeg;
                    else if (_saveFileDialog.FilterIndex == 4)
                        format = ImageFormat.Tiff;
                    else if (_saveFileDialog.FilterIndex == 5)
                        format = ImageFormat.Bmp;
                    Stream myStream = _saveFileDialog.OpenFile();
                    if (myStream != null)
                    {
                        _masterPane.GetImage().Save(myStream, format);
                        myStream.Close();
                    }
                }
            }
        }
        /// 
        /// Handler for the "Save Image As" context menu item.  Copies the current image to the selected
        /// Emf format file.
        /// 
        /// 
        /// Note that this handler saves as an Emf format only.  The default handler is
        /// , which allows for Bitmap or EMF formats.
        /// 
        public void SaveAsEmf()
        {
            if (_masterPane != null)
            {
                _saveFileDialog.Filter = "Emf Format (*.emf)|*.emf";
                if (_saveFileDialog.ShowDialog() == DialogResult.OK)
                {
                    Stream myStream = _saveFileDialog.OpenFile();
                    if (myStream != null)
                    {
                        myStream.Close();
                        _masterPane.GetMetafile().Save(_saveFileDialog.FileName);
                    }
                }
            }
        }
        /// 
        /// Handler for the "Show Values" context menu item.  Toggles the 
        /// property, which activates the point value tooltips.
        /// 
        /// 
        /// 
        protected void MenuClick_ShowValues(object sender, System.EventArgs e)
        {
            ToolStripMenuItem item = sender as ToolStripMenuItem;
            if (item != null)
                this.IsShowPointValues = !item.Checked;
        }
        /// 
        /// Handler for the "Set Scale to Default" context menu item.  Sets the scale ranging to
        /// full auto mode for all axes.
        /// 
        /// 
        /// This method differs from the  method in that it sets the scales
        /// to full auto mode.  The  method sets the scales to their initial
        /// setting prior to any user actions (which may or may not be full auto mode).
        /// 
        /// 
        /// 
        protected void MenuClick_RestoreScale(object sender, EventArgs e)
        {
            if (_masterPane != null)
            {
                GraphPane pane = _masterPane.FindPane(_menuClickPt);
                RestoreScale(pane);
            }
        }
        /// 
        /// Handler for the "Set Scale to Default" context menu item.  Sets the scale ranging to
        /// full auto mode for all axes.
        /// 
        /// 
        /// This method differs from the  method in that it sets the scales
        /// to full auto mode.  The  method sets the scales to their initial
        /// setting prior to any user actions (which may or may not be full auto mode).
        /// 
        /// The  object which is to have the
        /// scale restored
        public void RestoreScale(GraphPane primaryPane)
        {
            if (primaryPane != null)
            {
                //Go ahead and save the old zoomstates, which provides an "undo"-like capability
                //ZoomState oldState = primaryPane.ZoomStack.Push( primaryPane, ZoomState.StateType.Zoom );
                ZoomState oldState = new ZoomState(primaryPane, ZoomState.StateType.Zoom);
                using (Graphics g = this.CreateGraphics())
                {
                    if (_isSynchronizeXAxes || _isSynchronizeYAxes)
                    {
                        foreach (GraphPane pane in _masterPane._paneList)
                        {
                            pane.ZoomStack.Push(pane, ZoomState.StateType.Zoom);
                            ResetAutoScale(pane, g);
                        }
                    }
                    else
                    {
                        primaryPane.ZoomStack.Push(primaryPane, ZoomState.StateType.Zoom);
                        ResetAutoScale(primaryPane, g);
                    }
                    // Provide Callback to notify the user of zoom events
                    if (this.ZoomEvent != null)
                        this.ZoomEvent(this, oldState, new ZoomState(primaryPane, ZoomState.StateType.Zoom));
                    //g.Dispose();
                }
                Refresh();
            }
        }
        private void ResetAutoScale(GraphPane pane, Graphics g)
        {
            pane.XAxis.ResetAutoScale(pane, g);
            pane.X2Axis.ResetAutoScale(pane, g);
            foreach (YAxis axis in pane.YAxisList)
                axis.ResetAutoScale(pane, g);
            foreach (Y2Axis axis in pane.Y2AxisList)
                axis.ResetAutoScale(pane, g);
        }
        /*
                public void RestoreScale( GraphPane primaryPane )
                {
                    if ( primaryPane != null )
                    {
                        Graphics g = this.CreateGraphics();
                        ZoomState oldState = new ZoomState( primaryPane, ZoomState.StateType.Zoom );
                        //ZoomState newState = null;
                        if ( _isSynchronizeXAxes || _isSynchronizeYAxes )
                        {
                            foreach ( GraphPane pane in _masterPane._paneList )
                            {
                                if ( pane == primaryPane )
                                {
                                    pane.XAxis.ResetAutoScale( pane, g );
                                    foreach ( YAxis axis in pane.YAxisList )
                                        axis.ResetAutoScale( pane, g );
                                    foreach ( Y2Axis axis in pane.Y2AxisList )
                                        axis.ResetAutoScale( pane, g );
                                }
                            }
                        }
                        else
                        {
                            primaryPane.XAxis.ResetAutoScale( primaryPane, g );
                            foreach ( YAxis axis in primaryPane.YAxisList )
                                axis.ResetAutoScale( primaryPane, g );
                            foreach ( Y2Axis axis in primaryPane.Y2AxisList )
                                axis.ResetAutoScale( primaryPane, g );
                        }
                        // Provide Callback to notify the user of zoom events
                        if ( this.ZoomEvent != null )
                            this.ZoomEvent( this, oldState, new ZoomState( primaryPane, ZoomState.StateType.Zoom ) );
                        g.Dispose();
                        Refresh();
                    }
                }
        */
        /*
                public void ZoomOutAll( GraphPane primaryPane )
                {
                    if ( primaryPane != null && !primaryPane.ZoomStack.IsEmpty )
                    {
                        ZoomState.StateType type = primaryPane.ZoomStack.Top.Type;
                        ZoomState oldState = new ZoomState( primaryPane, type );
                        //ZoomState newState = pane.ZoomStack.PopAll( pane );
                        ZoomState newState = null;
                        if ( _isSynchronizeXAxes || _isSynchronizeYAxes )
                        {
                            foreach ( GraphPane pane in _masterPane._paneList )
                            {
                                ZoomState state = pane.ZoomStack.PopAll( pane );
                                if ( pane == primaryPane )
                                    newState = state;
                            }
                        }
                        else
                            newState = primaryPane.ZoomStack.PopAll( primaryPane );
                        // Provide Callback to notify the user of zoom events
                        if ( this.ZoomEvent != null )
                            this.ZoomEvent( this, oldState, newState );
                        Refresh();
                    }
                }
        */
        /// 
        /// Handler for the "UnZoom/UnPan" context menu item.  Restores the scale ranges to the values
        /// before the last zoom or pan operation.
        /// 
        /// 
        /// 
        protected void MenuClick_ZoomOut(System.Object sender, System.EventArgs e)
        {
            if (_masterPane != null)
            {
                GraphPane pane = _masterPane.FindPane(_menuClickPt);
                ZoomOut(pane);
            }
        }
        /// 
        /// Handler for the "UnZoom/UnPan" context menu item.  Restores the scale ranges to the values
        /// before the last zoom, pan, or scroll operation.
        /// 
        /// 
        /// Triggers a  for any type of undo (including pan, scroll, zoom, and
        /// wheelzoom).  This method will affect all the
        ///  objects in the  if
        ///  or  is true.
        /// 
        /// The primary  object which is to be
        /// zoomed out
        public void ZoomOut(GraphPane primaryPane)
        {
            if (primaryPane != null && !primaryPane.ZoomStack.IsEmpty)
            {
                ZoomState.StateType type = primaryPane.ZoomStack.Top.Type;
                ZoomState oldState = new ZoomState(primaryPane, type);
                ZoomState newState = null;
                if (_isSynchronizeXAxes || _isSynchronizeYAxes)
                {
                    foreach (GraphPane pane in _masterPane._paneList)
                    {
                        ZoomState state = pane.ZoomStack.Pop(pane);
                        if (pane == primaryPane)
                            newState = state;
                    }
                }
                else
                    newState = primaryPane.ZoomStack.Pop(primaryPane);
                // Provide Callback to notify the user of zoom events
                if (this.ZoomEvent != null)
                    this.ZoomEvent(this, oldState, newState);
                Refresh();
            }
        }
        /// 
        /// Handler for the "Undo All Zoom/Pan" context menu item.  Restores the scale ranges to the values
        /// before all zoom and pan operations
        /// 
        /// 
        /// This method differs from the  method in that it sets the scales
        /// to their initial setting prior to any user actions.  The  method
        /// sets the scales to full auto mode (regardless of what the initial setting may have been).
        /// 
        /// 
        /// 
        protected void MenuClick_ZoomOutAll(System.Object sender, System.EventArgs e)
        {
            if (_masterPane != null)
            {
                GraphPane pane = _masterPane.FindPane(_menuClickPt);
                ZoomOutAll(pane);
            }
        }
        /// 
        /// Handler for the "Undo All Zoom/Pan" context menu item.  Restores the scale ranges to the values
        /// before all zoom and pan operations
        /// 
        /// 
        /// This method differs from the  method in that it sets the scales
        /// to their initial setting prior to any user actions.  The  method
        /// sets the scales to full auto mode (regardless of what the initial setting may have been).
        /// 
        /// The  object which is to be zoomed out
        public void ZoomOutAll(GraphPane primaryPane)
        {
            if (primaryPane != null && !primaryPane.ZoomStack.IsEmpty)
            {
                ZoomState.StateType type = primaryPane.ZoomStack.Top.Type;
                ZoomState oldState = new ZoomState(primaryPane, type);
                //ZoomState newState = pane.ZoomStack.PopAll( pane );
                ZoomState newState = null;
                if (_isSynchronizeXAxes || _isSynchronizeYAxes)
                {
                    foreach (GraphPane pane in _masterPane._paneList)
                    {
                        ZoomState state = pane.ZoomStack.PopAll(pane);
                        if (pane == primaryPane)
                            newState = state;
                    }
                }
                else
                    newState = primaryPane.ZoomStack.PopAll(primaryPane);
                // Provide Callback to notify the user of zoom events
                if (this.ZoomEvent != null)
                    this.ZoomEvent(this, oldState, newState);
                Refresh();
            }
        }
        #endregion
        #region Printing
        /// 
        /// Handler for the "Page Setup..." context menu item.   Displays a
        /// .
        /// 
        /// 
        /// 
        protected void MenuClick_PageSetup(object sender, EventArgs e)
        {
            DoPageSetup();
        }
        /// 
        /// Handler for the "Print..." context menu item.   Displays a
        /// .
        /// 
        /// 
        /// 
        protected void MenuClick_Print(object sender, EventArgs e)
        {
            DoPrint();
        }
        /// 
        /// Rendering method used by the print context menu items
        /// 
        /// The applicable .
        /// A  instance providing
        /// page bounds, margins, and a Graphics instance for this printed output.
        /// 
        private void Graph_PrintPage(object sender, PrintPageEventArgs e)
        {
            PrintDocument pd = sender as PrintDocument;
            MasterPane mPane = this.MasterPane;
            bool[] isPenSave = new bool[mPane.PaneList.Count + 1];
            bool[] isFontSave = new bool[mPane.PaneList.Count + 1];
            isPenSave[0] = mPane.IsPenWidthScaled;
            isFontSave[0] = mPane.IsFontsScaled;
            for (int i = 0; i < mPane.PaneList.Count; i++)
            {
                isPenSave[i + 1] = mPane[i].IsPenWidthScaled;
                isFontSave[i + 1] = mPane[i].IsFontsScaled;
                if (_isPrintScaleAll)
                {
                    mPane[i].IsPenWidthScaled = true;
                    mPane[i].IsFontsScaled = true;
                }
            }
            RectangleF saveRect = mPane.Rect;
            SizeF newSize = mPane.Rect.Size;
            if (_isPrintFillPage && _isPrintKeepAspectRatio)
            {
                float xRatio = (float)e.MarginBounds.Width / (float)newSize.Width;
                float yRatio = (float)e.MarginBounds.Height / (float)newSize.Height;
                float ratio = Math.Min(xRatio, yRatio);
                newSize.Width *= ratio;
                newSize.Height *= ratio;
            }
            else if (_isPrintFillPage)
                newSize = e.MarginBounds.Size;
            mPane.ReSize(e.Graphics, new RectangleF(e.MarginBounds.Left,
                e.MarginBounds.Top, newSize.Width, newSize.Height));
            mPane.Draw(e.Graphics);
            using (Graphics g = this.CreateGraphics())
            {
                mPane.ReSize(g, saveRect);
                //g.Dispose();
            }
            mPane.IsPenWidthScaled = isPenSave[0];
            mPane.IsFontsScaled = isFontSave[0];
            for (int i = 0; i < mPane.PaneList.Count; i++)
            {
                mPane[i].IsPenWidthScaled = isPenSave[i + 1];
                mPane[i].IsFontsScaled = isFontSave[i + 1];
            }
        }
        /// 
        /// Gets or sets the  instance
        /// that is used for all of the context menu printing functions.
        /// 
        public PrintDocument PrintDocument
        {
            get
            {
                // Add a try/catch pair since the users of the control can't catch this one
                try
                {
                    if (_pdSave == null)
                    {
                        _pdSave = new PrintDocument();
                        _pdSave.PrintPage += new PrintPageEventHandler(Graph_PrintPage);
                    }
                }
                catch (Exception exception)
                {
                    MessageBox.Show(exception.Message);
                }
                return _pdSave;
            }
            set { _pdSave = value; }
        }
        /// 
        /// Display a  to the user, allowing them to modify
        /// the print settings for this .
        /// 
        public void DoPageSetup()
        {
            PrintDocument pd = PrintDocument;
            // Add a try/catch pair since the users of the control can't catch this one
            try
            {
                if (pd != null)
                {
                    //pd.PrintPage += new PrintPageEventHandler( GraphPrintPage );
                    PageSetupDialog setupDlg = new PageSetupDialog();
                    setupDlg.Document = pd;
                    if (setupDlg.ShowDialog() == DialogResult.OK)
                    {
                        pd.PrinterSettings = setupDlg.PrinterSettings;
                        pd.DefaultPageSettings = setupDlg.PageSettings;
                        // BUG in PrintDocument!!!  Converts in/mm repeatedly
                        // http://support.microsoft.com/?id=814355
                        // from http://www.vbinfozine.com/tpagesetupdialog.shtml, by Palo Mraz
                        //if ( System.Globalization.RegionInfo.CurrentRegion.IsMetric )
                        //{
                        //	setupDlg.Document.DefaultPageSettings.Margins = PrinterUnitConvert.Convert(
                        //	setupDlg.Document.DefaultPageSettings.Margins,
                        //	PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter );
                        //}
                    }
                }
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
        }
        /// 
        /// Display a  to the user, allowing them to select a
        /// printer and print the  contained in this
        /// .
        /// 
        public void DoPrint()
        {
            // Add a try/catch pair since the users of the control can't catch this one
            try
            {
                PrintDocument pd = PrintDocument;
                if (pd != null)
                {
                    //pd.PrintPage += new PrintPageEventHandler( Graph_PrintPage );
                    PrintDialog pDlg = new PrintDialog();
                    pDlg.Document = pd;
                    if (pDlg.ShowDialog() == DialogResult.OK)
                        pd.Print();
                }
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
        }
        /// 
        /// Display a , allowing the user to preview and
        /// subsequently print the  contained in this
        /// .
        /// 
        public void DoPrintPreview()
        {
            // Add a try/catch pair since the users of the control can't catch this one
            try
            {
                PrintDocument pd = PrintDocument;
                if (pd != null)
                {
                    PrintPreviewDialog ppd = new PrintPreviewDialog();
                    //pd.PrintPage += new PrintPageEventHandler( Graph_PrintPage );
                    ppd.Document = pd;
                    ppd.Show(this);
                }
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
        }
        #endregion
        #region Zoom States
        /// 
        /// Save the current states of the GraphPanes to a separate collection.  Save a single
        /// () GraphPane if the panes are not synchronized
        /// (see  and ),
        /// or save a list of states for all GraphPanes if the panes are synchronized.
        /// 
        /// The primary GraphPane on which zoom/pan/scroll operations
        /// are taking place
        /// The  that describes the
        /// current operation
        /// The  that corresponds to the
        /// .
        /// 
        private ZoomState ZoomStateSave(GraphPane primaryPane, ZoomState.StateType type)
        {
            ZoomStateClear();
            if (_isSynchronizeXAxes || _isSynchronizeYAxes)
            {
                foreach (GraphPane pane in _masterPane._paneList)
                {
                    ZoomState state = new ZoomState(pane, type);
                    if (pane == primaryPane)
                        _zoomState = state;
                    _zoomStateStack.Add(state);
                }
            }
            else
                _zoomState = new ZoomState(primaryPane, type);
            return _zoomState;
        }
        /// 
        /// Restore the states of the GraphPanes to a previously saved condition (via
        /// .  This is essentially an "undo" for live
        /// pan and scroll actions.  Restores a single
        /// () GraphPane if the panes are not synchronized
        /// (see  and ),
        /// or save a list of states for all GraphPanes if the panes are synchronized.
        /// 
        /// The primary GraphPane on which zoom/pan/scroll operations
        /// are taking place
        private void ZoomStateRestore(GraphPane primaryPane)
        {
            if (_isSynchronizeXAxes || _isSynchronizeYAxes)
            {
                for (int i = 0; i < _masterPane._paneList.Count; i++)
                {
                    if (i < _zoomStateStack.Count)
                        _zoomStateStack[i].ApplyState(_masterPane._paneList[i]);
                }
            }
            else if (_zoomState != null)
                _zoomState.ApplyState(primaryPane);
            ZoomStateClear();
        }
        /// 
        /// Place the previously saved states of the GraphPanes on the individual GraphPane
        ///  collections.  This provides for an
        /// option to undo the state change at a later time.  Save a single
        /// () GraphPane if the panes are not synchronized
        /// (see  and ),
        /// or save a list of states for all GraphPanes if the panes are synchronized.
        /// 
        /// The primary GraphPane on which zoom/pan/scroll operations
        /// are taking place
        /// The  that corresponds to the
        /// .
        /// 
        private void ZoomStatePush(GraphPane primaryPane)
        {
            if (_isSynchronizeXAxes || _isSynchronizeYAxes)
            {
                for (int i = 0; i < _masterPane._paneList.Count; i++)
                {
                    if (i < _zoomStateStack.Count)
                        _masterPane._paneList[i].ZoomStack.Add(_zoomStateStack[i]);
                }
            }
            else if (_zoomState != null)
                primaryPane.ZoomStack.Add(_zoomState);
            ZoomStateClear();
        }
        /// 
        /// Clear the collection of saved states.
        /// 
        private void ZoomStateClear()
        {
            _zoomStateStack.Clear();
            _zoomState = null;
        }
        /// 
        /// Clear all states from the undo stack for each GraphPane.
        /// 
        private void ZoomStatePurge()
        {
            foreach (GraphPane pane in _masterPane._paneList)
                pane.ZoomStack.Clear();
        }
        #endregion
    }
}