first commit
This commit is contained in:
463
ICSharpCode.TextEditor/Project/Src/Gui/TextAreaControl.cs
Normal file
463
ICSharpCode.TextEditor/Project/Src/Gui/TextAreaControl.cs
Normal file
@@ -0,0 +1,463 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// This class paints the textarea.
|
||||
/// </summary>
|
||||
[ToolboxItem(false)]
|
||||
public class TextAreaControl : Panel
|
||||
{
|
||||
TextEditorControl motherTextEditorControl;
|
||||
|
||||
HRuler hRuler = null;
|
||||
|
||||
VScrollBar vScrollBar = new VScrollBar();
|
||||
HScrollBar hScrollBar = new HScrollBar();
|
||||
TextArea textArea;
|
||||
bool doHandleMousewheel = true;
|
||||
bool disposed;
|
||||
|
||||
public TextArea TextArea {
|
||||
get {
|
||||
return textArea;
|
||||
}
|
||||
}
|
||||
|
||||
public SelectionManager SelectionManager {
|
||||
get {
|
||||
return textArea.SelectionManager;
|
||||
}
|
||||
}
|
||||
|
||||
public Caret Caret {
|
||||
get {
|
||||
return textArea.Caret;
|
||||
}
|
||||
}
|
||||
|
||||
[Browsable(false)]
|
||||
public IDocument Document {
|
||||
get {
|
||||
if (motherTextEditorControl != null)
|
||||
return motherTextEditorControl.Document;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ITextEditorProperties TextEditorProperties {
|
||||
get {
|
||||
if (motherTextEditorControl != null)
|
||||
return motherTextEditorControl.TextEditorProperties;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public VScrollBar VScrollBar {
|
||||
get {
|
||||
return vScrollBar;
|
||||
}
|
||||
}
|
||||
|
||||
public HScrollBar HScrollBar {
|
||||
get {
|
||||
return hScrollBar;
|
||||
}
|
||||
}
|
||||
|
||||
public bool DoHandleMousewheel {
|
||||
get {
|
||||
return doHandleMousewheel;
|
||||
}
|
||||
set {
|
||||
doHandleMousewheel = value;
|
||||
}
|
||||
}
|
||||
|
||||
public TextAreaControl(TextEditorControl motherTextEditorControl)
|
||||
{
|
||||
this.motherTextEditorControl = motherTextEditorControl;
|
||||
|
||||
this.textArea = new TextArea(motherTextEditorControl, this);
|
||||
Controls.Add(textArea);
|
||||
|
||||
vScrollBar.ValueChanged += new EventHandler(VScrollBarValueChanged);
|
||||
Controls.Add(this.vScrollBar);
|
||||
|
||||
hScrollBar.ValueChanged += new EventHandler(HScrollBarValueChanged);
|
||||
Controls.Add(this.hScrollBar);
|
||||
ResizeRedraw = true;
|
||||
|
||||
Document.TextContentChanged += DocumentTextContentChanged;
|
||||
Document.DocumentChanged += AdjustScrollBarsOnDocumentChange;
|
||||
Document.UpdateCommited += DocumentUpdateCommitted;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing) {
|
||||
if (!disposed) {
|
||||
disposed = true;
|
||||
Document.TextContentChanged -= DocumentTextContentChanged;
|
||||
Document.DocumentChanged -= AdjustScrollBarsOnDocumentChange;
|
||||
Document.UpdateCommited -= DocumentUpdateCommitted;
|
||||
motherTextEditorControl = null;
|
||||
if (vScrollBar != null) {
|
||||
vScrollBar.Dispose();
|
||||
vScrollBar = null;
|
||||
}
|
||||
if (hScrollBar != null) {
|
||||
hScrollBar.Dispose();
|
||||
hScrollBar = null;
|
||||
}
|
||||
if (hRuler != null) {
|
||||
hRuler.Dispose();
|
||||
hRuler = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
void DocumentTextContentChanged(object sender, EventArgs e)
|
||||
{
|
||||
// after the text content is changed abruptly, we need to validate the
|
||||
// caret position - otherwise the caret position is invalid for a short amount
|
||||
// of time, which can break client code that expects that the caret position is always valid
|
||||
Caret.ValidateCaretPos();
|
||||
}
|
||||
|
||||
protected override void OnResize(System.EventArgs e)
|
||||
{
|
||||
base.OnResize(e);
|
||||
ResizeTextArea();
|
||||
}
|
||||
|
||||
public void ResizeTextArea()
|
||||
{
|
||||
int y = 0;
|
||||
int h = 0;
|
||||
if (hRuler != null) {
|
||||
hRuler.Bounds = new Rectangle(0,
|
||||
0,
|
||||
Width - SystemInformation.HorizontalScrollBarArrowWidth,
|
||||
textArea.TextView.FontHeight);
|
||||
|
||||
y = hRuler.Bounds.Bottom;
|
||||
h = hRuler.Bounds.Height;
|
||||
}
|
||||
|
||||
textArea.Bounds = new Rectangle(0, y,
|
||||
Width - SystemInformation.HorizontalScrollBarArrowWidth,
|
||||
Height - SystemInformation.VerticalScrollBarArrowHeight - h);
|
||||
SetScrollBarBounds();
|
||||
}
|
||||
|
||||
public void SetScrollBarBounds()
|
||||
{
|
||||
vScrollBar.Bounds = new Rectangle(textArea.Bounds.Right, 0, SystemInformation.HorizontalScrollBarArrowWidth, Height - SystemInformation.VerticalScrollBarArrowHeight);
|
||||
hScrollBar.Bounds = new Rectangle(0,
|
||||
textArea.Bounds.Bottom,
|
||||
Width - SystemInformation.HorizontalScrollBarArrowWidth,
|
||||
SystemInformation.VerticalScrollBarArrowHeight);
|
||||
}
|
||||
|
||||
bool adjustScrollBarsOnNextUpdate;
|
||||
Point scrollToPosOnNextUpdate;
|
||||
|
||||
void AdjustScrollBarsOnDocumentChange(object sender, DocumentEventArgs e)
|
||||
{
|
||||
if (motherTextEditorControl.IsInUpdate == false) {
|
||||
AdjustScrollBarsClearCache();
|
||||
AdjustScrollBars();
|
||||
} else {
|
||||
adjustScrollBarsOnNextUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
void DocumentUpdateCommitted(object sender, EventArgs e)
|
||||
{
|
||||
if (motherTextEditorControl.IsInUpdate == false) {
|
||||
Caret.ValidateCaretPos();
|
||||
|
||||
// AdjustScrollBarsOnCommittedUpdate
|
||||
if (!scrollToPosOnNextUpdate.IsEmpty) {
|
||||
ScrollTo(scrollToPosOnNextUpdate.Y, scrollToPosOnNextUpdate.X);
|
||||
}
|
||||
if (adjustScrollBarsOnNextUpdate) {
|
||||
AdjustScrollBarsClearCache();
|
||||
AdjustScrollBars();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int[] lineLengthCache;
|
||||
const int LineLengthCacheAdditionalSize = 100;
|
||||
|
||||
void AdjustScrollBarsClearCache()
|
||||
{
|
||||
if (lineLengthCache != null) {
|
||||
if (lineLengthCache.Length < this.Document.TotalNumberOfLines + 2 * LineLengthCacheAdditionalSize) {
|
||||
lineLengthCache = null;
|
||||
} else {
|
||||
Array.Clear(lineLengthCache, 0, lineLengthCache.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AdjustScrollBars()
|
||||
{
|
||||
adjustScrollBarsOnNextUpdate = false;
|
||||
vScrollBar.Minimum = 0;
|
||||
// number of visible lines in document (folding!)
|
||||
vScrollBar.Maximum = textArea.MaxVScrollValue;
|
||||
int max = 0;
|
||||
|
||||
int firstLine = textArea.TextView.FirstVisibleLine;
|
||||
int lastLine = this.Document.GetFirstLogicalLine(textArea.TextView.FirstPhysicalLine + textArea.TextView.VisibleLineCount);
|
||||
if (lastLine >= this.Document.TotalNumberOfLines)
|
||||
lastLine = this.Document.TotalNumberOfLines - 1;
|
||||
|
||||
if (lineLengthCache == null || lineLengthCache.Length <= lastLine) {
|
||||
lineLengthCache = new int[lastLine + LineLengthCacheAdditionalSize];
|
||||
}
|
||||
|
||||
for (int lineNumber = firstLine; lineNumber <= lastLine; lineNumber++) {
|
||||
LineSegment lineSegment = this.Document.GetLineSegment(lineNumber);
|
||||
if (Document.FoldingManager.IsLineVisible(lineNumber)) {
|
||||
if (lineLengthCache[lineNumber] > 0) {
|
||||
max = Math.Max(max, lineLengthCache[lineNumber]);
|
||||
} else {
|
||||
int visualLength = textArea.TextView.GetVisualColumnFast(lineSegment, lineSegment.Length);
|
||||
lineLengthCache[lineNumber] = Math.Max(1, visualLength);
|
||||
max = Math.Max(max, visualLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
hScrollBar.Minimum = 0;
|
||||
hScrollBar.Maximum = (Math.Max(max + 20, textArea.TextView.VisibleColumnCount - 1));
|
||||
|
||||
vScrollBar.LargeChange = Math.Max(0, textArea.TextView.DrawingPosition.Height);
|
||||
vScrollBar.SmallChange = Math.Max(0, textArea.TextView.FontHeight);
|
||||
|
||||
hScrollBar.LargeChange = Math.Max(0, textArea.TextView.VisibleColumnCount - 1);
|
||||
hScrollBar.SmallChange = Math.Max(0, (int)textArea.TextView.SpaceWidth);
|
||||
}
|
||||
|
||||
public void OptionsChanged()
|
||||
{
|
||||
textArea.OptionsChanged();
|
||||
|
||||
if (textArea.TextEditorProperties.ShowHorizontalRuler) {
|
||||
if (hRuler == null) {
|
||||
hRuler = new HRuler(textArea);
|
||||
Controls.Add(hRuler);
|
||||
ResizeTextArea();
|
||||
} else {
|
||||
hRuler.Invalidate();
|
||||
}
|
||||
} else {
|
||||
if (hRuler != null) {
|
||||
Controls.Remove(hRuler);
|
||||
hRuler.Dispose();
|
||||
hRuler = null;
|
||||
ResizeTextArea();
|
||||
}
|
||||
}
|
||||
|
||||
AdjustScrollBars();
|
||||
}
|
||||
|
||||
void VScrollBarValueChanged(object sender, EventArgs e)
|
||||
{
|
||||
textArea.VirtualTop = new Point(textArea.VirtualTop.X, vScrollBar.Value);
|
||||
textArea.Invalidate();
|
||||
AdjustScrollBars();
|
||||
}
|
||||
|
||||
void HScrollBarValueChanged(object sender, EventArgs e)
|
||||
{
|
||||
textArea.VirtualTop = new Point(hScrollBar.Value * textArea.TextView.WideSpaceWidth, textArea.VirtualTop.Y);
|
||||
textArea.Invalidate();
|
||||
}
|
||||
|
||||
Util.MouseWheelHandler mouseWheelHandler = new Util.MouseWheelHandler();
|
||||
|
||||
public void HandleMouseWheel(MouseEventArgs e)
|
||||
{
|
||||
int scrollDistance = mouseWheelHandler.GetScrollAmount(e);
|
||||
if (scrollDistance == 0)
|
||||
return;
|
||||
if ((Control.ModifierKeys & Keys.Control) != 0 && TextEditorProperties.MouseWheelTextZoom) {
|
||||
if (scrollDistance > 0) {
|
||||
motherTextEditorControl.Font = new Font(motherTextEditorControl.Font.Name,
|
||||
motherTextEditorControl.Font.Size + 1);
|
||||
} else {
|
||||
motherTextEditorControl.Font = new Font(motherTextEditorControl.Font.Name,
|
||||
Math.Max(6, motherTextEditorControl.Font.Size - 1));
|
||||
}
|
||||
} else {
|
||||
if (TextEditorProperties.MouseWheelScrollDown)
|
||||
scrollDistance = -scrollDistance;
|
||||
int newValue = vScrollBar.Value + vScrollBar.SmallChange * scrollDistance;
|
||||
vScrollBar.Value = Math.Max(vScrollBar.Minimum, Math.Min(vScrollBar.Maximum - vScrollBar.LargeChange + 1, newValue));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMouseWheel(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseWheel(e);
|
||||
if (DoHandleMousewheel) {
|
||||
HandleMouseWheel(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void ScrollToCaret()
|
||||
{
|
||||
ScrollTo(textArea.Caret.Line, textArea.Caret.Column);
|
||||
}
|
||||
|
||||
public void ScrollTo(int line, int column)
|
||||
{
|
||||
if (motherTextEditorControl.IsInUpdate) {
|
||||
scrollToPosOnNextUpdate = new Point(column, line);
|
||||
return;
|
||||
} else {
|
||||
scrollToPosOnNextUpdate = Point.Empty;
|
||||
}
|
||||
|
||||
ScrollTo(line);
|
||||
|
||||
int curCharMin = (int)(this.hScrollBar.Value - this.hScrollBar.Minimum);
|
||||
int curCharMax = curCharMin + textArea.TextView.VisibleColumnCount;
|
||||
|
||||
int pos = textArea.TextView.GetVisualColumn(line, column);
|
||||
|
||||
if (textArea.TextView.VisibleColumnCount < 0) {
|
||||
hScrollBar.Value = 0;
|
||||
} else {
|
||||
if (pos < curCharMin) {
|
||||
hScrollBar.Value = (int)(Math.Max(0, pos - scrollMarginHeight));
|
||||
} else {
|
||||
if (pos > curCharMax) {
|
||||
hScrollBar.Value = (int)Math.Max(0, Math.Min(hScrollBar.Maximum, (pos - textArea.TextView.VisibleColumnCount + scrollMarginHeight)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int scrollMarginHeight = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that <paramref name="line"/> is visible.
|
||||
/// </summary>
|
||||
public void ScrollTo(int line)
|
||||
{
|
||||
line = Math.Max(0, Math.Min(Document.TotalNumberOfLines - 1, line));
|
||||
line = Document.GetVisibleLine(line);
|
||||
int curLineMin = textArea.TextView.FirstPhysicalLine;
|
||||
if (textArea.TextView.LineHeightRemainder > 0) {
|
||||
curLineMin ++;
|
||||
}
|
||||
|
||||
if (line - scrollMarginHeight + 3 < curLineMin) {
|
||||
this.vScrollBar.Value = Math.Max(0, Math.Min(this.vScrollBar.Maximum, (line - scrollMarginHeight + 3) * textArea.TextView.FontHeight)) ;
|
||||
VScrollBarValueChanged(this, EventArgs.Empty);
|
||||
} else {
|
||||
int curLineMax = curLineMin + this.textArea.TextView.VisibleLineCount;
|
||||
if (line + scrollMarginHeight - 1 > curLineMax) {
|
||||
if (this.textArea.TextView.VisibleLineCount == 1) {
|
||||
this.vScrollBar.Value = Math.Max(0, Math.Min(this.vScrollBar.Maximum, (line - scrollMarginHeight - 1) * textArea.TextView.FontHeight)) ;
|
||||
} else {
|
||||
this.vScrollBar.Value = Math.Min(this.vScrollBar.Maximum,
|
||||
(line - this.textArea.TextView.VisibleLineCount + scrollMarginHeight - 1)* textArea.TextView.FontHeight) ;
|
||||
}
|
||||
VScrollBarValueChanged(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scroll so that the specified line is centered.
|
||||
/// </summary>
|
||||
/// <param name="line">Line to center view on</param>
|
||||
/// <param name="treshold">If this action would cause scrolling by less than or equal to
|
||||
/// <paramref name="treshold"/> lines in any direction, don't scroll.
|
||||
/// Use -1 to always center the view.</param>
|
||||
public void CenterViewOn(int line, int treshold)
|
||||
{
|
||||
line = Math.Max(0, Math.Min(Document.TotalNumberOfLines - 1, line));
|
||||
// convert line to visible line:
|
||||
line = Document.GetVisibleLine(line);
|
||||
// subtract half the visible line count
|
||||
line -= textArea.TextView.VisibleLineCount / 2;
|
||||
|
||||
int curLineMin = textArea.TextView.FirstPhysicalLine;
|
||||
if (textArea.TextView.LineHeightRemainder > 0) {
|
||||
curLineMin ++;
|
||||
}
|
||||
if (Math.Abs(curLineMin - line) > treshold) {
|
||||
// scroll:
|
||||
this.vScrollBar.Value = Math.Max(0, Math.Min(this.vScrollBar.Maximum, (line - scrollMarginHeight + 3) * textArea.TextView.FontHeight)) ;
|
||||
VScrollBarValueChanged(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public void JumpTo(int line)
|
||||
{
|
||||
line = Math.Max(0, Math.Min(line, Document.TotalNumberOfLines - 1));
|
||||
string text = Document.GetText(Document.GetLineSegment(line));
|
||||
JumpTo(line, text.Length - text.TrimStart().Length);
|
||||
}
|
||||
|
||||
public void JumpTo(int line, int column)
|
||||
{
|
||||
textArea.Focus();
|
||||
textArea.SelectionManager.ClearSelection();
|
||||
textArea.Caret.Position = new TextLocation(column, line);
|
||||
textArea.SetDesiredColumn();
|
||||
ScrollToCaret();
|
||||
}
|
||||
|
||||
public event MouseEventHandler ShowContextMenu;
|
||||
|
||||
protected override void WndProc(ref Message m)
|
||||
{
|
||||
if (m.Msg == 0x007B) { // handle WM_CONTEXTMENU
|
||||
if (ShowContextMenu != null) {
|
||||
long lParam = m.LParam.ToInt64();
|
||||
int x = unchecked((short)(lParam & 0xffff));
|
||||
int y = unchecked((short)((lParam & 0xffff0000) >> 16));
|
||||
if (x == -1 && y == -1) {
|
||||
Point pos = Caret.ScreenPosition;
|
||||
ShowContextMenu(this, new MouseEventArgs(MouseButtons.None, 0, pos.X, pos.Y + textArea.TextView.FontHeight, 0));
|
||||
} else {
|
||||
Point pos = PointToClient(new Point(x, y));
|
||||
ShowContextMenu(this, new MouseEventArgs(MouseButtons.Right, 1, pos.X, pos.Y, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
base.WndProc(ref m);
|
||||
}
|
||||
|
||||
protected override void OnEnter(EventArgs e)
|
||||
{
|
||||
// SD2-1072 - Make sure the caret line is valid if anyone
|
||||
// has handlers for the Enter event.
|
||||
Caret.ValidateCaretPos();
|
||||
base.OnEnter(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user