Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

1215 lines (1107 sloc) 54.185 kb
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Diagnostics;
using System.Windows.Forms;
namespace MEditor
{
[System.ComponentModel.DefaultProperty("ParentRichTextBox")]
public class LineNums : System.Windows.Forms.Control
{
/// <summary>
/// Number currently
/// </summary>
private class LineNumberItem
{
internal int LineNumber;
internal Rectangle Rectangle;
internal LineNumberItem(int zLineNumber, Rectangle zRectangle)
{
this.LineNumber = zLineNumber;
this.Rectangle = zRectangle;
}
}
/// <summary>
/// Side to be docked to
/// </summary>
public enum LineNumberDockSide : byte
{
None = 0,
Left = 1,
Right = 2,
Height = 4
}
private RichTextBox zP = null;
/// <summary>
/// Parent textbox
/// </summary>
private RichTextBox zParent
{
get { return zP; }
set
{
//If there are events...
if (zP != null)
{
//Remove the event handlers
zP.LocationChanged -= zParent_Changed;
zP.Move -= zParent_Changed;
zP.Resize -= zParent_Changed;
zP.DockChanged -= zParent_Changed;
zP.TextChanged -= zParent_Changed;
zP.MultilineChanged -= zParent_Changed;
zP.HScroll -= zParent_Scroll;
zP.VScroll -= zParent_Scroll;
zP.ContentsResized -= zParent_ContentsResized;
zP.Disposed -= zParent_Disposed;
}
zP = value;
//If there are events...
if (zP != null)
{
//Make another set of Event Handlers
zP.LocationChanged += zParent_Changed;
zP.Move += zParent_Changed;
zP.Resize += zParent_Changed;
zP.DockChanged += zParent_Changed;
zP.TextChanged += zParent_Changed;
zP.MultilineChanged += zParent_Changed;
zP.HScroll += zParent_Scroll;
zP.VScroll += zParent_Scroll;
zP.ContentsResized += zParent_ContentsResized;
zP.Disposed += zParent_Disposed;
}
}
}
//Numbering Timer
private Timer withEventsField_zTimer = new Timer();
private Timer zTimer
{
get { return withEventsField_zTimer; }
set
{
//Timer tick
if (withEventsField_zTimer != null)
withEventsField_zTimer.Tick -= zTimer_Tick;
withEventsField_zTimer = value;
if (withEventsField_zTimer != null)
withEventsField_zTimer.Tick += zTimer_Tick;
}
}
//Auto sizing of numbers
private bool zAutoSizing = true;
private Size zAutoSizing_Size = new Size(0, 0);
private Rectangle zContentRectangle;
private LineNumberDockSide zDockSide = LineNumberDockSide.Left;
private bool zParentIsScrolling = false;
private bool zSeeThroughMode = false;
//Show gradient
private bool zGradient_Show = true;
private System.Drawing.Drawing2D.LinearGradientMode zGradient_Direction = System.Drawing.Drawing2D.LinearGradientMode.Horizontal;
private Color zGradient_StartColor = Color.FromArgb(0, 0, 0, 0);
private Color zGradient_EndColor = Color.LightSteelBlue;
//Show grid lines
private bool zGridLines_Show = true;
private float zGridLines_Thickness = 1;
private System.Drawing.Drawing2D.DashStyle zGridLines_Style = System.Drawing.Drawing2D.DashStyle.Dot;
private Color zGridLines_Color = Color.SlateGray;
//Show borders
private bool zBorderLines_Show = true;
private float zBorderLines_Thickness = 1;
private System.Drawing.Drawing2D.DashStyle zBorderLines_Style = System.Drawing.Drawing2D.DashStyle.Dot;
private Color zBorderLines_Color = Color.SlateGray;
//Show margine lines
private bool zMarginLines_Show = true;
private LineNumberDockSide zMarginLines_Side = LineNumberDockSide.Right;
private float zMarginLines_Thickness = 1;
private System.Drawing.Drawing2D.DashStyle zMarginLines_Style = System.Drawing.Drawing2D.DashStyle.Solid;
private Color zMarginLines_Color = Color.SlateGray;
//Show numbers
private bool zLineNumbers_Show = true;
private bool zLineNumbers_ShowLeadingZeroes = true;
private bool zLineNumbers_ShowAsHexadecimal = false;
private bool zLineNumbers_ClipByItemRectangle = true;
private Size zLineNumbers_Offset = new Size(0, 0);
private string zLineNumbers_Format = "0";
private ContentAlignment zLineNumbers_Alignment = ContentAlignment.TopRight;
private bool zLineNumbers_AntiAlias = true;
//Line Number list
private List<LineNumberItem> zLNIs = new List<LineNumberItem>();
private Point zPointInParent = new Point(0, 0);
private Point zPointInMe = new Point(0, 0);
private int zParentInMe = 0;
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Create new line numbering control
/// </summary>
public LineNums()
{
{
//Set the styles
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
Margin = new Padding(0);
Padding = new Padding(0, 0, 2, 0);
}
{
//Refresh on timer
zTimer.Enabled = true;
zTimer.Interval = 200;
zTimer.Stop();
}
Update_SizeAndPosition();
//Redraw numbering
Invalidate();
}
protected override void OnHandleCreated(System.EventArgs e)
{
base.OnHandleCreated(e);
//Don't autosize
AutoSize = false;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
[System.ComponentModel.Browsable(false)]
public override bool AutoSize
{
get { return base.AutoSize; }
set
{
base.AutoSize = value;
this.Invalidate();
}
}
[System.ComponentModel.Description("Use this property to automatically resize the control (and reposition it if needed).")]
[System.ComponentModel.Category("Additional Behavior")]
public bool AutoSizing
{
get { return zAutoSizing; }
set
{
zAutoSizing = value;
Refresh();
Invalidate();
}
}
/// <summary>
/// The Parent RTB to attach to
/// </summary>
[System.ComponentModel.Description("Use this property to enable Line Numbers for the chosen RichTextBox.")]
[System.ComponentModel.Category("Add LineNumbers to")]
public RichTextBox ParentBox
{
get { return zParent; }
set
{
zParent = value;
if (zParent != null)
{
Parent = zParent.Parent;
zParent.Refresh();
}
Text = "";
Refresh();
Invalidate();
}
}
/// <summary>
/// Side to attach to
/// </summary>
[System.ComponentModel.Description("Use this property to dock the Line Numbers to a chosen side of the chosen RichTextBox.")]
[System.ComponentModel.Category("Additional Behavior")]
public LineNumberDockSide DockSide
{
get { return zDockSide; }
set
{
zDockSide = value;
Refresh();
Invalidate();
}
}
/// <summary>
/// Act as overlay on box
/// </summary>
[System.ComponentModel.Description("Use this property to enable the control to act as an overlay ontop of the box.")]
[System.ComponentModel.Category("Additional Behavior")]
public bool _SeeThroughMode_
{
get { return zSeeThroughMode; }
set
{
zSeeThroughMode = value;
Invalidate();
}
}
/// <summary>
/// Show Borders on Control
/// </summary>
[System.ComponentModel.Description("BorderLines are shown on all sides of the LineNums control.")]
[System.ComponentModel.Category("Additional Behavior")]
public bool Show_BorderLines
{
get { return zBorderLines_Show; }
set
{
zBorderLines_Show = value;
Invalidate();
}
}
/// <summary>
/// Color of border lines
/// </summary>
[System.ComponentModel.Category("Additional Appearance")]
public Color BorderLines_Color
{
get { return zBorderLines_Color; }
set
{
zBorderLines_Color = value;
this.Invalidate();
}
}
/// <summary>
/// Thickness of borders
/// </summary>
[System.ComponentModel.Category("Additional Appearance")]
public float BorderLines_Thickness
{
get { return zBorderLines_Thickness; }
set
{
zBorderLines_Thickness = Math.Max(1, Math.Min(255, value));
Invalidate();
}
}
/// <summary>
/// Border style (Dashed, solid, dotted, etc.)
/// </summary>
[System.ComponentModel.Category("Additional Appearance")]
public System.Drawing.Drawing2D.DashStyle BorderLines_Style
{
get { return zBorderLines_Style; }
set
{
if (value == System.Drawing.Drawing2D.DashStyle.Custom)
value = System.Drawing.Drawing2D.DashStyle.Solid;
zBorderLines_Style = value;
Invalidate();
}
}
/// <summary>
/// Show grid between numbers
/// </summary>
[System.ComponentModel.Description("GridLines are the horizontal divider-lines shown above each Line Number.")]
[System.ComponentModel.Category("Additional Behavior")]
public bool Show_GridLines
{
get { return zGridLines_Show; }
set
{
zGridLines_Show = value;
Invalidate();
}
}
/// <summary>
/// Color of grid lines
/// </summary>
[System.ComponentModel.Category("Additional Appearance")]
public Color GridLines_Color
{
get { return zGridLines_Color; }
set
{
zGridLines_Color = value;
Invalidate();
}
}
/// <summary>
/// Thickness of grid
/// </summary>
[System.ComponentModel.Category("Additional Appearance")]
public float GridLines_Thickness
{
get { return zGridLines_Thickness; }
set
{
zGridLines_Thickness = Math.Max(1, Math.Min(255, value));
Invalidate();
}
}
/// <summary>
/// Grid style
/// </summary>
[System.ComponentModel.Category("Additional Appearance")]
public System.Drawing.Drawing2D.DashStyle GridLines_Style
{
get { return zGridLines_Style; }
set
{
if (value == System.Drawing.Drawing2D.DashStyle.Custom)
value = System.Drawing.Drawing2D.DashStyle.Solid;
zGridLines_Style = value;
Invalidate();
}
}
/// <summary>
/// Show the margine lines
/// </summary>
[System.ComponentModel.Description("Margin Lines on the Left or Right (or both in Height-mode) of the LineNums control.")]
[System.ComponentModel.Category("Additional Behavior")]
public bool Show_MarginLines
{
get { return zMarginLines_Show; }
set
{
zMarginLines_Show = value;
Invalidate();
}
}
/// <summary>
/// Side to show them
/// </summary>
[System.ComponentModel.Category("Additional Appearance")]
public LineNumberDockSide MarginLines_Side
{
get { return zMarginLines_Side; }
set
{
zMarginLines_Side = value;
Invalidate();
}
}
/// <summary>
/// Color of Margin lines
/// </summary>
[System.ComponentModel.Category("Additional Appearance")]
public Color MarginLines_Color
{
get { return zMarginLines_Color; }
set
{
zMarginLines_Color = value;
Invalidate();
}
}
/// <summary>
/// Thickness of margin lines
/// </summary>
[System.ComponentModel.Category("Additional Appearance")]
public float MarginLines_Thickness
{
get { return zMarginLines_Thickness; }
set
{
zMarginLines_Thickness = Math.Max(1, Math.Min(255, value));
Invalidate();
}
}
/// <summary>
/// Margin Line style
/// </summary>
[System.ComponentModel.Category("Additional Appearance")]
public System.Drawing.Drawing2D.DashStyle MarginLines_Style
{
get { return zMarginLines_Style; }
set
{
if (value == System.Drawing.Drawing2D.DashStyle.Custom) value = System.Drawing.Drawing2D.DashStyle.Solid;
zMarginLines_Style = value;
Invalidate();
}
}
/// <summary>
/// Show gradient on background
/// </summary>
[System.ComponentModel.Description("The Background Gradient is a gradual blend of two colors, shown in the back of each Line Number's item-area.")]
[System.ComponentModel.Category("Additional Behavior")]
public bool Show_BackgroundGradient
{
get { return zGradient_Show; }
set
{
zGradient_Show = value;
Invalidate();
}
}
/// <summary>
/// Gradient first color
/// </summary>
[System.ComponentModel.Category("Additional Appearance")]
public Color BackgroundGradient_AlphaColor
{
get { return zGradient_StartColor; }
set
{
zGradient_StartColor = value;
Invalidate();
}
}
/// <summary>
/// Gradient second color
/// </summary>
[System.ComponentModel.Category("Additional Appearance")]
public Color BackgroundGradient_BetaColor
{
get { return zGradient_EndColor; }
set
{
zGradient_EndColor = value;
Invalidate();
}
}
/// <summary>
/// Gradient color direction
/// </summary>
[System.ComponentModel.Category("Additional Appearance")]
public System.Drawing.Drawing2D.LinearGradientMode BackgroundGradient_Direction
{
get { return zGradient_Direction; }
set
{
zGradient_Direction = value;
Invalidate();
}
}
/// <summary>
/// Show line numbers
/// </summary>
[System.ComponentModel.Category("Additional Behavior")]
public bool Show_LineNrs
{
get { return zLineNumbers_Show; }
set
{
zLineNumbers_Show = value;
Invalidate();
}
}
/// <summary>
/// Force numbers to stay in container
/// </summary>
[System.ComponentModel.Description("Use this to set whether the Line Numbers are allowed to spill out of their item-area, or should be clipped by it.")]
[System.ComponentModel.Category("Additional Behavior")]
public bool LineNrs_ClippedByItemRectangle
{
get { return zLineNumbers_ClipByItemRectangle; }
set
{
zLineNumbers_ClipByItemRectangle = value;
Invalidate();
}
}
/// <summary>
/// Show leading zeros
/// </summary>
[System.ComponentModel.Description("Use this to set whether the Line Numbers should have leading zeroes (based on the total amount of textlines).")]
[System.ComponentModel.Category("Additional Behavior")]
public bool LineNrs_LeadingZeroes
{
get { return zLineNumbers_ShowLeadingZeroes; }
set
{
zLineNumbers_ShowLeadingZeroes = value;
this.Refresh();
this.Invalidate();
}
}
/// <summary>
/// Show numbers in hexadecimal format
/// </summary>
[System.ComponentModel.Description("Use this to set whether the Line Numbers should be shown as hexadecimal values.")]
[System.ComponentModel.Category("Additional Behavior")]
public bool LineNrs_AsHexadecimal
{
get { return zLineNumbers_ShowAsHexadecimal; }
set
{
zLineNumbers_ShowAsHexadecimal = value;
Refresh();
Invalidate();
}
}
/// <summary>
/// Manually reposition line numbers
/// </summary>
[System.ComponentModel.Description("Use this property to manually reposition the Line Numbers, relative to their current location.")]
[System.ComponentModel.Category("Additional Behavior")]
public Size LineNrs_Offset
{
get { return zLineNumbers_Offset; }
set
{
zLineNumbers_Offset = value;
Invalidate();
}
}
/// <summary>
/// Align line numbers
/// </summary>
[System.ComponentModel.Description("Use this to align the Line Numbers to a chosen corner (or center) within their item-area.")]
[System.ComponentModel.Category("Additional Behavior")]
public System.Drawing.ContentAlignment LineNrs_Alignment
{
get { return zLineNumbers_Alignment; }
set
{
zLineNumbers_Alignment = value;
Invalidate();
}
}
/// <summary>
/// Anti-alias numbers
/// </summary>
[System.ComponentModel.Description("Use this to apply Anti-Aliasing to the Line Numbers (high quality). Some fonts will look better without it, though.")]
[System.ComponentModel.Category("Additional Behavior")]
public bool LineNrs_AntiAlias
{
get { return zLineNumbers_AntiAlias; }
set
{
zLineNumbers_AntiAlias = value;
Refresh();
Invalidate();
}
}
/// <summary>
/// Set numbering font
/// </summary>
[System.ComponentModel.Browsable(true)]
public override System.Drawing.Font Font
{
get { return base.Font; }
set
{
base.Font = value;
Refresh();
Invalidate();
}
}
/// <summary>
/// Text of the numbering
/// </summary>
[System.ComponentModel.DefaultValue("")]
[System.ComponentModel.AmbientValue("")]
[System.ComponentModel.Browsable(false)]
public override string Text
{
get { return base.Text; }
set
{
base.Text = "";
Invalidate();
}
}
protected override void OnSizeChanged(System.EventArgs e)
{
if (this.DesignMode == true) this.Refresh();
base.OnSizeChanged(e);
Invalidate();
}
/// <summary>
/// Location changed
/// </summary>
protected override void OnLocationChanged(System.EventArgs e)
{
if (this.DesignMode == true) this.Refresh();
base.OnLocationChanged(e);
Invalidate();
}
public override void Refresh()
{
// Note: don't change the order here, first the Mybase.Refresh, then the Update_SizeAndPosition.
base.Refresh();
Update_SizeAndPosition();
}
/// <summary>
/// This Sub will run whenever Me.Refresh() is called. It applies the AutoSizing and DockSide settings.
/// </summary>
/// <remarks></remarks>
private void Update_SizeAndPosition()
{
//AutoSize container
if (AutoSize) return;
//Docked
if (Dock == DockStyle.Bottom || Dock == DockStyle.Fill || Dock == DockStyle.Top) return;
Point zNewLocation = this.Location;
Size zNewSize = this.Size;
if (zAutoSizing)
{
if (zParent == null)
{
// --- ReminderMessage sizing
if (zAutoSizing_Size.Width > 0) zNewSize.Width = zAutoSizing_Size.Width;
if (zAutoSizing_Size.Height > 0) zNewSize.Height = zAutoSizing_Size.Height;
this.Size = zNewSize;
}
//--- zParent isNot Nothing for the following cases
else if (Dock == DockStyle.Left || this.Dock == DockStyle.Right)
{
if (zAutoSizing_Size.Width > 0) zNewSize.Width = zAutoSizing_Size.Width;
this.Width = zNewSize.Width;
}
// --- DockSide is active L/R/H
else if (zDockSide != LineNumberDockSide.None)
{
if (zAutoSizing_Size.Width > 0) zNewSize.Width = zAutoSizing_Size.Width;
zNewSize.Height = zParent.Height;
if (zDockSide == LineNumberDockSide.Left) zNewLocation.X = zParent.Left - zNewSize.Width - 1;
if (zDockSide == LineNumberDockSide.Right) zNewLocation.X = zParent.Right + 1;
zNewLocation.Y = zParent.Top;
this.Location = zNewLocation;
this.Size = zNewSize;
}
// --- DockSide = None, but AutoSizing is still setting the Width
else if (zDockSide == LineNumberDockSide.None)
{
if (zAutoSizing_Size.Width > 0) zNewSize.Width = zAutoSizing_Size.Width;
this.Size = zNewSize;
}
}
else
{
if (zParent == null)
{
// --- ReminderMessage sizing
if (zAutoSizing_Size.Width > 0) zNewSize.Width = zAutoSizing_Size.Width;
if (zAutoSizing_Size.Height > 0) zNewSize.Height = zAutoSizing_Size.Height;
this.Size = zNewSize;
}
// --- No AutoSizing, but DockSide L/R/H is active so height and position need updates.
else if (zDockSide != LineNumberDockSide.None)
{
zNewSize.Height = zParent.Height;
if (zDockSide == LineNumberDockSide.Left) zNewLocation.X = zParent.Left - zNewSize.Width - 1;
if (zDockSide == LineNumberDockSide.Right) zNewLocation.X = zParent.Right + 1;
zNewLocation.Y = zParent.Top;
this.Location = zNewLocation;
this.Size = zNewSize;
}
}
}
/// <summary>
/// This Sub determines which textlines are visible in the ParentRichTextBox, and makes LineNumberItems (LineNumber + ItemRectangle)
/// for each visible line. They are put into the zLNIs List that will be used by the OnPaint event to draw the LineNumberItems.
/// </summary>
/// <remarks></remarks>
private void Update_VisibleLineNumberItems()
{
zLNIs.Clear();
zAutoSizing_Size = new Size(0, 0);
zLineNumbers_Format = "0";
//initial setting
// To measure the LineNumber's width, its Format 0 is replaced by w as that is likely to be one of the widest characters in non-monospace fonts.
if (zAutoSizing == true) zAutoSizing_Size = new Size(TextRenderer.MeasureText(zLineNumbers_Format.Replace('0', 'W'), this.Font).Width, 0);
if (zParent == null || string.IsNullOrEmpty(zParent.Text)) return;
// --- Make sure the LineNumbers are aligning to the same height as the zParent textlines by converting to screencoordinates
// and using that as an offset that gets added to the points for the LineNumberItems
zPointInParent = zParent.PointToScreen(zParent.ClientRectangle.Location);
zPointInMe = this.PointToScreen(new Point(0, 0));
// zParentInMe is the vertical offset to make the LineNumberItems line up with the textlines in zParent.
zParentInMe = zPointInParent.Y - zPointInMe.Y + 1;
// The first visible LineNumber may not be the first visible line of text in the RTB if the LineNumbercontrol's .Top is lower on the form than
// the .Top of the parent RichTextBox. Therefor, zPointInParent will now be used to find zPointInMe's equivalent height in zParent,
// which is needed to find the best StartIndex later on.
zPointInParent = zParent.PointToClient(zPointInMe);
// --- NOTES:
// Additional complication is the fact that when wordwrap is enabled on the RTB, the wordwrapped text spills into the RTB.Lines collection,
// so we need to split the text into lines ourselves, and use the Index of each zSplit-line's first character instead of the RTB's.
string[] zSplit = zParent.Text.Split(Environment.NewLine.ToCharArray());
if (zSplit.Length < 2)
{
// Just one line in the text = one linenumber
// NOTE: zContentRectangle is built by the zParent.ContentsResized event.
Point zPoint = zParent.GetPositionFromCharIndex(0);
zLNIs.Add(new LineNumberItem(1, new Rectangle(new Point(0, zPoint.Y - 1 + zParentInMe), new Size(this.Width, zContentRectangle.Height - zPoint.Y))));
}
else
{
// Multiple lines, but store only those LineNumberItems for lines that are visible.
TimeSpan zTimeSpan = new TimeSpan(DateTime.Now.Ticks);
Point zPoint = new Point(0, 0);
int zStartIndex = 0;
int zSplitStartLine = 0;
int zA = zParent.Text.Length - 1;
int zP = zPointInParent.Y;
this.FindStartIndex(ref zStartIndex, ref zA, ref zP);
// zStartIndex now holds the index of a character in the first visible line from zParent.Text
// Now it will be pointed at the first character of that line (chr(10) = Linefeed part of the vbCrLf constant)
zStartIndex = Math.Max(0, Math.Min(zParent.Text.Length - 1, zParent.Text.Substring(0, zStartIndex).LastIndexOf(Char.ConvertFromUtf32(10)) + 1));
// We now need to find out which zSplit-line that character is in, by counting the vbCrlf appearances that come before it.
zSplitStartLine = Math.Max(0, zParent.Text.Substring(0, zStartIndex).Split(Environment.NewLine.ToCharArray()).Length - 1);
// zStartIndex starts off pointing at the first character of the first visible line, and will be then be pointed to
// the index of the first character on the next line.
for (zA = zSplitStartLine; zA <= zSplit.Length - 1; zA++)
{
zPoint = zParent.GetPositionFromCharIndex(zStartIndex);
zStartIndex += Math.Max(1, zSplit[zA].Length + 1);
if (zPoint.Y + zParentInMe > this.Height) break; // TODO: might not be correct. Was : Exit For
// For performance reasons, the list of LineNumberItems (zLNIs) is first built with only the location of its
// itemrectangle being used. The height of those rectangles will be computed afterwards by comparing the items' Y coordinates.
zLNIs.Add(new LineNumberItem(zA + 1, new Rectangle(0, zPoint.Y - 1 + zParentInMe, this.Width, 1)));
if (zParentIsScrolling == true && DateTime.Now.Ticks > zTimeSpan.Ticks + 500000)
{
// The more lines there are in the RTB, the slower the RTB's .GetPositionFromCharIndex() method becomes
// To avoid those delays from interfering with the scrollingspeed, this speedbased exit for is applied (0.05 sec)
// zLNIs will have at least 1 item, and if that's the only one, then change its location to 0,0 to make it readable
if (zLNIs.Count == 1) zLNIs[0].Rectangle.Y = 0;
zParentIsScrolling = false;
zTimer.Start();
break; // TODO: might not be correct. Was : Exit For
}
}
if (zLNIs.Count == 0) return;
// Add an extra placeholder item to the end, to make the heightcomputation easier
if (zA < zSplit.Length)
{
// getting here means the for/next loop was exited before reaching the last zSplit textline
// zStartIndex will still be pointing to the startcharacter of the next line, so we can use that:
zPoint = zParent.GetPositionFromCharIndex(Math.Min(zStartIndex, zParent.Text.Length - 1));
zLNIs.Add(new LineNumberItem(-1, new Rectangle(0, zPoint.Y - 1 + zParentInMe, 0, 0)));
}
else
{
// getting here means the for/next loop ran to the end (zA is now zSplit.Length).
zLNIs.Add(new LineNumberItem(-1, new Rectangle(0, zContentRectangle.Bottom, 0, 0)));
}
// And now we can easily compute the height of the LineNumberItems by comparing each item's Y coordinate with that of the next line.
// There's at least two items in the list, and the last item is a "nextline-placeholder" that will be removed.
for (zA = 0; zA <= zLNIs.Count - 2; zA++)
{
zLNIs[zA].Rectangle.Height = Math.Max(1, zLNIs[zA + 1].Rectangle.Y - zLNIs[zA].Rectangle.Y);
}
// Removing the placeholder item
zLNIs.RemoveAt(zLNIs.Count - 1);
// Set the Format to the width of the highest possible number so that LeadingZeroes shows the correct amount of zeroes.
if (zLineNumbers_ShowAsHexadecimal == true)
{
zLineNumbers_Format = "".PadRight(zSplit.Length.ToString("X").Length, '0');
}
else
{
zLineNumbers_Format = "".PadRight(zSplit.Length.ToString().Length, '0');
}
}
// To measure the LineNumber's width, its Format 0 is replaced by w as that is likely to be one of the widest characters in non-monospace fonts.
if (zAutoSizing == true) zAutoSizing_Size = new Size(TextRenderer.MeasureText(zLineNumbers_Format.Replace('0', 'W'), this.Font).Width, 0);
}
/// <summary>
/// FindStartIndex is a recursive Sub (one that calls itself) to compute the first visible line that should have a LineNumber.
/// </summary>
/// <param name="zMin"> this will hold the eventual BestStartIndex when the Sub has completed its run.</param>
/// <param name="zMax"></param>
/// <param name="zTarget"></param>
/// <remarks></remarks>
private void FindStartIndex(ref int zMin, ref int zMax, ref int zTarget)
{
// Recursive Sub to compute best starting index - only run when zParent is known to exist
if (zMax == zMin + 1 | zMin == (zMax + zMin) / 2) return;
else if ((zParent.GetPositionFromCharIndex((zMax + zMin) / 2).Y) == zTarget)
zMin = (zMax + zMin) / 2;
else if ((zParent.GetPositionFromCharIndex((zMax + zMin) / 2).Y) > zTarget)
{
// Look again, in lower half
zMax = (zMax + zMin) / 2;
FindStartIndex(ref zMin, ref zMax, ref zTarget);
}
else if ((zParent.GetPositionFromCharIndex((zMax + zMin) / 2).Y) < zTarget)
{
// Look again, in top half
zMin = (zMax + zMin) / 2;
FindStartIndex(ref zMin, ref zMax, ref zTarget);
}
}
/// <summary>
/// OnPaint will go through the enabled elements (vertical ReminderMessage, GridLines, LineNumbers, BorderLines, MarginLines) and will
/// draw them if enabled. At the same time, it will build GraphicsPaths for each of those elements (that are enabled), which will be used
/// in SeeThroughMode (if it's active): the figures in the GraphicsPaths will form a customized outline for the control by setting them as the
/// Region of the LineNumber control. Note: the vertical ReminderMessages are only drawn during designtime.
/// </summary>
/// <param name="e"></param>
/// <remarks></remarks>
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
// Build the list of visible LineNumberItems (= zLNIs) first. (doesn't take long, so it can stay in OnPaint)
this.Update_VisibleLineNumberItems();
base.OnPaint(e);
// --- QualitySettings
if (zLineNumbers_AntiAlias == true)
{
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
}
else
{
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault;
}
// --- Local Declarations
string zTextToShow = "";
string zReminderToShow = "";
StringFormat zSF = new StringFormat();
SizeF zTextSize = default(SizeF);
Pen zPen = new Pen(this.ForeColor);
SolidBrush zBrush = new SolidBrush(this.ForeColor);
Point zPoint = new Point(0, 0);
Rectangle zItemClipRectangle = new Rectangle(0, 0, 0, 0);
// NOTE: The GraphicsPaths are only used for SeeThroughMode
// FillMode.Winding: combined outline ( Alternate: XOR'ed outline )
System.Drawing.Drawing2D.GraphicsPath zGP_GridLines = new System.Drawing.Drawing2D.GraphicsPath(System.Drawing.Drawing2D.FillMode.Winding);
System.Drawing.Drawing2D.GraphicsPath zGP_BorderLines = new System.Drawing.Drawing2D.GraphicsPath(System.Drawing.Drawing2D.FillMode.Winding);
System.Drawing.Drawing2D.GraphicsPath zGP_MarginLines = new System.Drawing.Drawing2D.GraphicsPath(System.Drawing.Drawing2D.FillMode.Winding);
System.Drawing.Drawing2D.GraphicsPath zGP_LineNumbers = new System.Drawing.Drawing2D.GraphicsPath(System.Drawing.Drawing2D.FillMode.Winding);
Region zRegion = new Region(base.ClientRectangle);
// Designer / No Visible Items
if (DesignMode)
{
// Show a vertical reminder message
if (zParent == null)
zReminderToShow = "-!- Set Parent RichTextBox -!-";
else
{
if (zLNIs.Count == 0)
zReminderToShow = "Line Numbers ( " + zParent.Name + " )";
}
if (zReminderToShow.Length > 0)
{
// --- Centering and Rotation for the reminder message
e.Graphics.TranslateTransform(this.Width / 2, this.Height / 2);
e.Graphics.RotateTransform(-90);
zSF.Alignment = StringAlignment.Center;
zSF.LineAlignment = StringAlignment.Center;
// --- Show the reminder message (with small shadow)
zTextSize = e.Graphics.MeasureString(zReminderToShow, this.Font, zPoint, zSF);
e.Graphics.DrawString(zReminderToShow, this.Font, Brushes.WhiteSmoke, 1, 1, zSF);
e.Graphics.DrawString(zReminderToShow, this.Font, Brushes.Firebrick, 0, 0, zSF);
e.Graphics.ResetTransform();
Rectangle zReminderRectangle = new Rectangle(Convert.ToInt32(this.Width / 2 - zTextSize.Height / 2), Convert.ToInt32(this.Height / 2 - zTextSize.Width / 2), Convert.ToInt32(zTextSize.Height), Convert.ToInt32(zTextSize.Width));
zGP_LineNumbers.AddRectangle(zReminderRectangle);
zGP_LineNumbers.CloseFigure();
if (zAutoSizing == true)
{
zReminderRectangle.Inflate(Convert.ToInt32(zTextSize.Height * 0.2), Convert.ToInt32(zTextSize.Width * 0.1));
zAutoSizing_Size = new Size(zReminderRectangle.Width, zReminderRectangle.Height);
}
}
}
// Runtime / Designer with Visible Items
if (zLNIs.Count > 0)
{
// The visible LineNumberItems with their BackgroundGradient and GridLines
// Loop through every visible LineNumberItem
System.Drawing.Drawing2D.LinearGradientBrush zLGB = null;
zPen = new Pen(zGridLines_Color, zGridLines_Thickness);
zPen.DashStyle = zGridLines_Style;
zSF.Alignment = StringAlignment.Near;
zSF.LineAlignment = StringAlignment.Near;
zSF.FormatFlags = StringFormatFlags.FitBlackBox | StringFormatFlags.NoClip | StringFormatFlags.NoWrap;
for (int zA = 0; zA <= zLNIs.Count - 1; zA++)
{
// BackgroundGradient
if (zGradient_Show)
{
try
{
zLGB = new System.Drawing.Drawing2D.LinearGradientBrush(zLNIs[zA].Rectangle, zGradient_StartColor, zGradient_EndColor, zGradient_Direction);
e.Graphics.FillRectangle(zLGB, zLNIs[zA].Rectangle);
}
catch (Exception)
{ }
}
// GridLines
if (zGridLines_Show)
{
e.Graphics.DrawLine(zPen, new Point(0, zLNIs[zA].Rectangle.Y), new Point(this.Width, zLNIs[zA].Rectangle.Y));
// NOTE: Every item in a GraphicsPath is a closed figure, so instead of adding gridlines as lines, we'll add them
// as rectangles that loop out of sight. Their height uses the zContentRectangle which is the maxsize of
// the ParentRichTextBox's contents.
// NOTE: Slight adjustment needed when the first item has a negative Y coordinate.
// This explains the " - zLNIs(0).Rectangle.Y" (which adds the negative size to the height
// to make sure the rectangle's bottompart stays out of sight)
zGP_GridLines.AddRectangle(new Rectangle(Convert.ToInt32(-zGridLines_Thickness), zLNIs[zA].Rectangle.Y, Convert.ToInt32(this.Width + zGridLines_Thickness * 2), Convert.ToInt32(this.Height - zLNIs[0].Rectangle.Y + zGridLines_Thickness)));
zGP_GridLines.CloseFigure();
}
// --- LineNumbers
if (zLineNumbers_Show)
{
// TextFormatting
if (zLineNumbers_ShowLeadingZeroes == true)
{
zTextToShow = (zLineNumbers_ShowAsHexadecimal ? zLNIs[zA].LineNumber.ToString("X") : zLNIs[zA].LineNumber.ToString(zLineNumbers_Format));
}
else
{
zTextToShow = (zLineNumbers_ShowAsHexadecimal ? zLNIs[zA].LineNumber.ToString("X") : zLNIs[zA].LineNumber.ToString());
}
// TextSizing
zTextSize = e.Graphics.MeasureString(zTextToShow, this.Font, zPoint, zSF);
// TextAlignment and positioning (zPoint = TopLeftCornerPoint of the text)
// TextAlignment, padding, manual offset (via LineNrs_Offset) and zTextSize are all included in the calculation of zPoint.
switch (zLineNumbers_Alignment)
{
case ContentAlignment.TopLeft:
zPoint = new Point(zLNIs[zA].Rectangle.Left + this.Padding.Left + zLineNumbers_Offset.Width, zLNIs[zA].Rectangle.Top + this.Padding.Top + zLineNumbers_Offset.Height);
break;
case ContentAlignment.MiddleLeft:
zPoint = new Point(zLNIs[zA].Rectangle.Left + this.Padding.Left + zLineNumbers_Offset.Width, Convert.ToInt32(zLNIs[zA].Rectangle.Top + (zLNIs[zA].Rectangle.Height / 2) + zLineNumbers_Offset.Height - zTextSize.Height / 2));
break;
case ContentAlignment.BottomLeft:
zPoint = new Point(zLNIs[zA].Rectangle.Left + this.Padding.Left + zLineNumbers_Offset.Width, Convert.ToInt32(zLNIs[zA].Rectangle.Bottom - this.Padding.Bottom + 1 + zLineNumbers_Offset.Height - zTextSize.Height));
break;
case ContentAlignment.TopCenter:
zPoint = new Point(Convert.ToInt32(zLNIs[zA].Rectangle.Width / 2 + zLineNumbers_Offset.Width - zTextSize.Width / 2), Convert.ToInt32(zLNIs[zA].Rectangle.Top + this.Padding.Top + zLineNumbers_Offset.Height));
break;
case ContentAlignment.MiddleCenter:
zPoint = new Point(Convert.ToInt32(zLNIs[zA].Rectangle.Width / 2 + zLineNumbers_Offset.Width - zTextSize.Width / 2), Convert.ToInt32(zLNIs[zA].Rectangle.Top + (zLNIs[zA].Rectangle.Height / 2) + zLineNumbers_Offset.Height - zTextSize.Height / 2));
break;
case ContentAlignment.BottomCenter:
zPoint = new Point(Convert.ToInt32(zLNIs[zA].Rectangle.Width / 2 + zLineNumbers_Offset.Width - zTextSize.Width / 2), Convert.ToInt32(zLNIs[zA].Rectangle.Bottom - this.Padding.Bottom + 1 + zLineNumbers_Offset.Height - zTextSize.Height));
break;
case ContentAlignment.TopRight:
zPoint = new Point(Convert.ToInt32(zLNIs[zA].Rectangle.Right - this.Padding.Right + zLineNumbers_Offset.Width - zTextSize.Width), Convert.ToInt32(zLNIs[zA].Rectangle.Top + this.Padding.Top + zLineNumbers_Offset.Height));
break;
case ContentAlignment.MiddleRight:
zPoint = new Point(Convert.ToInt32(zLNIs[zA].Rectangle.Right - this.Padding.Right + zLineNumbers_Offset.Width - zTextSize.Width), Convert.ToInt32(zLNIs[zA].Rectangle.Top + (zLNIs[zA].Rectangle.Height / 2) + zLineNumbers_Offset.Height - zTextSize.Height / 2));
break;
case ContentAlignment.BottomRight:
zPoint = new Point(Convert.ToInt32(zLNIs[zA].Rectangle.Right - this.Padding.Right + zLineNumbers_Offset.Width - zTextSize.Width), Convert.ToInt32(zLNIs[zA].Rectangle.Bottom - this.Padding.Bottom + 1 + zLineNumbers_Offset.Height - zTextSize.Height));
break;
}
// TextClipping
zItemClipRectangle = new Rectangle(zPoint, zTextSize.ToSize());
if (zLineNumbers_ClipByItemRectangle == true)
{
// If selected, the text will be clipped so that it doesn't spill out of its own LineNumberItem-area.
// Only the part of the text inside the LineNumberItem.Rectangle should be visible, so intersect with the ItemRectangle
// The SetClip method temporary restricts the drawing area of the control for whatever is drawn next.
zItemClipRectangle.Intersect(zLNIs[zA].Rectangle);
e.Graphics.SetClip(zItemClipRectangle);
}
// TextDrawing
e.Graphics.DrawString(zTextToShow, this.Font, zBrush, zPoint, zSF);
e.Graphics.ResetClip();
// The GraphicsPath for the LineNumber is just a rectangle behind the text, to keep the paintingspeed high and avoid ugly artifacts.
zGP_LineNumbers.AddRectangle(zItemClipRectangle);
zGP_LineNumbers.CloseFigure();
}
}
// --- GridLinesThickness and Linestyle in SeeThroughMode. All GraphicsPath lines are drawn as solid to keep the paintingspeed high.
if (zGridLines_Show)
{
zPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
zGP_GridLines.Widen(zPen);
}
// --- Memory CleanUp
if (zLGB != null) zLGB.Dispose();
}
// Both Design and Runtime
Point zP_Left = new Point(Convert.ToInt32(Math.Floor(zBorderLines_Thickness / 2)), Convert.ToInt32(Math.Floor(zBorderLines_Thickness / 2)));
Point zP_Right = new Point(Convert.ToInt32(this.Width - Math.Ceiling(zBorderLines_Thickness / 2)), Convert.ToInt32(this.Height - Math.Ceiling(zBorderLines_Thickness / 2)));
// --- BorderLines
Point[] zBorderLines_Points = { new Point(zP_Left.X, zP_Left.Y), new Point(zP_Right.X, zP_Left.Y), new Point(zP_Right.X, zP_Right.Y), new Point(zP_Left.X, zP_Right.Y), new Point(zP_Left.X, zP_Left.Y) };
if (zBorderLines_Show)
{
zPen = new Pen(zBorderLines_Color, zBorderLines_Thickness);
zPen.DashStyle = zBorderLines_Style;
e.Graphics.DrawLines(zPen, zBorderLines_Points);
zGP_BorderLines.AddLines(zBorderLines_Points);
zGP_BorderLines.CloseFigure();
// BorderThickness and Style for SeeThroughMode
zPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
zGP_BorderLines.Widen(zPen);
}
// --- MarginLines
if (zMarginLines_Show && zMarginLines_Side > LineNumberDockSide.None)
{
zP_Left = new Point(Convert.ToInt32(-zMarginLines_Thickness), Convert.ToInt32(-zMarginLines_Thickness));
zP_Right = new Point(Convert.ToInt32(this.Width + zMarginLines_Thickness), Convert.ToInt32(this.Height + zMarginLines_Thickness));
zPen = new Pen(zMarginLines_Color, zMarginLines_Thickness);
zPen.DashStyle = zMarginLines_Style;
if (zMarginLines_Side == LineNumberDockSide.Left | zMarginLines_Side == LineNumberDockSide.Height)
{
e.Graphics.DrawLine(zPen, new Point(Convert.ToInt32(Math.Floor(Convert.ToDecimal(zMarginLines_Thickness / 2))), 0), new Point(Convert.ToInt32(Math.Floor(zMarginLines_Thickness / 2)), this.Height - 1));
zP_Left = new Point(Convert.ToInt32(Math.Ceiling(zMarginLines_Thickness / 2)), Convert.ToInt32(-zMarginLines_Thickness));
}
if (zMarginLines_Side == LineNumberDockSide.Right | zMarginLines_Side == LineNumberDockSide.Height)
{
e.Graphics.DrawLine(zPen, new Point(Convert.ToInt32(this.Width - Math.Ceiling(zMarginLines_Thickness / 2)), 0), new Point(Convert.ToInt32(this.Width - Math.Ceiling(zMarginLines_Thickness / 2)), this.Height - 1));
zP_Right = new Point(Convert.ToInt32(this.Width - Math.Ceiling(Convert.ToDecimal(zMarginLines_Thickness / 2))), Convert.ToInt32(this.Height + zMarginLines_Thickness));
}
// GraphicsPath for the MarginLines(s):
// MarginLines(s) are drawn as a rectangle connecting the zP_Left and zP_Right points, which are either inside or
// outside of sight, depending on whether the MarginLines at that side is visible. zP_Left: TopLeft and ZP_Right: BottomRight
zGP_MarginLines.AddRectangle(new Rectangle(zP_Left, new Size(zP_Right.X - zP_Left.X, zP_Right.Y - zP_Left.Y)));
zPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
zGP_MarginLines.Widen(zPen);
}
// SeeThroughMode
// combine all the GraphicsPaths (= zGP_... ) and set them as the region for the control.
if (zSeeThroughMode)
{
zRegion.MakeEmpty();
zRegion.Union(zGP_BorderLines);
zRegion.Union(zGP_MarginLines);
zRegion.Union(zGP_GridLines);
zRegion.Union(zGP_LineNumbers);
}
// --- Region
if (zRegion.GetBounds(e.Graphics).IsEmpty)
{
// Note: If the control is in a condition that would show it as empty, then a border-region is still drawn regardless of it's borders on/off state.
// This is added to make sure that the bounds of the control are never lost (it would remain empty if this was not done).
zGP_BorderLines.AddLines(zBorderLines_Points);
zGP_BorderLines.CloseFigure();
zPen = new Pen(zBorderLines_Color, 1);
zPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
zGP_BorderLines.Widen(zPen);
zRegion = new Region(zGP_BorderLines);
}
Region = zRegion;
// --- Memory CleanUp
if (zPen != null) zPen.Dispose();
if (zBrush != null) zPen.Dispose();
if (zRegion != null) zRegion.Dispose();
if (zGP_GridLines != null) zGP_GridLines.Dispose();
if (zGP_BorderLines != null) zGP_BorderLines.Dispose();
if (zGP_MarginLines != null) zGP_MarginLines.Dispose();
if (zGP_LineNumbers != null) zGP_LineNumbers.Dispose();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
private void zTimer_Tick(object sender, System.EventArgs e)
{
zParentIsScrolling = false;
zTimer.Stop();
Invalidate();
}
private void zParent_Changed(object sender, System.EventArgs e)
{
Refresh();
Invalidate();
}
private void zParent_Scroll(object sender, System.EventArgs e)
{
zParentIsScrolling = true;
Invalidate();
//Refresh();
}
private void zParent_ContentsResized(object sender, System.Windows.Forms.ContentsResizedEventArgs e)
{
zContentRectangle = e.NewRectangle;
Refresh();
Invalidate();
}
/// <summary>
/// Parent box is gone
/// </summary>
private void zParent_Disposed(object sender, System.EventArgs e)
{
ParentBox = null;
Refresh();
Invalidate();
}
}
}
Jump to Line
Something went wrong with that request. Please try again.