#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

808 lines
22 KiB

// <file>
// <copyright see="prj:///doc/copyright.txt">2002-2005 AlphaSierraPapa</copyright>
// <license see="prj:///doc/license.txt">GNU General Public License</license>
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections;
using System.IO;
using System.ComponentModel;
using System.Drawing;
using System.Threading;
using System.Drawing.Text;
using System.Drawing.Drawing2D;
using System.Drawing.Printing;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.Remoting;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml;
using ICSharpCode.TextEditor.Actions;
using ICSharpCode.TextEditor.Document;
using ICSharpCode.TextEditor.Gui.CompletionWindow;
namespace ICSharpCode.TextEditor
{
public delegate bool KeyEventHandler(char ch);
public delegate bool DialogKeyProcessor(Keys keyData);
/// <summary>
/// This class paints the textarea.
/// </summary>
[ToolboxItem(false)]
public class TextArea : UserControl
{
public static bool HiddenMouseCursor = false;
Point virtualTop = new Point(0, 0);
TextAreaControl motherTextAreaControl;
TextEditorControl motherTextEditorControl;
ArrayList bracketshemes = new ArrayList();
TextAreaClipboardHandler textAreaClipboardHandler;
bool autoClearSelection = false;
ArrayList leftMargins = new ArrayList();
ArrayList topMargins = new ArrayList();
TextView textView;
GutterMargin gutterMargin;
FoldMargin foldMargin;
IconBarMargin iconBarMargin;
SelectionManager selectionManager;
Caret caret;
public TextEditorControl MotherTextEditorControl {
get {
return motherTextEditorControl;
}
}
public TextAreaControl MotherTextAreaControl {
get {
return motherTextAreaControl;
}
}
public SelectionManager SelectionManager {
get {
return selectionManager;
}
}
public Caret Caret {
get {
return caret;
}
}
public TextView TextView {
get {
return textView;
}
}
public GutterMargin GutterMargin {
get {
return gutterMargin;
}
}
public FoldMargin FoldMargin {
get {
return foldMargin;
}
}
public IconBarMargin IconBarMargin {
get {
return iconBarMargin;
}
}
public Encoding Encoding {
get {
return motherTextEditorControl.Encoding;
}
}
public int MaxVScrollValue {
get {
return (Document.GetVisibleLine(Document.TotalNumberOfLines - 1) + 1 + TextView.VisibleLineCount * 2 / 3) * TextView.FontHeight;
}
}
public Point VirtualTop {
get {
return virtualTop;
}
set {
Point newVirtualTop = new Point(value.X, Math.Min(MaxVScrollValue, Math.Max(0, value.Y)));
if (virtualTop != newVirtualTop) {
virtualTop = newVirtualTop;
motherTextAreaControl.VScrollBar.Value = virtualTop.Y;
Invalidate();
}
}
}
public bool AutoClearSelection {
get {
return autoClearSelection;
}
set {
autoClearSelection = value;
}
}
[Browsable(false)]
public IDocument Document {
get {
return motherTextEditorControl.Document;
}
}
public TextAreaClipboardHandler ClipboardHandler {
get {
return textAreaClipboardHandler;
}
}
public ITextEditorProperties TextEditorProperties {
get {
return motherTextEditorControl.TextEditorProperties;
}
}
public TextArea(TextEditorControl motherTextEditorControl, TextAreaControl motherTextAreaControl)
{
this.motherTextAreaControl = motherTextAreaControl;
this.motherTextEditorControl = motherTextEditorControl;
caret = new Caret(this);
selectionManager = new SelectionManager(Document);
this.textAreaClipboardHandler = new TextAreaClipboardHandler(this);
ResizeRedraw = true;
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
// SetStyle(ControlStyles.AllPaintingInWmPaint, true);
// SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.Opaque, false);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.Selectable, true);
textView = new TextView(this);
gutterMargin = new GutterMargin(this);
foldMargin = new FoldMargin(this);
iconBarMargin = new IconBarMargin(this);
leftMargins.AddRange(new AbstractMargin[] { iconBarMargin, gutterMargin, foldMargin });
OptionsChanged();
new TextAreaMouseHandler(this).Attach();
new TextAreaDragDropHandler().Attach(this);
bracketshemes.Add(new BracketHighlightingSheme('{', '}'));
bracketshemes.Add(new BracketHighlightingSheme('(', ')'));
bracketshemes.Add(new BracketHighlightingSheme('[', ']'));
caret.PositionChanged += new EventHandler(SearchMatchingBracket);
Document.TextContentChanged += new EventHandler(TextContentChanged);
Document.FoldingManager.FoldingsChanged += new EventHandler(DocumentFoldingsChanged);
}
public void UpdateMatchingBracket()
{
SearchMatchingBracket(null, null);
}
void TextContentChanged(object sender, EventArgs e)
{
Caret.Position = new Point(0, 0);
SelectionManager.SelectionCollection.Clear();
}
void SearchMatchingBracket(object sender, EventArgs e)
{
if (!TextEditorProperties.ShowMatchingBracket) {
textView.Highlight = null;
return;
}
bool changed = false;
if (caret.Offset == 0) {
if (textView.Highlight != null) {
int line = textView.Highlight.OpenBrace.Y;
int line2 = textView.Highlight.CloseBrace.Y;
textView.Highlight = null;
UpdateLine(line);
UpdateLine(line2);
}
return;
}
foreach (BracketHighlightingSheme bracketsheme in bracketshemes) {
// if (bracketsheme.IsInside(textareapainter.Document, textareapainter.Document.Caret.Offset)) {
Highlight highlight = bracketsheme.GetHighlight(Document, Caret.Offset - 1);
if (textView.Highlight != null && textView.Highlight.OpenBrace.Y >=0 && textView.Highlight.OpenBrace.Y < Document.TotalNumberOfLines) {
UpdateLine(textView.Highlight.OpenBrace.Y);
}
if (textView.Highlight != null && textView.Highlight.CloseBrace.Y >=0 && textView.Highlight.CloseBrace.Y < Document.TotalNumberOfLines) {
UpdateLine(textView.Highlight.CloseBrace.Y);
}
textView.Highlight = highlight;
if (highlight != null) {
changed = true;
break;
}
// }
}
if (changed || textView.Highlight != null) {
int line = textView.Highlight.OpenBrace.Y;
int line2 = textView.Highlight.CloseBrace.Y;
if (!changed) {
textView.Highlight = null;
}
UpdateLine(line);
UpdateLine(line2);
}
}
public void SetDesiredColumn()
{
Caret.DesiredColumn = TextView.GetDrawingXPos(Caret.Line, Caret.Column) + (int)(VirtualTop.X * textView.WideSpaceWidth);
}
public void SetCaretToDesiredColumn(int caretLine)
{
Caret.Position = textView.GetLogicalColumn(Caret.Line, Caret.DesiredColumn + (int)(VirtualTop.X * textView.WideSpaceWidth));
}
public void OptionsChanged()
{
UpdateMatchingBracket();
textView.OptionsChanged();
caret.RecreateCaret();
caret.UpdateCaretPosition();
Refresh();
}
AbstractMargin lastMouseInMargin;
protected override void OnMouseLeave(System.EventArgs e)
{
base.OnMouseLeave(e);
this.Cursor = Cursors.Default;
if (lastMouseInMargin != null) {
lastMouseInMargin.HandleMouseLeave(EventArgs.Empty);
lastMouseInMargin = null;
}
}
protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
{
base.OnMouseDown(e);
foreach (AbstractMargin margin in leftMargins) {
if (margin.DrawingPosition.Contains(e.X, e.Y)) {
margin.HandleMouseDown(new Point(e.X, e.Y), e.Button);
}
}
}
// static because the mouse can only be in one text area and we don't want to have
// tooltips of text areas from inactive tabs floating around.
static DeclarationViewWindow toolTip;
static string oldToolTip;
bool toolTipSet;
public bool ToolTipVisible {
get {
return toolTipSet;
}
}
public void SetToolTip(string text)
{
if (toolTip == null) toolTip = new DeclarationViewWindow(this.FindForm());
toolTipSet = (text != null);
if (oldToolTip == text)
return;
if (text == null) {
toolTip.Hide();
} else {
Point p = Control.MousePosition;
p.Offset(3, 3);
toolTip.Location = p;
toolTip.Description = text;
toolTip.Show();
}
oldToolTip = text;
}
protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e)
{
toolTipSet = false;
base.OnMouseMove(e);
if (!toolTipSet)
SetToolTip(null);
foreach (AbstractMargin margin in leftMargins) {
if (margin.DrawingPosition.Contains(e.X, e.Y)) {
this.Cursor = margin.Cursor;
margin.HandleMouseMove(new Point(e.X, e.Y), e.Button);
if (lastMouseInMargin != margin) {
if (lastMouseInMargin != null) {
lastMouseInMargin.HandleMouseLeave(EventArgs.Empty);
}
lastMouseInMargin = margin;
}
return;
}
}
if (lastMouseInMargin != null) {
lastMouseInMargin.HandleMouseLeave(EventArgs.Empty);
lastMouseInMargin = null;
}
if (textView.DrawingPosition.Contains(e.X, e.Y)) {
this.Cursor = textView.Cursor;
return;
}
this.Cursor = Cursors.Default;
}
AbstractMargin updateMargin = null;
public void Refresh(AbstractMargin margin)
{
updateMargin = margin;
Invalidate(updateMargin.DrawingPosition);
Update();
updateMargin = null;
}
protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs pevent)
{
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
int currentXPos = 0;
int currentYPos = 0;
bool adjustScrollBars = false;
Graphics g = e.Graphics;
Rectangle clipRectangle = e.ClipRectangle;
if (updateMargin != null) {
updateMargin.Paint(g, updateMargin.DrawingPosition);
// clipRectangle.Intersect(updateMargin.DrawingPosition);
}
if (clipRectangle.Width <= 0 || clipRectangle.Height <= 0) {
return;
}
if (this.TextEditorProperties.UseAntiAliasedFont) {
g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
} else {
g.TextRenderingHint = TextRenderingHint.SystemDefault;
}
foreach (AbstractMargin margin in leftMargins) {
if (margin.IsVisible) {
Rectangle marginRectangle = new Rectangle(currentXPos , currentYPos, margin.Size.Width, Height - currentYPos);
if (marginRectangle != margin.DrawingPosition) {
adjustScrollBars = true;
margin.DrawingPosition = marginRectangle;
}
currentXPos += margin.DrawingPosition.Width;
if (clipRectangle.IntersectsWith(marginRectangle)) {
marginRectangle.Intersect(clipRectangle);
if (!marginRectangle.IsEmpty) {
margin.Paint(g, marginRectangle);
}
}
}
}
Rectangle textViewArea = new Rectangle(currentXPos, currentYPos, Width - currentXPos, Height - currentYPos);
if (textViewArea != textView.DrawingPosition) {
adjustScrollBars = true;
textView.DrawingPosition = textViewArea;
}
if (clipRectangle.IntersectsWith(textViewArea)) {
textViewArea.Intersect(clipRectangle);
if (!textViewArea.IsEmpty) {
textView.Paint(g, textViewArea);
}
}
if (adjustScrollBars) {
this.motherTextAreaControl.AdjustScrollBars(null, null);
}
Caret.UpdateCaretPosition();
base.OnPaint(e);
}
void DocumentFoldingsChanged(object sender, EventArgs e)
{
this.motherTextAreaControl.AdjustScrollBars(null, null);
}
#region keyboard handling methods
/// <summary>
/// This method is called on each Keypress
/// </summary>
/// <returns>
/// True, if the key is handled by this method and should NOT be
/// inserted in the textarea.
/// </returns>
protected internal virtual bool HandleKeyPress(char ch)
{
if (KeyEventHandler != null) {
return KeyEventHandler(ch);
}
return false;
}
public void SimulateKeyPress(char ch)
{
if (Document.ReadOnly) {
return;
}
if (TextEditorProperties.UseCustomLine == true) {
if (SelectionManager.HasSomethingSelected) {
if (Document.CustomLineManager.IsReadOnly(SelectionManager.SelectionCollection[0], false))
return;
} else if (Document.CustomLineManager.IsReadOnly(Caret.Line, false) == true)
return;
}
if (ch < ' ') {
return;
}
if (!HiddenMouseCursor && TextEditorProperties.HideMouseCursor) {
HiddenMouseCursor = true;
Cursor.Hide();
}
motherTextEditorControl.BeginUpdate();
// INSERT char
if (!HandleKeyPress(ch)) {
switch (Caret.CaretMode) {
case CaretMode.InsertMode:
InsertChar(ch);
break;
case CaretMode.OverwriteMode:
ReplaceChar(ch);
break;
default:
Debug.Assert(false, "Unknown caret mode " + Caret.CaretMode);
break;
}
}
int currentLineNr = Caret.Line;
int delta = Document.FormattingStrategy.FormatLine(this, currentLineNr, Document.PositionToOffset(Caret.Position), ch);
motherTextEditorControl.EndUpdate();
if (delta != 0) {
// this.motherTextEditorControl.UpdateLines(currentLineNr, currentLineNr);
}
}
protected override void OnKeyPress(System.Windows.Forms.KeyPressEventArgs e)
{
base.OnKeyPress(e);
SimulateKeyPress(e.KeyChar);
}
/// <summary>
/// This method executes a dialog key
/// </summary>
public bool ExecuteDialogKey(Keys keyData)
{
// try, if a dialog key processor was set to use this
if (DoProcessDialogKey != null && DoProcessDialogKey(keyData)) {
return true;
}
if (keyData == Keys.Back || keyData == Keys.Delete || keyData == Keys.Enter) {
if (TextEditorProperties.UseCustomLine == true) {
if (SelectionManager.HasSomethingSelected) {
if (Document.CustomLineManager.IsReadOnly(SelectionManager.SelectionCollection[0], false))
return true;
} else {
int curLineNr = Document.GetLineNumberForOffset(Caret.Offset);
if (Document.CustomLineManager.IsReadOnly(curLineNr, false) == true)
return true;
if ((Caret.Column == 0) && (curLineNr - 1 >= 0) && keyData == Keys.Back &&
Document.CustomLineManager.IsReadOnly(curLineNr - 1, false) == true)
return true;
if (keyData == Keys.Delete) {
LineSegment curLine = Document.GetLineSegment(curLineNr);
if (curLine.Offset + curLine.Length == Caret.Offset &&
Document.CustomLineManager.IsReadOnly(curLineNr + 1, false) == true) {
return true;
}
}
}
}
}
// if not (or the process was 'silent', use the standard edit actions
IEditAction action = motherTextEditorControl.GetEditAction(keyData);
AutoClearSelection = true;
if (action != null) {
motherTextEditorControl.BeginUpdate();
try {
lock (Document) {
action.Execute(this);
if (SelectionManager.HasSomethingSelected && AutoClearSelection /*&& caretchanged*/) {
if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal) {
SelectionManager.ClearSelection();
}
}
}
} finally {
motherTextEditorControl.EndUpdate();
Caret.UpdateCaretPosition();
}
return true;
}
return false;
}
protected override bool ProcessDialogKey(Keys keyData)
{
return ExecuteDialogKey(keyData) || base.ProcessDialogKey(keyData);
}
#endregion
public void ScrollToCaret()
{
motherTextAreaControl.ScrollToCaret();
}
public void ScrollTo(int line)
{
motherTextAreaControl.ScrollTo(line);
}
public void BeginUpdate()
{
motherTextEditorControl.BeginUpdate();
}
public void EndUpdate()
{
motherTextEditorControl.EndUpdate();
}
public bool EnableCutOrPaste
{
get {
if (TextEditorProperties.UseCustomLine == true) {
if (SelectionManager.HasSomethingSelected == true) {
if (Document.CustomLineManager.IsReadOnly(SelectionManager.SelectionCollection[0], false))
return false;
}
if (Document.CustomLineManager.IsReadOnly(Caret.Line, false) == true)
return false;
}
return true;
}
}
string GenerateWhitespaceString(int length)
{
return new String(' ', length);
}
/// <remarks>
/// Inserts a single character at the caret position
/// </remarks>
public void InsertChar(char ch)
{
bool updating = motherTextEditorControl.IsUpdating;
if (!updating) {
BeginUpdate();
}
// filter out forgein whitespace chars and replace them with standard space (ASCII 32)
if (Char.IsWhiteSpace(ch) && ch != '\t' && ch != '\n') {
ch = ' ';
}
bool removedText = false;
if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal &&
SelectionManager.SelectionCollection.Count > 0) {
Caret.Position = SelectionManager.SelectionCollection[0].StartPosition;
SelectionManager.RemoveSelectedText();
removedText = true;
}
LineSegment caretLine = Document.GetLineSegment(Caret.Line);
int offset = Caret.Offset;
// use desired column for generated whitespaces
int dc=Math.Min(Caret.Column,Caret.DesiredColumn);
if (caretLine.Length < dc && ch != '\n') {
Document.Insert(offset, GenerateWhitespaceString(dc - caretLine.Length) + ch);
} else {
Document.Insert(offset, ch.ToString());
}
++Caret.Column;
if (removedText) {
Document.UndoStack.UndoLast(2);
}
if (!updating) {
EndUpdate();
UpdateLineToEnd(Caret.Line, Caret.Column);
}
// I prefer to set NOT the standard column, if you type something
// ++Caret.DesiredColumn;
}
/// <remarks>
/// Inserts a whole string at the caret position
/// </remarks>
public void InsertString(string str)
{
bool updating = motherTextEditorControl.IsUpdating;
if (!updating) {
BeginUpdate();
}
try {
bool removedText = false;
if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal &&
SelectionManager.SelectionCollection.Count > 0) {
Caret.Position = SelectionManager.SelectionCollection[0].StartPosition;
SelectionManager.RemoveSelectedText();
removedText = true;
}
int oldOffset = Document.PositionToOffset(Caret.Position);
int oldLine = Caret.Line;
LineSegment caretLine = Document.GetLineSegment(Caret.Line);
if (caretLine.Length < Caret.Column) {
int whiteSpaceLength = Caret.Column - caretLine.Length;
Document.Insert(oldOffset, GenerateWhitespaceString(whiteSpaceLength) + str);
Caret.Position = Document.OffsetToPosition(oldOffset + str.Length + whiteSpaceLength);
} else {
Document.Insert(oldOffset, str);
Caret.Position = Document.OffsetToPosition(oldOffset + str.Length);
}
if (removedText) {
Document.UndoStack.UndoLast(2);
}
if (oldLine != Caret.Line) {
UpdateToEnd(oldLine);
} else {
UpdateLineToEnd(Caret.Line, Caret.Column);
}
} finally {
if (!updating) {
EndUpdate();
}
}
}
/// <remarks>
/// Replaces a char at the caret position
/// </remarks>
public void ReplaceChar(char ch)
{
bool updating = motherTextEditorControl.IsUpdating;
if (!updating) {
BeginUpdate();
}
if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal && SelectionManager.SelectionCollection.Count > 0) {
Caret.Position = SelectionManager.SelectionCollection[0].StartPosition;
SelectionManager.RemoveSelectedText();
}
int lineNr = Caret.Line;
LineSegment line = Document.GetLineSegment(lineNr);
int offset = Document.PositionToOffset(Caret.Position);
if (offset < line.Offset + line.Length) {
Document.Replace(offset, 1, ch.ToString());
} else {
Document.Insert(offset, ch.ToString());
}
if (!updating) {
EndUpdate();
UpdateLineToEnd(lineNr, Caret.Column);
}
++Caret.Column;
// ++Caret.DesiredColumn;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing) {
Caret.Dispose();
}
}
#region UPDATE Commands
internal void UpdateLine(int line)
{
UpdateLines(0, line, line);
}
internal void UpdateLines(int lineBegin, int lineEnd)
{
UpdateLines(0, lineBegin, lineEnd);
}
internal void UpdateToEnd(int lineBegin)
{
// if (lineBegin > FirstPhysicalLine + textView.VisibleLineCount) {
// return;
// }
lineBegin = Math.Min(lineBegin, FirstPhysicalLine);
int y = Math.Max( 0, (int)(lineBegin * textView.FontHeight));
y = Math.Max(0, y - this.virtualTop.Y);
Rectangle r = new Rectangle(0,
y,
Width,
Height - y);
Invalidate(r);
}
internal void UpdateLineToEnd(int lineNr, int xStart)
{
UpdateLines(xStart, lineNr, lineNr);
}
internal void UpdateLine(int line, int begin, int end)
{
UpdateLines(line, line);
}
int FirstPhysicalLine {
get {
return VirtualTop.Y / textView.FontHeight;
}
}
internal void UpdateLines(int xPos, int lineBegin, int lineEnd)
{
// if (lineEnd < FirstPhysicalLine || lineBegin > FirstPhysicalLine + textView.VisibleLineCount) {
// return;
// }
InvalidateLines((int)(xPos * this.TextView.WideSpaceWidth), lineBegin, lineEnd);
}
void InvalidateLines(int xPos, int lineBegin, int lineEnd)
{
lineBegin = Math.Max(Document.GetVisibleLine(lineBegin), FirstPhysicalLine);
lineEnd = Math.Min(Document.GetVisibleLine(lineEnd), FirstPhysicalLine + textView.VisibleLineCount);
int y = Math.Max( 0, (int)(lineBegin * textView.FontHeight));
int height = Math.Min(textView.DrawingPosition.Height, (int)((1 + lineEnd - lineBegin) * (textView.FontHeight + 1)));
Rectangle r = new Rectangle(0,
y - 1 - this.virtualTop.Y,
Width,
height + 3);
Invalidate(r);
}
#endregion
public event KeyEventHandler KeyEventHandler;
public event DialogKeyProcessor DoProcessDialogKey;
//internal void
}
}