Click here to Skip to main content
15,885,537 members
Articles / Containers / Docker

SVG Artiste - An SVG Editor

Rate me:
Please Sign up or sign in to vote.
4.69/5 (23 votes)
3 Aug 2010CPOL14 min read 95.9K   15.4K   93  
A Vector based tool to create and edit SVG images
/*=======================================================================
� Copyright (C) Lyquidity Solutions Limited.� All rights reserved.
�
  THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  PARTICULAR PURPOSE.

  LYQUIDITY SOLUTIONS LIMITED DOES NOT IMPOSE ANY LIMITATION ON THE
  USE OF THIS CODE AND IT AN BE USED IN COMMERCIAL APPLICATIONS.  LYQUIDIY
  ACCEPTS NO RESPONSIBLY FOR ANY LIABILTY WHATEVER AND WILL NOT PROVIDE
  ANY SUPPORT TO USER OR THEIR CLIENTS.
  
  History:
  
  2005-09-30 Changed DrawControl() method to (at least) fix the bug
             occuring when the start value < 0.

=======================================================================*/

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
#if !FRAMEWORKMENUS
using Lyquidity.UtilityLibrary.MenusA;
using Lyquidity.UtilityLibrary.General;
using System.ComponentModel.Design;
#endif

namespace Lyquidity.UtilityLibrary.Controls
{
	#region Enumerations

	public enum enumOrientation
	{
		orHorizontal = 0,
		orVertical = 1
	}

	public enum enumScaleMode
	{
		smPoints = 0,
		smPixels = 1,
		smCentimetres = 2,
		smInches = 3,
		smMillimetres = 4
	}

	public enum enumRulerAlignment
	{
		raTopOrLeft,
		raMiddle,
		raBottomOrRight
	}


	internal enum Msg
	{
		WM_MOUSEMOVE              = 0x0200,
		WM_MOUSELEAVE             = 0x02A3,
		WM_NCMOUSELEAVE           = 0x02A2,
	}

	#endregion

	/// <summary>
	/// Summary description for RulerControl.
	/// </summary>
	[ToolboxItem(true)]
	[ToolboxBitmap(typeof(RulerControl), "Ruler.bmp")]
	public class RulerControl : System.Windows.Forms.Control, IMessageFilter
	{

		#region Internal Variables

#if !FRAMEWORKMENUS
		private PopupMenu			_mnuContext			= null;
#endif
		private int					_Scale;
		private bool				_bDrawLine			= false;
		private bool				_bInControl			= false;
		private int					_iMousePosition		= 1;
		private int					_iOldMousePosition	= -1;
		private Bitmap				_Bitmap				= null;

		#endregion

		#region Property variable

		private enumOrientation		_Orientation;
		private enumScaleMode		_ScaleMode;
		private enumRulerAlignment	_RulerAlignment     = enumRulerAlignment.raBottomOrRight;
		private Border3DStyle		_i3DBorderStyle     = Border3DStyle.Etched;
		private int					_iMajorInterval     = 100;
		private int					_iNumberOfDivisions = 10;
		private int					_DivisionMarkFactor = 5;
		private int					_MiddleMarkFactor	= 3;
		private double				_ZoomFactor         = 1;
		private double				_StartValue			= 0;
		private bool				_bMouseTrackingOn   = false;
		private bool				_VerticalNumbers	= true;
		private bool				_bActualSize		= true;
		private float				_DpiX				= 96;

		#endregion

		#region Event Arguments

		public class ScaleModeChangedEventArgs : EventArgs
		{
			public enumScaleMode Mode;

			public ScaleModeChangedEventArgs(enumScaleMode Mode) : base()
			{
				this.Mode = Mode;
			}
		}

		public class HooverValueEventArgs : EventArgs
		{
			public double Value;

			public HooverValueEventArgs(double Value) : base()
			{
				this.Value = Value;
			}
		}


		#endregion

		#region Delegates

		public delegate void ScaleModeChangedEvent(object sender, ScaleModeChangedEventArgs e);
		public delegate void HooverValueEvent(object sender, HooverValueEventArgs e);
		// public delegate void ClickEvent(object sender, ClickEventArgs e);

		#endregion

		#region Events

		public event ScaleModeChangedEvent ScaleModeChanged;
		public event HooverValueEvent HooverValue;

		#endregion

		/// <summary> 
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;

		#region Constrcutors/Destructors

		public RulerControl()
		{
			base.BackColor = System.Drawing.Color.White;
			base.ForeColor = System.Drawing.Color.Black;

			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();

#if !FRAMEWORKMENUS

			// Create the popup menu object
			_mnuContext = new PopupMenu();

			// Create the menu objects
			MenuCommand mnuPoints = new MenuCommand("Points", new EventHandler(Popup_Click));
			mnuPoints.Tag = enumScaleMode.smPoints;
			MenuCommand mnuPixels = new MenuCommand("Pixels", new EventHandler(Popup_Click));
			mnuPixels.Tag = enumScaleMode.smPixels;
			MenuCommand mnuCentimetres = new MenuCommand("Centimetres", new EventHandler(Popup_Click));
			mnuCentimetres.Tag = enumScaleMode.smCentimetres;
			MenuCommand mnuInches = new MenuCommand("Inches", new EventHandler(Popup_Click));
			mnuInches.Tag = enumScaleMode.smInches;
			MenuCommand mnuInches = new MenuCommand("Millimetres", new EventHandler(Popup_Click));
			mnuInches.Tag = enumScaleMode.smMillimetres;

			// Define the list of menu commands
			_mnuContext.MenuCommands.AddRange(new MenuCommand[]{mnuPoints, mnuPixels, mnuCentimetres, mnuInches});

			// Define the properties to get appearance to match MenuControl
			_mnuContext.Style = VisualStyle.IDE;

#endif

#if FRAMEWORKMENUS

			System.Windows.Forms.MenuItem mnuPoints = new System.Windows.Forms.MenuItem("Points", new EventHandler(Popup_Click));
			System.Windows.Forms.MenuItem mnuPixels = new System.Windows.Forms.MenuItem("Pixels", new EventHandler(Popup_Click));
			System.Windows.Forms.MenuItem mnuCentimetres = new System.Windows.Forms.MenuItem("Centimetres", new EventHandler(Popup_Click));
			System.Windows.Forms.MenuItem mnuInches = new System.Windows.Forms.MenuItem("Inches", new EventHandler(Popup_Click));
			System.Windows.Forms.MenuItem mnuMillimetres = new System.Windows.Forms.MenuItem("Millimetres", new EventHandler(Popup_Click));
			ContextMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {mnuPoints, mnuPixels, mnuCentimetres, mnuInches, mnuMillimetres});
#endif
			Graphics g = this.CreateGraphics();
			_DpiX = g.DpiX;
			ScaleMode = enumScaleMode.smPoints;
		}

		#endregion

		#region Methods

		[EditorBrowsable(EditorBrowsableState.Never)]
		public bool PreFilterMessage(ref Message m)
		{
			if (!this._bMouseTrackingOn) return false;

			if (m.Msg == (int)Msg.WM_MOUSEMOVE)
			{
				int X = 0;
				int Y = 0;

				// The mouse coordinate are measured in screen coordinates because thats what 
				// Control.MousePosition returns.  The Message,LParam value is not used because
				// it returns the mouse position relative to the control the mouse is over. 
				// Chalk and cheese.

				Point pointScreen = Control.MousePosition;

				// Get the origin of this control in screen coordinates so that later we can 
				// compare it against the mouse point to determine it we've hit this control.

				Point pointClientOrigin = new Point(X, Y);
				pointClientOrigin = PointToScreen(pointClientOrigin);

				_bDrawLine = false;
				_bInControl = false;

				HooverValueEventArgs eHoover = null;

				// Work out whether the mouse is within the Y-axis bounds of a vertital ruler or 
				// within the X-axis bounds of a horizontal ruler

				if (this.Orientation == enumOrientation.orHorizontal)
				{
					_bDrawLine = (pointScreen.X >= pointClientOrigin.X) && (pointScreen.X <= pointClientOrigin.X + this.Width);
				}
				else
				{
					_bDrawLine = (pointScreen.Y >= pointClientOrigin.Y) && (pointScreen.Y <= pointClientOrigin.Y + this.Height);
				}

				// If the mouse is in valid position...
				if (_bDrawLine)
				{
					// ...workout the position of the mouse relative to the 
					X = pointScreen.X-pointClientOrigin.X;
					Y = pointScreen.Y-pointClientOrigin.Y;

					// Determine whether the mouse is within the bounds of the control itself
					_bInControl = (this.ClientRectangle.Contains(new Point(X, Y)));

					// Make the relative mouse position available in pixel relative to this control's origin
					ChangeMousePosition((this.Orientation == enumOrientation.orHorizontal) ? X : Y);
					eHoover = new HooverValueEventArgs(CalculateValue(_iMousePosition));

				} 
				else
				{
					ChangeMousePosition(-1);
					eHoover = new HooverValueEventArgs(_iMousePosition);
				}

				// Paint directly by calling the OnPaint() method.  This way the background is not 
				// hosed by the call to Invalidate() so paining occurs without the hint of a flicker
				PaintEventArgs e = null;
				try
				{
					e = new PaintEventArgs(this.CreateGraphics(), this.ClientRectangle);
					OnPaint(e);
				}
				finally
				{
					e.Graphics.Dispose();
				}

				OnHooverValue(eHoover);
			}

			if ((m.Msg == (int)Msg.WM_MOUSELEAVE) || 
				(m.Msg == (int)Msg.WM_NCMOUSELEAVE))
			{
				_bDrawLine = false;
				PaintEventArgs paintArgs = null;
				try
				{
					paintArgs = new PaintEventArgs(this.CreateGraphics(), this.ClientRectangle);
					this.OnPaint(paintArgs);
				}
				finally
				{
					paintArgs.Graphics.Dispose();
				}
			}

			return false;  // Whether or not the message is filtered
		}


		public double PixelToScaleValue(int iOffset)
		{
			return this.CalculateValue(iOffset);
		}

		public int ScaleValueToPixel(double nScaleValue)
		{
			return CalculatePixel(nScaleValue);
		}

		#endregion

		#region Component Designer generated code
		/// <summary> 
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			// 
			// RulerControl
			// 
			this.Name = "RulerControl";
			this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.RulerControl_MouseUp);

			base.SetStyle(System.Windows.Forms.ControlStyles.DoubleBuffer, true);

#if FRAMEWORKMENUS
			this.ContextMenu = new ContextMenu();
			this.ContextMenu.Popup +=new EventHandler(ContextMenu_Popup);
#endif

		}
		#endregion

		#region Overrides

		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		protected override void OnResize(EventArgs e)
		{
			base.OnResize (e);

			// Take private resize actions here
			_Bitmap = null;
			this.Invalidate();
		}

		public override void Refresh()
		{
			base.Refresh ();
			this.Invalidate();
		}


		[Description("Draws the ruler marks in the scale requested.")]
		protected override void OnPaint(PaintEventArgs e)
		{
			base.OnPaint (e);
			DrawControl(e.Graphics);
		}

		protected override void OnVisibleChanged(EventArgs e)
		{
			base.OnVisibleChanged (e);

			if (this.Visible)
			{
				if (_bMouseTrackingOn) Application.AddMessageFilter(this);
			}
			else
			{
				// DOn't change the tracking state so that the filter will be added again when the control is made visible again; 
				if (_bMouseTrackingOn) RemoveMessageFilter(); 
			}
		}

		protected override void OnHandleDestroyed(EventArgs e)
		{
			base.OnHandleDestroyed (e);
			RemoveMessageFilter();
			_bMouseTrackingOn = false;
		}

		protected override void OnControlRemoved(ControlEventArgs e)
		{
			base.OnControlRemoved (e);
			RemoveMessageFilter();
			_bMouseTrackingOn = false;
		}

		private void RemoveMessageFilter()
		{
			try
			{
				if (_bMouseTrackingOn) 
				{
					Application.RemoveMessageFilter(this);
				}
			} 
			catch {}
		}

		#endregion

		#region Event Handlers

		private void RulerControl_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
		{
//			if (e.Button.Equals(MouseButtons.Right)) 
		}

		private void RulerControl_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
		{
#if FRAMEWORKMENUS
			if ((Control.MouseButtons & MouseButtons.Right) != 0) 
			{
				this.ContextMenu.Show(this, PointToClient(Control.MousePosition));
#else
			if ((e.Button & MouseButtons.Right) != 0)
			{
				_mnuContext.TrackPopup(this.PointToScreen(new Point(e.X, e.Y)));
#endif
			}
			else
			{
				EventArgs eClick = new EventArgs();
				this.OnClick(eClick);
			}
		}

		private void Popup_Click(object sender, EventArgs e)
		{
#if FRAMEWORKMENUS
			System.Windows.Forms.MenuItem item = (System.Windows.Forms.MenuItem)sender;
			ScaleMode = (enumScaleMode)item.Index;
#else
			MenuCommand item = (MenuCommand)sender;
			ScaleMode = (enumScaleMode)item.Tag;
#endif
			_Bitmap = null;
			Invalidate();
		}

		protected void OnHooverValue(HooverValueEventArgs e)
		{
			if (HooverValue != null) HooverValue(this, e);
		}

		protected void OnScaleModeChanged(ScaleModeChangedEventArgs e)
		{
			if (ScaleModeChanged != null) ScaleModeChanged(this, e);
		}

		protected override void OnEnter(EventArgs e)
		{
			base.OnEnter (e);
			_bDrawLine = false;
			Invalidate();
		}

		protected override void OnLeave(EventArgs e)
		{
			base.OnLeave (e);
			Invalidate();
		}

		private void ContextMenu_Popup(object sender, EventArgs e)
		{
			System.Diagnostics.Debug.WriteLine("Popup");
		}

		#endregion

		#region Properties

		[
		DefaultValue(typeof(Border3DStyle),"Etched"),
		Description("The border style use the Windows.Forms.Border3DStyle type"),
		Category("Ruler"),
		]
		public Border3DStyle BorderStyle
		{
			get
			{
				return _i3DBorderStyle;
			}
			set
			{
				_i3DBorderStyle = value;
				_Bitmap = null;
				Invalidate();
			}
		}

		[Description("Horizontal or vertical layout")]
		[Category("Ruler")]
		public enumOrientation Orientation
		{ 
			get { return _Orientation; }
			set 
			{
				_Orientation = value;
				_Bitmap = null;
				Invalidate();
			}
		}

		[Description("A value from which the ruler marking should be shown.  Default is zero.")]
		[Category("Ruler")]
		public double StartValue
		{
			get { return _StartValue; }
			set 
			{
				_StartValue = value;
				_Bitmap = null;
				Invalidate();
			}
		}

		[Description("The scale to use")]
		[Category("Ruler")]
		public enumScaleMode ScaleMode
		{
			get { return _ScaleMode; }
			set 
			{
				enumScaleMode iOldScaleMode = _ScaleMode;
				_ScaleMode = value;    

				if (_iMajorInterval == DefaultMajorInterval(iOldScaleMode))
				{
					// Set the default Scale and MajorInterval value
					_Scale = DefaultScale(_ScaleMode);
					_iMajorInterval = DefaultMajorInterval(_ScaleMode);

				} 
				else
				{
					MajorInterval = _iMajorInterval;
				}

				// Use the current start value (if there is one)
				this.StartValue = this._StartValue;

				// Setup the menu
				for(int i = 0; i <= 4; i++)
#if FRAMEWORKMENUS
					ContextMenu.MenuItems[i].Checked = false;

				ContextMenu.MenuItems[(int)value].Checked = true;
#else
					_mnuContext.MenuCommands[i].Checked = false;

				_mnuContext.MenuCommands[(int)value].Checked = true;
#endif

				ScaleModeChangedEventArgs e = new ScaleModeChangedEventArgs(value);
				this.OnScaleModeChanged(e);
			}
		}

		[Description("The value of the major interval.  When displaying inches, 1 is a typical value.  When displaying Points, 36 or 72 might good values.")]
		[Category("Ruler")]
		public int MajorInterval
		{
			get { return _iMajorInterval; }
			set 
			{ 
				if (value <=0) throw new Exception("The major interval value cannot be less than one");
				_iMajorInterval = value;
				_Scale = DefaultScale(_ScaleMode) * _iMajorInterval / DefaultMajorInterval(_ScaleMode);
				_Bitmap = null;
				Invalidate();
			}
		}

		[Description("How many divisions should be shown between each major interval")]
		[Category("Ruler")]
		public int Divisions
		{
			get { return _iNumberOfDivisions; }
			set 
			{
				if (value <=0) throw new Exception("The number of divisions cannot be less than one");
				_iNumberOfDivisions = value;
				_Bitmap = null;
				Invalidate();
			}
		}

		[Description("The height or width of this control multiplied by the reciprocal of this number will be used to compute the height of the non-middle division marks.")]
		[Category("Ruler")]
		public int DivisionMarkFactor
		{
			get { return _DivisionMarkFactor; }
			set 
			{ 
				if (value <=0) throw new Exception("The Division Mark Factor cannot be less than one");
				_DivisionMarkFactor = value;
				_Bitmap = null;
				Invalidate();
			}
		}

		[Description("The height or width of this control multiplied by the reciprocal of this number will be used to compute the height of the middle division mark.")]
		[Category("Ruler")]
		public int MiddleMarkFactor
		{
			get { return _MiddleMarkFactor; }
			set
			{
				if (value <=0) throw new Exception("The Middle Mark Factor cannot be less than one");
				_MiddleMarkFactor = value;
				_Bitmap = null;
				Invalidate();
			}
		}

		[Description("The value of the current mouse position expressed in unit of the scale set (centimetres, inches, etc.")]
		[Category("Ruler")]
		public double ScaleValue
		{
			get {return CalculateValue(_iMousePosition); }
		}

		[Description("TRUE if a line is displayed to track the current position of the mouse and events are generated as the mouse moves.")]
		[Category("Ruler")]
		public bool MouseTrackingOn
		{
			get { return _bMouseTrackingOn; }
			set 
			{ 
				if (value == _bMouseTrackingOn) return;
				
				if (value)
				{
					// Tracking is being enabled so add the message filter hook
					if (this.Visible) Application.AddMessageFilter(this);
				}
				else
				{
					// Tracking is being disabled so remove the message filter hook
					Application.RemoveMessageFilter(this);
					ChangeMousePosition(-1);
				}

				_bMouseTrackingOn = value;

				_Bitmap = null;
				Invalidate();
			}
		}

		[Description("The font used to display the division number")]
		public override Font Font
		{
			get
			{
				return base.Font;
			}
			set
			{
				base.Font = value;
				_Bitmap = null;
				Invalidate();
			}
		}

		[Description("Return the mouse position as number of pixels from the top or left of the control.  -1 means that the mouse is positioned before or after the control.")]
		[Category("Ruler")]
		public int MouseLocation
		{
			get { return _iMousePosition; }
		}

		[DefaultValue(typeof(Color), "ControlDarkDark")]
		[Description("The color used to lines and numbers on the ruler")]
		public override Color ForeColor
		{
			get
			{
				return base.ForeColor;
			}
			set
			{
				base.ForeColor = value;
				_Bitmap = null;
				Invalidate();
			}
		}

		[DefaultValue(typeof(Color), "White")]
		[Description("The color used to paint the background of the ruler")]
		public override Color BackColor
		{
			get
			{
				return base.BackColor;
			}
			set
			{
				base.BackColor = value;
				_Bitmap = null;
				Invalidate();
			}
		}


		[Description("")]
		[Category("Ruler")]
		public bool VerticalNumbers
		{
			get { return _VerticalNumbers; }
			set
			{
				_VerticalNumbers = value;
				_Bitmap = null;
				Invalidate();
			}
		}

		[Description("A factor between 0.1 and 10 by which the displayed scale will be zoomed.")]
		[Category("Ruler")]
		public double ZoomFactor
		{
			get { return _ZoomFactor; }
			set 
			{
				if ((value < 0.1) || (value > 10)) throw new Exception("Zoom factor can be between 10% and 1000%");
				if (_ZoomFactor == value) return;
				_ZoomFactor = value;
				this.ScaleMode = _ScaleMode;
				_Bitmap = null;
				Invalidate();
			}
		}


		[Description("True if the ruler measurement is to be based on the systems pixels per inch figure")]
		[Category("Ruler")]
		public bool ActualSize
		{
			get { return _bActualSize; }
			set 
			{
				if (_bActualSize == value) return;
				_bActualSize = value;
				this.ScaleMode = _ScaleMode;
				_Bitmap = null;
				Invalidate();
			}
		}

		[Description("Determines how the ruler markings are displayed")]
		[Category("Ruler")]
		public enumRulerAlignment RulerAlignment
		{
			get { return _RulerAlignment; }
			set 
			{
				if (_RulerAlignment == value) return;
				_RulerAlignment = value;
				_Bitmap = null;
				Invalidate();
			}
		}


		#endregion

		#region Private functions

		private double CalculateValue(int iOffset)
		{
			if (iOffset < 0) return 0;

			double nValue = ((double)iOffset-Start()) / (double)_Scale * (double)_iMajorInterval;
			return nValue + this._StartValue;
		}

		[Description("May not return zero even when a -ve scale number is given as the returned value needs to allow for the border thickness")]
		private int CalculatePixel(double nScaleValue)
		{

			double nValue = nScaleValue - this._StartValue;
			if (nValue < 0) return Start();  // Start is the offset to the actual display area to allow for the border (if any)

			int iOffset = Convert.ToInt32(nValue / (double)_iMajorInterval * (double)_Scale);

			return iOffset + Start();
		}

		public void RenderTrackLine(Graphics g)
		{
			if (_bMouseTrackingOn & _bDrawLine)
			{
				int iOffset = Offset();

				// Optionally render Mouse tracking line
				switch(Orientation)
				{
					case enumOrientation.orHorizontal:
						Line(g, _iMousePosition, iOffset, _iMousePosition, Height - iOffset);
						break;
					case enumOrientation.orVertical:
						Line (g, iOffset, _iMousePosition, Width - iOffset, _iMousePosition);
						break;
				}
			}
		}

		private void DrawControl(Graphics graphics)
		{
			Graphics g = null;

			if (!this.Visible) return;

			// Bug reported by Kristoffer F 
			if (this.Width < 1 || this.Height < 1) 
			{
#if DEBUG
				System.Diagnostics.Trace.WriteLine("Minimised?");
#endif
				return;
			}

			if (_Bitmap == null)
			{

				int  iValueOffset = 0;
				int  iScaleStartValue;

				// Create a bitmap
				_Bitmap = new Bitmap(this.Width, this.Height);

				g = Graphics.FromImage(_Bitmap);

				try
				{
					// Wash the background with BackColor
					g.FillRectangle(new SolidBrush(this.BackColor), 0, 0, _Bitmap.Width, _Bitmap.Height);

					if (this.StartValue >= 0) 
					{
						iScaleStartValue = Convert.ToInt32(_StartValue * _Scale / _iMajorInterval);  // Convert value to pixels
					} 
					else
					{
						// If the start value is -ve then assume that we are starting just above zero
						// For example if the requested value -1.1 then make believe that the start is
						// +0.9.  We can fix up the printing of numbers later.
						double dStartValue = Math.Ceiling(Math.Abs(_StartValue))-Math.Abs(_StartValue);

						// Compute the offset that is to be used with the start point is -ve
						// This will be subtracted from the number calculated for the display numeral
						iScaleStartValue = Convert.ToInt32(dStartValue * _Scale / _iMajorInterval);  // Convert value to pixels
						iValueOffset = Convert.ToInt32(Math.Ceiling(Math.Abs(_StartValue)));
					};

					// Paint the lines on the image
					int iScale = _Scale;

					int iStart = Start();  // iStart is the pixel number on which to start.
					int iEnd = (this.Orientation == enumOrientation.orHorizontal) ? Width : Height;

#if DEBUG
					if (this.Orientation == enumOrientation.orVertical)
					{
						System.Diagnostics.Debug.WriteLine("Vert");
					}
#endif
					for(int j = iStart; j <= iEnd; j += iScale)
					{
						int iLeft = _Scale;  // Make an assumption that we're starting at zero or on a major increment
						int jOffset = j+iScaleStartValue;

						iScale = ((jOffset-iStart) % _Scale);  // Get the mod value to see if this is "big line" opportunity

						// If it is, draw big line
						if (iScale == 0)
						{
							if (_RulerAlignment != enumRulerAlignment.raMiddle)
							{
								if (this.Orientation == enumOrientation.orHorizontal)
									Line(g, j, 0, j, Height);
								else
									Line (g, 0, j, Width, j);
							}

							iLeft = _Scale;     // Set the for loop increment
						} 
						else
						{
							iLeft = _Scale - Math.Abs(iScale);     // Set the for loop increment
						}

						iScale = iLeft;

						int iValue = (((jOffset-iStart)/_Scale)+1) * _iMajorInterval;

						// Accommodate the offset if the starting point is -ve
						iValue -= iValueOffset;
						DrawValue(g, iValue, j - iStart, iScale);

						int iUsed = 0;

						// TO DO: This must be wrong when the start is negative and not a whole number
						//Draw small lines
						for(int i = 0; i < _iNumberOfDivisions; i++)
						{
							// Get the increment for the next mark
							int iX = Convert.ToInt32(Math.Round((double)(_Scale-iUsed)/(double)(_iNumberOfDivisions - i),0)); // Use a spreading algorithm rather that using expensive floating point numbers

							// So the next mark will have used up 
							iUsed += iX;

							if (iUsed >= (_Scale-iLeft))
							{
								iX = iUsed+j-(_Scale-iLeft);

								// Is it an even number and, if so, is it the middle value?
								bool bMiddleMark = ((_iNumberOfDivisions & 0x1) == 0) & (i+1==_iNumberOfDivisions/2);
								bool bShowMiddleMark = bMiddleMark;
								bool bLastDivisionMark = (i+1 == _iNumberOfDivisions);
								bool bLastAlignMiddleDivisionMark =  bLastDivisionMark & (_RulerAlignment == enumRulerAlignment.raMiddle);
								bool bShowDivisionMark = !bMiddleMark & !bLastAlignMiddleDivisionMark;

								if (bShowMiddleMark)
								{
									DivisionMark(g, iX, _MiddleMarkFactor);  // Height or Width will be 1/3
								} 
								else if (bShowDivisionMark)
								{
									DivisionMark(g, iX, _DivisionMarkFactor);  // Height or Width will be 1/5
								}
							}
						}
					}

					if (_i3DBorderStyle != Border3DStyle.Flat)
						ControlPaint.DrawBorder3D(g, this.ClientRectangle, this._i3DBorderStyle );

				}
				catch(Exception ex)
				{
					System.Diagnostics.Debug.WriteLine(ex.Message);
				}
				finally 
				{
					g.Dispose();
				}
			}

			g = graphics;

			try
			{

				// Always draw the bitmap
				g.DrawImage(_Bitmap, this.ClientRectangle);

				RenderTrackLine(g);
			}
			catch(Exception ex)
			{
				System.Diagnostics.Debug.WriteLine(ex.Message);
			}
			finally
			{
				GC.Collect();
			}

		}

		private void DivisionMark(Graphics g, int iPosition, int iProportion)
		{
			// This function is affected by the RulerAlignment setting

			int iMarkStart = 0, iMarkEnd = 0;

			if (this.Orientation == enumOrientation.orHorizontal)
			{

				switch(_RulerAlignment)
				{
					case enumRulerAlignment.raBottomOrRight:
					{
						iMarkStart = Height - Height/iProportion;
						iMarkEnd = Height;
						break;
					}
					case enumRulerAlignment.raMiddle:
					{
						iMarkStart = (Height - Height/iProportion)/2 -1;
						iMarkEnd = iMarkStart + Height/iProportion;
						break;
					}
					case enumRulerAlignment.raTopOrLeft:
					{
						iMarkStart = 0;
						iMarkEnd = Height/iProportion;
						break;
					}
				}

				Line(g, iPosition, iMarkStart, iPosition, iMarkEnd);
			}
			else
			{

				switch(_RulerAlignment)
				{
					case enumRulerAlignment.raBottomOrRight:
					{
						iMarkStart = Width - Width/iProportion;
						iMarkEnd = Width;
						break;
					}
					case enumRulerAlignment.raMiddle:
					{
						iMarkStart = (Width - Width/iProportion)/2 -1;
						iMarkEnd = iMarkStart + Width/iProportion;
						break;
					}
					case enumRulerAlignment.raTopOrLeft:
					{
						iMarkStart = 0;
						iMarkEnd = Width/iProportion;
						break;
					}
				}

				Line(g, iMarkStart, iPosition, iMarkEnd, iPosition);
			}
		}

		private void DrawValue(Graphics g, int iValue, int iPosition, int iSpaceAvailable)
		{

			// The sizing operation is common to all options
			StringFormat format = new StringFormat(StringFormatFlags.MeasureTrailingSpaces);
			if (_VerticalNumbers)
				format.FormatFlags |= StringFormatFlags.DirectionVertical;
			
			SizeF size = g.MeasureString((iValue).ToString(), this.Font, iSpaceAvailable, format);

			Point drawingPoint;
			int iX = 0;
			int iY = 0;

			if (this.Orientation == enumOrientation.orHorizontal)
			{
				switch(_RulerAlignment)
				{
					case enumRulerAlignment.raBottomOrRight:
					{
						iX = iPosition + iSpaceAvailable - (int)size.Width - 2;
						iY = 2;
						break;
					}
					case enumRulerAlignment.raMiddle:
					{
						iX = iPosition + iSpaceAvailable - (int)size.Width/2;
						iY = (Height - (int)size.Height)/2 - 2;
						break;
					}
					case enumRulerAlignment.raTopOrLeft:
					{
						iX = iPosition + iSpaceAvailable - (int)size.Width - 2;
						iY = Height - 2 - (int)size.Height;
						break;
					}
				}

				drawingPoint = new Point(iX, iY);
			}
			else
			{
				switch(_RulerAlignment)
				{
					case enumRulerAlignment.raBottomOrRight:
					{
						iX = 2;
						iY = iPosition + iSpaceAvailable - (int)size.Height - 2;
						break;
					}
					case enumRulerAlignment.raMiddle:
					{
						iX = (Width - (int)size.Width)/2 - 1;
						iY = iPosition + iSpaceAvailable - (int)size.Height/2;
						break;
					}
					case enumRulerAlignment.raTopOrLeft:
					{
						iX = Width - 2 - (int)size.Width;
						iY = iPosition + iSpaceAvailable - (int)size.Height - 2;
						break;
					}
				}

				drawingPoint = new Point(iX, iY);
			}

			// The drawstring function is common to all operations

			g.DrawString(iValue.ToString(), this.Font, new SolidBrush(this.ForeColor), drawingPoint, format);
		}

		private void Line(Graphics g, int x1, int y1, int x2, int y2)
		{
			using(SolidBrush brush = new SolidBrush(this.ForeColor))
			{
				using(Pen pen = new Pen(brush))
				{
					g.DrawLine(pen, x1, y1, x2, y2);
					pen.Dispose();
					brush.Dispose();
				}
			}
		}

		private int DefaultScale(enumScaleMode iScaleMode)
		{
			int iScale = 100;

			// Set scaling
			switch(iScaleMode)
			{
					// Determines the *relative* proportions of each scale
				case enumScaleMode.smPoints:
					iScale = 660; // 132;
					break;
				case enumScaleMode.smPixels:
					iScale = 100;
					break;
				case enumScaleMode.smCentimetres:
					iScale = 262; // 53;
					break;
				case enumScaleMode.smInches:
					iScale = 660; // 132;
					break;
				case enumScaleMode.smMillimetres:
					iScale = 27;
					break;
/*
				case enumScaleMode.smPoints:
					iScale = 96;
					break;
				case enumScaleMode.smPixels:
					iScale = 100;
					break;
				case enumScaleMode.smCentimetres:
					iScale = 38;
					break;
				case enumScaleMode.smInches:
					iScale = 96;
					break;
				case enumScaleMode.smMillimetres:
					iScale = 4;
					break;
*/
			}

			if (iScaleMode == enumScaleMode.smPixels)
				return Convert.ToInt32((double)iScale * _ZoomFactor);
			else
				return Convert.ToInt32((double)iScale * _ZoomFactor * (double)(_bActualSize ? (double)_DpiX/(float)480 : 0.2));
		}

		private int DefaultMajorInterval(enumScaleMode iScaleMode)
		{
			int iInterval = 10;

			// Set scaling
			switch(iScaleMode)
			{
					// Determines the *relative* proportions of each scale
				case enumScaleMode.smPoints:
					iInterval = 72;
					break;
				case enumScaleMode.smPixels:
					iInterval = 100;
					break;
				case enumScaleMode.smCentimetres:
					iInterval = 1;
					break;
				case enumScaleMode.smInches:
					iInterval = 1;
					break;
				case enumScaleMode.smMillimetres:
					iInterval = 1;
					break;
			}

			return iInterval;
		}

		private int Offset()
		{
			int iOffset = 0;

			switch(this._i3DBorderStyle)
			{
				case Border3DStyle.Flat: iOffset = 0; break;
				case Border3DStyle.Adjust: iOffset = 0; break;
				case Border3DStyle.Sunken: iOffset = 2; break;
				case Border3DStyle.Bump: iOffset = 2; break;
				case Border3DStyle.Etched: iOffset = 2; break;
				case Border3DStyle.Raised: iOffset = 2; break;
				case Border3DStyle.RaisedInner: iOffset = 1; break;
				case Border3DStyle.RaisedOuter: iOffset = 1; break;
				case Border3DStyle.SunkenInner: iOffset = 1; break;
				case Border3DStyle.SunkenOuter: iOffset = 1; break;
				default: iOffset = 0; break;
			}

			return iOffset;
		}

		private int Start()
		{
			int iStart = 0;

			switch(this._i3DBorderStyle)
			{
				case Border3DStyle.Flat: iStart = 0; break;
				case Border3DStyle.Adjust: iStart = 0; break;
				case Border3DStyle.Sunken: iStart = 1; break;
				case Border3DStyle.Bump: iStart = 1; break;
				case Border3DStyle.Etched: iStart = 1; break;
				case Border3DStyle.Raised: iStart = 1; break;
				case Border3DStyle.RaisedInner: iStart = 0; break;
				case Border3DStyle.RaisedOuter: iStart = 0; break;
				case Border3DStyle.SunkenInner: iStart = 0; break;
				case Border3DStyle.SunkenOuter: iStart = 0; break;
				default: iStart = 0; break;
			}
			return iStart;
		}

		private void ChangeMousePosition(int iNewPosition)
		{
			this._iOldMousePosition = this._iMousePosition;
			this._iMousePosition = iNewPosition;
		}

	}

	#endregion

}

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

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

License

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


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

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

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

His ambition is to be an impressive software maker.

Comments and Discussions