first commit
This commit is contained in:
115
ICSharpCode.TextEditor/Project/Src/Gui/AbstractMargin.cs
Normal file
115
ICSharpCode.TextEditor/Project/Src/Gui/AbstractMargin.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
// <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.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
public delegate void MarginMouseEventHandler(AbstractMargin sender, Point mousepos, MouseButtons mouseButtons);
|
||||
public delegate void MarginPaintEventHandler(AbstractMargin sender, Graphics g, Rectangle rect);
|
||||
|
||||
/// <summary>
|
||||
/// This class views the line numbers and folding markers.
|
||||
/// </summary>
|
||||
public abstract class AbstractMargin
|
||||
{
|
||||
Cursor cursor = Cursors.Default;
|
||||
|
||||
[CLSCompliant(false)]
|
||||
protected Rectangle drawingPosition = new Rectangle(0, 0, 0, 0);
|
||||
[CLSCompliant(false)]
|
||||
protected TextArea textArea;
|
||||
|
||||
public Rectangle DrawingPosition {
|
||||
get {
|
||||
return drawingPosition;
|
||||
}
|
||||
set {
|
||||
drawingPosition = value;
|
||||
}
|
||||
}
|
||||
|
||||
public TextArea TextArea {
|
||||
get {
|
||||
return textArea;
|
||||
}
|
||||
}
|
||||
|
||||
public IDocument Document {
|
||||
get {
|
||||
return textArea.Document;
|
||||
}
|
||||
}
|
||||
|
||||
public ITextEditorProperties TextEditorProperties {
|
||||
get {
|
||||
return textArea.Document.TextEditorProperties;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Cursor Cursor {
|
||||
get {
|
||||
return cursor;
|
||||
}
|
||||
set {
|
||||
cursor = value;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Size Size {
|
||||
get {
|
||||
return new Size(-1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsVisible {
|
||||
get {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected AbstractMargin(TextArea textArea)
|
||||
{
|
||||
this.textArea = textArea;
|
||||
}
|
||||
|
||||
public virtual void HandleMouseDown(Point mousepos, MouseButtons mouseButtons)
|
||||
{
|
||||
if (MouseDown != null) {
|
||||
MouseDown(this, mousepos, mouseButtons);
|
||||
}
|
||||
}
|
||||
public virtual void HandleMouseMove(Point mousepos, MouseButtons mouseButtons)
|
||||
{
|
||||
if (MouseMove != null) {
|
||||
MouseMove(this, mousepos, mouseButtons);
|
||||
}
|
||||
}
|
||||
public virtual void HandleMouseLeave(EventArgs e)
|
||||
{
|
||||
if (MouseLeave != null) {
|
||||
MouseLeave(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Paint(Graphics g, Rectangle rect)
|
||||
{
|
||||
if (Painted != null) {
|
||||
Painted(this, g, rect);
|
||||
}
|
||||
}
|
||||
|
||||
public event MarginPaintEventHandler Painted;
|
||||
public event MarginMouseEventHandler MouseDown;
|
||||
public event MarginMouseEventHandler MouseMove;
|
||||
public event EventHandler MouseLeave;
|
||||
}
|
||||
}
|
||||
86
ICSharpCode.TextEditor/Project/Src/Gui/BracketHighlighter.cs
Normal file
86
ICSharpCode.TextEditor/Project/Src/Gui/BracketHighlighter.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
// <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.Drawing;
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
public class Highlight
|
||||
{
|
||||
public TextLocation OpenBrace { get; set; }
|
||||
public TextLocation CloseBrace { get; set; }
|
||||
|
||||
public Highlight(TextLocation openBrace, TextLocation closeBrace)
|
||||
{
|
||||
this.OpenBrace = openBrace;
|
||||
this.CloseBrace = closeBrace;
|
||||
}
|
||||
}
|
||||
|
||||
public class BracketHighlightingSheme
|
||||
{
|
||||
char opentag;
|
||||
char closingtag;
|
||||
|
||||
public char OpenTag {
|
||||
get {
|
||||
return opentag;
|
||||
}
|
||||
set {
|
||||
opentag = value;
|
||||
}
|
||||
}
|
||||
|
||||
public char ClosingTag {
|
||||
get {
|
||||
return closingtag;
|
||||
}
|
||||
set {
|
||||
closingtag = value;
|
||||
}
|
||||
}
|
||||
|
||||
public BracketHighlightingSheme(char opentag, char closingtag)
|
||||
{
|
||||
this.opentag = opentag;
|
||||
this.closingtag = closingtag;
|
||||
}
|
||||
|
||||
public Highlight GetHighlight(IDocument document, int offset)
|
||||
{
|
||||
int searchOffset;
|
||||
if (document.TextEditorProperties.BracketMatchingStyle == BracketMatchingStyle.After) {
|
||||
searchOffset = offset;
|
||||
} else {
|
||||
searchOffset = offset + 1;
|
||||
}
|
||||
char word = document.GetCharAt(Math.Max(0, Math.Min(document.TextLength - 1, searchOffset)));
|
||||
|
||||
TextLocation endP = document.OffsetToPosition(searchOffset);
|
||||
if (word == opentag) {
|
||||
if (searchOffset < document.TextLength) {
|
||||
int bracketOffset = TextUtilities.SearchBracketForward(document, searchOffset + 1, opentag, closingtag);
|
||||
if (bracketOffset >= 0) {
|
||||
TextLocation p = document.OffsetToPosition(bracketOffset);
|
||||
return new Highlight(p, endP);
|
||||
}
|
||||
}
|
||||
} else if (word == closingtag) {
|
||||
if (searchOffset > 0) {
|
||||
int bracketOffset = TextUtilities.SearchBracketBackward(document, searchOffset - 1, opentag, closingtag);
|
||||
if (bracketOffset >= 0) {
|
||||
TextLocation p = document.OffsetToPosition(bracketOffset);
|
||||
return new Highlight(p, endP);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
ICSharpCode.TextEditor/Project/Src/Gui/BrushRegistry.cs
Normal file
65
ICSharpCode.TextEditor/Project/Src/Gui/BrushRegistry.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
// <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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains brushes/pens for the text editor to speed up drawing. Re-Creation of brushes and pens
|
||||
/// seems too costly.
|
||||
/// </summary>
|
||||
public class BrushRegistry
|
||||
{
|
||||
static Dictionary<Color, Brush> brushes = new Dictionary<Color, Brush>();
|
||||
static Dictionary<Color, Pen> pens = new Dictionary<Color, Pen>();
|
||||
static Dictionary<Color, Pen> dotPens = new Dictionary<Color, Pen>();
|
||||
|
||||
public static Brush GetBrush(Color color)
|
||||
{
|
||||
lock (brushes) {
|
||||
Brush brush;
|
||||
if (!brushes.TryGetValue(color, out brush)) {
|
||||
brush = new SolidBrush(color);
|
||||
brushes.Add(color, brush);
|
||||
}
|
||||
return brush;
|
||||
}
|
||||
}
|
||||
|
||||
public static Pen GetPen(Color color)
|
||||
{
|
||||
lock (pens) {
|
||||
Pen pen;
|
||||
if (!pens.TryGetValue(color, out pen)) {
|
||||
pen = new Pen(color);
|
||||
pens.Add(color, pen);
|
||||
}
|
||||
return pen;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly float[] dotPattern = { 1, 1, 1, 1 };
|
||||
|
||||
public static Pen GetDotPen(Color color)
|
||||
{
|
||||
lock (dotPens) {
|
||||
Pen pen;
|
||||
if (!dotPens.TryGetValue(color, out pen)) {
|
||||
pen = new Pen(color);
|
||||
pen.DashPattern = dotPattern;
|
||||
dotPens.Add(color, pen);
|
||||
}
|
||||
return pen;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
510
ICSharpCode.TextEditor/Project/Src/Gui/Caret.cs
Normal file
510
ICSharpCode.TextEditor/Project/Src/Gui/Caret.cs
Normal file
@@ -0,0 +1,510 @@
|
||||
// <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.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// In this enumeration are all caret modes listed.
|
||||
/// </summary>
|
||||
public enum CaretMode {
|
||||
/// <summary>
|
||||
/// If the caret is in insert mode typed characters will be
|
||||
/// inserted at the caret position
|
||||
/// </summary>
|
||||
InsertMode,
|
||||
|
||||
/// <summary>
|
||||
/// If the caret is in overwirte mode typed characters will
|
||||
/// overwrite the character at the caret position
|
||||
/// </summary>
|
||||
OverwriteMode
|
||||
}
|
||||
|
||||
|
||||
public class Caret : System.IDisposable
|
||||
{
|
||||
int line = 0;
|
||||
int column = 0;
|
||||
int desiredXPos = 0;
|
||||
CaretMode caretMode;
|
||||
|
||||
static bool caretCreated = false;
|
||||
bool hidden = true;
|
||||
TextArea textArea;
|
||||
Point currentPos = new Point(-1, -1);
|
||||
Ime ime = null;
|
||||
CaretImplementation caretImplementation;
|
||||
|
||||
/// <value>
|
||||
/// The 'prefered' xPos in which the caret moves, when it is moved
|
||||
/// up/down. Measured in pixels, not in characters!
|
||||
/// </value>
|
||||
public int DesiredColumn {
|
||||
get {
|
||||
return desiredXPos;
|
||||
}
|
||||
set {
|
||||
desiredXPos = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// The current caret mode.
|
||||
/// </value>
|
||||
public CaretMode CaretMode {
|
||||
get {
|
||||
return caretMode;
|
||||
}
|
||||
set {
|
||||
caretMode = value;
|
||||
OnCaretModeChanged(EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public int Line {
|
||||
get {
|
||||
return line;
|
||||
}
|
||||
set {
|
||||
line = value;
|
||||
ValidateCaretPos();
|
||||
UpdateCaretPosition();
|
||||
OnPositionChanged(EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public int Column {
|
||||
get {
|
||||
return column;
|
||||
}
|
||||
set {
|
||||
column = value;
|
||||
ValidateCaretPos();
|
||||
UpdateCaretPosition();
|
||||
OnPositionChanged(EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public TextLocation Position {
|
||||
get {
|
||||
return new TextLocation(column, line);
|
||||
}
|
||||
set {
|
||||
line = value.Y;
|
||||
column = value.X;
|
||||
ValidateCaretPos();
|
||||
UpdateCaretPosition();
|
||||
OnPositionChanged(EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public int Offset {
|
||||
get {
|
||||
return textArea.Document.PositionToOffset(Position);
|
||||
}
|
||||
}
|
||||
|
||||
public Caret(TextArea textArea)
|
||||
{
|
||||
this.textArea = textArea;
|
||||
textArea.GotFocus += new EventHandler(GotFocus);
|
||||
textArea.LostFocus += new EventHandler(LostFocus);
|
||||
if (Environment.OSVersion.Platform == PlatformID.Unix)
|
||||
caretImplementation = new ManagedCaret(this);
|
||||
else
|
||||
caretImplementation = new Win32Caret(this);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
textArea.GotFocus -= new EventHandler(GotFocus);
|
||||
textArea.LostFocus -= new EventHandler(LostFocus);
|
||||
textArea = null;
|
||||
caretImplementation.Dispose();
|
||||
}
|
||||
|
||||
public TextLocation ValidatePosition(TextLocation pos)
|
||||
{
|
||||
int line = Math.Max(0, Math.Min(textArea.Document.TotalNumberOfLines - 1, pos.Y));
|
||||
int column = Math.Max(0, pos.X);
|
||||
|
||||
if (column == int.MaxValue || !textArea.TextEditorProperties.AllowCaretBeyondEOL) {
|
||||
LineSegment lineSegment = textArea.Document.GetLineSegment(line);
|
||||
column = Math.Min(column, lineSegment.Length);
|
||||
}
|
||||
return new TextLocation(column, line);
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// If the caret position is outside the document text bounds
|
||||
/// it is set to the correct position by calling ValidateCaretPos.
|
||||
/// </remarks>
|
||||
public void ValidateCaretPos()
|
||||
{
|
||||
line = Math.Max(0, Math.Min(textArea.Document.TotalNumberOfLines - 1, line));
|
||||
column = Math.Max(0, column);
|
||||
|
||||
if (column == int.MaxValue || !textArea.TextEditorProperties.AllowCaretBeyondEOL) {
|
||||
LineSegment lineSegment = textArea.Document.GetLineSegment(line);
|
||||
column = Math.Min(column, lineSegment.Length);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateCaret()
|
||||
{
|
||||
while (!caretCreated) {
|
||||
switch (caretMode) {
|
||||
case CaretMode.InsertMode:
|
||||
caretCreated = caretImplementation.Create(2, textArea.TextView.FontHeight);
|
||||
break;
|
||||
case CaretMode.OverwriteMode:
|
||||
caretCreated = caretImplementation.Create((int)textArea.TextView.SpaceWidth, textArea.TextView.FontHeight);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (currentPos.X < 0) {
|
||||
ValidateCaretPos();
|
||||
currentPos = ScreenPosition;
|
||||
}
|
||||
caretImplementation.SetPosition(currentPos.X, currentPos.Y);
|
||||
caretImplementation.Show();
|
||||
}
|
||||
|
||||
public void RecreateCaret()
|
||||
{
|
||||
Log("RecreateCaret");
|
||||
DisposeCaret();
|
||||
if (!hidden) {
|
||||
CreateCaret();
|
||||
}
|
||||
}
|
||||
|
||||
void DisposeCaret()
|
||||
{
|
||||
if (caretCreated) {
|
||||
caretCreated = false;
|
||||
caretImplementation.Hide();
|
||||
caretImplementation.Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void GotFocus(object sender, EventArgs e)
|
||||
{
|
||||
Log("GotFocus, IsInUpdate=" + textArea.MotherTextEditorControl.IsInUpdate);
|
||||
hidden = false;
|
||||
if (!textArea.MotherTextEditorControl.IsInUpdate) {
|
||||
CreateCaret();
|
||||
UpdateCaretPosition();
|
||||
}
|
||||
}
|
||||
|
||||
void LostFocus(object sender, EventArgs e)
|
||||
{
|
||||
Log("LostFocus");
|
||||
hidden = true;
|
||||
DisposeCaret();
|
||||
}
|
||||
|
||||
public Point ScreenPosition {
|
||||
get {
|
||||
int xpos = textArea.TextView.GetDrawingXPos(this.line, this.column);
|
||||
return new Point(textArea.TextView.DrawingPosition.X + xpos,
|
||||
textArea.TextView.DrawingPosition.Y
|
||||
+ (textArea.Document.GetVisibleLine(this.line)) * textArea.TextView.FontHeight
|
||||
- textArea.TextView.TextArea.VirtualTop.Y);
|
||||
}
|
||||
}
|
||||
int oldLine = -1;
|
||||
bool outstandingUpdate;
|
||||
|
||||
internal void OnEndUpdate()
|
||||
{
|
||||
if (outstandingUpdate)
|
||||
UpdateCaretPosition();
|
||||
}
|
||||
|
||||
void PaintCaretLine(Graphics g)
|
||||
{
|
||||
if (!textArea.Document.TextEditorProperties.CaretLine)
|
||||
return;
|
||||
|
||||
HighlightColor caretLineColor = textArea.Document.HighlightingStrategy.GetColorFor("CaretLine");
|
||||
|
||||
g.DrawLine(BrushRegistry.GetDotPen(caretLineColor.Color),
|
||||
currentPos.X,
|
||||
0,
|
||||
currentPos.X,
|
||||
textArea.DisplayRectangle.Height);
|
||||
}
|
||||
|
||||
public void UpdateCaretPosition()
|
||||
{
|
||||
Log("UpdateCaretPosition");
|
||||
|
||||
if (textArea.TextEditorProperties.CaretLine) {
|
||||
textArea.Invalidate();
|
||||
} else {
|
||||
if (caretImplementation.RequireRedrawOnPositionChange) {
|
||||
textArea.UpdateLine(oldLine);
|
||||
if (line != oldLine)
|
||||
textArea.UpdateLine(line);
|
||||
} else {
|
||||
if (textArea.MotherTextAreaControl.TextEditorProperties.LineViewerStyle == LineViewerStyle.FullRow && oldLine != line) {
|
||||
textArea.UpdateLine(oldLine);
|
||||
textArea.UpdateLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
oldLine = line;
|
||||
|
||||
|
||||
if (hidden || textArea.MotherTextEditorControl.IsInUpdate) {
|
||||
outstandingUpdate = true;
|
||||
return;
|
||||
} else {
|
||||
outstandingUpdate = false;
|
||||
}
|
||||
ValidateCaretPos();
|
||||
int lineNr = this.line;
|
||||
int xpos = textArea.TextView.GetDrawingXPos(lineNr, this.column);
|
||||
//LineSegment lineSegment = textArea.Document.GetLineSegment(lineNr);
|
||||
Point pos = ScreenPosition;
|
||||
if (xpos >= 0) {
|
||||
CreateCaret();
|
||||
bool success = caretImplementation.SetPosition(pos.X, pos.Y);
|
||||
if (!success) {
|
||||
caretImplementation.Destroy();
|
||||
caretCreated = false;
|
||||
UpdateCaretPosition();
|
||||
}
|
||||
} else {
|
||||
caretImplementation.Destroy();
|
||||
}
|
||||
|
||||
// set the input method editor location
|
||||
if (ime == null) {
|
||||
ime = new Ime(textArea.Handle, textArea.Document.TextEditorProperties.Font);
|
||||
} else {
|
||||
ime.HWnd = textArea.Handle;
|
||||
ime.Font = textArea.Document.TextEditorProperties.Font;
|
||||
}
|
||||
ime.SetIMEWindowLocation(pos.X, pos.Y);
|
||||
|
||||
currentPos = pos;
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
static void Log(string text)
|
||||
{
|
||||
//Console.WriteLine(text);
|
||||
}
|
||||
|
||||
#region Caret implementation
|
||||
internal void PaintCaret(Graphics g)
|
||||
{
|
||||
caretImplementation.PaintCaret(g);
|
||||
PaintCaretLine(g);
|
||||
}
|
||||
|
||||
abstract class CaretImplementation : IDisposable
|
||||
{
|
||||
public bool RequireRedrawOnPositionChange;
|
||||
|
||||
public abstract bool Create(int width, int height);
|
||||
public abstract void Hide();
|
||||
public abstract void Show();
|
||||
public abstract bool SetPosition(int x, int y);
|
||||
public abstract void PaintCaret(Graphics g);
|
||||
public abstract void Destroy();
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
class ManagedCaret : CaretImplementation
|
||||
{
|
||||
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer { Interval = 300 };
|
||||
bool visible;
|
||||
bool blink = true;
|
||||
int x, y, width, height;
|
||||
TextArea textArea;
|
||||
Caret parentCaret;
|
||||
|
||||
public ManagedCaret(Caret caret)
|
||||
{
|
||||
base.RequireRedrawOnPositionChange = true;
|
||||
this.textArea = caret.textArea;
|
||||
this.parentCaret = caret;
|
||||
timer.Tick += CaretTimerTick;
|
||||
}
|
||||
|
||||
void CaretTimerTick(object sender, EventArgs e)
|
||||
{
|
||||
blink = !blink;
|
||||
if (visible)
|
||||
textArea.UpdateLine(parentCaret.Line);
|
||||
}
|
||||
|
||||
public override bool Create(int width, int height)
|
||||
{
|
||||
this.visible = true;
|
||||
this.width = width - 2;
|
||||
this.height = height;
|
||||
timer.Enabled = true;
|
||||
return true;
|
||||
}
|
||||
public override void Hide()
|
||||
{
|
||||
visible = false;
|
||||
}
|
||||
public override void Show()
|
||||
{
|
||||
visible = true;
|
||||
}
|
||||
public override bool SetPosition(int x, int y)
|
||||
{
|
||||
this.x = x - 1;
|
||||
this.y = y;
|
||||
return true;
|
||||
}
|
||||
public override void PaintCaret(Graphics g)
|
||||
{
|
||||
if (visible && blink)
|
||||
g.DrawRectangle(Pens.Gray, x, y, width, height);
|
||||
}
|
||||
public override void Destroy()
|
||||
{
|
||||
visible = false;
|
||||
timer.Enabled = false;
|
||||
}
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
timer.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class Win32Caret : CaretImplementation
|
||||
{
|
||||
[DllImport("User32.dll")]
|
||||
static extern bool CreateCaret(IntPtr hWnd, int hBitmap, int nWidth, int nHeight);
|
||||
|
||||
[DllImport("User32.dll")]
|
||||
static extern bool SetCaretPos(int x, int y);
|
||||
|
||||
[DllImport("User32.dll")]
|
||||
static extern bool DestroyCaret();
|
||||
|
||||
[DllImport("User32.dll")]
|
||||
static extern bool ShowCaret(IntPtr hWnd);
|
||||
|
||||
[DllImport("User32.dll")]
|
||||
static extern bool HideCaret(IntPtr hWnd);
|
||||
|
||||
TextArea textArea;
|
||||
|
||||
public Win32Caret(Caret caret)
|
||||
{
|
||||
this.textArea = caret.textArea;
|
||||
}
|
||||
|
||||
public override bool Create(int width, int height)
|
||||
{
|
||||
return CreateCaret(textArea.Handle, 0, width, height);
|
||||
}
|
||||
public override void Hide()
|
||||
{
|
||||
HideCaret(textArea.Handle);
|
||||
}
|
||||
public override void Show()
|
||||
{
|
||||
ShowCaret(textArea.Handle);
|
||||
}
|
||||
public override bool SetPosition(int x, int y)
|
||||
{
|
||||
return SetCaretPos(x, y);
|
||||
}
|
||||
public override void PaintCaret(Graphics g)
|
||||
{
|
||||
}
|
||||
public override void Destroy()
|
||||
{
|
||||
DestroyCaret();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
bool firePositionChangedAfterUpdateEnd;
|
||||
|
||||
void FirePositionChangedAfterUpdateEnd(object sender, EventArgs e)
|
||||
{
|
||||
OnPositionChanged(EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected virtual void OnPositionChanged(EventArgs e)
|
||||
{
|
||||
if (this.textArea.MotherTextEditorControl.IsInUpdate) {
|
||||
if (firePositionChangedAfterUpdateEnd == false) {
|
||||
firePositionChangedAfterUpdateEnd = true;
|
||||
this.textArea.Document.UpdateCommited += FirePositionChangedAfterUpdateEnd;
|
||||
}
|
||||
return;
|
||||
} else if (firePositionChangedAfterUpdateEnd) {
|
||||
this.textArea.Document.UpdateCommited -= FirePositionChangedAfterUpdateEnd;
|
||||
firePositionChangedAfterUpdateEnd = false;
|
||||
}
|
||||
|
||||
List<FoldMarker> foldings = textArea.Document.FoldingManager.GetFoldingsFromPosition(line, column);
|
||||
bool shouldUpdate = false;
|
||||
foreach (FoldMarker foldMarker in foldings) {
|
||||
shouldUpdate |= foldMarker.IsFolded;
|
||||
foldMarker.IsFolded = false;
|
||||
}
|
||||
|
||||
if (shouldUpdate) {
|
||||
textArea.Document.FoldingManager.NotifyFoldingsChanged(EventArgs.Empty);
|
||||
}
|
||||
|
||||
if (PositionChanged != null) {
|
||||
PositionChanged(this, e);
|
||||
}
|
||||
textArea.ScrollToCaret();
|
||||
}
|
||||
|
||||
protected virtual void OnCaretModeChanged(EventArgs e)
|
||||
{
|
||||
if (CaretModeChanged != null) {
|
||||
CaretModeChanged(this, e);
|
||||
}
|
||||
caretImplementation.Hide();
|
||||
caretImplementation.Destroy();
|
||||
caretCreated = false;
|
||||
CreateCaret();
|
||||
caretImplementation.Show();
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Is called each time the caret is moved.
|
||||
/// </remarks>
|
||||
public event EventHandler PositionChanged;
|
||||
|
||||
/// <remarks>
|
||||
/// Is called each time the CaretMode has changed.
|
||||
/// </remarks>
|
||||
public event EventHandler CaretModeChanged;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
// <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.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ICSharpCode.TextEditor.Gui.CompletionWindow
|
||||
{
|
||||
/// <summary>
|
||||
/// Description of AbstractCompletionWindow.
|
||||
/// </summary>
|
||||
public abstract class AbstractCompletionWindow : System.Windows.Forms.Form
|
||||
{
|
||||
protected TextEditorControl control;
|
||||
protected Size drawingSize;
|
||||
Rectangle workingScreen;
|
||||
Form parentForm;
|
||||
|
||||
protected AbstractCompletionWindow(Form parentForm, TextEditorControl control)
|
||||
{
|
||||
workingScreen = Screen.GetWorkingArea(parentForm);
|
||||
// SetStyle(ControlStyles.Selectable, false);
|
||||
this.parentForm = parentForm;
|
||||
this.control = control;
|
||||
|
||||
SetLocation();
|
||||
StartPosition = FormStartPosition.Manual;
|
||||
FormBorderStyle = FormBorderStyle.None;
|
||||
ShowInTaskbar = false;
|
||||
MinimumSize = new Size(1, 1);
|
||||
Size = new Size(1, 1);
|
||||
}
|
||||
|
||||
protected virtual void SetLocation()
|
||||
{
|
||||
TextArea textArea = control.ActiveTextAreaControl.TextArea;
|
||||
TextLocation caretPos = textArea.Caret.Position;
|
||||
|
||||
int xpos = textArea.TextView.GetDrawingXPos(caretPos.Y, caretPos.X);
|
||||
int rulerHeight = textArea.TextEditorProperties.ShowHorizontalRuler ? textArea.TextView.FontHeight : 0;
|
||||
Point pos = new Point(textArea.TextView.DrawingPosition.X + xpos,
|
||||
textArea.TextView.DrawingPosition.Y + (textArea.Document.GetVisibleLine(caretPos.Y)) * textArea.TextView.FontHeight
|
||||
- textArea.TextView.TextArea.VirtualTop.Y + textArea.TextView.FontHeight + rulerHeight);
|
||||
|
||||
Point location = control.ActiveTextAreaControl.PointToScreen(pos);
|
||||
|
||||
// set bounds
|
||||
Rectangle bounds = new Rectangle(location, drawingSize);
|
||||
|
||||
if (!workingScreen.Contains(bounds)) {
|
||||
if (bounds.Right > workingScreen.Right) {
|
||||
bounds.X = workingScreen.Right - bounds.Width;
|
||||
}
|
||||
if (bounds.Left < workingScreen.Left) {
|
||||
bounds.X = workingScreen.Left;
|
||||
}
|
||||
if (bounds.Top < workingScreen.Top) {
|
||||
bounds.Y = workingScreen.Top;
|
||||
}
|
||||
if (bounds.Bottom > workingScreen.Bottom) {
|
||||
bounds.Y = bounds.Y - bounds.Height - control.ActiveTextAreaControl.TextArea.TextView.FontHeight;
|
||||
if (bounds.Bottom > workingScreen.Bottom) {
|
||||
bounds.Y = workingScreen.Bottom - bounds.Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
Bounds = bounds;
|
||||
}
|
||||
|
||||
protected override CreateParams CreateParams {
|
||||
get {
|
||||
CreateParams p = base.CreateParams;
|
||||
AddShadowToWindow(p);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
static int shadowStatus;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a shadow to the create params if it is supported by the operating system.
|
||||
/// </summary>
|
||||
public static void AddShadowToWindow(CreateParams createParams)
|
||||
{
|
||||
if (shadowStatus == 0) {
|
||||
// Test OS version
|
||||
shadowStatus = -1; // shadow not supported
|
||||
if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
|
||||
Version ver = Environment.OSVersion.Version;
|
||||
if (ver.Major > 5 || ver.Major == 5 && ver.Minor >= 1) {
|
||||
shadowStatus = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shadowStatus == 1) {
|
||||
createParams.ClassStyle |= 0x00020000; // set CS_DROPSHADOW
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ShowWithoutActivation {
|
||||
get {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected void ShowCompletionWindow()
|
||||
{
|
||||
Owner = parentForm;
|
||||
Enabled = true;
|
||||
this.Show();
|
||||
|
||||
control.Focus();
|
||||
|
||||
if (parentForm != null) {
|
||||
parentForm.LocationChanged += new EventHandler(this.ParentFormLocationChanged);
|
||||
}
|
||||
|
||||
control.ActiveTextAreaControl.VScrollBar.ValueChanged += new EventHandler(ParentFormLocationChanged);
|
||||
control.ActiveTextAreaControl.HScrollBar.ValueChanged += new EventHandler(ParentFormLocationChanged);
|
||||
control.ActiveTextAreaControl.TextArea.DoProcessDialogKey += new DialogKeyProcessor(ProcessTextAreaKey);
|
||||
control.ActiveTextAreaControl.Caret.PositionChanged += new EventHandler(CaretOffsetChanged);
|
||||
control.ActiveTextAreaControl.TextArea.LostFocus += new EventHandler(this.TextEditorLostFocus);
|
||||
control.Resize += new EventHandler(ParentFormLocationChanged);
|
||||
|
||||
foreach (Control c in Controls) {
|
||||
c.MouseMove += ControlMouseMove;
|
||||
}
|
||||
}
|
||||
|
||||
void ParentFormLocationChanged(object sender, EventArgs e)
|
||||
{
|
||||
SetLocation();
|
||||
}
|
||||
|
||||
public virtual bool ProcessKeyEvent(char ch)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual bool ProcessTextAreaKey(Keys keyData)
|
||||
{
|
||||
if (!Visible) {
|
||||
return false;
|
||||
}
|
||||
switch (keyData) {
|
||||
case Keys.Escape:
|
||||
Close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual void CaretOffsetChanged(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
protected void TextEditorLostFocus(object sender, EventArgs e)
|
||||
{
|
||||
if (!control.ActiveTextAreaControl.TextArea.Focused && !this.ContainsFocus) {
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
base.OnClosed(e);
|
||||
|
||||
// take out the inserted methods
|
||||
parentForm.LocationChanged -= new EventHandler(ParentFormLocationChanged);
|
||||
|
||||
foreach (Control c in Controls) {
|
||||
c.MouseMove -= ControlMouseMove;
|
||||
}
|
||||
|
||||
if (control.ActiveTextAreaControl.VScrollBar != null) {
|
||||
control.ActiveTextAreaControl.VScrollBar.ValueChanged -= new EventHandler(ParentFormLocationChanged);
|
||||
}
|
||||
if (control.ActiveTextAreaControl.HScrollBar != null) {
|
||||
control.ActiveTextAreaControl.HScrollBar.ValueChanged -= new EventHandler(ParentFormLocationChanged);
|
||||
}
|
||||
|
||||
control.ActiveTextAreaControl.TextArea.LostFocus -= new EventHandler(this.TextEditorLostFocus);
|
||||
control.ActiveTextAreaControl.Caret.PositionChanged -= new EventHandler(CaretOffsetChanged);
|
||||
control.ActiveTextAreaControl.TextArea.DoProcessDialogKey -= new DialogKeyProcessor(ProcessTextAreaKey);
|
||||
control.Resize -= new EventHandler(ParentFormLocationChanged);
|
||||
Dispose();
|
||||
}
|
||||
|
||||
protected override void OnMouseMove(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseMove(e);
|
||||
ControlMouseMove(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the mouse moves over this form or any child control.
|
||||
/// Shows the mouse cursor on the text area if it has been hidden.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Derived classes should attach this handler to the MouseMove event
|
||||
/// of all created controls which are not added to the Controls
|
||||
/// collection.
|
||||
/// </remarks>
|
||||
protected void ControlMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
control.ActiveTextAreaControl.TextArea.ShowHiddenCursor(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
// <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.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ICSharpCode.TextEditor.Gui.CompletionWindow
|
||||
{
|
||||
/// <summary>
|
||||
/// Description of CodeCompletionListView.
|
||||
/// </summary>
|
||||
public class CodeCompletionListView : System.Windows.Forms.UserControl
|
||||
{
|
||||
ICompletionData[] completionData;
|
||||
int firstItem = 0;
|
||||
int selectedItem = -1;
|
||||
ImageList imageList;
|
||||
|
||||
public ImageList ImageList {
|
||||
get {
|
||||
return imageList;
|
||||
}
|
||||
set {
|
||||
imageList = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int FirstItem {
|
||||
get {
|
||||
return firstItem;
|
||||
}
|
||||
set {
|
||||
if (firstItem != value) {
|
||||
firstItem = value;
|
||||
OnFirstItemChanged(EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ICompletionData SelectedCompletionData {
|
||||
get {
|
||||
if (selectedItem < 0) {
|
||||
return null;
|
||||
}
|
||||
return completionData[selectedItem];
|
||||
}
|
||||
}
|
||||
|
||||
public int ItemHeight {
|
||||
get {
|
||||
return Math.Max(imageList.ImageSize.Height, (int)(Font.Height * 1.25));
|
||||
}
|
||||
}
|
||||
|
||||
public int MaxVisibleItem {
|
||||
get {
|
||||
return Height / ItemHeight;
|
||||
}
|
||||
}
|
||||
|
||||
public CodeCompletionListView(ICompletionData[] completionData)
|
||||
{
|
||||
Array.Sort(completionData, DefaultCompletionData.Compare);
|
||||
this.completionData = completionData;
|
||||
|
||||
// this.KeyDown += new System.Windows.Forms.KeyEventHandler(OnKey);
|
||||
// SetStyle(ControlStyles.Selectable, false);
|
||||
// SetStyle(ControlStyles.UserPaint, true);
|
||||
// SetStyle(ControlStyles.DoubleBuffer, false);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (completionData != null) {
|
||||
Array.Clear(completionData, 0, completionData.Length);
|
||||
}
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
public void SelectIndex(int index)
|
||||
{
|
||||
int oldSelectedItem = selectedItem;
|
||||
int oldFirstItem = firstItem;
|
||||
|
||||
index = Math.Max(0, index);
|
||||
selectedItem = Math.Max(0, Math.Min(completionData.Length - 1, index));
|
||||
if (selectedItem < firstItem) {
|
||||
FirstItem = selectedItem;
|
||||
}
|
||||
if (firstItem + MaxVisibleItem <= selectedItem) {
|
||||
FirstItem = selectedItem - MaxVisibleItem + 1;
|
||||
}
|
||||
if (oldSelectedItem != selectedItem) {
|
||||
if (firstItem != oldFirstItem) {
|
||||
Invalidate();
|
||||
} else {
|
||||
int min = Math.Min(selectedItem, oldSelectedItem) - firstItem;
|
||||
int max = Math.Max(selectedItem, oldSelectedItem) - firstItem;
|
||||
Invalidate(new Rectangle(0, 1 + min * ItemHeight, Width, (max - min + 1) * ItemHeight));
|
||||
}
|
||||
OnSelectedItemChanged(EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public void CenterViewOn(int index)
|
||||
{
|
||||
int oldFirstItem = this.FirstItem;
|
||||
int firstItem = index - MaxVisibleItem / 2;
|
||||
if (firstItem < 0)
|
||||
this.FirstItem = 0;
|
||||
else if (firstItem >= completionData.Length - MaxVisibleItem)
|
||||
this.FirstItem = completionData.Length - MaxVisibleItem;
|
||||
else
|
||||
this.FirstItem = firstItem;
|
||||
if (this.FirstItem != oldFirstItem) {
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearSelection()
|
||||
{
|
||||
if (selectedItem < 0)
|
||||
return;
|
||||
int itemNum = selectedItem - firstItem;
|
||||
selectedItem = -1;
|
||||
Invalidate(new Rectangle(0, itemNum * ItemHeight, Width, (itemNum + 1) * ItemHeight + 1));
|
||||
Update();
|
||||
OnSelectedItemChanged(EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void PageDown()
|
||||
{
|
||||
SelectIndex(selectedItem + MaxVisibleItem);
|
||||
}
|
||||
|
||||
public void PageUp()
|
||||
{
|
||||
SelectIndex(selectedItem - MaxVisibleItem);
|
||||
}
|
||||
|
||||
public void SelectNextItem()
|
||||
{
|
||||
SelectIndex(selectedItem + 1);
|
||||
}
|
||||
|
||||
public void SelectPrevItem()
|
||||
{
|
||||
SelectIndex(selectedItem - 1);
|
||||
}
|
||||
|
||||
public void SelectItemWithStart(string startText)
|
||||
{
|
||||
if (startText == null || startText.Length == 0) return;
|
||||
string originalStartText = startText;
|
||||
startText = startText.ToLower();
|
||||
int bestIndex = -1;
|
||||
int bestQuality = -1;
|
||||
// Qualities: 0 = match start
|
||||
// 1 = match start case sensitive
|
||||
// 2 = full match
|
||||
// 3 = full match case sensitive
|
||||
double bestPriority = 0;
|
||||
for (int i = 0; i < completionData.Length; ++i) {
|
||||
string itemText = completionData[i].Text;
|
||||
string lowerText = itemText.ToLower();
|
||||
if (lowerText.StartsWith(startText)) {
|
||||
double priority = completionData[i].Priority;
|
||||
int quality;
|
||||
if (lowerText == startText) {
|
||||
if (itemText == originalStartText)
|
||||
quality = 3;
|
||||
else
|
||||
quality = 2;
|
||||
} else if (itemText.StartsWith(originalStartText)) {
|
||||
quality = 1;
|
||||
} else {
|
||||
quality = 0;
|
||||
}
|
||||
bool useThisItem;
|
||||
if (bestQuality < quality) {
|
||||
useThisItem = true;
|
||||
} else {
|
||||
if (bestIndex == selectedItem) {
|
||||
useThisItem = false;
|
||||
} else if (i == selectedItem) {
|
||||
useThisItem = bestQuality == quality;
|
||||
} else {
|
||||
useThisItem = bestQuality == quality && bestPriority < priority;
|
||||
}
|
||||
}
|
||||
if (useThisItem) {
|
||||
bestIndex = i;
|
||||
bestPriority = priority;
|
||||
bestQuality = quality;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestIndex < 0) {
|
||||
ClearSelection();
|
||||
} else {
|
||||
if (bestIndex < firstItem || firstItem + MaxVisibleItem <= bestIndex) {
|
||||
SelectIndex(bestIndex);
|
||||
CenterViewOn(bestIndex);
|
||||
} else {
|
||||
SelectIndex(bestIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs pe)
|
||||
{
|
||||
float yPos = 1;
|
||||
float itemHeight = ItemHeight;
|
||||
// Maintain aspect ratio
|
||||
int imageWidth = (int)(itemHeight * imageList.ImageSize.Width / imageList.ImageSize.Height);
|
||||
|
||||
int curItem = firstItem;
|
||||
Graphics g = pe.Graphics;
|
||||
while (curItem < completionData.Length && yPos < Height) {
|
||||
RectangleF drawingBackground = new RectangleF(1, yPos, Width - 2, itemHeight);
|
||||
if (drawingBackground.IntersectsWith(pe.ClipRectangle)) {
|
||||
// draw Background
|
||||
if (curItem == selectedItem) {
|
||||
g.FillRectangle(SystemBrushes.Highlight, drawingBackground);
|
||||
} else {
|
||||
g.FillRectangle(SystemBrushes.Window, drawingBackground);
|
||||
}
|
||||
|
||||
// draw Icon
|
||||
int xPos = 0;
|
||||
if (imageList != null && completionData[curItem].ImageIndex < imageList.Images.Count) {
|
||||
g.DrawImage(imageList.Images[completionData[curItem].ImageIndex], new RectangleF(1, yPos, imageWidth, itemHeight));
|
||||
xPos = imageWidth;
|
||||
}
|
||||
|
||||
// draw text
|
||||
if (curItem == selectedItem) {
|
||||
g.DrawString(completionData[curItem].Text, Font, SystemBrushes.HighlightText, xPos, yPos);
|
||||
} else {
|
||||
g.DrawString(completionData[curItem].Text, Font, SystemBrushes.WindowText, xPos, yPos);
|
||||
}
|
||||
}
|
||||
|
||||
yPos += itemHeight;
|
||||
++curItem;
|
||||
}
|
||||
g.DrawRectangle(SystemPens.Control, new Rectangle(0, 0, Width - 1, Height - 1));
|
||||
}
|
||||
|
||||
protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
|
||||
{
|
||||
float yPos = 1;
|
||||
int curItem = firstItem;
|
||||
float itemHeight = ItemHeight;
|
||||
|
||||
while (curItem < completionData.Length && yPos < Height) {
|
||||
RectangleF drawingBackground = new RectangleF(1, yPos, Width - 2, itemHeight);
|
||||
if (drawingBackground.Contains(e.X, e.Y)) {
|
||||
SelectIndex(curItem);
|
||||
break;
|
||||
}
|
||||
yPos += itemHeight;
|
||||
++curItem;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPaintBackground(PaintEventArgs pe)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnSelectedItemChanged(EventArgs e)
|
||||
{
|
||||
if (SelectedItemChanged != null) {
|
||||
SelectedItemChanged(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnFirstItemChanged(EventArgs e)
|
||||
{
|
||||
if (FirstItemChanged != null) {
|
||||
FirstItemChanged(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler SelectedItemChanged;
|
||||
public event EventHandler FirstItemChanged;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
// <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.Drawing;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Forms;
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
|
||||
namespace ICSharpCode.TextEditor.Gui.CompletionWindow
|
||||
{
|
||||
public class CodeCompletionWindow : AbstractCompletionWindow
|
||||
{
|
||||
ICompletionData[] completionData;
|
||||
CodeCompletionListView codeCompletionListView;
|
||||
VScrollBar vScrollBar = new VScrollBar();
|
||||
ICompletionDataProvider dataProvider;
|
||||
IDocument document;
|
||||
bool showDeclarationWindow = true;
|
||||
bool fixedListViewWidth = true;
|
||||
const int ScrollbarWidth = 16;
|
||||
const int MaxListLength = 10;
|
||||
|
||||
int startOffset;
|
||||
int endOffset;
|
||||
DeclarationViewWindow declarationViewWindow = null;
|
||||
Rectangle workingScreen;
|
||||
|
||||
public static CodeCompletionWindow ShowCompletionWindow(Form parent, TextEditorControl control, string fileName, ICompletionDataProvider completionDataProvider, char firstChar)
|
||||
{
|
||||
return ShowCompletionWindow(parent, control, fileName, completionDataProvider, firstChar, true, true);
|
||||
}
|
||||
|
||||
public static CodeCompletionWindow ShowCompletionWindow(Form parent, TextEditorControl control, string fileName, ICompletionDataProvider completionDataProvider, char firstChar, bool showDeclarationWindow, bool fixedListViewWidth)
|
||||
{
|
||||
ICompletionData[] completionData = completionDataProvider.GenerateCompletionData(fileName, control.ActiveTextAreaControl.TextArea, firstChar);
|
||||
if (completionData == null || completionData.Length == 0) {
|
||||
return null;
|
||||
}
|
||||
CodeCompletionWindow codeCompletionWindow = new CodeCompletionWindow(completionDataProvider, completionData, parent, control, showDeclarationWindow, fixedListViewWidth);
|
||||
codeCompletionWindow.CloseWhenCaretAtBeginning = firstChar == '\0';
|
||||
codeCompletionWindow.ShowCompletionWindow();
|
||||
return codeCompletionWindow;
|
||||
}
|
||||
|
||||
CodeCompletionWindow(ICompletionDataProvider completionDataProvider, ICompletionData[] completionData, Form parentForm, TextEditorControl control, bool showDeclarationWindow, bool fixedListViewWidth) : base(parentForm, control)
|
||||
{
|
||||
this.dataProvider = completionDataProvider;
|
||||
this.completionData = completionData;
|
||||
this.document = control.Document;
|
||||
this.showDeclarationWindow = showDeclarationWindow;
|
||||
this.fixedListViewWidth = fixedListViewWidth;
|
||||
|
||||
workingScreen = Screen.GetWorkingArea(Location);
|
||||
startOffset = control.ActiveTextAreaControl.Caret.Offset + 1;
|
||||
endOffset = startOffset;
|
||||
if (completionDataProvider.PreSelection != null) {
|
||||
startOffset -= completionDataProvider.PreSelection.Length + 1;
|
||||
endOffset--;
|
||||
}
|
||||
|
||||
codeCompletionListView = new CodeCompletionListView(completionData);
|
||||
codeCompletionListView.ImageList = completionDataProvider.ImageList;
|
||||
codeCompletionListView.Dock = DockStyle.Fill;
|
||||
codeCompletionListView.SelectedItemChanged += new EventHandler(CodeCompletionListViewSelectedItemChanged);
|
||||
codeCompletionListView.DoubleClick += new EventHandler(CodeCompletionListViewDoubleClick);
|
||||
codeCompletionListView.Click += new EventHandler(CodeCompletionListViewClick);
|
||||
Controls.Add(codeCompletionListView);
|
||||
|
||||
if (completionData.Length > MaxListLength) {
|
||||
vScrollBar.Dock = DockStyle.Right;
|
||||
vScrollBar.Minimum = 0;
|
||||
vScrollBar.Maximum = completionData.Length - 1;
|
||||
vScrollBar.SmallChange = 1;
|
||||
vScrollBar.LargeChange = MaxListLength;
|
||||
codeCompletionListView.FirstItemChanged += new EventHandler(CodeCompletionListViewFirstItemChanged);
|
||||
Controls.Add(vScrollBar);
|
||||
}
|
||||
|
||||
this.drawingSize = GetListViewSize();
|
||||
SetLocation();
|
||||
|
||||
if (declarationViewWindow == null) {
|
||||
declarationViewWindow = new DeclarationViewWindow(parentForm);
|
||||
}
|
||||
SetDeclarationViewLocation();
|
||||
declarationViewWindow.ShowDeclarationViewWindow();
|
||||
declarationViewWindow.MouseMove += ControlMouseMove;
|
||||
control.Focus();
|
||||
CodeCompletionListViewSelectedItemChanged(this, EventArgs.Empty);
|
||||
|
||||
if (completionDataProvider.DefaultIndex >= 0) {
|
||||
codeCompletionListView.SelectIndex(completionDataProvider.DefaultIndex);
|
||||
}
|
||||
|
||||
if (completionDataProvider.PreSelection != null) {
|
||||
CaretOffsetChanged(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
vScrollBar.ValueChanged += VScrollBarValueChanged;
|
||||
document.DocumentAboutToBeChanged += DocumentAboutToBeChanged;
|
||||
}
|
||||
|
||||
bool inScrollUpdate;
|
||||
|
||||
void CodeCompletionListViewFirstItemChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (inScrollUpdate) return;
|
||||
inScrollUpdate = true;
|
||||
vScrollBar.Value = Math.Min(vScrollBar.Maximum, codeCompletionListView.FirstItem);
|
||||
inScrollUpdate = false;
|
||||
}
|
||||
|
||||
void VScrollBarValueChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (inScrollUpdate) return;
|
||||
inScrollUpdate = true;
|
||||
codeCompletionListView.FirstItem = vScrollBar.Value;
|
||||
codeCompletionListView.Refresh();
|
||||
control.ActiveTextAreaControl.TextArea.Focus();
|
||||
inScrollUpdate = false;
|
||||
}
|
||||
|
||||
void SetDeclarationViewLocation()
|
||||
{
|
||||
// This method uses the side with more free space
|
||||
int leftSpace = Bounds.Left - workingScreen.Left;
|
||||
int rightSpace = workingScreen.Right - Bounds.Right;
|
||||
Point pos;
|
||||
// The declaration view window has better line break when used on
|
||||
// the right side, so prefer the right side to the left.
|
||||
if (rightSpace * 2 > leftSpace) {
|
||||
declarationViewWindow.FixedWidth = false;
|
||||
pos = new Point(Bounds.Right, Bounds.Top);
|
||||
if (declarationViewWindow.Location != pos) {
|
||||
declarationViewWindow.Location = pos;
|
||||
}
|
||||
} else {
|
||||
declarationViewWindow.Width = declarationViewWindow.GetRequiredLeftHandSideWidth(new Point(Bounds.Left, Bounds.Top));
|
||||
declarationViewWindow.FixedWidth = true;
|
||||
if (Bounds.Left < declarationViewWindow.Width) {
|
||||
pos = new Point(0, Bounds.Top);
|
||||
} else {
|
||||
pos = new Point(Bounds.Left - declarationViewWindow.Width, Bounds.Top);
|
||||
}
|
||||
if (declarationViewWindow.Location != pos) {
|
||||
declarationViewWindow.Location = pos;
|
||||
}
|
||||
declarationViewWindow.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SetLocation()
|
||||
{
|
||||
base.SetLocation();
|
||||
if (declarationViewWindow != null) {
|
||||
SetDeclarationViewLocation();
|
||||
}
|
||||
}
|
||||
|
||||
Util.MouseWheelHandler mouseWheelHandler = new Util.MouseWheelHandler();
|
||||
|
||||
public void HandleMouseWheel(MouseEventArgs e)
|
||||
{
|
||||
int scrollDistance = mouseWheelHandler.GetScrollAmount(e);
|
||||
if (scrollDistance == 0)
|
||||
return;
|
||||
if (control.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));
|
||||
}
|
||||
|
||||
void CodeCompletionListViewSelectedItemChanged(object sender, EventArgs e)
|
||||
{
|
||||
ICompletionData data = codeCompletionListView.SelectedCompletionData;
|
||||
if (showDeclarationWindow && data != null && data.Description != null && data.Description.Length > 0) {
|
||||
declarationViewWindow.Description = data.Description;
|
||||
SetDeclarationViewLocation();
|
||||
} else {
|
||||
declarationViewWindow.Description = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ProcessKeyEvent(char ch)
|
||||
{
|
||||
switch (dataProvider.ProcessKey(ch)) {
|
||||
case CompletionDataProviderKeyResult.BeforeStartKey:
|
||||
// increment start+end, then process as normal char
|
||||
++startOffset;
|
||||
++endOffset;
|
||||
return base.ProcessKeyEvent(ch);
|
||||
case CompletionDataProviderKeyResult.NormalKey:
|
||||
// just process normally
|
||||
return base.ProcessKeyEvent(ch);
|
||||
case CompletionDataProviderKeyResult.InsertionKey:
|
||||
return InsertSelectedItem(ch);
|
||||
default:
|
||||
throw new InvalidOperationException("Invalid return value of dataProvider.ProcessKey");
|
||||
}
|
||||
}
|
||||
|
||||
void DocumentAboutToBeChanged(object sender, DocumentEventArgs e)
|
||||
{
|
||||
// => startOffset test required so that this startOffset/endOffset are not incremented again
|
||||
// for BeforeStartKey characters
|
||||
if (e.Offset >= startOffset && e.Offset <= endOffset) {
|
||||
if (e.Length > 0) { // length of removed region
|
||||
endOffset -= e.Length;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(e.Text)) {
|
||||
endOffset += e.Text.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When this flag is set, code completion closes if the caret moves to the
|
||||
/// beginning of the allowed range. This is useful in Ctrl+Space and "complete when typing",
|
||||
/// but not in dot-completion.
|
||||
/// </summary>
|
||||
public bool CloseWhenCaretAtBeginning { get; set; }
|
||||
|
||||
protected override void CaretOffsetChanged(object sender, EventArgs e)
|
||||
{
|
||||
int offset = control.ActiveTextAreaControl.Caret.Offset;
|
||||
if (offset == startOffset) {
|
||||
if (CloseWhenCaretAtBeginning)
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
if (offset < startOffset || offset > endOffset) {
|
||||
Close();
|
||||
} else {
|
||||
codeCompletionListView.SelectItemWithStart(control.Document.GetText(startOffset, offset - startOffset));
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ProcessTextAreaKey(Keys keyData)
|
||||
{
|
||||
if (!Visible) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (keyData) {
|
||||
case Keys.Home:
|
||||
codeCompletionListView.SelectIndex(0);
|
||||
return true;
|
||||
case Keys.End:
|
||||
codeCompletionListView.SelectIndex(completionData.Length-1);
|
||||
return true;
|
||||
case Keys.PageDown:
|
||||
codeCompletionListView.PageDown();
|
||||
return true;
|
||||
case Keys.PageUp:
|
||||
codeCompletionListView.PageUp();
|
||||
return true;
|
||||
case Keys.Down:
|
||||
codeCompletionListView.SelectNextItem();
|
||||
return true;
|
||||
case Keys.Up:
|
||||
codeCompletionListView.SelectPrevItem();
|
||||
return true;
|
||||
case Keys.Tab:
|
||||
InsertSelectedItem('\t');
|
||||
return true;
|
||||
case Keys.Return:
|
||||
InsertSelectedItem('\n');
|
||||
return true;
|
||||
}
|
||||
return base.ProcessTextAreaKey(keyData);
|
||||
}
|
||||
|
||||
void CodeCompletionListViewDoubleClick(object sender, EventArgs e)
|
||||
{
|
||||
InsertSelectedItem('\0');
|
||||
}
|
||||
|
||||
void CodeCompletionListViewClick(object sender, EventArgs e)
|
||||
{
|
||||
control.ActiveTextAreaControl.TextArea.Focus();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing) {
|
||||
document.DocumentAboutToBeChanged -= DocumentAboutToBeChanged;
|
||||
if (codeCompletionListView != null) {
|
||||
codeCompletionListView.Dispose();
|
||||
codeCompletionListView = null;
|
||||
}
|
||||
if (declarationViewWindow != null) {
|
||||
declarationViewWindow.Dispose();
|
||||
declarationViewWindow = null;
|
||||
}
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
bool InsertSelectedItem(char ch)
|
||||
{
|
||||
document.DocumentAboutToBeChanged -= DocumentAboutToBeChanged;
|
||||
ICompletionData data = codeCompletionListView.SelectedCompletionData;
|
||||
bool result = false;
|
||||
if (data != null) {
|
||||
control.BeginUpdate();
|
||||
|
||||
try {
|
||||
if (endOffset - startOffset > 0) {
|
||||
control.Document.Remove(startOffset, endOffset - startOffset);
|
||||
}
|
||||
Debug.Assert(startOffset <= document.TextLength);
|
||||
result = dataProvider.InsertAction(data, control.ActiveTextAreaControl.TextArea, startOffset, ch);
|
||||
} finally {
|
||||
control.EndUpdate();
|
||||
}
|
||||
}
|
||||
Close();
|
||||
return result;
|
||||
}
|
||||
|
||||
Size GetListViewSize()
|
||||
{
|
||||
int height = codeCompletionListView.ItemHeight * Math.Min(MaxListLength, completionData.Length);
|
||||
int width = codeCompletionListView.ItemHeight * 10;
|
||||
if (!fixedListViewWidth) {
|
||||
width = GetListViewWidth(width, height);
|
||||
}
|
||||
return new Size(width, height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list view width large enough to handle the longest completion data
|
||||
/// text string.
|
||||
/// </summary>
|
||||
/// <param name="defaultWidth">The default width of the list view.</param>
|
||||
/// <param name="height">The height of the list view. This is
|
||||
/// used to determine if the scrollbar is visible.</param>
|
||||
/// <returns>The list view width to accommodate the longest completion
|
||||
/// data text string; otherwise the default width.</returns>
|
||||
int GetListViewWidth(int defaultWidth, int height)
|
||||
{
|
||||
float width = defaultWidth;
|
||||
using (Graphics graphics = codeCompletionListView.CreateGraphics()) {
|
||||
for (int i = 0; i < completionData.Length; ++i) {
|
||||
float itemWidth = graphics.MeasureString(completionData[i].Text.ToString(), codeCompletionListView.Font).Width;
|
||||
if(itemWidth > width) {
|
||||
width = itemWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float totalItemsHeight = codeCompletionListView.ItemHeight * completionData.Length;
|
||||
if (totalItemsHeight > height) {
|
||||
width += ScrollbarWidth; // Compensate for scroll bar.
|
||||
}
|
||||
return (int)width;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
// <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.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using ICSharpCode.TextEditor.Util;
|
||||
|
||||
namespace ICSharpCode.TextEditor.Gui.CompletionWindow
|
||||
{
|
||||
public interface IDeclarationViewWindow
|
||||
{
|
||||
string Description {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
void ShowDeclarationViewWindow();
|
||||
void CloseDeclarationViewWindow();
|
||||
}
|
||||
|
||||
public class DeclarationViewWindow : Form, IDeclarationViewWindow
|
||||
{
|
||||
string description = string.Empty;
|
||||
bool fixedWidth;
|
||||
|
||||
public string Description {
|
||||
get {
|
||||
return description;
|
||||
}
|
||||
set {
|
||||
description = value;
|
||||
if (value == null && Visible) {
|
||||
Visible = false;
|
||||
} else if (value != null) {
|
||||
if (!Visible) ShowDeclarationViewWindow();
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool FixedWidth {
|
||||
get {
|
||||
return fixedWidth;
|
||||
}
|
||||
set {
|
||||
fixedWidth = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetRequiredLeftHandSideWidth(Point p) {
|
||||
if (description != null && description.Length > 0) {
|
||||
using (Graphics g = CreateGraphics()) {
|
||||
Size s = TipPainterTools.GetLeftHandSideDrawingSizeHelpTipFromCombinedDescription(this, g, Font, null, description, p);
|
||||
return s.Width;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public bool HideOnClick;
|
||||
|
||||
public DeclarationViewWindow(Form parent)
|
||||
{
|
||||
SetStyle(ControlStyles.Selectable, false);
|
||||
StartPosition = FormStartPosition.Manual;
|
||||
FormBorderStyle = FormBorderStyle.None;
|
||||
Owner = parent;
|
||||
ShowInTaskbar = false;
|
||||
Size = new Size(0, 0);
|
||||
base.CreateHandle();
|
||||
}
|
||||
|
||||
protected override CreateParams CreateParams {
|
||||
get {
|
||||
CreateParams p = base.CreateParams;
|
||||
AbstractCompletionWindow.AddShadowToWindow(p);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ShowWithoutActivation {
|
||||
get {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnClick(EventArgs e)
|
||||
{
|
||||
base.OnClick(e);
|
||||
if (HideOnClick) Hide();
|
||||
}
|
||||
|
||||
public void ShowDeclarationViewWindow()
|
||||
{
|
||||
Show();
|
||||
}
|
||||
|
||||
public void CloseDeclarationViewWindow()
|
||||
{
|
||||
Close();
|
||||
Dispose();
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs pe)
|
||||
{
|
||||
if (description != null && description.Length > 0) {
|
||||
if (fixedWidth) {
|
||||
TipPainterTools.DrawFixedWidthHelpTipFromCombinedDescription(this, pe.Graphics, Font, null, description);
|
||||
} else {
|
||||
TipPainterTools.DrawHelpTipFromCombinedDescription(this, pe.Graphics, Font, null, description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPaintBackground(PaintEventArgs pe)
|
||||
{
|
||||
pe.Graphics.FillRectangle(SystemBrushes.Info, pe.ClipRectangle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// <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;
|
||||
|
||||
namespace ICSharpCode.TextEditor.Gui.CompletionWindow
|
||||
{
|
||||
public interface ICompletionData
|
||||
{
|
||||
int ImageIndex {
|
||||
get;
|
||||
}
|
||||
|
||||
string Text {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
string Description {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a priority value for the completion data item.
|
||||
/// When selecting items by their start characters, the item with the highest
|
||||
/// priority is selected first.
|
||||
/// </summary>
|
||||
double Priority {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert the element represented by the completion data into the text
|
||||
/// editor.
|
||||
/// </summary>
|
||||
/// <param name="textArea">TextArea to insert the completion data in.</param>
|
||||
/// <param name="ch">Character that should be inserted after the completion data.
|
||||
/// \0 when no character should be inserted.</param>
|
||||
/// <returns>Returns true when the insert action has processed the character
|
||||
/// <paramref name="ch"/>; false when the character was not processed.</returns>
|
||||
bool InsertAction(TextArea textArea, char ch);
|
||||
}
|
||||
|
||||
public class DefaultCompletionData : ICompletionData
|
||||
{
|
||||
string text;
|
||||
string description;
|
||||
int imageIndex;
|
||||
|
||||
public int ImageIndex {
|
||||
get {
|
||||
return imageIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public string Text {
|
||||
get {
|
||||
return text;
|
||||
}
|
||||
set {
|
||||
text = value;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string Description {
|
||||
get {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
double priority;
|
||||
|
||||
public double Priority {
|
||||
get {
|
||||
return priority;
|
||||
}
|
||||
set {
|
||||
priority = value;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool InsertAction(TextArea textArea, char ch)
|
||||
{
|
||||
textArea.InsertString(text);
|
||||
return false;
|
||||
}
|
||||
|
||||
public DefaultCompletionData(string text, int imageIndex)
|
||||
{
|
||||
this.text = text;
|
||||
this.imageIndex = imageIndex;
|
||||
}
|
||||
|
||||
public DefaultCompletionData(string text, string description, int imageIndex)
|
||||
{
|
||||
this.text = text;
|
||||
this.description = description;
|
||||
this.imageIndex = imageIndex;
|
||||
}
|
||||
|
||||
public static int Compare(ICompletionData a, ICompletionData b)
|
||||
{
|
||||
if (a == null)
|
||||
throw new ArgumentNullException("a");
|
||||
if (b == null)
|
||||
throw new ArgumentNullException("b");
|
||||
return string.Compare(a.Text, b.Text, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// <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.Windows.Forms;
|
||||
|
||||
namespace ICSharpCode.TextEditor.Gui.CompletionWindow
|
||||
{
|
||||
public interface ICompletionDataProvider
|
||||
{
|
||||
ImageList ImageList {
|
||||
get;
|
||||
}
|
||||
string PreSelection {
|
||||
get;
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the index of the element in the list that is chosen by default.
|
||||
/// </summary>
|
||||
int DefaultIndex {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a keypress. Returns the action to be run with the key.
|
||||
/// </summary>
|
||||
CompletionDataProviderKeyResult ProcessKey(char key);
|
||||
|
||||
/// <summary>
|
||||
/// Executes the insertion. The provider should set the caret position and then
|
||||
/// call data.InsertAction.
|
||||
/// </summary>
|
||||
bool InsertAction(ICompletionData data, TextArea textArea, int insertionOffset, char key);
|
||||
|
||||
/// <summary>
|
||||
/// Generates the completion data. This method is called by the text editor control.
|
||||
/// </summary>
|
||||
ICompletionData[] GenerateCompletionData(string fileName, TextArea textArea, char charTyped);
|
||||
}
|
||||
|
||||
public enum CompletionDataProviderKeyResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Normal key, used to choose an entry from the completion list
|
||||
/// </summary>
|
||||
NormalKey,
|
||||
/// <summary>
|
||||
/// This key triggers insertion of the completed expression
|
||||
/// </summary>
|
||||
InsertionKey,
|
||||
/// <summary>
|
||||
/// Increment both start and end offset of completion region when inserting this
|
||||
/// key. Can be used to insert whitespace (or other characters) in front of the expression
|
||||
/// while the completion window is open.
|
||||
/// </summary>
|
||||
BeforeStartKey
|
||||
}
|
||||
}
|
||||
190
ICSharpCode.TextEditor/Project/Src/Gui/DrawableLine.cs
Normal file
190
ICSharpCode.TextEditor/Project/Src/Gui/DrawableLine.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// A class that is able to draw a line on any control (outside the text editor)
|
||||
/// </summary>
|
||||
public class DrawableLine
|
||||
{
|
||||
static StringFormat sf = (StringFormat)System.Drawing.StringFormat.GenericTypographic.Clone();
|
||||
|
||||
List<SimpleTextWord> words = new List<SimpleTextWord>();
|
||||
SizeF spaceSize;
|
||||
Font monospacedFont;
|
||||
Font boldMonospacedFont;
|
||||
|
||||
private class SimpleTextWord {
|
||||
internal TextWordType Type;
|
||||
internal string Word;
|
||||
internal bool Bold;
|
||||
internal Color Color;
|
||||
|
||||
public SimpleTextWord(TextWordType Type, string Word, bool Bold, Color Color)
|
||||
{
|
||||
this.Type = Type;
|
||||
this.Word = Word;
|
||||
this.Bold = Bold;
|
||||
this.Color = Color;
|
||||
}
|
||||
|
||||
internal readonly static SimpleTextWord Space = new SimpleTextWord(TextWordType.Space, " ", false, Color.Black);
|
||||
internal readonly static SimpleTextWord Tab = new SimpleTextWord(TextWordType.Tab, "\t", false, Color.Black);
|
||||
}
|
||||
|
||||
public DrawableLine(IDocument document, LineSegment line, Font monospacedFont, Font boldMonospacedFont)
|
||||
{
|
||||
this.monospacedFont = monospacedFont;
|
||||
this.boldMonospacedFont = boldMonospacedFont;
|
||||
if (line.Words != null) {
|
||||
foreach (TextWord word in line.Words) {
|
||||
if (word.Type == TextWordType.Space) {
|
||||
words.Add(SimpleTextWord.Space);
|
||||
} else if (word.Type == TextWordType.Tab) {
|
||||
words.Add(SimpleTextWord.Tab);
|
||||
} else {
|
||||
words.Add(new SimpleTextWord(TextWordType.Word, word.Word, word.Bold, word.Color));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
words.Add(new SimpleTextWord(TextWordType.Word, document.GetText(line), false, Color.Black));
|
||||
}
|
||||
}
|
||||
|
||||
public int LineLength {
|
||||
get {
|
||||
int length = 0;
|
||||
foreach (SimpleTextWord word in words) {
|
||||
length += word.Word.Length;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetBold(int startIndex, int endIndex, bool bold)
|
||||
{
|
||||
if (startIndex < 0)
|
||||
throw new ArgumentException("startIndex must be >= 0");
|
||||
if (startIndex > endIndex)
|
||||
throw new ArgumentException("startIndex must be <= endIndex");
|
||||
if (startIndex == endIndex) return;
|
||||
int pos = 0;
|
||||
for (int i = 0; i < words.Count; i++) {
|
||||
SimpleTextWord word = words[i];
|
||||
if (pos >= endIndex)
|
||||
break;
|
||||
int wordEnd = pos + word.Word.Length;
|
||||
// 3 possibilities:
|
||||
if (startIndex <= pos && endIndex >= wordEnd) {
|
||||
// word is fully in region:
|
||||
word.Bold = bold;
|
||||
} else if (startIndex <= pos) {
|
||||
// beginning of word is in region
|
||||
int inRegionLength = endIndex - pos;
|
||||
SimpleTextWord newWord = new SimpleTextWord(word.Type, word.Word.Substring(inRegionLength), word.Bold, word.Color);
|
||||
words.Insert(i + 1, newWord);
|
||||
|
||||
word.Bold = bold;
|
||||
word.Word = word.Word.Substring(0, inRegionLength);
|
||||
} else if (startIndex < wordEnd) {
|
||||
// end of word is in region (or middle of word is in region)
|
||||
int notInRegionLength = startIndex - pos;
|
||||
|
||||
SimpleTextWord newWord = new SimpleTextWord(word.Type, word.Word.Substring(notInRegionLength), word.Bold, word.Color);
|
||||
// newWord.Bold will be set in the next iteration
|
||||
words.Insert(i + 1, newWord);
|
||||
|
||||
word.Word = word.Word.Substring(0, notInRegionLength);
|
||||
}
|
||||
pos = wordEnd;
|
||||
}
|
||||
}
|
||||
|
||||
public static float DrawDocumentWord(Graphics g, string word, PointF position, Font font, Color foreColor)
|
||||
{
|
||||
if (word == null || word.Length == 0) {
|
||||
return 0f;
|
||||
}
|
||||
SizeF wordSize = g.MeasureString(word, font, 32768, sf);
|
||||
|
||||
g.DrawString(word,
|
||||
font,
|
||||
BrushRegistry.GetBrush(foreColor),
|
||||
position,
|
||||
sf);
|
||||
return wordSize.Width;
|
||||
}
|
||||
|
||||
public SizeF GetSpaceSize(Graphics g)
|
||||
{
|
||||
if (spaceSize.IsEmpty) {
|
||||
spaceSize = g.MeasureString("-", boldMonospacedFont, new PointF(0, 0), sf);
|
||||
}
|
||||
return spaceSize;
|
||||
}
|
||||
|
||||
public void DrawLine(Graphics g, ref float xPos, float xOffset, float yPos, Color c)
|
||||
{
|
||||
SizeF spaceSize = GetSpaceSize(g);
|
||||
foreach (SimpleTextWord word in words) {
|
||||
switch (word.Type) {
|
||||
case TextWordType.Space:
|
||||
xPos += spaceSize.Width;
|
||||
break;
|
||||
case TextWordType.Tab:
|
||||
float tabWidth = spaceSize.Width * 4;
|
||||
xPos += tabWidth;
|
||||
xPos = (int)((xPos + 2) / tabWidth) * tabWidth;
|
||||
break;
|
||||
case TextWordType.Word:
|
||||
xPos += DrawDocumentWord(g,
|
||||
word.Word,
|
||||
new PointF(xPos + xOffset, yPos),
|
||||
word.Bold ? boldMonospacedFont : monospacedFont,
|
||||
c == Color.Empty ? word.Color : c
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawLine(Graphics g, ref float xPos, float xOffset, float yPos)
|
||||
{
|
||||
DrawLine(g, ref xPos, xOffset, yPos, Color.Empty);
|
||||
}
|
||||
|
||||
public float MeasureWidth(Graphics g, float xPos)
|
||||
{
|
||||
SizeF spaceSize = GetSpaceSize(g);
|
||||
foreach (SimpleTextWord word in words) {
|
||||
switch (word.Type) {
|
||||
case TextWordType.Space:
|
||||
xPos += spaceSize.Width;
|
||||
break;
|
||||
case TextWordType.Tab:
|
||||
float tabWidth = spaceSize.Width * 4;
|
||||
xPos += tabWidth;
|
||||
xPos = (int)((xPos + 2) / tabWidth) * tabWidth;
|
||||
break;
|
||||
case TextWordType.Word:
|
||||
if (word.Word != null && word.Word.Length > 0) {
|
||||
xPos += g.MeasureString(word.Word, word.Bold ? boldMonospacedFont : monospacedFont, 32768, sf).Width;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return xPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
276
ICSharpCode.TextEditor/Project/Src/Gui/FoldMargin.cs
Normal file
276
ICSharpCode.TextEditor/Project/Src/Gui/FoldMargin.cs
Normal file
@@ -0,0 +1,276 @@
|
||||
// <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.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// This class views the line numbers and folding markers.
|
||||
/// </summary>
|
||||
public class FoldMargin : AbstractMargin
|
||||
{
|
||||
int selectedFoldLine = -1;
|
||||
|
||||
public override Size Size {
|
||||
get {
|
||||
return new Size((int)(textArea.TextView.FontHeight),
|
||||
-1);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsVisible {
|
||||
get {
|
||||
return textArea.TextEditorProperties.EnableFolding;
|
||||
}
|
||||
}
|
||||
|
||||
public FoldMargin(TextArea textArea) : base(textArea)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Paint(Graphics g, Rectangle rect)
|
||||
{
|
||||
if (rect.Width <= 0 || rect.Height <= 0) {
|
||||
return;
|
||||
}
|
||||
HighlightColor lineNumberPainterColor = textArea.Document.HighlightingStrategy.GetColorFor("LineNumbers");
|
||||
|
||||
|
||||
for (int y = 0; y < (DrawingPosition.Height + textArea.TextView.VisibleLineDrawingRemainder) / textArea.TextView.FontHeight + 1; ++y) {
|
||||
Rectangle markerRectangle = new Rectangle(DrawingPosition.X,
|
||||
DrawingPosition.Top + y * textArea.TextView.FontHeight - textArea.TextView.VisibleLineDrawingRemainder,
|
||||
DrawingPosition.Width,
|
||||
textArea.TextView.FontHeight);
|
||||
|
||||
if (rect.IntersectsWith(markerRectangle)) {
|
||||
// draw dotted separator line
|
||||
if (textArea.Document.TextEditorProperties.ShowLineNumbers) {
|
||||
g.FillRectangle(BrushRegistry.GetBrush(textArea.Enabled ? lineNumberPainterColor.BackgroundColor : SystemColors.InactiveBorder),
|
||||
markerRectangle);
|
||||
|
||||
g.DrawLine(BrushRegistry.GetDotPen(lineNumberPainterColor.Color),
|
||||
base.drawingPosition.X,
|
||||
markerRectangle.Y,
|
||||
base.drawingPosition.X,
|
||||
markerRectangle.Bottom);
|
||||
} else {
|
||||
g.FillRectangle(BrushRegistry.GetBrush(textArea.Enabled ? lineNumberPainterColor.BackgroundColor : SystemColors.InactiveBorder), markerRectangle);
|
||||
}
|
||||
|
||||
int currentLine = textArea.Document.GetFirstLogicalLine(textArea.TextView.FirstPhysicalLine + y);
|
||||
if (currentLine < textArea.Document.TotalNumberOfLines) {
|
||||
PaintFoldMarker(g, currentLine, markerRectangle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SelectedFoldingFrom(List<FoldMarker> list)
|
||||
{
|
||||
if (list != null) {
|
||||
for (int i = 0; i < list.Count; ++i) {
|
||||
if (this.selectedFoldLine == list[i].StartLine) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PaintFoldMarker(Graphics g, int lineNumber, Rectangle drawingRectangle)
|
||||
{
|
||||
HighlightColor foldLineColor = textArea.Document.HighlightingStrategy.GetColorFor("FoldLine");
|
||||
HighlightColor selectedFoldLine = textArea.Document.HighlightingStrategy.GetColorFor("SelectedFoldLine");
|
||||
|
||||
List<FoldMarker> foldingsWithStart = textArea.Document.FoldingManager.GetFoldingsWithStart(lineNumber);
|
||||
List<FoldMarker> foldingsBetween = textArea.Document.FoldingManager.GetFoldingsContainsLineNumber(lineNumber);
|
||||
List<FoldMarker> foldingsWithEnd = textArea.Document.FoldingManager.GetFoldingsWithEnd(lineNumber);
|
||||
|
||||
bool isFoldStart = foldingsWithStart.Count > 0;
|
||||
bool isBetween = foldingsBetween.Count > 0;
|
||||
bool isFoldEnd = foldingsWithEnd.Count > 0;
|
||||
|
||||
bool isStartSelected = SelectedFoldingFrom(foldingsWithStart);
|
||||
bool isBetweenSelected = SelectedFoldingFrom(foldingsBetween);
|
||||
bool isEndSelected = SelectedFoldingFrom(foldingsWithEnd);
|
||||
|
||||
int foldMarkerSize = (int)Math.Round(textArea.TextView.FontHeight * 0.57f);
|
||||
foldMarkerSize -= (foldMarkerSize) % 2;
|
||||
int foldMarkerYPos = drawingRectangle.Y + (int)((drawingRectangle.Height - foldMarkerSize) / 2);
|
||||
int xPos = drawingRectangle.X + (drawingRectangle.Width - foldMarkerSize) / 2 + foldMarkerSize / 2;
|
||||
|
||||
|
||||
if (isFoldStart) {
|
||||
bool isVisible = true;
|
||||
bool moreLinedOpenFold = false;
|
||||
foreach (FoldMarker foldMarker in foldingsWithStart) {
|
||||
if (foldMarker.IsFolded) {
|
||||
isVisible = false;
|
||||
} else {
|
||||
moreLinedOpenFold = foldMarker.EndLine > foldMarker.StartLine;
|
||||
}
|
||||
}
|
||||
|
||||
bool isFoldEndFromUpperFold = false;
|
||||
foreach (FoldMarker foldMarker in foldingsWithEnd) {
|
||||
if (foldMarker.EndLine > foldMarker.StartLine && !foldMarker.IsFolded) {
|
||||
isFoldEndFromUpperFold = true;
|
||||
}
|
||||
}
|
||||
|
||||
DrawFoldMarker(g, new RectangleF(drawingRectangle.X + (drawingRectangle.Width - foldMarkerSize) / 2,
|
||||
foldMarkerYPos,
|
||||
foldMarkerSize,
|
||||
foldMarkerSize),
|
||||
isVisible,
|
||||
isStartSelected
|
||||
);
|
||||
|
||||
// draw line above fold marker
|
||||
if (isBetween || isFoldEndFromUpperFold) {
|
||||
g.DrawLine(BrushRegistry.GetPen(isBetweenSelected ? selectedFoldLine.Color : foldLineColor.Color),
|
||||
xPos,
|
||||
drawingRectangle.Top,
|
||||
xPos,
|
||||
foldMarkerYPos - 1);
|
||||
}
|
||||
|
||||
// draw line below fold marker
|
||||
if (isBetween || moreLinedOpenFold) {
|
||||
g.DrawLine(BrushRegistry.GetPen(isEndSelected || (isStartSelected && isVisible) || isBetweenSelected ? selectedFoldLine.Color : foldLineColor.Color),
|
||||
xPos,
|
||||
foldMarkerYPos + foldMarkerSize + 1,
|
||||
xPos,
|
||||
drawingRectangle.Bottom);
|
||||
}
|
||||
} else {
|
||||
if (isFoldEnd) {
|
||||
int midy = drawingRectangle.Top + drawingRectangle.Height / 2;
|
||||
|
||||
// draw fold end marker
|
||||
g.DrawLine(BrushRegistry.GetPen(isEndSelected ? selectedFoldLine.Color : foldLineColor.Color),
|
||||
xPos,
|
||||
midy,
|
||||
xPos + foldMarkerSize / 2,
|
||||
midy);
|
||||
|
||||
// draw line above fold end marker
|
||||
// must be drawn after fold marker because it might have a different color than the fold marker
|
||||
g.DrawLine(BrushRegistry.GetPen(isBetweenSelected || isEndSelected ? selectedFoldLine.Color : foldLineColor.Color),
|
||||
xPos,
|
||||
drawingRectangle.Top,
|
||||
xPos,
|
||||
midy);
|
||||
|
||||
// draw line below fold end marker
|
||||
if (isBetween) {
|
||||
g.DrawLine(BrushRegistry.GetPen(isBetweenSelected ? selectedFoldLine.Color : foldLineColor.Color),
|
||||
xPos,
|
||||
midy + 1,
|
||||
xPos,
|
||||
drawingRectangle.Bottom);
|
||||
}
|
||||
} else if (isBetween) {
|
||||
// just draw the line :)
|
||||
g.DrawLine(BrushRegistry.GetPen(isBetweenSelected ? selectedFoldLine.Color : foldLineColor.Color),
|
||||
xPos,
|
||||
drawingRectangle.Top,
|
||||
xPos,
|
||||
drawingRectangle.Bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void HandleMouseMove(Point mousepos, MouseButtons mouseButtons)
|
||||
{
|
||||
bool showFolding = textArea.Document.TextEditorProperties.EnableFolding;
|
||||
int physicalLine = + (int)((mousepos.Y + textArea.VirtualTop.Y) / textArea.TextView.FontHeight);
|
||||
int realline = textArea.Document.GetFirstLogicalLine(physicalLine);
|
||||
|
||||
if (!showFolding || realline < 0 || realline + 1 >= textArea.Document.TotalNumberOfLines) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<FoldMarker> foldMarkers = textArea.Document.FoldingManager.GetFoldingsWithStart(realline);
|
||||
int oldSelection = selectedFoldLine;
|
||||
if (foldMarkers.Count > 0) {
|
||||
selectedFoldLine = realline;
|
||||
} else {
|
||||
selectedFoldLine = -1;
|
||||
}
|
||||
if (oldSelection != selectedFoldLine) {
|
||||
textArea.Refresh(this);
|
||||
}
|
||||
}
|
||||
|
||||
public override void HandleMouseDown(Point mousepos, MouseButtons mouseButtons)
|
||||
{
|
||||
bool showFolding = textArea.Document.TextEditorProperties.EnableFolding;
|
||||
int physicalLine = + (int)((mousepos.Y + textArea.VirtualTop.Y) / textArea.TextView.FontHeight);
|
||||
int realline = textArea.Document.GetFirstLogicalLine(physicalLine);
|
||||
|
||||
// focus the textarea if the user clicks on the line number view
|
||||
textArea.Focus();
|
||||
|
||||
if (!showFolding || realline < 0 || realline + 1 >= textArea.Document.TotalNumberOfLines) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<FoldMarker> foldMarkers = textArea.Document.FoldingManager.GetFoldingsWithStart(realline);
|
||||
foreach (FoldMarker fm in foldMarkers) {
|
||||
fm.IsFolded = !fm.IsFolded;
|
||||
}
|
||||
textArea.Document.FoldingManager.NotifyFoldingsChanged(EventArgs.Empty);
|
||||
}
|
||||
|
||||
public override void HandleMouseLeave(EventArgs e)
|
||||
{
|
||||
if (selectedFoldLine != -1) {
|
||||
selectedFoldLine = -1;
|
||||
textArea.Refresh(this);
|
||||
}
|
||||
}
|
||||
|
||||
#region Drawing functions
|
||||
void DrawFoldMarker(Graphics g, RectangleF rectangle, bool isOpened, bool isSelected)
|
||||
{
|
||||
HighlightColor foldMarkerColor = textArea.Document.HighlightingStrategy.GetColorFor("FoldMarker");
|
||||
HighlightColor foldLineColor = textArea.Document.HighlightingStrategy.GetColorFor("FoldLine");
|
||||
HighlightColor selectedFoldLine = textArea.Document.HighlightingStrategy.GetColorFor("SelectedFoldLine");
|
||||
|
||||
Rectangle intRect = new Rectangle((int)rectangle.X, (int)rectangle.Y, (int)rectangle.Width, (int)rectangle.Height);
|
||||
g.FillRectangle(BrushRegistry.GetBrush(foldMarkerColor.BackgroundColor), intRect);
|
||||
g.DrawRectangle(BrushRegistry.GetPen(isSelected ? selectedFoldLine.Color : foldLineColor.Color), intRect);
|
||||
|
||||
int space = (int)Math.Round(((double)rectangle.Height) / 8d) + 1;
|
||||
int mid = intRect.Height / 2 + intRect.Height % 2;
|
||||
|
||||
// draw minus
|
||||
g.DrawLine(BrushRegistry.GetPen(foldMarkerColor.Color),
|
||||
rectangle.X + space,
|
||||
rectangle.Y + mid,
|
||||
rectangle.X + rectangle.Width - space,
|
||||
rectangle.Y + mid);
|
||||
|
||||
// draw plus
|
||||
if (!isOpened) {
|
||||
g.DrawLine(BrushRegistry.GetPen(foldMarkerColor.Color),
|
||||
rectangle.X + mid,
|
||||
rectangle.Y + space,
|
||||
rectangle.X + mid,
|
||||
rectangle.Y + rectangle.Height - space);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
159
ICSharpCode.TextEditor/Project/Src/Gui/GutterMargin.cs
Normal file
159
ICSharpCode.TextEditor/Project/Src/Gui/GutterMargin.cs
Normal file
@@ -0,0 +1,159 @@
|
||||
// <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.Drawing;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// This class views the line numbers and folding markers.
|
||||
/// </summary>
|
||||
public class GutterMargin : AbstractMargin, IDisposable
|
||||
{
|
||||
StringFormat numberStringFormat = (StringFormat)StringFormat.GenericTypographic.Clone();
|
||||
|
||||
public static Cursor RightLeftCursor;
|
||||
|
||||
static GutterMargin()
|
||||
{
|
||||
using (var ms = new MemoryStream(Properties.Resources.RightArrow))
|
||||
{
|
||||
RightLeftCursor = new Cursor(ms);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
numberStringFormat.Dispose();
|
||||
}
|
||||
|
||||
public override Cursor Cursor {
|
||||
get {
|
||||
return RightLeftCursor;
|
||||
}
|
||||
}
|
||||
|
||||
public override Size Size {
|
||||
get {
|
||||
return new Size((int)(textArea.TextView.WideSpaceWidth
|
||||
* Math.Max(3, (int)Math.Log10(textArea.Document.TotalNumberOfLines) + 1)),
|
||||
-1);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsVisible {
|
||||
get {
|
||||
return textArea.TextEditorProperties.ShowLineNumbers;
|
||||
}
|
||||
}
|
||||
|
||||
public GutterMargin(TextArea textArea) : base(textArea)
|
||||
{
|
||||
numberStringFormat.LineAlignment = StringAlignment.Far;
|
||||
numberStringFormat.FormatFlags = StringFormatFlags.MeasureTrailingSpaces | StringFormatFlags.FitBlackBox |
|
||||
StringFormatFlags.NoWrap | StringFormatFlags.NoClip;
|
||||
}
|
||||
|
||||
public override void Paint(Graphics g, Rectangle rect)
|
||||
{
|
||||
if (rect.Width <= 0 || rect.Height <= 0) {
|
||||
return;
|
||||
}
|
||||
HighlightColor lineNumberPainterColor = textArea.Document.HighlightingStrategy.GetColorFor("LineNumbers");
|
||||
int fontHeight = textArea.TextView.FontHeight;
|
||||
Brush fillBrush = textArea.Enabled ? BrushRegistry.GetBrush(lineNumberPainterColor.BackgroundColor) : SystemBrushes.InactiveBorder;
|
||||
Brush drawBrush = BrushRegistry.GetBrush(lineNumberPainterColor.Color);
|
||||
for (int y = 0; y < (DrawingPosition.Height + textArea.TextView.VisibleLineDrawingRemainder) / fontHeight + 1; ++y) {
|
||||
int ypos = drawingPosition.Y + fontHeight * y - textArea.TextView.VisibleLineDrawingRemainder;
|
||||
Rectangle backgroundRectangle = new Rectangle(drawingPosition.X, ypos, drawingPosition.Width, fontHeight);
|
||||
if (rect.IntersectsWith(backgroundRectangle)) {
|
||||
g.FillRectangle(fillBrush, backgroundRectangle);
|
||||
int curLine = textArea.Document.GetFirstLogicalLine(textArea.Document.GetVisibleLine(textArea.TextView.FirstVisibleLine) + y);
|
||||
|
||||
if (curLine < textArea.Document.TotalNumberOfLines) {
|
||||
g.DrawString((curLine + 1).ToString(),
|
||||
lineNumberPainterColor.GetFont(TextEditorProperties.FontContainer),
|
||||
drawBrush,
|
||||
backgroundRectangle,
|
||||
numberStringFormat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void HandleMouseDown(Point mousepos, MouseButtons mouseButtons)
|
||||
{
|
||||
TextLocation selectionStartPos;
|
||||
|
||||
textArea.SelectionManager.selectFrom.where = WhereFrom.Gutter;
|
||||
int realline = textArea.TextView.GetLogicalLine(mousepos.Y);
|
||||
if (realline >= 0 && realline < textArea.Document.TotalNumberOfLines) {
|
||||
// shift-select
|
||||
if((Control.ModifierKeys & Keys.Shift) != 0) {
|
||||
if(!textArea.SelectionManager.HasSomethingSelected && realline != textArea.Caret.Position.Y) {
|
||||
if (realline >= textArea.Caret.Position.Y)
|
||||
{ // at or below starting selection, place the cursor on the next line
|
||||
// nothing is selected so make a new selection from cursor
|
||||
selectionStartPos = textArea.Caret.Position;
|
||||
// whole line selection - start of line to start of next line
|
||||
if (realline < textArea.Document.TotalNumberOfLines - 1)
|
||||
{
|
||||
textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.Document, selectionStartPos, new TextLocation(0, realline + 1)));
|
||||
textArea.Caret.Position = new TextLocation(0, realline + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.Document, selectionStartPos, new TextLocation(textArea.Document.GetLineSegment(realline).Length + 1, realline)));
|
||||
textArea.Caret.Position = new TextLocation(textArea.Document.GetLineSegment(realline).Length + 1, realline);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // prior lines to starting selection, place the cursor on the same line as the new selection
|
||||
// nothing is selected so make a new selection from cursor
|
||||
selectionStartPos = textArea.Caret.Position;
|
||||
// whole line selection - start of line to start of next line
|
||||
textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.Document, selectionStartPos, new TextLocation(selectionStartPos.X, selectionStartPos.Y)));
|
||||
textArea.SelectionManager.ExtendSelection(new TextLocation(selectionStartPos.X, selectionStartPos.Y), new TextLocation(0, realline));
|
||||
textArea.Caret.Position = new TextLocation(0, realline);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// let MouseMove handle a shift-click in a gutter
|
||||
MouseEventArgs e = new MouseEventArgs(mouseButtons, 1, mousepos.X, mousepos.Y, 0);
|
||||
textArea.RaiseMouseMove(e);
|
||||
}
|
||||
} else { // this is a new selection with no shift-key
|
||||
// sync the textareamousehandler mouse location
|
||||
// (fixes problem with clicking out into a menu then back to the gutter whilst
|
||||
// there is a selection)
|
||||
textArea.mousepos = mousepos;
|
||||
|
||||
selectionStartPos = new TextLocation(0, realline);
|
||||
textArea.SelectionManager.ClearSelection();
|
||||
// whole line selection - start of line to start of next line
|
||||
if (realline < textArea.Document.TotalNumberOfLines - 1)
|
||||
{
|
||||
textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.Document, selectionStartPos, new TextLocation(selectionStartPos.X, selectionStartPos.Y + 1)));
|
||||
textArea.Caret.Position = new TextLocation(selectionStartPos.X, selectionStartPos.Y + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.Document, new TextLocation(0, realline), new TextLocation(textArea.Document.GetLineSegment(realline).Length + 1, selectionStartPos.Y)));
|
||||
textArea.Caret.Position = new TextLocation(textArea.Document.GetLineSegment(realline).Length + 1, selectionStartPos.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
54
ICSharpCode.TextEditor/Project/Src/Gui/HRuler.cs
Normal file
54
ICSharpCode.TextEditor/Project/Src/Gui/HRuler.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
// <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.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Horizontal ruler - text column measuring ruler at the top of the text area.
|
||||
/// </summary>
|
||||
public class HRuler : Control
|
||||
{
|
||||
TextArea textArea;
|
||||
|
||||
public HRuler(TextArea textArea)
|
||||
{
|
||||
this.textArea = textArea;
|
||||
}
|
||||
|
||||
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
|
||||
{
|
||||
Graphics g = e.Graphics;
|
||||
int num = 0;
|
||||
for (float x = textArea.TextView.DrawingPosition.Left; x < textArea.TextView.DrawingPosition.Right; x += textArea.TextView.WideSpaceWidth) {
|
||||
int offset = (Height * 2) / 3;
|
||||
if (num % 5 == 0) {
|
||||
offset = (Height * 4) / 5;
|
||||
}
|
||||
|
||||
if (num % 10 == 0) {
|
||||
offset = 1;
|
||||
}
|
||||
++num;
|
||||
g.DrawLine(Pens.Black,
|
||||
(int)x, offset, (int)x, Height - offset);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e)
|
||||
{
|
||||
e.Graphics.FillRectangle(Brushes.White,
|
||||
new Rectangle(0,
|
||||
0,
|
||||
Width,
|
||||
Height));
|
||||
}
|
||||
}
|
||||
}
|
||||
254
ICSharpCode.TextEditor/Project/Src/Gui/IconBarMargin.cs
Normal file
254
ICSharpCode.TextEditor/Project/Src/Gui/IconBarMargin.cs
Normal file
@@ -0,0 +1,254 @@
|
||||
// <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.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// This class views the line numbers and folding markers.
|
||||
/// </summary>
|
||||
public class IconBarMargin : AbstractMargin
|
||||
{
|
||||
const int iconBarWidth = 18;
|
||||
|
||||
static readonly Size iconBarSize = new Size(iconBarWidth, -1);
|
||||
|
||||
public override Size Size {
|
||||
get {
|
||||
return iconBarSize;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsVisible {
|
||||
get {
|
||||
return textArea.TextEditorProperties.IsIconBarVisible;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public IconBarMargin(TextArea textArea) : base(textArea)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Paint(Graphics g, Rectangle rect)
|
||||
{
|
||||
if (rect.Width <= 0 || rect.Height <= 0) {
|
||||
return;
|
||||
}
|
||||
// paint background
|
||||
g.FillRectangle(SystemBrushes.Control, new Rectangle(drawingPosition.X, rect.Top, drawingPosition.Width - 1, rect.Height));
|
||||
g.DrawLine(SystemPens.ControlDark, base.drawingPosition.Right - 1, rect.Top, base.drawingPosition.Right - 1, rect.Bottom);
|
||||
|
||||
// paint icons
|
||||
foreach (Bookmark mark in textArea.Document.BookmarkManager.Marks) {
|
||||
int lineNumber = textArea.Document.GetVisibleLine(mark.LineNumber);
|
||||
int lineHeight = textArea.TextView.FontHeight;
|
||||
int yPos = (int)(lineNumber * lineHeight) - textArea.VirtualTop.Y;
|
||||
if (IsLineInsideRegion(yPos, yPos + lineHeight, rect.Y, rect.Bottom)) {
|
||||
if (lineNumber == textArea.Document.GetVisibleLine(mark.LineNumber - 1)) {
|
||||
// marker is inside folded region, do not draw it
|
||||
continue;
|
||||
}
|
||||
mark.Draw(this, g, new Point(0, yPos));
|
||||
}
|
||||
}
|
||||
base.Paint(g, rect);
|
||||
}
|
||||
|
||||
public override void HandleMouseDown(Point mousePos, MouseButtons mouseButtons)
|
||||
{
|
||||
int clickedVisibleLine = (mousePos.Y + textArea.VirtualTop.Y) / textArea.TextView.FontHeight;
|
||||
int lineNumber = textArea.Document.GetFirstLogicalLine(clickedVisibleLine);
|
||||
|
||||
if ((mouseButtons & MouseButtons.Right) == MouseButtons.Right) {
|
||||
if (textArea.Caret.Line != lineNumber) {
|
||||
textArea.Caret.Line = lineNumber;
|
||||
}
|
||||
}
|
||||
|
||||
IList<Bookmark> marks = textArea.Document.BookmarkManager.Marks;
|
||||
List<Bookmark> marksInLine = new List<Bookmark>();
|
||||
int oldCount = marks.Count;
|
||||
foreach (Bookmark mark in marks) {
|
||||
if (mark.LineNumber == lineNumber) {
|
||||
marksInLine.Add(mark);
|
||||
}
|
||||
}
|
||||
for (int i = marksInLine.Count - 1; i >= 0; i--) {
|
||||
Bookmark mark = marksInLine[i];
|
||||
if (mark.Click(textArea, new MouseEventArgs(mouseButtons, 1, mousePos.X, mousePos.Y, 0))) {
|
||||
if (oldCount != marks.Count) {
|
||||
textArea.UpdateLine(lineNumber);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
base.HandleMouseDown(mousePos, mouseButtons);
|
||||
}
|
||||
|
||||
#region Drawing functions
|
||||
public void DrawBreakpoint(Graphics g, int y, bool isEnabled, bool isHealthy)
|
||||
{
|
||||
int diameter = Math.Min(iconBarWidth - 2, textArea.TextView.FontHeight);
|
||||
Rectangle rect = new Rectangle(1,
|
||||
y + (textArea.TextView.FontHeight - diameter) / 2,
|
||||
diameter,
|
||||
diameter);
|
||||
|
||||
|
||||
using (GraphicsPath path = new GraphicsPath()) {
|
||||
path.AddEllipse(rect);
|
||||
using (PathGradientBrush pthGrBrush = new PathGradientBrush(path)) {
|
||||
pthGrBrush.CenterPoint = new PointF(rect.Left + rect.Width / 3 , rect.Top + rect.Height / 3);
|
||||
pthGrBrush.CenterColor = Color.MistyRose;
|
||||
Color[] colors = {isHealthy ? Color.Firebrick : Color.Olive};
|
||||
pthGrBrush.SurroundColors = colors;
|
||||
|
||||
if (isEnabled) {
|
||||
g.FillEllipse(pthGrBrush, rect);
|
||||
} else {
|
||||
g.FillEllipse(SystemBrushes.Control, rect);
|
||||
using (Pen pen = new Pen(pthGrBrush)) {
|
||||
g.DrawEllipse(pen, new Rectangle(rect.X + 1, rect.Y + 1, rect.Width - 2, rect.Height - 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawBookmark(Graphics g, int y, bool isEnabled)
|
||||
{
|
||||
int delta = textArea.TextView.FontHeight / 8;
|
||||
Rectangle rect = new Rectangle(1, y + delta, base.drawingPosition.Width - 4, textArea.TextView.FontHeight - delta * 2);
|
||||
|
||||
if (isEnabled) {
|
||||
using (Brush brush = new LinearGradientBrush(new Point(rect.Left, rect.Top),
|
||||
new Point(rect.Right, rect.Bottom),
|
||||
Color.SkyBlue,
|
||||
Color.White)) {
|
||||
FillRoundRect(g, brush, rect);
|
||||
}
|
||||
} else {
|
||||
FillRoundRect(g, Brushes.White, rect);
|
||||
}
|
||||
using (Brush brush = new LinearGradientBrush(new Point(rect.Left, rect.Top),
|
||||
new Point(rect.Right, rect.Bottom),
|
||||
Color.SkyBlue,
|
||||
Color.Blue)) {
|
||||
using (Pen pen = new Pen(brush)) {
|
||||
DrawRoundRect(g, pen, rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawArrow(Graphics g, int y)
|
||||
{
|
||||
int delta = textArea.TextView.FontHeight / 8;
|
||||
Rectangle rect = new Rectangle(1, y + delta, base.drawingPosition.Width - 4, textArea.TextView.FontHeight - delta * 2);
|
||||
using (Brush brush = new LinearGradientBrush(new Point(rect.Left, rect.Top),
|
||||
new Point(rect.Right, rect.Bottom),
|
||||
Color.LightYellow,
|
||||
Color.Yellow)) {
|
||||
FillArrow(g, brush, rect);
|
||||
}
|
||||
|
||||
using (Brush brush = new LinearGradientBrush(new Point(rect.Left, rect.Top),
|
||||
new Point(rect.Right, rect.Bottom),
|
||||
Color.Yellow,
|
||||
Color.Brown)) {
|
||||
using (Pen pen = new Pen(brush)) {
|
||||
DrawArrow(g, pen, rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GraphicsPath CreateArrowGraphicsPath(Rectangle r)
|
||||
{
|
||||
GraphicsPath gp = new GraphicsPath();
|
||||
int halfX = r.Width / 2;
|
||||
int halfY = r.Height/ 2;
|
||||
gp.AddLine(r.X, r.Y + halfY/2, r.X + halfX, r.Y + halfY/2);
|
||||
gp.AddLine(r.X + halfX, r.Y + halfY/2, r.X + halfX, r.Y);
|
||||
gp.AddLine(r.X + halfX, r.Y, r.Right, r.Y + halfY);
|
||||
gp.AddLine(r.Right, r.Y + halfY, r.X + halfX, r.Bottom);
|
||||
gp.AddLine(r.X + halfX, r.Bottom, r.X + halfX, r.Bottom - halfY/2);
|
||||
gp.AddLine(r.X + halfX, r.Bottom - halfY/2, r.X, r.Bottom - halfY/2);
|
||||
gp.AddLine(r.X, r.Bottom - halfY/2, r.X, r.Y + halfY/2);
|
||||
gp.CloseFigure();
|
||||
return gp;
|
||||
}
|
||||
|
||||
GraphicsPath CreateRoundRectGraphicsPath(Rectangle r)
|
||||
{
|
||||
GraphicsPath gp = new GraphicsPath();
|
||||
int radius = r.Width / 2;
|
||||
gp.AddLine(r.X + radius, r.Y, r.Right - radius, r.Y);
|
||||
gp.AddArc(r.Right - radius, r.Y, radius, radius, 270, 90);
|
||||
|
||||
gp.AddLine(r.Right, r.Y + radius, r.Right, r.Bottom - radius);
|
||||
gp.AddArc(r.Right - radius, r.Bottom - radius, radius, radius, 0, 90);
|
||||
|
||||
gp.AddLine(r.Right - radius, r.Bottom, r.X + radius, r.Bottom);
|
||||
gp.AddArc(r.X, r.Bottom - radius, radius, radius, 90, 90);
|
||||
|
||||
gp.AddLine(r.X, r.Bottom - radius, r.X, r.Y + radius);
|
||||
gp.AddArc(r.X, r.Y, radius, radius, 180, 90);
|
||||
|
||||
gp.CloseFigure();
|
||||
return gp;
|
||||
}
|
||||
|
||||
void DrawRoundRect(Graphics g, Pen p , Rectangle r)
|
||||
{
|
||||
using (GraphicsPath gp = CreateRoundRectGraphicsPath(r)) {
|
||||
g.DrawPath(p, gp);
|
||||
}
|
||||
}
|
||||
|
||||
void FillRoundRect(Graphics g, Brush b , Rectangle r)
|
||||
{
|
||||
using (GraphicsPath gp = CreateRoundRectGraphicsPath(r)) {
|
||||
g.FillPath(b, gp);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawArrow(Graphics g, Pen p , Rectangle r)
|
||||
{
|
||||
using (GraphicsPath gp = CreateArrowGraphicsPath(r)) {
|
||||
g.DrawPath(p, gp);
|
||||
}
|
||||
}
|
||||
|
||||
void FillArrow(Graphics g, Brush b , Rectangle r)
|
||||
{
|
||||
using (GraphicsPath gp = CreateArrowGraphicsPath(r)) {
|
||||
g.FillPath(b, gp);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
static bool IsLineInsideRegion(int top, int bottom, int regionTop, int regionBottom)
|
||||
{
|
||||
if (top >= regionTop && top <= regionBottom) {
|
||||
// Region overlaps the line's top edge.
|
||||
return true;
|
||||
} else if(regionTop > top && regionTop < bottom) {
|
||||
// Region's top edge inside line.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
179
ICSharpCode.TextEditor/Project/Src/Gui/Ime.cs
Normal file
179
ICSharpCode.TextEditor/Project/Src/Gui/Ime.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Shinsaku Nakagawa" email="shinsaku@users.sourceforge.jp"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Used internally, not for own use.
|
||||
/// </summary>
|
||||
internal class Ime
|
||||
{
|
||||
public Ime(IntPtr hWnd, Font font)
|
||||
{
|
||||
// For unknown reasons, the IME support is causing crashes when used in a WOW64 process
|
||||
// or when used in .NET 4.0. We'll disable IME support in those cases.
|
||||
string PROCESSOR_ARCHITEW6432 = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432");
|
||||
if (PROCESSOR_ARCHITEW6432 == "IA64" || PROCESSOR_ARCHITEW6432 == "AMD64" || Environment.OSVersion.Platform == PlatformID.Unix || Environment.Version >= new Version(4,0)) {
|
||||
disableIME = true;
|
||||
} else {
|
||||
this.hIMEWnd = ImmGetDefaultIMEWnd(hWnd);
|
||||
}
|
||||
this.hWnd = hWnd;
|
||||
this.font = font;
|
||||
SetIMEWindowFont(font);
|
||||
}
|
||||
|
||||
private Font font = null;
|
||||
public Font Font
|
||||
{
|
||||
get {
|
||||
return font;
|
||||
}
|
||||
set {
|
||||
if (!value.Equals(font)) {
|
||||
font = value;
|
||||
lf = null;
|
||||
SetIMEWindowFont(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IntPtr HWnd
|
||||
{
|
||||
set {
|
||||
if (this.hWnd != value) {
|
||||
this.hWnd = value;
|
||||
if (!disableIME)
|
||||
this.hIMEWnd = ImmGetDefaultIMEWnd(value);
|
||||
SetIMEWindowFont(font);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ DllImport("imm32.dll") ]
|
||||
private static extern IntPtr ImmGetDefaultIMEWnd(IntPtr hWnd);
|
||||
|
||||
[ DllImport("user32.dll") ]
|
||||
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, COMPOSITIONFORM lParam);
|
||||
[ DllImport("user32.dll") ]
|
||||
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, [In, MarshalAs(UnmanagedType.LPStruct)] LOGFONT lParam);
|
||||
|
||||
[ StructLayout(LayoutKind.Sequential) ]
|
||||
private class COMPOSITIONFORM
|
||||
{
|
||||
public int dwStyle = 0;
|
||||
public POINT ptCurrentPos = null;
|
||||
public RECT rcArea = null;
|
||||
}
|
||||
|
||||
[ StructLayout(LayoutKind.Sequential) ]
|
||||
private class POINT
|
||||
{
|
||||
public int x = 0;
|
||||
public int y = 0;
|
||||
}
|
||||
|
||||
[ StructLayout(LayoutKind.Sequential) ]
|
||||
private class RECT
|
||||
{
|
||||
public int left = 0;
|
||||
public int top = 0;
|
||||
public int right = 0;
|
||||
public int bottom = 0;
|
||||
}
|
||||
|
||||
private const int WM_IME_CONTROL = 0x0283;
|
||||
|
||||
private const int IMC_SETCOMPOSITIONWINDOW = 0x000c;
|
||||
private IntPtr hIMEWnd;
|
||||
private IntPtr hWnd;
|
||||
private const int CFS_POINT = 0x0002;
|
||||
|
||||
[ StructLayout(LayoutKind.Sequential) ]
|
||||
private class LOGFONT
|
||||
{
|
||||
public int lfHeight = 0;
|
||||
public int lfWidth = 0;
|
||||
public int lfEscapement = 0;
|
||||
public int lfOrientation = 0;
|
||||
public int lfWeight = 0;
|
||||
public byte lfItalic = 0;
|
||||
public byte lfUnderline = 0;
|
||||
public byte lfStrikeOut = 0;
|
||||
public byte lfCharSet = 0;
|
||||
public byte lfOutPrecision = 0;
|
||||
public byte lfClipPrecision = 0;
|
||||
public byte lfQuality = 0;
|
||||
public byte lfPitchAndFamily = 0;
|
||||
[ MarshalAs(UnmanagedType.ByValTStr, SizeConst=32) ] public string lfFaceName = null;
|
||||
}
|
||||
private const int IMC_SETCOMPOSITIONFONT = 0x000a;
|
||||
LOGFONT lf = null;
|
||||
static bool disableIME;
|
||||
|
||||
private void SetIMEWindowFont(Font f)
|
||||
{
|
||||
if (disableIME || hIMEWnd == IntPtr.Zero) return;
|
||||
|
||||
if (lf == null) {
|
||||
lf = new LOGFONT();
|
||||
f.ToLogFont(lf);
|
||||
lf.lfFaceName = f.Name; // This is very important! "Font.ToLogFont" Method sets invalid value to LOGFONT.lfFaceName
|
||||
}
|
||||
|
||||
try {
|
||||
SendMessage(
|
||||
hIMEWnd,
|
||||
WM_IME_CONTROL,
|
||||
new IntPtr(IMC_SETCOMPOSITIONFONT),
|
||||
lf
|
||||
);
|
||||
} catch (AccessViolationException ex) {
|
||||
Handle(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetIMEWindowLocation(int x, int y)
|
||||
{
|
||||
if (disableIME || hIMEWnd == IntPtr.Zero) return;
|
||||
|
||||
POINT p = new POINT();
|
||||
p.x = x;
|
||||
p.y = y;
|
||||
|
||||
COMPOSITIONFORM lParam = new COMPOSITIONFORM();
|
||||
lParam.dwStyle = CFS_POINT;
|
||||
lParam.ptCurrentPos = p;
|
||||
lParam.rcArea = new RECT();
|
||||
|
||||
try {
|
||||
SendMessage(
|
||||
hIMEWnd,
|
||||
WM_IME_CONTROL,
|
||||
new IntPtr(IMC_SETCOMPOSITIONWINDOW),
|
||||
lParam
|
||||
);
|
||||
} catch (AccessViolationException ex) {
|
||||
Handle(ex);
|
||||
}
|
||||
}
|
||||
|
||||
void Handle(Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
if (!disableIME) {
|
||||
disableIME = true;
|
||||
MessageBox.Show("Error calling IME: " + ex.Message + "\nIME is disabled.", "IME error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// <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;
|
||||
|
||||
namespace ICSharpCode.TextEditor.Gui.InsightWindow
|
||||
{
|
||||
public interface IInsightDataProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Tells the insight provider to prepare its data.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The name of the edited file</param>
|
||||
/// <param name="textArea">The text area in which the file is being edited</param>
|
||||
void SetupDataProvider(string fileName, TextArea textArea);
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the insight provider that the caret offset has changed.
|
||||
/// </summary>
|
||||
/// <returns>Return true to close the insight window (e.g. when the
|
||||
/// caret was moved outside the region where insight is displayed for).
|
||||
/// Return false to keep the window open.</returns>
|
||||
bool CaretOffsetChanged();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text to display in the insight window.
|
||||
/// </summary>
|
||||
/// <param name="number">The number of the active insight entry.
|
||||
/// Multiple insight entries might be multiple overloads of the same method.</param>
|
||||
/// <returns>The text to display, e.g. a multi-line string where
|
||||
/// the first line is the method definition, followed by a description.</returns>
|
||||
string GetInsightData(int number);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of available insight entries, e.g. the number of available
|
||||
/// overloads to call.
|
||||
/// </summary>
|
||||
int InsightDataCount {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the entry to initially select.
|
||||
/// </summary>
|
||||
int DefaultIndex {
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
// <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.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using ICSharpCode.TextEditor.Gui.CompletionWindow;
|
||||
using ICSharpCode.TextEditor.Util;
|
||||
|
||||
namespace ICSharpCode.TextEditor.Gui.InsightWindow
|
||||
{
|
||||
public class InsightWindow : AbstractCompletionWindow
|
||||
{
|
||||
public InsightWindow(Form parentForm, TextEditorControl control) : base(parentForm, control)
|
||||
{
|
||||
SetStyle(ControlStyles.UserPaint, true);
|
||||
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
||||
}
|
||||
|
||||
public void ShowInsightWindow()
|
||||
{
|
||||
if (!Visible) {
|
||||
if (insightDataProviderStack.Count > 0) {
|
||||
ShowCompletionWindow();
|
||||
}
|
||||
} else {
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
#region Event handling routines
|
||||
protected override bool ProcessTextAreaKey(Keys keyData)
|
||||
{
|
||||
if (!Visible) {
|
||||
return false;
|
||||
}
|
||||
switch (keyData) {
|
||||
case Keys.Down:
|
||||
if (DataProvider != null && DataProvider.InsightDataCount > 0) {
|
||||
CurrentData = (CurrentData + 1) % DataProvider.InsightDataCount;
|
||||
Refresh();
|
||||
}
|
||||
return true;
|
||||
case Keys.Up:
|
||||
if (DataProvider != null && DataProvider.InsightDataCount > 0) {
|
||||
CurrentData = (CurrentData + DataProvider.InsightDataCount - 1) % DataProvider.InsightDataCount;
|
||||
Refresh();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return base.ProcessTextAreaKey(keyData);
|
||||
}
|
||||
|
||||
protected override void CaretOffsetChanged(object sender, EventArgs e)
|
||||
{
|
||||
// move the window under the caret (don't change the x position)
|
||||
TextLocation caretPos = control.ActiveTextAreaControl.Caret.Position;
|
||||
int y = (int)((1 + caretPos.Y) * control.ActiveTextAreaControl.TextArea.TextView.FontHeight)
|
||||
- control.ActiveTextAreaControl.TextArea.VirtualTop.Y - 1
|
||||
+ control.ActiveTextAreaControl.TextArea.TextView.DrawingPosition.Y;
|
||||
|
||||
int xpos = control.ActiveTextAreaControl.TextArea.TextView.GetDrawingXPos(caretPos.Y, caretPos.X);
|
||||
int ypos = (control.ActiveTextAreaControl.Document.GetVisibleLine(caretPos.Y) + 1) * control.ActiveTextAreaControl.TextArea.TextView.FontHeight
|
||||
- control.ActiveTextAreaControl.TextArea.VirtualTop.Y;
|
||||
int rulerHeight = control.TextEditorProperties.ShowHorizontalRuler ? control.ActiveTextAreaControl.TextArea.TextView.FontHeight : 0;
|
||||
|
||||
Point p = control.ActiveTextAreaControl.PointToScreen(new Point(xpos, ypos + rulerHeight));
|
||||
if (p.Y != Location.Y) {
|
||||
Location = p;
|
||||
}
|
||||
|
||||
while (DataProvider != null && DataProvider.CaretOffsetChanged()) {
|
||||
CloseCurrentDataProvider();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMouseDown(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseDown(e);
|
||||
control.ActiveTextAreaControl.TextArea.Focus();
|
||||
if (TipPainterTools.DrawingRectangle1.Contains(e.X, e.Y)) {
|
||||
CurrentData = (CurrentData + DataProvider.InsightDataCount - 1) % DataProvider.InsightDataCount;
|
||||
Refresh();
|
||||
}
|
||||
if (TipPainterTools.DrawingRectangle2.Contains(e.X, e.Y)) {
|
||||
CurrentData = (CurrentData + 1) % DataProvider.InsightDataCount;
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
MouseWheelHandler mouseWheelHandler = new MouseWheelHandler();
|
||||
|
||||
public void HandleMouseWheel(MouseEventArgs e)
|
||||
{
|
||||
if (DataProvider != null && DataProvider.InsightDataCount > 0) {
|
||||
int distance = mouseWheelHandler.GetScrollAmount(e);
|
||||
if (control.TextEditorProperties.MouseWheelScrollDown)
|
||||
distance = -distance;
|
||||
if (distance > 0) {
|
||||
CurrentData = (CurrentData + 1) % DataProvider.InsightDataCount;
|
||||
} else if (distance < 0) {
|
||||
CurrentData = (CurrentData + DataProvider.InsightDataCount - 1) % DataProvider.InsightDataCount;
|
||||
}
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
#region Insight Window Drawing routines
|
||||
protected override void OnPaint(PaintEventArgs pe)
|
||||
{
|
||||
string methodCountMessage = null, description;
|
||||
if (DataProvider == null || DataProvider.InsightDataCount < 1) {
|
||||
description = "Unknown Method";
|
||||
} else {
|
||||
if (DataProvider.InsightDataCount > 1) {
|
||||
methodCountMessage = control.GetRangeDescription(CurrentData + 1, DataProvider.InsightDataCount);
|
||||
}
|
||||
description = DataProvider.GetInsightData(CurrentData);
|
||||
}
|
||||
|
||||
drawingSize = TipPainterTools.GetDrawingSizeHelpTipFromCombinedDescription(this,
|
||||
pe.Graphics,
|
||||
Font,
|
||||
methodCountMessage,
|
||||
description);
|
||||
if (drawingSize != Size) {
|
||||
SetLocation();
|
||||
} else {
|
||||
TipPainterTools.DrawHelpTipFromCombinedDescription(this, pe.Graphics, Font, methodCountMessage, description);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPaintBackground(PaintEventArgs pe)
|
||||
{
|
||||
pe.Graphics.FillRectangle(SystemBrushes.Info, pe.ClipRectangle);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region InsightDataProvider handling
|
||||
Stack<InsightDataProviderStackElement> insightDataProviderStack = new Stack<InsightDataProviderStackElement>();
|
||||
|
||||
int CurrentData {
|
||||
get {
|
||||
return insightDataProviderStack.Peek().currentData;
|
||||
}
|
||||
set {
|
||||
insightDataProviderStack.Peek().currentData = value;
|
||||
}
|
||||
}
|
||||
|
||||
IInsightDataProvider DataProvider {
|
||||
get {
|
||||
if (insightDataProviderStack.Count == 0) {
|
||||
return null;
|
||||
}
|
||||
return insightDataProviderStack.Peek().dataProvider;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddInsightDataProvider(IInsightDataProvider provider, string fileName)
|
||||
{
|
||||
provider.SetupDataProvider(fileName, control.ActiveTextAreaControl.TextArea);
|
||||
if (provider.InsightDataCount > 0) {
|
||||
insightDataProviderStack.Push(new InsightDataProviderStackElement(provider));
|
||||
}
|
||||
}
|
||||
|
||||
void CloseCurrentDataProvider()
|
||||
{
|
||||
insightDataProviderStack.Pop();
|
||||
if (insightDataProviderStack.Count == 0) {
|
||||
Close();
|
||||
} else {
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
class InsightDataProviderStackElement
|
||||
{
|
||||
public int currentData;
|
||||
public IInsightDataProvider dataProvider;
|
||||
|
||||
public InsightDataProviderStackElement(IInsightDataProvider dataProvider)
|
||||
{
|
||||
this.currentData = Math.Max(dataProvider.DefaultIndex, 0);
|
||||
this.dataProvider = dataProvider;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
947
ICSharpCode.TextEditor/Project/Src/Gui/TextArea.cs
Normal file
947
ICSharpCode.TextEditor/Project/Src/Gui/TextArea.cs
Normal file
@@ -0,0 +1,947 @@
|
||||
// <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.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Text;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
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 : Control
|
||||
{
|
||||
bool hiddenMouseCursor = false;
|
||||
/// <summary>
|
||||
/// The position where the mouse cursor was when it was hidden. Sometimes the text editor gets MouseMove
|
||||
/// events when typing text even if the mouse is not moved.
|
||||
/// </summary>
|
||||
Point mouseCursorHidePosition;
|
||||
|
||||
Point virtualTop = new Point(0, 0);
|
||||
TextAreaControl motherTextAreaControl;
|
||||
TextEditorControl motherTextEditorControl;
|
||||
|
||||
List<BracketHighlightingSheme> bracketshemes = new List<BracketHighlightingSheme>();
|
||||
TextAreaClipboardHandler textAreaClipboardHandler;
|
||||
bool autoClearSelection = false;
|
||||
|
||||
List<AbstractMargin> leftMargins = new List<AbstractMargin>();
|
||||
|
||||
TextView textView;
|
||||
GutterMargin gutterMargin;
|
||||
FoldMargin foldMargin;
|
||||
IconBarMargin iconBarMargin;
|
||||
|
||||
SelectionManager selectionManager;
|
||||
Caret caret;
|
||||
|
||||
internal Point mousepos = new Point(0, 0);
|
||||
//public Point selectionStartPos = new Point(0,0);
|
||||
|
||||
bool disposed;
|
||||
|
||||
[Browsable(false)]
|
||||
public IList<AbstractMargin> LeftMargins {
|
||||
get {
|
||||
return leftMargins.AsReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertLeftMargin(int index, AbstractMargin margin)
|
||||
{
|
||||
leftMargins.Insert(index, margin);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
caret.UpdateCaretPosition();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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 TextLocation(0, 0);
|
||||
SelectionManager.SelectionCollection.Clear();
|
||||
}
|
||||
void SearchMatchingBracket(object sender, EventArgs e)
|
||||
{
|
||||
if (!TextEditorProperties.ShowMatchingBracket) {
|
||||
textView.Highlight = null;
|
||||
return;
|
||||
}
|
||||
int oldLine1 = -1, oldLine2 = -1;
|
||||
if (textView.Highlight != null && textView.Highlight.OpenBrace.Y >=0 && textView.Highlight.OpenBrace.Y < Document.TotalNumberOfLines) {
|
||||
oldLine1 = textView.Highlight.OpenBrace.Y;
|
||||
}
|
||||
if (textView.Highlight != null && textView.Highlight.CloseBrace.Y >=0 && textView.Highlight.CloseBrace.Y < Document.TotalNumberOfLines) {
|
||||
oldLine2 = textView.Highlight.CloseBrace.Y;
|
||||
}
|
||||
textView.Highlight = FindMatchingBracketHighlight();
|
||||
if (oldLine1 >= 0)
|
||||
UpdateLine(oldLine1);
|
||||
if (oldLine2 >= 0 && oldLine2 != oldLine1)
|
||||
UpdateLine(oldLine2);
|
||||
if (textView.Highlight != null) {
|
||||
int newLine1 = textView.Highlight.OpenBrace.Y;
|
||||
int newLine2 = textView.Highlight.CloseBrace.Y;
|
||||
if (newLine1 != oldLine1 && newLine1 != oldLine2)
|
||||
UpdateLine(newLine1);
|
||||
if (newLine2 != oldLine1 && newLine2 != oldLine2 && newLine2 != newLine1)
|
||||
UpdateLine(newLine2);
|
||||
}
|
||||
}
|
||||
|
||||
public Highlight FindMatchingBracketHighlight()
|
||||
{
|
||||
if (Caret.Offset == 0)
|
||||
return null;
|
||||
foreach (BracketHighlightingSheme bracketsheme in bracketshemes) {
|
||||
Highlight highlight = bracketsheme.GetHighlight(Document, Caret.Offset - 1);
|
||||
if (highlight != null) {
|
||||
return highlight;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetDesiredColumn()
|
||||
{
|
||||
Caret.DesiredColumn = TextView.GetDrawingXPos(Caret.Line, Caret.Column) + VirtualTop.X;
|
||||
}
|
||||
|
||||
public void SetCaretToDesiredColumn()
|
||||
{
|
||||
FoldMarker dummy;
|
||||
Caret.Position = textView.GetLogicalColumn(Caret.Line, Caret.DesiredColumn + VirtualTop.X, out dummy);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
CloseToolTip();
|
||||
}
|
||||
|
||||
protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
|
||||
{
|
||||
// this corrects weird problems when text is selected,
|
||||
// then a menu item is selected, then the text is
|
||||
// clicked again - it correctly synchronises the
|
||||
// click position
|
||||
mousepos = new Point(e.X, e.Y);
|
||||
|
||||
base.OnMouseDown(e);
|
||||
CloseToolTip();
|
||||
|
||||
foreach (AbstractMargin margin in leftMargins) {
|
||||
if (margin.DrawingPosition.Contains(e.X, e.Y)) {
|
||||
margin.HandleMouseDown(new Point(e.X, e.Y), e.Button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the mouse cursor if it has been hidden.
|
||||
/// </summary>
|
||||
/// <param name="forceShow"><c>true</c> to always show the cursor or <c>false</c> to show it only if it has been moved since it was hidden.</param>
|
||||
internal void ShowHiddenCursor(bool forceShow)
|
||||
{
|
||||
if (hiddenMouseCursor) {
|
||||
if (mouseCursorHidePosition != Cursor.Position || forceShow) {
|
||||
Cursor.Show();
|
||||
hiddenMouseCursor = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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;
|
||||
|
||||
void SetToolTip(string text, int lineNumber)
|
||||
{
|
||||
if (toolTip == null || toolTip.IsDisposed)
|
||||
toolTip = new DeclarationViewWindow(this.FindForm());
|
||||
if (oldToolTip == text)
|
||||
return;
|
||||
if (text == null) {
|
||||
toolTip.Hide();
|
||||
} else {
|
||||
Point p = Control.MousePosition;
|
||||
Point cp = PointToClient(p);
|
||||
if (lineNumber >= 0) {
|
||||
lineNumber = this.Document.GetVisibleLine(lineNumber);
|
||||
p.Y = (p.Y - cp.Y) + (lineNumber * this.TextView.FontHeight) - this.virtualTop.Y;
|
||||
}
|
||||
p.Offset(3, 3);
|
||||
toolTip.Owner = this.FindForm();
|
||||
toolTip.Location = p;
|
||||
toolTip.Description = text;
|
||||
toolTip.HideOnClick = true;
|
||||
toolTip.Show();
|
||||
}
|
||||
oldToolTip = text;
|
||||
}
|
||||
|
||||
public event ToolTipRequestEventHandler ToolTipRequest;
|
||||
|
||||
protected virtual void OnToolTipRequest(ToolTipRequestEventArgs e)
|
||||
{
|
||||
if (ToolTipRequest != null) {
|
||||
ToolTipRequest(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
bool toolTipActive;
|
||||
/// <summary>
|
||||
/// Rectangle in text area that caused the current tool tip.
|
||||
/// Prevents tooltip from re-showing when it was closed because of a click or keyboard
|
||||
/// input and the mouse was not used.
|
||||
/// </summary>
|
||||
Rectangle toolTipRectangle;
|
||||
|
||||
void CloseToolTip()
|
||||
{
|
||||
if (toolTipActive) {
|
||||
//Console.WriteLine("Closing tooltip");
|
||||
toolTipActive = false;
|
||||
SetToolTip(null, -1);
|
||||
}
|
||||
ResetMouseEventArgs();
|
||||
}
|
||||
|
||||
protected override void OnMouseHover(EventArgs e)
|
||||
{
|
||||
base.OnMouseHover(e);
|
||||
//Console.WriteLine("Hover raised at " + PointToClient(Control.MousePosition));
|
||||
if (MouseButtons == MouseButtons.None) {
|
||||
RequestToolTip(PointToClient(Control.MousePosition));
|
||||
} else {
|
||||
CloseToolTip();
|
||||
}
|
||||
}
|
||||
|
||||
protected void RequestToolTip(Point mousePos)
|
||||
{
|
||||
if (toolTipRectangle.Contains(mousePos)) {
|
||||
if (!toolTipActive)
|
||||
ResetMouseEventArgs();
|
||||
return;
|
||||
}
|
||||
|
||||
//Console.WriteLine("Request tooltip for " + mousePos);
|
||||
|
||||
toolTipRectangle = new Rectangle(mousePos.X - 4, mousePos.Y - 4, 8, 8);
|
||||
|
||||
TextLocation logicPos = textView.GetLogicalPosition(mousePos.X - textView.DrawingPosition.Left,
|
||||
mousePos.Y - textView.DrawingPosition.Top);
|
||||
bool inDocument = textView.DrawingPosition.Contains(mousePos)
|
||||
&& logicPos.Y >= 0 && logicPos.Y < Document.TotalNumberOfLines;
|
||||
ToolTipRequestEventArgs args = new ToolTipRequestEventArgs(mousePos, logicPos, inDocument);
|
||||
OnToolTipRequest(args);
|
||||
if (args.ToolTipShown) {
|
||||
//Console.WriteLine("Set tooltip to " + args.toolTipText);
|
||||
toolTipActive = true;
|
||||
SetToolTip(args.toolTipText, inDocument ? logicPos.Y + 1 : -1);
|
||||
} else {
|
||||
CloseToolTip();
|
||||
}
|
||||
}
|
||||
|
||||
// external interface to the attached event
|
||||
internal void RaiseMouseMove(MouseEventArgs e)
|
||||
{
|
||||
OnMouseMove(e);
|
||||
}
|
||||
|
||||
protected override void OnMouseMove(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseMove(e);
|
||||
if (!toolTipRectangle.Contains(e.Location)) {
|
||||
toolTipRectangle = Rectangle.Empty;
|
||||
if (toolTipActive)
|
||||
RequestToolTip(e.Location);
|
||||
}
|
||||
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)) {
|
||||
TextLocation realmousepos = TextView.GetLogicalPosition(e.X - TextView.DrawingPosition.X, e.Y - TextView.DrawingPosition.Y);
|
||||
if(SelectionManager.IsSelected(Document.PositionToOffset(realmousepos)) && MouseButtons == MouseButtons.None) {
|
||||
// mouse is hovering over a selection, so show default mouse
|
||||
this.Cursor = Cursors.Default;
|
||||
} else {
|
||||
// mouse is hovering over text area, not a selection, so show the textView cursor
|
||||
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;
|
||||
|
||||
bool isFullRepaint = clipRectangle.X == 0 && clipRectangle.Y == 0
|
||||
&& clipRectangle.Width == this.Width && clipRectangle.Height == this.Height;
|
||||
|
||||
g.TextRenderingHint = this.TextEditorProperties.TextRenderingHint;
|
||||
|
||||
if (updateMargin != null) {
|
||||
updateMargin.Paint(g, updateMargin.DrawingPosition);
|
||||
// clipRectangle.Intersect(updateMargin.DrawingPosition);
|
||||
}
|
||||
|
||||
if (clipRectangle.Width <= 0 || clipRectangle.Height <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (AbstractMargin margin in leftMargins) {
|
||||
if (margin.IsVisible) {
|
||||
Rectangle marginRectangle = new Rectangle(currentXPos , currentYPos, margin.Size.Width, Height - currentYPos);
|
||||
if (marginRectangle != margin.DrawingPosition) {
|
||||
// margin changed size
|
||||
if (!isFullRepaint && !clipRectangle.Contains(marginRectangle)) {
|
||||
Invalidate(); // do a full repaint
|
||||
}
|
||||
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;
|
||||
// update caret position (but outside of WM_PAINT!)
|
||||
BeginInvoke((MethodInvoker)caret.UpdateCaretPosition);
|
||||
}
|
||||
if (clipRectangle.IntersectsWith(textViewArea)) {
|
||||
textViewArea.Intersect(clipRectangle);
|
||||
if (!textViewArea.IsEmpty) {
|
||||
textView.Paint(g, textViewArea);
|
||||
}
|
||||
}
|
||||
|
||||
if (adjustScrollBars) {
|
||||
this.motherTextAreaControl.AdjustScrollBars();
|
||||
}
|
||||
|
||||
// we cannot update the caret position here, it's not allowed to call the caret API inside WM_PAINT
|
||||
//Caret.UpdateCaretPosition();
|
||||
|
||||
base.OnPaint(e);
|
||||
}
|
||||
void DocumentFoldingsChanged(object sender, EventArgs e)
|
||||
{
|
||||
Caret.UpdateCaretPosition();
|
||||
Invalidate();
|
||||
this.motherTextAreaControl.AdjustScrollBars();
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
// Fixes SD2-747: Form containing the text editor and a button with a shortcut
|
||||
protected override bool IsInputChar(char charCode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool IsReadOnly(int offset)
|
||||
{
|
||||
if (Document.ReadOnly) {
|
||||
return true;
|
||||
}
|
||||
if (TextEditorProperties.SupportReadOnlySegments) {
|
||||
return Document.MarkerStrategy.GetMarkers(offset).Exists(m=>m.IsReadOnly);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsReadOnly(int offset, int length)
|
||||
{
|
||||
if (Document.ReadOnly) {
|
||||
return true;
|
||||
}
|
||||
if (TextEditorProperties.SupportReadOnlySegments) {
|
||||
return Document.MarkerStrategy.GetMarkers(offset, length).Exists(m=>m.IsReadOnly);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void SimulateKeyPress(char ch)
|
||||
{
|
||||
if (SelectionManager.HasSomethingSelected) {
|
||||
if (SelectionManager.SelectionIsReadonly)
|
||||
return;
|
||||
} else if (IsReadOnly(Caret.Offset)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ch < ' ') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hiddenMouseCursor && TextEditorProperties.HideMouseCursor) {
|
||||
if (this.ClientRectangle.Contains(PointToClient(Cursor.Position))) {
|
||||
mouseCursorHidePosition = Cursor.Position;
|
||||
hiddenMouseCursor = true;
|
||||
Cursor.Hide();
|
||||
}
|
||||
}
|
||||
CloseToolTip();
|
||||
|
||||
BeginUpdate();
|
||||
Document.UndoStack.StartUndoGroup();
|
||||
try {
|
||||
// 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;
|
||||
Document.FormattingStrategy.FormatLine(this, currentLineNr, Document.PositionToOffset(Caret.Position), ch);
|
||||
|
||||
EndUpdate();
|
||||
} finally {
|
||||
Document.UndoStack.EndUndoGroup();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnKeyPress(KeyPressEventArgs e)
|
||||
{
|
||||
base.OnKeyPress(e);
|
||||
SimulateKeyPress(e.KeyChar);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
/// <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 not (or the process was 'silent', use the standard edit actions
|
||||
IEditAction action = motherTextEditorControl.GetEditAction(keyData);
|
||||
AutoClearSelection = true;
|
||||
if (action != null) {
|
||||
BeginUpdate();
|
||||
try {
|
||||
lock (Document) {
|
||||
action.Execute(this);
|
||||
if (SelectionManager.HasSomethingSelected && AutoClearSelection /*&& caretchanged*/) {
|
||||
if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal) {
|
||||
SelectionManager.ClearSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
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 (motherTextAreaControl == null)
|
||||
return false;
|
||||
if (SelectionManager.HasSomethingSelected)
|
||||
return !SelectionManager.SelectionIsReadonly;
|
||||
else
|
||||
return !IsReadOnly(Caret.Offset);
|
||||
}
|
||||
}
|
||||
|
||||
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.IsInUpdate;
|
||||
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 = ' ';
|
||||
}
|
||||
|
||||
Document.UndoStack.StartUndoGroup();
|
||||
if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal &&
|
||||
SelectionManager.SelectionCollection.Count > 0) {
|
||||
Caret.Position = SelectionManager.SelectionCollection[0].StartPosition;
|
||||
SelectionManager.RemoveSelectedText();
|
||||
}
|
||||
LineSegment caretLine = Document.GetLineSegment(Caret.Line);
|
||||
int offset = Caret.Offset;
|
||||
// use desired column for generated whitespaces
|
||||
int dc = Caret.Column;
|
||||
if (caretLine.Length < dc && ch != '\n') {
|
||||
Document.Insert(offset, GenerateWhitespaceString(dc - caretLine.Length) + ch);
|
||||
} else {
|
||||
Document.Insert(offset, ch.ToString());
|
||||
}
|
||||
Document.UndoStack.EndUndoGroup();
|
||||
++Caret.Column;
|
||||
|
||||
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.IsInUpdate;
|
||||
if (!updating) {
|
||||
BeginUpdate();
|
||||
}
|
||||
try {
|
||||
Document.UndoStack.StartUndoGroup();
|
||||
if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal &&
|
||||
SelectionManager.SelectionCollection.Count > 0) {
|
||||
Caret.Position = SelectionManager.SelectionCollection[0].StartPosition;
|
||||
SelectionManager.RemoveSelectedText();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
Document.UndoStack.EndUndoGroup();
|
||||
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.IsInUpdate;
|
||||
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) {
|
||||
if (!disposed) {
|
||||
disposed = true;
|
||||
if (caret != null) {
|
||||
caret.PositionChanged -= new EventHandler(SearchMatchingBracket);
|
||||
caret.Dispose();
|
||||
}
|
||||
if (selectionManager != null) {
|
||||
selectionManager.Dispose();
|
||||
}
|
||||
Document.TextContentChanged -= new EventHandler(TextContentChanged);
|
||||
Document.FoldingManager.FoldingsChanged -= new EventHandler(DocumentFoldingsChanged);
|
||||
motherTextAreaControl = null;
|
||||
motherTextEditorControl = null;
|
||||
foreach (AbstractMargin margin in leftMargins) {
|
||||
if (margin is IDisposable)
|
||||
(margin as IDisposable).Dispose();
|
||||
}
|
||||
textView.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 = Document.GetVisibleLine(lineBegin);
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
// <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.Drawing;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
using ICSharpCode.TextEditor.Util;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
public class TextAreaClipboardHandler
|
||||
{
|
||||
TextArea textArea;
|
||||
|
||||
public bool EnableCut {
|
||||
get {
|
||||
return textArea.EnableCutOrPaste; //textArea.SelectionManager.HasSomethingSelected;
|
||||
}
|
||||
}
|
||||
|
||||
public bool EnableCopy {
|
||||
get {
|
||||
return true; //textArea.SelectionManager.HasSomethingSelected;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate bool ClipboardContainsTextDelegate();
|
||||
|
||||
/// <summary>
|
||||
/// Is called when CachedClipboardContainsText should be updated.
|
||||
/// If this property is null (the default value), the text editor uses
|
||||
/// System.Windows.Forms.Clipboard.ContainsText.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This property is useful if you want to prevent the default Clipboard.ContainsText
|
||||
/// behaviour that waits for the clipboard to be available - the clipboard might
|
||||
/// never become available if it is owned by a process that is paused by the debugger.
|
||||
/// </remarks>
|
||||
public static ClipboardContainsTextDelegate GetClipboardContainsText;
|
||||
|
||||
public bool EnablePaste {
|
||||
get {
|
||||
if (!textArea.EnableCutOrPaste)
|
||||
return false;
|
||||
ClipboardContainsTextDelegate d = GetClipboardContainsText;
|
||||
if (d != null) {
|
||||
return d();
|
||||
} else {
|
||||
try {
|
||||
return Clipboard.ContainsText();
|
||||
} catch (ExternalException) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool EnableDelete {
|
||||
get {
|
||||
return textArea.SelectionManager.HasSomethingSelected && !textArea.SelectionManager.SelectionIsReadonly;
|
||||
}
|
||||
}
|
||||
|
||||
public bool EnableSelectAll {
|
||||
get {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public TextAreaClipboardHandler(TextArea textArea)
|
||||
{
|
||||
this.textArea = textArea;
|
||||
textArea.SelectionManager.SelectionChanged += new EventHandler(DocumentSelectionChanged);
|
||||
}
|
||||
|
||||
void DocumentSelectionChanged(object sender, EventArgs e)
|
||||
{
|
||||
// ((DefaultWorkbench)WorkbenchSingleton.Workbench).UpdateToolbars();
|
||||
}
|
||||
|
||||
const string LineSelectedType = "MSDEVLineSelect"; // This is the type VS 2003 and 2005 use for flagging a whole line copy
|
||||
|
||||
bool CopyTextToClipboard(string stringToCopy, bool asLine)
|
||||
{
|
||||
if (stringToCopy.Length > 0) {
|
||||
DataObject dataObject = new DataObject();
|
||||
dataObject.SetData(DataFormats.UnicodeText, true, stringToCopy);
|
||||
if (asLine) {
|
||||
MemoryStream lineSelected = new MemoryStream(1);
|
||||
lineSelected.WriteByte(1);
|
||||
dataObject.SetData(LineSelectedType, false, lineSelected);
|
||||
}
|
||||
// Default has no highlighting, therefore we don't need RTF output
|
||||
if (textArea.Document.HighlightingStrategy.Name != "Default") {
|
||||
dataObject.SetData(DataFormats.Rtf, RtfWriter.GenerateRtf(textArea));
|
||||
}
|
||||
OnCopyText(new CopyTextEventArgs(stringToCopy));
|
||||
|
||||
SafeSetClipboard(dataObject);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Code duplication: TextAreaClipboardHandler.cs also has SafeSetClipboard
|
||||
[ThreadStatic] static int SafeSetClipboardDataVersion;
|
||||
|
||||
static void SafeSetClipboard(object dataObject)
|
||||
{
|
||||
// Work around ExternalException bug. (SD2-426)
|
||||
// Best reproducable inside Virtual PC.
|
||||
int version = unchecked(++SafeSetClipboardDataVersion);
|
||||
try {
|
||||
Clipboard.SetDataObject(dataObject, true);
|
||||
} catch (ExternalException) {
|
||||
Timer timer = new Timer();
|
||||
timer.Interval = 100;
|
||||
timer.Tick += delegate {
|
||||
timer.Stop();
|
||||
timer.Dispose();
|
||||
if (SafeSetClipboardDataVersion == version) {
|
||||
try {
|
||||
Clipboard.SetDataObject(dataObject, true, 10, 50);
|
||||
} catch (ExternalException) { }
|
||||
}
|
||||
};
|
||||
timer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
bool CopyTextToClipboard(string stringToCopy)
|
||||
{
|
||||
return CopyTextToClipboard(stringToCopy, false);
|
||||
}
|
||||
|
||||
public void Cut(object sender, EventArgs e)
|
||||
{
|
||||
if (textArea.SelectionManager.HasSomethingSelected) {
|
||||
if (CopyTextToClipboard(textArea.SelectionManager.SelectedText)) {
|
||||
if (textArea.SelectionManager.SelectionIsReadonly)
|
||||
return;
|
||||
// Remove text
|
||||
textArea.BeginUpdate();
|
||||
textArea.Caret.Position = textArea.SelectionManager.SelectionCollection[0].StartPosition;
|
||||
textArea.SelectionManager.RemoveSelectedText();
|
||||
textArea.EndUpdate();
|
||||
}
|
||||
} else if (textArea.Document.TextEditorProperties.CutCopyWholeLine) {
|
||||
// No text was selected, select and cut the entire line
|
||||
int curLineNr = textArea.Document.GetLineNumberForOffset(textArea.Caret.Offset);
|
||||
LineSegment lineWhereCaretIs = textArea.Document.GetLineSegment(curLineNr);
|
||||
string caretLineText = textArea.Document.GetText(lineWhereCaretIs.Offset, lineWhereCaretIs.TotalLength);
|
||||
textArea.SelectionManager.SetSelection(textArea.Document.OffsetToPosition(lineWhereCaretIs.Offset), textArea.Document.OffsetToPosition(lineWhereCaretIs.Offset + lineWhereCaretIs.TotalLength));
|
||||
if (CopyTextToClipboard(caretLineText, true)) {
|
||||
if (textArea.SelectionManager.SelectionIsReadonly)
|
||||
return;
|
||||
// remove line
|
||||
textArea.BeginUpdate();
|
||||
textArea.Caret.Position = textArea.Document.OffsetToPosition(lineWhereCaretIs.Offset);
|
||||
textArea.SelectionManager.RemoveSelectedText();
|
||||
textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToEnd, new TextLocation(0, curLineNr)));
|
||||
textArea.EndUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Copy(object sender, EventArgs e)
|
||||
{
|
||||
if (!CopyTextToClipboard(textArea.SelectionManager.SelectedText) && textArea.Document.TextEditorProperties.CutCopyWholeLine) {
|
||||
// No text was selected, select the entire line, copy it, and then deselect
|
||||
int curLineNr = textArea.Document.GetLineNumberForOffset(textArea.Caret.Offset);
|
||||
LineSegment lineWhereCaretIs = textArea.Document.GetLineSegment(curLineNr);
|
||||
string caretLineText = textArea.Document.GetText(lineWhereCaretIs.Offset, lineWhereCaretIs.TotalLength);
|
||||
CopyTextToClipboard(caretLineText, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void Paste(object sender, EventArgs e)
|
||||
{
|
||||
if (!textArea.EnableCutOrPaste) {
|
||||
return;
|
||||
}
|
||||
// Clipboard.GetDataObject may throw an exception...
|
||||
for (int i = 0;; i++) {
|
||||
try {
|
||||
IDataObject data = Clipboard.GetDataObject();
|
||||
if (data == null)
|
||||
return;
|
||||
bool fullLine = data.GetDataPresent(LineSelectedType);
|
||||
if (data.GetDataPresent(DataFormats.UnicodeText)) {
|
||||
string text = (string)data.GetData(DataFormats.UnicodeText);
|
||||
// we got NullReferenceExceptions here, apparently the clipboard can contain null strings
|
||||
if (!string.IsNullOrEmpty(text)) {
|
||||
textArea.Document.UndoStack.StartUndoGroup();
|
||||
try {
|
||||
if (textArea.SelectionManager.HasSomethingSelected) {
|
||||
textArea.Caret.Position = textArea.SelectionManager.SelectionCollection[0].StartPosition;
|
||||
textArea.SelectionManager.RemoveSelectedText();
|
||||
}
|
||||
if (fullLine) {
|
||||
int col = textArea.Caret.Column;
|
||||
textArea.Caret.Column = 0;
|
||||
if (!textArea.IsReadOnly(textArea.Caret.Offset))
|
||||
textArea.InsertString(text);
|
||||
textArea.Caret.Column = col;
|
||||
} else {
|
||||
// textArea.EnableCutOrPaste already checked readonly for this case
|
||||
textArea.InsertString(text);
|
||||
}
|
||||
} finally {
|
||||
textArea.Document.UndoStack.EndUndoGroup();
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
} catch (ExternalException) {
|
||||
// GetDataObject does not provide RetryTimes parameter
|
||||
if (i > 5) throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete(object sender, EventArgs e)
|
||||
{
|
||||
new ICSharpCode.TextEditor.Actions.Delete().Execute(textArea);
|
||||
}
|
||||
|
||||
public void SelectAll(object sender, EventArgs e)
|
||||
{
|
||||
new ICSharpCode.TextEditor.Actions.SelectWholeDocument().Execute(textArea);
|
||||
}
|
||||
|
||||
protected virtual void OnCopyText(CopyTextEventArgs e)
|
||||
{
|
||||
if (CopyText != null) {
|
||||
CopyText(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
public event CopyTextEventHandler CopyText;
|
||||
}
|
||||
|
||||
public delegate void CopyTextEventHandler(object sender, CopyTextEventArgs e);
|
||||
public class CopyTextEventArgs : EventArgs
|
||||
{
|
||||
string text;
|
||||
|
||||
public string Text {
|
||||
get {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
public CopyTextEventArgs(string text)
|
||||
{
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
// <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.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
public class TextAreaDragDropHandler
|
||||
{
|
||||
public static Action<Exception> OnDragDropException = ex => MessageBox.Show(ex.ToString());
|
||||
|
||||
TextArea textArea;
|
||||
|
||||
public void Attach(TextArea textArea)
|
||||
{
|
||||
this.textArea = textArea;
|
||||
textArea.AllowDrop = true;
|
||||
|
||||
textArea.DragEnter += MakeDragEventHandler(OnDragEnter);
|
||||
textArea.DragDrop += MakeDragEventHandler(OnDragDrop);
|
||||
textArea.DragOver += MakeDragEventHandler(OnDragOver);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a drag'n'drop event handler.
|
||||
/// Windows Forms swallows unhandled exceptions during drag'n'drop, so we report them here.
|
||||
/// </summary>
|
||||
static DragEventHandler MakeDragEventHandler(DragEventHandler h)
|
||||
{
|
||||
return (sender, e) => {
|
||||
try {
|
||||
h(sender, e);
|
||||
} catch (Exception ex) {
|
||||
OnDragDropException(ex);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static DragDropEffects GetDragDropEffect(DragEventArgs e)
|
||||
{
|
||||
if ((e.AllowedEffect & DragDropEffects.Move) > 0 &&
|
||||
(e.AllowedEffect & DragDropEffects.Copy) > 0) {
|
||||
return (e.KeyState & 8) > 0 ? DragDropEffects.Copy : DragDropEffects.Move;
|
||||
} else if ((e.AllowedEffect & DragDropEffects.Move) > 0) {
|
||||
return DragDropEffects.Move;
|
||||
} else if ((e.AllowedEffect & DragDropEffects.Copy) > 0) {
|
||||
return DragDropEffects.Copy;
|
||||
}
|
||||
return DragDropEffects.None;
|
||||
}
|
||||
|
||||
protected void OnDragEnter(object sender, DragEventArgs e)
|
||||
{
|
||||
if (e.Data.GetDataPresent(typeof(string))) {
|
||||
e.Effect = GetDragDropEffect(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void InsertString(int offset, string str)
|
||||
{
|
||||
textArea.Document.Insert(offset, str);
|
||||
|
||||
textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.Document,
|
||||
textArea.Document.OffsetToPosition(offset),
|
||||
textArea.Document.OffsetToPosition(offset + str.Length)));
|
||||
textArea.Caret.Position = textArea.Document.OffsetToPosition(offset + str.Length);
|
||||
textArea.Refresh();
|
||||
}
|
||||
|
||||
protected void OnDragDrop(object sender, DragEventArgs e)
|
||||
{
|
||||
Point p = textArea.PointToClient(new Point(e.X, e.Y));
|
||||
|
||||
if (e.Data.GetDataPresent(typeof(string))) {
|
||||
textArea.BeginUpdate();
|
||||
textArea.Document.UndoStack.StartUndoGroup();
|
||||
try {
|
||||
int offset = textArea.Caret.Offset;
|
||||
if (textArea.IsReadOnly(offset)) {
|
||||
// prevent dragging text into readonly section
|
||||
return;
|
||||
}
|
||||
if (e.Data.GetDataPresent(typeof(DefaultSelection))) {
|
||||
ISelection sel = (ISelection)e.Data.GetData(typeof(DefaultSelection));
|
||||
if (sel.ContainsPosition(textArea.Caret.Position)) {
|
||||
return;
|
||||
}
|
||||
if (GetDragDropEffect(e) == DragDropEffects.Move) {
|
||||
if (SelectionManager.SelectionIsReadOnly(textArea.Document, sel)) {
|
||||
// prevent dragging text out of readonly section
|
||||
return;
|
||||
}
|
||||
int len = sel.Length;
|
||||
textArea.Document.Remove(sel.Offset, len);
|
||||
if (sel.Offset < offset) {
|
||||
offset -= len;
|
||||
}
|
||||
}
|
||||
}
|
||||
textArea.SelectionManager.ClearSelection();
|
||||
InsertString(offset, (string)e.Data.GetData(typeof(string)));
|
||||
textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
|
||||
} finally {
|
||||
textArea.Document.UndoStack.EndUndoGroup();
|
||||
textArea.EndUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void OnDragOver(object sender, DragEventArgs e)
|
||||
{
|
||||
if (!textArea.Focused) {
|
||||
textArea.Focus();
|
||||
}
|
||||
|
||||
Point p = textArea.PointToClient(new Point(e.X, e.Y));
|
||||
|
||||
if (textArea.TextView.DrawingPosition.Contains(p.X, p.Y)) {
|
||||
TextLocation realmousepos= textArea.TextView.GetLogicalPosition(p.X - textArea.TextView.DrawingPosition.X,
|
||||
p.Y - textArea.TextView.DrawingPosition.Y);
|
||||
int lineNr = Math.Min(textArea.Document.TotalNumberOfLines - 1, Math.Max(0, realmousepos.Y));
|
||||
|
||||
textArea.Caret.Position = new TextLocation(realmousepos.X, lineNr);
|
||||
textArea.SetDesiredColumn();
|
||||
if (e.Data.GetDataPresent(typeof(string)) && !textArea.IsReadOnly(textArea.Caret.Offset)) {
|
||||
e.Effect = GetDragDropEffect(e);
|
||||
} else {
|
||||
e.Effect = DragDropEffects.None;
|
||||
}
|
||||
} else {
|
||||
e.Effect = DragDropEffects.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
492
ICSharpCode.TextEditor/Project/Src/Gui/TextAreaMouseHandler.cs
Normal file
492
ICSharpCode.TextEditor/Project/Src/Gui/TextAreaMouseHandler.cs
Normal file
@@ -0,0 +1,492 @@
|
||||
// <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.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// This class handles all mouse stuff for a textArea.
|
||||
/// </summary>
|
||||
public class TextAreaMouseHandler
|
||||
{
|
||||
TextArea textArea;
|
||||
bool doubleclick = false;
|
||||
bool clickedOnSelectedText = false;
|
||||
|
||||
MouseButtons button;
|
||||
|
||||
static readonly Point nilPoint = new Point(-1, -1);
|
||||
Point mousedownpos = nilPoint;
|
||||
Point lastmousedownpos = nilPoint;
|
||||
|
||||
bool gotmousedown = false;
|
||||
bool dodragdrop = false;
|
||||
|
||||
public TextAreaMouseHandler(TextArea ttextArea)
|
||||
{
|
||||
textArea = ttextArea;
|
||||
}
|
||||
|
||||
public void Attach()
|
||||
{
|
||||
textArea.Click += new EventHandler(TextAreaClick);
|
||||
textArea.MouseMove += new MouseEventHandler(TextAreaMouseMove);
|
||||
|
||||
textArea.MouseDown += new MouseEventHandler(OnMouseDown);
|
||||
textArea.DoubleClick += new EventHandler(OnDoubleClick);
|
||||
textArea.MouseLeave += new EventHandler(OnMouseLeave);
|
||||
textArea.MouseUp += new MouseEventHandler(OnMouseUp);
|
||||
textArea.LostFocus += new EventHandler(TextAreaLostFocus);
|
||||
textArea.ToolTipRequest += new ToolTipRequestEventHandler(OnToolTipRequest);
|
||||
}
|
||||
|
||||
void OnToolTipRequest(object sender, ToolTipRequestEventArgs e)
|
||||
{
|
||||
if (e.ToolTipShown)
|
||||
return;
|
||||
Point mousepos = e.MousePosition;
|
||||
FoldMarker marker = textArea.TextView.GetFoldMarkerFromPosition(mousepos.X - textArea.TextView.DrawingPosition.X,
|
||||
mousepos.Y - textArea.TextView.DrawingPosition.Y);
|
||||
if (marker != null && marker.IsFolded) {
|
||||
StringBuilder sb = new StringBuilder(marker.InnerText);
|
||||
|
||||
// max 10 lines
|
||||
int endLines = 0;
|
||||
for (int i = 0; i < sb.Length; ++i) {
|
||||
if (sb[i] == '\n') {
|
||||
++endLines;
|
||||
if (endLines >= 10) {
|
||||
sb.Remove(i + 1, sb.Length - i - 1);
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("...");
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.Replace("\t", " ");
|
||||
e.ShowToolTip(sb.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
List<TextMarker> markers = textArea.Document.MarkerStrategy.GetMarkers(e.LogicalPosition);
|
||||
foreach (TextMarker tm in markers) {
|
||||
if (tm.ToolTip != null) {
|
||||
e.ShowToolTip(tm.ToolTip.Replace("\t", " "));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShowHiddenCursorIfMovedOrLeft()
|
||||
{
|
||||
textArea.ShowHiddenCursor(!textArea.Focused ||
|
||||
!textArea.ClientRectangle.Contains(textArea.PointToClient(Cursor.Position)));
|
||||
}
|
||||
|
||||
void TextAreaLostFocus(object sender, EventArgs e)
|
||||
{
|
||||
// The call to ShowHiddenCursorIfMovedOrLeft is delayed
|
||||
// until pending messages have been processed
|
||||
// so that it can properly detect whether the TextArea
|
||||
// has really lost focus.
|
||||
// For example, the CodeCompletionWindow gets focus when it is shown,
|
||||
// but immediately gives back focus to the TextArea.
|
||||
textArea.BeginInvoke(new MethodInvoker(ShowHiddenCursorIfMovedOrLeft));
|
||||
}
|
||||
|
||||
void OnMouseLeave(object sender, EventArgs e)
|
||||
{
|
||||
ShowHiddenCursorIfMovedOrLeft();
|
||||
gotmousedown = false;
|
||||
mousedownpos = nilPoint;
|
||||
}
|
||||
|
||||
void OnMouseUp(object sender, MouseEventArgs e)
|
||||
{
|
||||
textArea.SelectionManager.selectFrom.where = WhereFrom.None;
|
||||
gotmousedown = false;
|
||||
mousedownpos = nilPoint;
|
||||
}
|
||||
|
||||
void TextAreaClick(object sender, EventArgs e)
|
||||
{
|
||||
Point mousepos;
|
||||
mousepos = textArea.mousepos;
|
||||
|
||||
if (dodragdrop)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (clickedOnSelectedText && textArea.TextView.DrawingPosition.Contains(mousepos.X, mousepos.Y))
|
||||
{
|
||||
textArea.SelectionManager.ClearSelection();
|
||||
|
||||
TextLocation clickPosition = textArea.TextView.GetLogicalPosition(
|
||||
mousepos.X - textArea.TextView.DrawingPosition.X,
|
||||
mousepos.Y - textArea.TextView.DrawingPosition.Y);
|
||||
textArea.Caret.Position = clickPosition;
|
||||
textArea.SetDesiredColumn();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TextAreaMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
textArea.mousepos = e.Location;
|
||||
|
||||
// honour the starting selection strategy
|
||||
switch (textArea.SelectionManager.selectFrom.where)
|
||||
{
|
||||
case WhereFrom.Gutter:
|
||||
ExtendSelectionToMouse();
|
||||
return;
|
||||
|
||||
case WhereFrom.TArea:
|
||||
break;
|
||||
|
||||
}
|
||||
textArea.ShowHiddenCursor(false);
|
||||
if (dodragdrop) {
|
||||
dodragdrop = false;
|
||||
return;
|
||||
}
|
||||
|
||||
doubleclick = false;
|
||||
textArea.mousepos = new Point(e.X, e.Y);
|
||||
|
||||
if (clickedOnSelectedText) {
|
||||
if (Math.Abs(mousedownpos.X - e.X) >= SystemInformation.DragSize.Width / 2 ||
|
||||
Math.Abs(mousedownpos.Y - e.Y) >= SystemInformation.DragSize.Height / 2)
|
||||
{
|
||||
clickedOnSelectedText = false;
|
||||
ISelection selection = textArea.SelectionManager.GetSelectionAt(textArea.Caret.Offset);
|
||||
if (selection != null) {
|
||||
string text = selection.SelectedText;
|
||||
bool isReadOnly = SelectionManager.SelectionIsReadOnly(textArea.Document, selection);
|
||||
if (text != null && text.Length > 0) {
|
||||
DataObject dataObject = new DataObject ();
|
||||
dataObject.SetData(DataFormats.UnicodeText, true, text);
|
||||
dataObject.SetData(selection);
|
||||
dodragdrop = true;
|
||||
textArea.DoDragDrop(dataObject, isReadOnly ? DragDropEffects.All & ~DragDropEffects.Move : DragDropEffects.All);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Button == MouseButtons.Left) {
|
||||
if (gotmousedown && textArea.SelectionManager.selectFrom.where == WhereFrom.TArea)
|
||||
{
|
||||
ExtendSelectionToMouse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExtendSelectionToMouse()
|
||||
{
|
||||
Point mousepos;
|
||||
mousepos = textArea.mousepos;
|
||||
TextLocation realmousepos = textArea.TextView.GetLogicalPosition(
|
||||
Math.Max(0, mousepos.X - textArea.TextView.DrawingPosition.X),
|
||||
mousepos.Y - textArea.TextView.DrawingPosition.Y);
|
||||
int y = realmousepos.Y;
|
||||
realmousepos = textArea.Caret.ValidatePosition(realmousepos);
|
||||
TextLocation oldPos = textArea.Caret.Position;
|
||||
if (oldPos == realmousepos && textArea.SelectionManager.selectFrom.where != WhereFrom.Gutter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// the selection is from the gutter
|
||||
if (textArea.SelectionManager.selectFrom.where == WhereFrom.Gutter) {
|
||||
if(realmousepos.Y < textArea.SelectionManager.SelectionStart.Y) {
|
||||
// the selection has moved above the startpoint
|
||||
textArea.Caret.Position = new TextLocation(0, realmousepos.Y);
|
||||
} else {
|
||||
// the selection has moved below the startpoint
|
||||
textArea.Caret.Position = textArea.SelectionManager.NextValidPosition(realmousepos.Y);
|
||||
}
|
||||
} else {
|
||||
textArea.Caret.Position = realmousepos;
|
||||
}
|
||||
|
||||
// moves selection across whole words for double-click initiated selection
|
||||
if (!minSelection.IsEmpty && textArea.SelectionManager.SelectionCollection.Count > 0 && textArea.SelectionManager.selectFrom.where == WhereFrom.TArea) {
|
||||
// Extend selection when selection was started with double-click
|
||||
ISelection selection = textArea.SelectionManager.SelectionCollection[0];
|
||||
TextLocation min = textArea.SelectionManager.GreaterEqPos(minSelection, maxSelection) ? maxSelection : minSelection;
|
||||
TextLocation max = textArea.SelectionManager.GreaterEqPos(minSelection, maxSelection) ? minSelection : maxSelection;
|
||||
if (textArea.SelectionManager.GreaterEqPos(max, realmousepos) && textArea.SelectionManager.GreaterEqPos(realmousepos, min)) {
|
||||
textArea.SelectionManager.SetSelection(min, max);
|
||||
} else if (textArea.SelectionManager.GreaterEqPos(max, realmousepos)) {
|
||||
int moff = textArea.Document.PositionToOffset(realmousepos);
|
||||
min = textArea.Document.OffsetToPosition(FindWordStart(textArea.Document, moff));
|
||||
textArea.SelectionManager.SetSelection(min, max);
|
||||
} else {
|
||||
int moff = textArea.Document.PositionToOffset(realmousepos);
|
||||
max = textArea.Document.OffsetToPosition(FindWordEnd(textArea.Document, moff));
|
||||
textArea.SelectionManager.SetSelection(min, max);
|
||||
}
|
||||
} else {
|
||||
textArea.SelectionManager.ExtendSelection(oldPos, textArea.Caret.Position);
|
||||
}
|
||||
textArea.SetDesiredColumn();
|
||||
}
|
||||
|
||||
void DoubleClickSelectionExtend()
|
||||
{
|
||||
Point mousepos;
|
||||
mousepos = textArea.mousepos;
|
||||
|
||||
textArea.SelectionManager.ClearSelection();
|
||||
if (textArea.TextView.DrawingPosition.Contains(mousepos.X, mousepos.Y))
|
||||
{
|
||||
FoldMarker marker = textArea.TextView.GetFoldMarkerFromPosition(mousepos.X - textArea.TextView.DrawingPosition.X,
|
||||
mousepos.Y - textArea.TextView.DrawingPosition.Y);
|
||||
if (marker != null && marker.IsFolded) {
|
||||
marker.IsFolded = false;
|
||||
textArea.MotherTextAreaControl.AdjustScrollBars();
|
||||
}
|
||||
if (textArea.Caret.Offset < textArea.Document.TextLength) {
|
||||
switch (textArea.Document.GetCharAt(textArea.Caret.Offset)) {
|
||||
case '"':
|
||||
if (textArea.Caret.Offset < textArea.Document.TextLength) {
|
||||
int next = FindNext(textArea.Document, textArea.Caret.Offset + 1, '"');
|
||||
minSelection = textArea.Caret.Position;
|
||||
if (next > textArea.Caret.Offset && next < textArea.Document.TextLength)
|
||||
next += 1;
|
||||
maxSelection = textArea.Document.OffsetToPosition(next);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
minSelection = textArea.Document.OffsetToPosition(FindWordStart(textArea.Document, textArea.Caret.Offset));
|
||||
maxSelection = textArea.Document.OffsetToPosition(FindWordEnd(textArea.Document, textArea.Caret.Offset));
|
||||
break;
|
||||
|
||||
}
|
||||
textArea.Caret.Position = maxSelection;
|
||||
textArea.SelectionManager.ExtendSelection(minSelection, maxSelection);
|
||||
}
|
||||
|
||||
if (textArea.SelectionManager.selectionCollection.Count > 0) {
|
||||
ISelection selection = textArea.SelectionManager.selectionCollection[0];
|
||||
|
||||
selection.StartPosition = minSelection;
|
||||
selection.EndPosition = maxSelection;
|
||||
textArea.SelectionManager.SelectionStart = minSelection;
|
||||
}
|
||||
|
||||
// after a double-click selection, the caret is placed correctly,
|
||||
// but it is not positioned internally. The effect is when the cursor
|
||||
// is moved up or down a line, the caret will take on the column first
|
||||
// clicked on for the double-click
|
||||
textArea.SetDesiredColumn();
|
||||
|
||||
// HACK WARNING !!!
|
||||
// must refresh here, because when a error tooltip is showed and the underlined
|
||||
// code is double clicked the textArea don't update corrctly, updateline doesn't
|
||||
// work ... but the refresh does.
|
||||
// Mike
|
||||
textArea.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void OnMouseDown(object sender, MouseEventArgs e)
|
||||
{
|
||||
Point mousepos;
|
||||
textArea.mousepos = e.Location;
|
||||
mousepos = e.Location;
|
||||
|
||||
if (dodragdrop)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (doubleclick) {
|
||||
doubleclick = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (textArea.TextView.DrawingPosition.Contains(mousepos.X, mousepos.Y)) {
|
||||
gotmousedown = true;
|
||||
textArea.SelectionManager.selectFrom.where = WhereFrom.TArea;
|
||||
button = e.Button;
|
||||
|
||||
// double-click
|
||||
if (button == MouseButtons.Left && e.Clicks == 2) {
|
||||
int deltaX = Math.Abs(lastmousedownpos.X - e.X);
|
||||
int deltaY = Math.Abs(lastmousedownpos.Y - e.Y);
|
||||
if (deltaX <= SystemInformation.DoubleClickSize.Width &&
|
||||
deltaY <= SystemInformation.DoubleClickSize.Height) {
|
||||
DoubleClickSelectionExtend();
|
||||
lastmousedownpos = new Point(e.X, e.Y);
|
||||
|
||||
if (textArea.SelectionManager.selectFrom.where == WhereFrom.Gutter) {
|
||||
if (!minSelection.IsEmpty && !maxSelection.IsEmpty && textArea.SelectionManager.SelectionCollection.Count > 0) {
|
||||
textArea.SelectionManager.SelectionCollection[0].StartPosition = minSelection;
|
||||
textArea.SelectionManager.SelectionCollection[0].EndPosition = maxSelection;
|
||||
textArea.SelectionManager.SelectionStart = minSelection;
|
||||
|
||||
minSelection = TextLocation.Empty;
|
||||
maxSelection = TextLocation.Empty;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
minSelection = TextLocation.Empty;
|
||||
maxSelection = TextLocation.Empty;
|
||||
|
||||
lastmousedownpos = mousedownpos = new Point(e.X, e.Y);
|
||||
|
||||
if (button == MouseButtons.Left) {
|
||||
FoldMarker marker = textArea.TextView.GetFoldMarkerFromPosition(mousepos.X - textArea.TextView.DrawingPosition.X,
|
||||
mousepos.Y - textArea.TextView.DrawingPosition.Y);
|
||||
if (marker != null && marker.IsFolded) {
|
||||
if (textArea.SelectionManager.HasSomethingSelected) {
|
||||
clickedOnSelectedText = true;
|
||||
}
|
||||
|
||||
TextLocation startLocation = new TextLocation(marker.StartColumn, marker.StartLine);
|
||||
TextLocation endLocation = new TextLocation(marker.EndColumn, marker.EndLine);
|
||||
textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.TextView.Document, startLocation, endLocation));
|
||||
textArea.Caret.Position = startLocation;
|
||||
textArea.SetDesiredColumn();
|
||||
textArea.Focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift) {
|
||||
ExtendSelectionToMouse();
|
||||
} else {
|
||||
TextLocation realmousepos = textArea.TextView.GetLogicalPosition(mousepos.X - textArea.TextView.DrawingPosition.X, mousepos.Y - textArea.TextView.DrawingPosition.Y);
|
||||
clickedOnSelectedText = false;
|
||||
|
||||
int offset = textArea.Document.PositionToOffset(realmousepos);
|
||||
|
||||
if (textArea.SelectionManager.HasSomethingSelected &&
|
||||
textArea.SelectionManager.IsSelected(offset)) {
|
||||
clickedOnSelectedText = true;
|
||||
} else {
|
||||
textArea.SelectionManager.ClearSelection();
|
||||
if (mousepos.Y > 0 && mousepos.Y < textArea.TextView.DrawingPosition.Height) {
|
||||
TextLocation pos = new TextLocation();
|
||||
pos.Y = Math.Min(textArea.Document.TotalNumberOfLines - 1, realmousepos.Y);
|
||||
pos.X = realmousepos.X;
|
||||
textArea.Caret.Position = pos;
|
||||
textArea.SetDesiredColumn();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (button == MouseButtons.Right) {
|
||||
// Rightclick sets the cursor to the click position unless
|
||||
// the previous selection was clicked
|
||||
TextLocation realmousepos = textArea.TextView.GetLogicalPosition(mousepos.X - textArea.TextView.DrawingPosition.X, mousepos.Y - textArea.TextView.DrawingPosition.Y);
|
||||
int offset = textArea.Document.PositionToOffset(realmousepos);
|
||||
if (!textArea.SelectionManager.HasSomethingSelected ||
|
||||
!textArea.SelectionManager.IsSelected(offset))
|
||||
{
|
||||
textArea.SelectionManager.ClearSelection();
|
||||
if (mousepos.Y > 0 && mousepos.Y < textArea.TextView.DrawingPosition.Height) {
|
||||
TextLocation pos = new TextLocation();
|
||||
pos.Y = Math.Min(textArea.Document.TotalNumberOfLines - 1, realmousepos.Y);
|
||||
pos.X = realmousepos.X;
|
||||
textArea.Caret.Position = pos;
|
||||
textArea.SetDesiredColumn();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
textArea.Focus();
|
||||
}
|
||||
|
||||
int FindNext(IDocument document, int offset, char ch)
|
||||
{
|
||||
LineSegment line = document.GetLineSegmentForOffset(offset);
|
||||
int endPos = line.Offset + line.Length;
|
||||
|
||||
while (offset < endPos && document.GetCharAt(offset) != ch) {
|
||||
++offset;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
bool IsSelectableChar(char ch)
|
||||
{
|
||||
return char.IsLetterOrDigit(ch) || ch=='_';
|
||||
}
|
||||
|
||||
int FindWordStart(IDocument document, int offset)
|
||||
{
|
||||
LineSegment line = document.GetLineSegmentForOffset(offset);
|
||||
|
||||
if (offset > 0 && char.IsWhiteSpace(document.GetCharAt(offset - 1)) && char.IsWhiteSpace(document.GetCharAt(offset))) {
|
||||
while (offset > line.Offset && char.IsWhiteSpace(document.GetCharAt(offset - 1))) {
|
||||
--offset;
|
||||
}
|
||||
} else if (IsSelectableChar(document.GetCharAt(offset)) || (offset > 0 && char.IsWhiteSpace(document.GetCharAt(offset)) && IsSelectableChar(document.GetCharAt(offset - 1)))) {
|
||||
while (offset > line.Offset && IsSelectableChar(document.GetCharAt(offset - 1))) {
|
||||
--offset;
|
||||
}
|
||||
} else {
|
||||
if (offset > 0 && !char.IsWhiteSpace(document.GetCharAt(offset - 1)) && !IsSelectableChar(document.GetCharAt(offset - 1)) ) {
|
||||
return Math.Max(0, offset - 1);
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
int FindWordEnd(IDocument document, int offset)
|
||||
{
|
||||
LineSegment line = document.GetLineSegmentForOffset(offset);
|
||||
if (line.Length == 0)
|
||||
return offset;
|
||||
int endPos = line.Offset + line.Length;
|
||||
offset = Math.Min(offset, endPos - 1);
|
||||
|
||||
if (IsSelectableChar(document.GetCharAt(offset))) {
|
||||
while (offset < endPos && IsSelectableChar(document.GetCharAt(offset))) {
|
||||
++offset;
|
||||
}
|
||||
} else if (char.IsWhiteSpace(document.GetCharAt(offset))) {
|
||||
if (offset > 0 && char.IsWhiteSpace(document.GetCharAt(offset - 1))) {
|
||||
while (offset < endPos && char.IsWhiteSpace(document.GetCharAt(offset))) {
|
||||
++offset;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Math.Max(0, offset + 1);
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
TextLocation minSelection = TextLocation.Empty;
|
||||
TextLocation maxSelection = TextLocation.Empty;
|
||||
|
||||
void OnDoubleClick(object sender, System.EventArgs e)
|
||||
{
|
||||
if (dodragdrop) {
|
||||
return;
|
||||
}
|
||||
|
||||
textArea.SelectionManager.selectFrom.where = WhereFrom.TArea;
|
||||
doubleclick = true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
85
ICSharpCode.TextEditor/Project/Src/Gui/TextAreaUpdate.cs
Normal file
85
ICSharpCode.TextEditor/Project/Src/Gui/TextAreaUpdate.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
// <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.Drawing;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// This enum describes all implemented request types
|
||||
/// </summary>
|
||||
public enum TextAreaUpdateType {
|
||||
WholeTextArea,
|
||||
SingleLine,
|
||||
SinglePosition,
|
||||
PositionToLineEnd,
|
||||
PositionToEnd,
|
||||
LinesBetween
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class is used to request an update of the textarea
|
||||
/// </summary>
|
||||
public class TextAreaUpdate
|
||||
{
|
||||
TextLocation position;
|
||||
TextAreaUpdateType type;
|
||||
|
||||
public TextAreaUpdateType TextAreaUpdateType {
|
||||
get {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
public TextLocation Position {
|
||||
get {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="TextAreaUpdate"/>
|
||||
/// </summary>
|
||||
public TextAreaUpdate(TextAreaUpdateType type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="TextAreaUpdate"/>
|
||||
/// </summary>
|
||||
public TextAreaUpdate(TextAreaUpdateType type, TextLocation position)
|
||||
{
|
||||
this.type = type;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="TextAreaUpdate"/>
|
||||
/// </summary>
|
||||
public TextAreaUpdate(TextAreaUpdateType type, int startLine, int endLine)
|
||||
{
|
||||
this.type = type;
|
||||
this.position = new TextLocation(startLine, endLine);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="TextAreaUpdate"/>
|
||||
/// </summary>
|
||||
public TextAreaUpdate(TextAreaUpdateType type, int singleLine)
|
||||
{
|
||||
this.type = type;
|
||||
this.position = new TextLocation(0, singleLine);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[TextAreaUpdate: Type={0}, Position={1}]", type, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
396
ICSharpCode.TextEditor/Project/Src/Gui/TextEditorControl.cs
Normal file
396
ICSharpCode.TextEditor/Project/Src/Gui/TextEditorControl.cs
Normal file
@@ -0,0 +1,396 @@
|
||||
// <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.Drawing.Printing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is used for a basic text area control
|
||||
/// </summary>
|
||||
[ToolboxBitmap("ICSharpCode.TextEditor.Resources.TextEditorControl.bmp")]
|
||||
[ToolboxItem(true)]
|
||||
public class TextEditorControl : TextEditorControlBase
|
||||
{
|
||||
protected Panel textAreaPanel = new Panel();
|
||||
TextAreaControl primaryTextArea;
|
||||
Splitter textAreaSplitter = null;
|
||||
TextAreaControl secondaryTextArea = null;
|
||||
|
||||
PrintDocument printDocument = null;
|
||||
|
||||
[Browsable(false)]
|
||||
public PrintDocument PrintDocument {
|
||||
get {
|
||||
if (printDocument == null) {
|
||||
printDocument = new PrintDocument();
|
||||
printDocument.BeginPrint += new PrintEventHandler(this.BeginPrint);
|
||||
printDocument.PrintPage += new PrintPageEventHandler(this.PrintPage);
|
||||
}
|
||||
return printDocument;
|
||||
}
|
||||
}
|
||||
|
||||
TextAreaControl activeTextAreaControl;
|
||||
|
||||
public override TextAreaControl ActiveTextAreaControl {
|
||||
get {
|
||||
return activeTextAreaControl;
|
||||
}
|
||||
}
|
||||
|
||||
protected void SetActiveTextAreaControl(TextAreaControl value)
|
||||
{
|
||||
if (activeTextAreaControl != value) {
|
||||
activeTextAreaControl = value;
|
||||
|
||||
if (ActiveTextAreaControlChanged != null) {
|
||||
ActiveTextAreaControlChanged(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler ActiveTextAreaControlChanged;
|
||||
|
||||
public TextEditorControl()
|
||||
{
|
||||
SetStyle(ControlStyles.ContainerControl, true);
|
||||
|
||||
textAreaPanel.Dock = DockStyle.Fill;
|
||||
|
||||
Document = (new DocumentFactory()).CreateDocument();
|
||||
Document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy();
|
||||
|
||||
primaryTextArea = new TextAreaControl(this);
|
||||
activeTextAreaControl = primaryTextArea;
|
||||
primaryTextArea.TextArea.GotFocus += delegate {
|
||||
SetActiveTextAreaControl(primaryTextArea);
|
||||
};
|
||||
primaryTextArea.Dock = DockStyle.Fill;
|
||||
textAreaPanel.Controls.Add(primaryTextArea);
|
||||
InitializeTextAreaControl(primaryTextArea);
|
||||
Controls.Add(textAreaPanel);
|
||||
ResizeRedraw = true;
|
||||
Document.UpdateCommited += new EventHandler(CommitUpdateRequested);
|
||||
OptionsChanged();
|
||||
}
|
||||
|
||||
protected virtual void InitializeTextAreaControl(TextAreaControl newControl)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OptionsChanged()
|
||||
{
|
||||
primaryTextArea.OptionsChanged();
|
||||
if (secondaryTextArea != null) {
|
||||
secondaryTextArea.OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void Split()
|
||||
{
|
||||
if (secondaryTextArea == null) {
|
||||
secondaryTextArea = new TextAreaControl(this);
|
||||
secondaryTextArea.Dock = DockStyle.Bottom;
|
||||
secondaryTextArea.Height = Height / 2;
|
||||
|
||||
secondaryTextArea.TextArea.GotFocus += delegate {
|
||||
SetActiveTextAreaControl(secondaryTextArea);
|
||||
};
|
||||
|
||||
textAreaSplitter = new Splitter();
|
||||
textAreaSplitter.BorderStyle = BorderStyle.FixedSingle ;
|
||||
textAreaSplitter.Height = 8;
|
||||
textAreaSplitter.Dock = DockStyle.Bottom;
|
||||
textAreaPanel.Controls.Add(textAreaSplitter);
|
||||
textAreaPanel.Controls.Add(secondaryTextArea);
|
||||
InitializeTextAreaControl(secondaryTextArea);
|
||||
secondaryTextArea.OptionsChanged();
|
||||
} else {
|
||||
SetActiveTextAreaControl(primaryTextArea);
|
||||
|
||||
textAreaPanel.Controls.Remove(secondaryTextArea);
|
||||
textAreaPanel.Controls.Remove(textAreaSplitter);
|
||||
|
||||
secondaryTextArea.Dispose();
|
||||
textAreaSplitter.Dispose();
|
||||
secondaryTextArea = null;
|
||||
textAreaSplitter = null;
|
||||
}
|
||||
}
|
||||
|
||||
[Browsable(false)]
|
||||
public bool EnableUndo {
|
||||
get {
|
||||
return Document.UndoStack.CanUndo;
|
||||
}
|
||||
}
|
||||
|
||||
[Browsable(false)]
|
||||
public bool EnableRedo {
|
||||
get {
|
||||
return Document.UndoStack.CanRedo;
|
||||
}
|
||||
}
|
||||
|
||||
public void Undo()
|
||||
{
|
||||
if (Document.ReadOnly) {
|
||||
return;
|
||||
}
|
||||
if (Document.UndoStack.CanUndo) {
|
||||
BeginUpdate();
|
||||
Document.UndoStack.Undo();
|
||||
|
||||
Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
|
||||
this.primaryTextArea.TextArea.UpdateMatchingBracket();
|
||||
if (secondaryTextArea != null) {
|
||||
this.secondaryTextArea.TextArea.UpdateMatchingBracket();
|
||||
}
|
||||
EndUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public void Redo()
|
||||
{
|
||||
if (Document.ReadOnly) {
|
||||
return;
|
||||
}
|
||||
if (Document.UndoStack.CanRedo) {
|
||||
BeginUpdate();
|
||||
Document.UndoStack.Redo();
|
||||
|
||||
Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
|
||||
this.primaryTextArea.TextArea.UpdateMatchingBracket();
|
||||
if (secondaryTextArea != null) {
|
||||
this.secondaryTextArea.TextArea.UpdateMatchingBracket();
|
||||
}
|
||||
EndUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SetHighlighting(string name)
|
||||
{
|
||||
Document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy(name);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing) {
|
||||
if (printDocument != null) {
|
||||
printDocument.BeginPrint -= new PrintEventHandler(this.BeginPrint);
|
||||
printDocument.PrintPage -= new PrintPageEventHandler(this.PrintPage);
|
||||
printDocument = null;
|
||||
}
|
||||
Document.UndoStack.ClearAll();
|
||||
Document.UpdateCommited -= new EventHandler(CommitUpdateRequested);
|
||||
if (textAreaPanel != null) {
|
||||
if (secondaryTextArea != null) {
|
||||
secondaryTextArea.Dispose();
|
||||
textAreaSplitter.Dispose();
|
||||
secondaryTextArea = null;
|
||||
textAreaSplitter = null;
|
||||
}
|
||||
if (primaryTextArea != null) {
|
||||
primaryTextArea.Dispose();
|
||||
}
|
||||
textAreaPanel.Dispose();
|
||||
textAreaPanel = null;
|
||||
}
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Update Methods
|
||||
public override void EndUpdate()
|
||||
{
|
||||
base.EndUpdate();
|
||||
Document.CommitUpdate();
|
||||
if (!IsInUpdate) {
|
||||
ActiveTextAreaControl.Caret.OnEndUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void CommitUpdateRequested(object sender, EventArgs e)
|
||||
{
|
||||
if (IsInUpdate) {
|
||||
return;
|
||||
}
|
||||
foreach (TextAreaUpdate update in Document.UpdateQueue) {
|
||||
switch (update.TextAreaUpdateType) {
|
||||
case TextAreaUpdateType.PositionToEnd:
|
||||
this.primaryTextArea.TextArea.UpdateToEnd(update.Position.Y);
|
||||
if (this.secondaryTextArea != null) {
|
||||
this.secondaryTextArea.TextArea.UpdateToEnd(update.Position.Y);
|
||||
}
|
||||
break;
|
||||
case TextAreaUpdateType.PositionToLineEnd:
|
||||
case TextAreaUpdateType.SingleLine:
|
||||
this.primaryTextArea.TextArea.UpdateLine(update.Position.Y);
|
||||
if (this.secondaryTextArea != null) {
|
||||
this.secondaryTextArea.TextArea.UpdateLine(update.Position.Y);
|
||||
}
|
||||
break;
|
||||
case TextAreaUpdateType.SinglePosition:
|
||||
this.primaryTextArea.TextArea.UpdateLine(update.Position.Y, update.Position.X, update.Position.X);
|
||||
if (this.secondaryTextArea != null) {
|
||||
this.secondaryTextArea.TextArea.UpdateLine(update.Position.Y, update.Position.X, update.Position.X);
|
||||
}
|
||||
break;
|
||||
case TextAreaUpdateType.LinesBetween:
|
||||
this.primaryTextArea.TextArea.UpdateLines(update.Position.X, update.Position.Y);
|
||||
if (this.secondaryTextArea != null) {
|
||||
this.secondaryTextArea.TextArea.UpdateLines(update.Position.X, update.Position.Y);
|
||||
}
|
||||
break;
|
||||
case TextAreaUpdateType.WholeTextArea:
|
||||
this.primaryTextArea.TextArea.Invalidate();
|
||||
if (this.secondaryTextArea != null) {
|
||||
this.secondaryTextArea.TextArea.Invalidate();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
Document.UpdateQueue.Clear();
|
||||
// this.primaryTextArea.TextArea.Update();
|
||||
// if (this.secondaryTextArea != null) {
|
||||
// this.secondaryTextArea.TextArea.Update();
|
||||
// }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Printing routines
|
||||
int curLineNr = 0;
|
||||
float curTabIndent = 0;
|
||||
StringFormat printingStringFormat;
|
||||
|
||||
void BeginPrint(object sender, PrintEventArgs ev)
|
||||
{
|
||||
curLineNr = 0;
|
||||
printingStringFormat = (StringFormat)System.Drawing.StringFormat.GenericTypographic.Clone();
|
||||
|
||||
// 100 should be enough for everyone ...err ?
|
||||
float[] tabStops = new float[100];
|
||||
for (int i = 0; i < tabStops.Length; ++i) {
|
||||
tabStops[i] = TabIndent * primaryTextArea.TextArea.TextView.WideSpaceWidth;
|
||||
}
|
||||
|
||||
printingStringFormat.SetTabStops(0, tabStops);
|
||||
}
|
||||
|
||||
void Advance(ref float x, ref float y, float maxWidth, float size, float fontHeight)
|
||||
{
|
||||
if (x + size < maxWidth) {
|
||||
x += size;
|
||||
} else {
|
||||
x = curTabIndent;
|
||||
y += fontHeight;
|
||||
}
|
||||
}
|
||||
|
||||
// btw. I hate source code duplication ... but this time I don't care !!!!
|
||||
float MeasurePrintingHeight(Graphics g, LineSegment line, float maxWidth)
|
||||
{
|
||||
float xPos = 0;
|
||||
float yPos = 0;
|
||||
float fontHeight = Font.GetHeight(g);
|
||||
// bool gotNonWhitespace = false;
|
||||
curTabIndent = 0;
|
||||
FontContainer fontContainer = TextEditorProperties.FontContainer;
|
||||
foreach (TextWord word in line.Words) {
|
||||
switch (word.Type) {
|
||||
case TextWordType.Space:
|
||||
Advance(ref xPos, ref yPos, maxWidth, primaryTextArea.TextArea.TextView.SpaceWidth, fontHeight);
|
||||
// if (!gotNonWhitespace) {
|
||||
// curTabIndent = xPos;
|
||||
// }
|
||||
break;
|
||||
case TextWordType.Tab:
|
||||
Advance(ref xPos, ref yPos, maxWidth, TabIndent * primaryTextArea.TextArea.TextView.WideSpaceWidth, fontHeight);
|
||||
// if (!gotNonWhitespace) {
|
||||
// curTabIndent = xPos;
|
||||
// }
|
||||
break;
|
||||
case TextWordType.Word:
|
||||
// if (!gotNonWhitespace) {
|
||||
// gotNonWhitespace = true;
|
||||
// curTabIndent += TabIndent * primaryTextArea.TextArea.TextView.GetWidth(' ');
|
||||
// }
|
||||
SizeF drawingSize = g.MeasureString(word.Word, word.GetFont(fontContainer), new SizeF(maxWidth, fontHeight * 100), printingStringFormat);
|
||||
Advance(ref xPos, ref yPos, maxWidth, drawingSize.Width, fontHeight);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return yPos + fontHeight;
|
||||
}
|
||||
|
||||
void DrawLine(Graphics g, LineSegment line, float yPos, RectangleF margin)
|
||||
{
|
||||
float xPos = 0;
|
||||
float fontHeight = Font.GetHeight(g);
|
||||
// bool gotNonWhitespace = false;
|
||||
curTabIndent = 0 ;
|
||||
|
||||
FontContainer fontContainer = TextEditorProperties.FontContainer;
|
||||
foreach (TextWord word in line.Words) {
|
||||
switch (word.Type) {
|
||||
case TextWordType.Space:
|
||||
Advance(ref xPos, ref yPos, margin.Width, primaryTextArea.TextArea.TextView.SpaceWidth, fontHeight);
|
||||
// if (!gotNonWhitespace) {
|
||||
// curTabIndent = xPos;
|
||||
// }
|
||||
break;
|
||||
case TextWordType.Tab:
|
||||
Advance(ref xPos, ref yPos, margin.Width, TabIndent * primaryTextArea.TextArea.TextView.WideSpaceWidth, fontHeight);
|
||||
// if (!gotNonWhitespace) {
|
||||
// curTabIndent = xPos;
|
||||
// }
|
||||
break;
|
||||
case TextWordType.Word:
|
||||
// if (!gotNonWhitespace) {
|
||||
// gotNonWhitespace = true;
|
||||
// curTabIndent += TabIndent * primaryTextArea.TextArea.TextView.GetWidth(' ');
|
||||
// }
|
||||
g.DrawString(word.Word, word.GetFont(fontContainer), BrushRegistry.GetBrush(word.Color), xPos + margin.X, yPos);
|
||||
SizeF drawingSize = g.MeasureString(word.Word, word.GetFont(fontContainer), new SizeF(margin.Width, fontHeight * 100), printingStringFormat);
|
||||
Advance(ref xPos, ref yPos, margin.Width, drawingSize.Width, fontHeight);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintPage(object sender, PrintPageEventArgs ev)
|
||||
{
|
||||
Graphics g = ev.Graphics;
|
||||
float yPos = ev.MarginBounds.Top;
|
||||
|
||||
while (curLineNr < Document.TotalNumberOfLines) {
|
||||
LineSegment curLine = Document.GetLineSegment(curLineNr);
|
||||
if (curLine.Words != null) {
|
||||
float drawingHeight = MeasurePrintingHeight(g, curLine, ev.MarginBounds.Width);
|
||||
if (drawingHeight + yPos > ev.MarginBounds.Bottom) {
|
||||
break;
|
||||
}
|
||||
|
||||
DrawLine(g, curLine, yPos, ev.MarginBounds);
|
||||
yPos += drawingHeight;
|
||||
}
|
||||
++curLineNr;
|
||||
}
|
||||
|
||||
// If more lines exist, print another page.
|
||||
ev.HasMorePages = curLineNr < Document.TotalNumberOfLines;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
759
ICSharpCode.TextEditor/Project/Src/Gui/TextEditorControlBase.cs
Normal file
759
ICSharpCode.TextEditor/Project/Src/Gui/TextEditorControlBase.cs
Normal file
@@ -0,0 +1,759 @@
|
||||
// <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.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Text;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using ICSharpCode.TextEditor.Actions;
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is used for a basic text area control
|
||||
/// </summary>
|
||||
[ToolboxItem(false)]
|
||||
public abstract class TextEditorControlBase : UserControl
|
||||
{
|
||||
string currentFileName = null;
|
||||
int updateLevel = 0;
|
||||
protected IDocument document; // Stef Heyenrath : Changed to protected
|
||||
|
||||
/// <summary>
|
||||
/// This hashtable contains all editor keys, where
|
||||
/// the key is the key combination and the value the
|
||||
/// action.
|
||||
/// </summary>
|
||||
protected Dictionary<Keys, IEditAction> editactions = new Dictionary<Keys, IEditAction>();
|
||||
|
||||
[Browsable(false)]
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public ITextEditorProperties TextEditorProperties {
|
||||
get {
|
||||
return document.TextEditorProperties;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
Encoding encoding;
|
||||
|
||||
/// <value>
|
||||
/// Current file's character encoding
|
||||
/// </value>
|
||||
[Browsable(false)]
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public Encoding Encoding {
|
||||
get {
|
||||
if (encoding == null)
|
||||
return TextEditorProperties.Encoding;
|
||||
return encoding;
|
||||
}
|
||||
set {
|
||||
encoding = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// The current file name
|
||||
/// </value>
|
||||
[Browsable(false)]
|
||||
[ReadOnly(true)]
|
||||
public string FileName {
|
||||
get {
|
||||
return currentFileName;
|
||||
}
|
||||
set {
|
||||
if (currentFileName != value) {
|
||||
currentFileName = value;
|
||||
OnFileNameChanged(EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// The current document
|
||||
/// </value>
|
||||
[Browsable(false)]
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public IDocument Document {
|
||||
get {
|
||||
return document;
|
||||
}
|
||||
set {
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
if (document != null) {
|
||||
document.DocumentChanged -= OnDocumentChanged;
|
||||
}
|
||||
document = value;
|
||||
document.UndoStack.TextEditorControl = this;
|
||||
document.DocumentChanged += OnDocumentChanged;
|
||||
}
|
||||
}
|
||||
|
||||
protected void OnDocumentChanged(object sender, EventArgs e) // Stef Heyenrath : changed to protected
|
||||
{
|
||||
OnTextChanged(e);
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true)]
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
|
||||
[Editor("System.ComponentModel.Design.MultilineStringEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(System.Drawing.Design.UITypeEditor))]
|
||||
public override string Text {
|
||||
get {
|
||||
return Document.TextContent;
|
||||
}
|
||||
set {
|
||||
Document.TextContent = value;
|
||||
}
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true)]
|
||||
public new event EventHandler TextChanged
|
||||
{
|
||||
add { base.TextChanged += value; }
|
||||
remove { base.TextChanged -= value; }
|
||||
}
|
||||
|
||||
static Font ParseFont(string font)
|
||||
{
|
||||
string[] descr = font.Split(new char[]{',', '='});
|
||||
return new Font(descr[1], float.Parse(descr[3]));
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// If set to true the contents can't be altered.
|
||||
/// </value>
|
||||
[Browsable(false)]
|
||||
public bool IsReadOnly {
|
||||
get {
|
||||
return Document.ReadOnly;
|
||||
}
|
||||
set {
|
||||
Document.ReadOnly = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// true, if the textarea is updating it's status, while
|
||||
/// it updates it status no redraw operation occurs.
|
||||
/// </value>
|
||||
[Browsable(false)]
|
||||
public bool IsInUpdate {
|
||||
get {
|
||||
return updateLevel > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// supposedly this is the way to do it according to .NET docs,
|
||||
/// as opposed to setting the size in the constructor
|
||||
/// </value>
|
||||
protected override Size DefaultSize {
|
||||
get {
|
||||
return new Size(100, 100);
|
||||
}
|
||||
}
|
||||
|
||||
#region Document Properties
|
||||
/// <value>
|
||||
/// If true spaces are shown in the textarea
|
||||
/// </value>
|
||||
[Category("Appearance")]
|
||||
[DefaultValue(false)]
|
||||
[Description("If true spaces are shown in the textarea")]
|
||||
public bool ShowSpaces {
|
||||
get {
|
||||
return document.TextEditorProperties.ShowSpaces;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.ShowSpaces = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// Specifies the quality of text rendering (whether to use hinting and/or anti-aliasing).
|
||||
/// </value>
|
||||
[Category("Appearance")]
|
||||
[DefaultValue(TextRenderingHint.SystemDefault)]
|
||||
[Description("Specifies the quality of text rendering (whether to use hinting and/or anti-aliasing).")]
|
||||
public TextRenderingHint TextRenderingHint {
|
||||
get {
|
||||
return document.TextEditorProperties.TextRenderingHint;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.TextRenderingHint = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// If true tabs are shown in the textarea
|
||||
/// </value>
|
||||
[Category("Appearance")]
|
||||
[DefaultValue(false)]
|
||||
[Description("If true tabs are shown in the textarea")]
|
||||
public bool ShowTabs {
|
||||
get {
|
||||
return document.TextEditorProperties.ShowTabs;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.ShowTabs = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// If true EOL markers are shown in the textarea
|
||||
/// </value>
|
||||
[Category("Appearance")]
|
||||
[DefaultValue(false)]
|
||||
[Description("If true EOL markers are shown in the textarea")]
|
||||
public bool ShowEOLMarkers {
|
||||
get {
|
||||
return document.TextEditorProperties.ShowEOLMarker;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.ShowEOLMarker = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// If true the horizontal ruler is shown in the textarea
|
||||
/// </value>
|
||||
[Category("Appearance")]
|
||||
[DefaultValue(false)]
|
||||
[Description("If true the horizontal ruler is shown in the textarea")]
|
||||
public bool ShowHRuler {
|
||||
get {
|
||||
return document.TextEditorProperties.ShowHorizontalRuler;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.ShowHorizontalRuler = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// If true the vertical ruler is shown in the textarea
|
||||
/// </value>
|
||||
[Category("Appearance")]
|
||||
[DefaultValue(true)]
|
||||
[Description("If true the vertical ruler is shown in the textarea")]
|
||||
public bool ShowVRuler {
|
||||
get {
|
||||
return document.TextEditorProperties.ShowVerticalRuler;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.ShowVerticalRuler = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// The row in which the vertical ruler is displayed
|
||||
/// </value>
|
||||
[Category("Appearance")]
|
||||
[DefaultValue(80)]
|
||||
[Description("The row in which the vertical ruler is displayed")]
|
||||
public int VRulerRow {
|
||||
get {
|
||||
return document.TextEditorProperties.VerticalRulerRow;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.VerticalRulerRow = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// If true line numbers are shown in the textarea
|
||||
/// </value>
|
||||
[Category("Appearance")]
|
||||
[DefaultValue(true)]
|
||||
[Description("If true line numbers are shown in the textarea")]
|
||||
public bool ShowLineNumbers {
|
||||
get {
|
||||
return document.TextEditorProperties.ShowLineNumbers;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.ShowLineNumbers = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// If true invalid lines are marked in the textarea
|
||||
/// </value>
|
||||
[Category("Appearance")]
|
||||
[DefaultValue(false)]
|
||||
[Description("If true invalid lines are marked in the textarea")]
|
||||
public bool ShowInvalidLines {
|
||||
get {
|
||||
return document.TextEditorProperties.ShowInvalidLines;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.ShowInvalidLines = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// If true folding is enabled in the textarea
|
||||
/// </value>
|
||||
[Category("Appearance")]
|
||||
[DefaultValue(true)]
|
||||
[Description("If true folding is enabled in the textarea")]
|
||||
public bool EnableFolding {
|
||||
get {
|
||||
return document.TextEditorProperties.EnableFolding;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.EnableFolding = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
[Category("Appearance")]
|
||||
[DefaultValue(true)]
|
||||
[Description("If true matching brackets are highlighted")]
|
||||
public bool ShowMatchingBracket {
|
||||
get {
|
||||
return document.TextEditorProperties.ShowMatchingBracket;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.ShowMatchingBracket = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
[Category("Appearance")]
|
||||
[DefaultValue(false)]
|
||||
[Description("If true the icon bar is displayed")]
|
||||
public bool IsIconBarVisible {
|
||||
get {
|
||||
return document.TextEditorProperties.IsIconBarVisible;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.IsIconBarVisible = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// The width in spaces of a tab character
|
||||
/// </value>
|
||||
[Category("Appearance")]
|
||||
[DefaultValue(4)]
|
||||
[Description("The width in spaces of a tab character")]
|
||||
public int TabIndent {
|
||||
get {
|
||||
return document.TextEditorProperties.TabIndent;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.TabIndent = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// The line viewer style
|
||||
/// </value>
|
||||
[Category("Appearance")]
|
||||
[DefaultValue(LineViewerStyle.None)]
|
||||
[Description("The line viewer style")]
|
||||
public LineViewerStyle LineViewerStyle {
|
||||
get {
|
||||
return document.TextEditorProperties.LineViewerStyle;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.LineViewerStyle = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// The indent style
|
||||
/// </value>
|
||||
[Category("Behavior")]
|
||||
[DefaultValue(IndentStyle.Smart)]
|
||||
[Description("The indent style")]
|
||||
public IndentStyle IndentStyle {
|
||||
get {
|
||||
return document.TextEditorProperties.IndentStyle;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.IndentStyle = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// if true spaces are converted to tabs
|
||||
/// </value>
|
||||
[Category("Behavior")]
|
||||
[DefaultValue(false)]
|
||||
[Description("Converts tabs to spaces while typing")]
|
||||
public bool ConvertTabsToSpaces {
|
||||
get {
|
||||
return document.TextEditorProperties.ConvertTabsToSpaces;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.ConvertTabsToSpaces = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// if true spaces are converted to tabs
|
||||
/// </value>
|
||||
[Category("Behavior")]
|
||||
[DefaultValue(false)]
|
||||
[Description("Hide the mouse cursor while typing")]
|
||||
public bool HideMouseCursor {
|
||||
get {
|
||||
return document.TextEditorProperties.HideMouseCursor;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.HideMouseCursor = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// if true spaces are converted to tabs
|
||||
/// </value>
|
||||
[Category("Behavior")]
|
||||
[DefaultValue(false)]
|
||||
[Description("Allows the caret to be placed beyond the end of line")]
|
||||
public bool AllowCaretBeyondEOL {
|
||||
get {
|
||||
return document.TextEditorProperties.AllowCaretBeyondEOL;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.AllowCaretBeyondEOL = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
/// <value>
|
||||
/// if true spaces are converted to tabs
|
||||
/// </value>
|
||||
[Category("Behavior")]
|
||||
[DefaultValue(BracketMatchingStyle.After)]
|
||||
[Description("Specifies if the bracket matching should match the bracket before or after the caret.")]
|
||||
public BracketMatchingStyle BracketMatchingStyle {
|
||||
get {
|
||||
return document.TextEditorProperties.BracketMatchingStyle;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.BracketMatchingStyle = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// The base font of the text area. No bold or italic fonts
|
||||
/// can be used because bold/italic is reserved for highlighting
|
||||
/// purposes.
|
||||
/// </value>
|
||||
[Browsable(true)]
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
|
||||
[DefaultValue(typeof(Font), null)] // Stef_H
|
||||
[Description("The base font of the text area. No bold or italic fonts can be used because bold/italic is reserved for highlighting purposes.")]
|
||||
public override Font Font {
|
||||
get {
|
||||
return document.TextEditorProperties.Font;
|
||||
}
|
||||
set {
|
||||
document.TextEditorProperties.Font = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
public abstract TextAreaControl ActiveTextAreaControl {
|
||||
get;
|
||||
}
|
||||
|
||||
protected TextEditorControlBase()
|
||||
{
|
||||
GenerateDefaultActions();
|
||||
HighlightingManager.Manager.ReloadSyntaxHighlighting += new EventHandler(OnReloadHighlighting);
|
||||
}
|
||||
|
||||
protected virtual void OnReloadHighlighting(object sender, EventArgs e)
|
||||
{
|
||||
if (Document.HighlightingStrategy != null) {
|
||||
try {
|
||||
Document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy(Document.HighlightingStrategy.Name);
|
||||
} catch (HighlightingDefinitionInvalidException ex) {
|
||||
MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEditAction(Keys keyData)
|
||||
{
|
||||
return editactions.ContainsKey(keyData);
|
||||
}
|
||||
|
||||
internal IEditAction GetEditAction(Keys keyData)
|
||||
{
|
||||
if (!IsEditAction(keyData)) {
|
||||
return null;
|
||||
}
|
||||
return (IEditAction)editactions[keyData];
|
||||
}
|
||||
|
||||
void GenerateDefaultActions()
|
||||
{
|
||||
editactions[Keys.Left] = new CaretLeft();
|
||||
editactions[Keys.Left | Keys.Shift] = new ShiftCaretLeft();
|
||||
editactions[Keys.Left | Keys.Control] = new WordLeft();
|
||||
editactions[Keys.Left | Keys.Control | Keys.Shift] = new ShiftWordLeft();
|
||||
editactions[Keys.Right] = new CaretRight();
|
||||
editactions[Keys.Right | Keys.Shift] = new ShiftCaretRight();
|
||||
editactions[Keys.Right | Keys.Control] = new WordRight();
|
||||
editactions[Keys.Right | Keys.Control | Keys.Shift] = new ShiftWordRight();
|
||||
editactions[Keys.Up] = new CaretUp();
|
||||
editactions[Keys.Up | Keys.Shift] = new ShiftCaretUp();
|
||||
editactions[Keys.Up | Keys.Control] = new ScrollLineUp();
|
||||
editactions[Keys.Down] = new CaretDown();
|
||||
editactions[Keys.Down | Keys.Shift] = new ShiftCaretDown();
|
||||
editactions[Keys.Down | Keys.Control] = new ScrollLineDown();
|
||||
|
||||
editactions[Keys.Insert] = new ToggleEditMode();
|
||||
editactions[Keys.Insert | Keys.Control] = new Copy();
|
||||
editactions[Keys.Insert | Keys.Shift] = new Paste();
|
||||
editactions[Keys.Delete] = new Delete();
|
||||
editactions[Keys.Delete | Keys.Shift] = new Cut();
|
||||
editactions[Keys.Home] = new Home();
|
||||
editactions[Keys.Home | Keys.Shift] = new ShiftHome();
|
||||
editactions[Keys.Home | Keys.Control] = new MoveToStart();
|
||||
editactions[Keys.Home | Keys.Control | Keys.Shift] = new ShiftMoveToStart();
|
||||
editactions[Keys.End] = new End();
|
||||
editactions[Keys.End | Keys.Shift] = new ShiftEnd();
|
||||
editactions[Keys.End | Keys.Control] = new MoveToEnd();
|
||||
editactions[Keys.End | Keys.Control | Keys.Shift] = new ShiftMoveToEnd();
|
||||
editactions[Keys.PageUp] = new MovePageUp();
|
||||
editactions[Keys.PageUp | Keys.Shift] = new ShiftMovePageUp();
|
||||
editactions[Keys.PageDown] = new MovePageDown();
|
||||
editactions[Keys.PageDown | Keys.Shift] = new ShiftMovePageDown();
|
||||
|
||||
editactions[Keys.Return] = new Return();
|
||||
editactions[Keys.Tab] = new Tab();
|
||||
editactions[Keys.Tab | Keys.Shift] = new ShiftTab();
|
||||
editactions[Keys.Back] = new Backspace();
|
||||
editactions[Keys.Back | Keys.Shift] = new Backspace();
|
||||
|
||||
editactions[Keys.X | Keys.Control] = new Cut();
|
||||
editactions[Keys.C | Keys.Control] = new Copy();
|
||||
editactions[Keys.V | Keys.Control] = new Paste();
|
||||
|
||||
editactions[Keys.A | Keys.Control] = new SelectWholeDocument();
|
||||
editactions[Keys.Escape] = new ClearAllSelections();
|
||||
|
||||
editactions[Keys.Divide | Keys.Control] = new ToggleComment();
|
||||
editactions[Keys.OemQuestion | Keys.Control] = new ToggleComment();
|
||||
|
||||
editactions[Keys.Back | Keys.Alt] = new Actions.Undo();
|
||||
editactions[Keys.Z | Keys.Control] = new Actions.Undo();
|
||||
editactions[Keys.Y | Keys.Control] = new Redo();
|
||||
|
||||
editactions[Keys.Delete | Keys.Control] = new DeleteWord();
|
||||
editactions[Keys.Back | Keys.Control] = new WordBackspace();
|
||||
editactions[Keys.D | Keys.Control] = new DeleteLine();
|
||||
editactions[Keys.D | Keys.Shift | Keys.Control] = new DeleteToLineEnd();
|
||||
|
||||
editactions[Keys.B | Keys.Control] = new GotoMatchingBrace();
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Call this method before a long update operation this
|
||||
/// 'locks' the text area so that no screen update occurs.
|
||||
/// </remarks>
|
||||
public virtual void BeginUpdate()
|
||||
{
|
||||
++updateLevel;
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Call this method to 'unlock' the text area. After this call
|
||||
/// screen update can occur. But no automatical refresh occurs you
|
||||
/// have to commit the updates in the queue.
|
||||
/// </remarks>
|
||||
public virtual void EndUpdate()
|
||||
{
|
||||
Debug.Assert(updateLevel > 0);
|
||||
updateLevel = Math.Max(0, updateLevel - 1);
|
||||
}
|
||||
|
||||
public void LoadFile(string fileName)
|
||||
{
|
||||
LoadFile(fileName, true, true);
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Loads a file given by fileName
|
||||
/// </remarks>
|
||||
/// <param name="fileName">The name of the file to open</param>
|
||||
/// <param name="autoLoadHighlighting">Automatically load the highlighting for the file</param>
|
||||
/// <param name="autodetectEncoding">Automatically detect file encoding and set Encoding property to the detected encoding.</param>
|
||||
public void LoadFile(string fileName, bool autoLoadHighlighting, bool autodetectEncoding)
|
||||
{
|
||||
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) {
|
||||
LoadFile(fileName, fs, autoLoadHighlighting, autodetectEncoding);
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Loads a file from the specified stream.
|
||||
/// </remarks>
|
||||
/// <param name="fileName">The name of the file to open. Used to find the correct highlighting strategy
|
||||
/// if autoLoadHighlighting is active, and sets the filename property to this value.</param>
|
||||
/// <param name="stream">The stream to actually load the file content from.</param>
|
||||
/// <param name="autoLoadHighlighting">Automatically load the highlighting for the file</param>
|
||||
/// <param name="autodetectEncoding">Automatically detect file encoding and set Encoding property to the detected encoding.</param>
|
||||
public void LoadFile(string fileName, Stream stream, bool autoLoadHighlighting, bool autodetectEncoding)
|
||||
{
|
||||
if (stream == null)
|
||||
throw new ArgumentNullException("stream");
|
||||
|
||||
BeginUpdate();
|
||||
document.TextContent = string.Empty;
|
||||
document.UndoStack.ClearAll();
|
||||
document.BookmarkManager.Clear();
|
||||
if (autoLoadHighlighting) {
|
||||
try {
|
||||
document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategyForFile(fileName);
|
||||
} catch (HighlightingDefinitionInvalidException ex) {
|
||||
MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
if (autodetectEncoding) {
|
||||
Encoding encoding = this.Encoding;
|
||||
Document.TextContent = Util.FileReader.ReadFileContent(stream, ref encoding);
|
||||
this.Encoding = encoding;
|
||||
} else {
|
||||
using (StreamReader reader = new StreamReader(fileName, this.Encoding)) {
|
||||
Document.TextContent = reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
this.FileName = fileName;
|
||||
Document.UpdateQueue.Clear();
|
||||
EndUpdate();
|
||||
|
||||
OptionsChanged();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the document can be saved with the current encoding without losing data.
|
||||
/// </summary>
|
||||
public bool CanSaveWithCurrentEncoding()
|
||||
{
|
||||
if (encoding == null || Util.FileReader.IsUnicode(encoding))
|
||||
return true;
|
||||
// not a unicode codepage
|
||||
string text = document.TextContent;
|
||||
return encoding.GetString(encoding.GetBytes(text)) == text;
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Saves the text editor content into the file.
|
||||
/// </remarks>
|
||||
public void SaveFile(string fileName)
|
||||
{
|
||||
using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) {
|
||||
SaveFile(fs);
|
||||
}
|
||||
this.FileName = fileName;
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Saves the text editor content into the specified stream.
|
||||
/// Does not close the stream.
|
||||
/// </remarks>
|
||||
public void SaveFile(Stream stream)
|
||||
{
|
||||
StreamWriter streamWriter = new StreamWriter(stream, this.Encoding ?? Encoding.UTF8);
|
||||
|
||||
// save line per line to apply the LineTerminator to all lines
|
||||
// (otherwise we might save files with mixed-up line endings)
|
||||
foreach (LineSegment line in Document.LineSegmentCollection) {
|
||||
streamWriter.Write(Document.GetText(line.Offset, line.Length));
|
||||
if (line.DelimiterLength > 0) {
|
||||
char charAfterLine = Document.GetCharAt(line.Offset + line.Length);
|
||||
if (charAfterLine != '\n' && charAfterLine != '\r')
|
||||
throw new InvalidOperationException("The document cannot be saved because it is corrupted.");
|
||||
// only save line terminator if the line has one
|
||||
streamWriter.Write(document.TextEditorProperties.LineTerminator);
|
||||
}
|
||||
}
|
||||
streamWriter.Flush();
|
||||
}
|
||||
|
||||
public abstract void OptionsChanged();
|
||||
|
||||
// Localization ISSUES
|
||||
|
||||
// used in insight window
|
||||
public virtual string GetRangeDescription(int selectedItem, int itemCount)
|
||||
{
|
||||
StringBuilder sb=new StringBuilder(selectedItem.ToString());
|
||||
sb.Append(" from ");
|
||||
sb.Append(itemCount.ToString());
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Overwritten refresh method that does nothing if the control is in
|
||||
/// an update cycle.
|
||||
/// </remarks>
|
||||
public override void Refresh()
|
||||
{
|
||||
if (IsInUpdate) {
|
||||
return;
|
||||
}
|
||||
base.Refresh();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing) {
|
||||
HighlightingManager.Manager.ReloadSyntaxHighlighting -= new EventHandler(OnReloadHighlighting);
|
||||
document.HighlightingStrategy = null;
|
||||
document.UndoStack.TextEditorControl = null;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
protected virtual void OnFileNameChanged(EventArgs e)
|
||||
{
|
||||
if (FileNameChanged != null) {
|
||||
FileNameChanged(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler FileNameChanged;
|
||||
}
|
||||
}
|
||||
541
ICSharpCode.TextEditor/Project/Src/Gui/TextEditorControlEx.cs
Normal file
541
ICSharpCode.TextEditor/Project/Src/Gui/TextEditorControlEx.cs
Normal file
@@ -0,0 +1,541 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using ICSharpCode.TextEditor.Actions;
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
using ICSharpCode.TextEditor.Properties;
|
||||
using ICSharpCode.TextEditor.Src.Actions;
|
||||
using ICSharpCode.TextEditor.Src.Document.FoldingStrategy;
|
||||
using ICSharpCode.TextEditor.Src.Document.HighlightingStrategy.SyntaxModes;
|
||||
using ICSharpCode.TextEditor.UserControls;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
[ToolboxBitmap("ICSharpCode.TextEditor.Resources.TextEditorControl.bmp")]
|
||||
[ToolboxItem(true)]
|
||||
public class TextEditorControlEx : TextEditorControl
|
||||
{
|
||||
private bool _contextMenuEnabled;
|
||||
private bool _contextMenuShowDefaultIcons;
|
||||
private bool _contextMenuShowShortCutKeys;
|
||||
private readonly FindAndReplaceForm _findForm = new FindAndReplaceForm();
|
||||
|
||||
public TextEditorControlEx()
|
||||
{
|
||||
editactions[Keys.Control | Keys.F] = new EditFindAction(_findForm, this);
|
||||
editactions[Keys.Control | Keys.H] = new EditReplaceAction(_findForm, this);
|
||||
editactions[Keys.F3] = new FindAgainAction(_findForm, this);
|
||||
editactions[Keys.F3 | Keys.Shift] = new FindAgainReverseAction(_findForm, this);
|
||||
editactions[Keys.Control | Keys.G] = new GoToLineNumberAction();
|
||||
|
||||
// Add additional Syntax highlighting providers
|
||||
HighlightingManager.Manager.AddSyntaxModeFileProvider(new ResourceSyntaxModeProviderEx());
|
||||
|
||||
TextChanged += TextChangedEventHandler;
|
||||
}
|
||||
|
||||
protected override void OnLoad(EventArgs e)
|
||||
{
|
||||
if (ContextMenuEnabled)
|
||||
{
|
||||
AssignContextMenu(CreateNewContextMenu(ContextMenuShowDefaultIcons, ContextMenuShowShortCutKeys));
|
||||
}
|
||||
|
||||
base.OnLoad(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
TextChanged -= TextChangedEventHandler;
|
||||
}
|
||||
|
||||
private void TextChangedEventHandler(object sender, EventArgs e)
|
||||
{
|
||||
var editor = sender as TextEditorControlEx;
|
||||
if (editor != null)
|
||||
{
|
||||
bool vScrollBarIsNeeded = editor.Document.TotalNumberOfLines > ActiveTextAreaControl.TextArea.TextView.VisibleLineCount;
|
||||
if (ActiveTextAreaControl.VScrollBar.Visible && HideVScrollBarIfPossible && !vScrollBarIsNeeded)
|
||||
{
|
||||
ActiveTextAreaControl.ShowScrollBars(Orientation.Vertical, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Extended properties
|
||||
public string SelectedText
|
||||
{
|
||||
get
|
||||
{
|
||||
return ActiveTextAreaControl.SelectionManager.SelectedText;
|
||||
}
|
||||
}
|
||||
|
||||
public string[] Lines
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.Text.Split(new[] { "\r\n" }, StringSplitOptions.None);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Designer Properties
|
||||
/// <value>
|
||||
/// The current document
|
||||
/// </value>
|
||||
[Browsable(false)]
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public new IDocument Document
|
||||
{
|
||||
get
|
||||
{
|
||||
return document;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (document != null)
|
||||
{
|
||||
document.DocumentChanged -= OnDocumentChanged;
|
||||
document.DocumentChanged -= OnDocumentChangedDoUpdateContextMenu;
|
||||
}
|
||||
|
||||
document = value;
|
||||
document.UndoStack.TextEditorControl = this;
|
||||
document.DocumentChanged += OnDocumentChanged;
|
||||
document.DocumentChanged += OnDocumentChangedDoUpdateContextMenu;
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// The base font of the text area. No bold or italic fonts
|
||||
/// can be used because bold/italic is reserved for highlighting
|
||||
/// purposes.
|
||||
/// </value>
|
||||
[Browsable(true)]
|
||||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
|
||||
[DefaultValue(typeof(Font), null)]
|
||||
[Description("The base font of the text area. No bold or italic fonts can be used because bold/italic is reserved for highlighting purposes.")]
|
||||
public override Font Font
|
||||
{
|
||||
get
|
||||
{
|
||||
return Document.TextEditorProperties.Font;
|
||||
}
|
||||
set
|
||||
{
|
||||
Document.TextEditorProperties.Font = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
[Category("Appearance")]
|
||||
[DefaultValue(false)]
|
||||
[Description("Hide the vertical ScrollBar if it's not needed. ")]
|
||||
public bool HideVScrollBarIfPossible { get; set; }
|
||||
|
||||
private string _foldingStrategy;
|
||||
[Category("Appearance")]
|
||||
[Description("Set the Folding Strategy. Supported : XML and CSharp.")]
|
||||
public string FoldingStrategy
|
||||
{
|
||||
get
|
||||
{
|
||||
return _foldingStrategy;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetFoldingStrategy(value);
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private string _syntaxHighlighting;
|
||||
[Category("Appearance")]
|
||||
[Description("Sets the Syntax Highlighting.")]
|
||||
public string SyntaxHighlighting
|
||||
{
|
||||
get
|
||||
{
|
||||
return _syntaxHighlighting;
|
||||
}
|
||||
set
|
||||
{
|
||||
_syntaxHighlighting = value;
|
||||
SetHighlighting(_syntaxHighlighting);
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
[Category("Behavior")]
|
||||
[DefaultValue(false)]
|
||||
[Description("If true document is readonly.")]
|
||||
[Browsable(true)]
|
||||
public new bool IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return Document.ReadOnly;
|
||||
}
|
||||
set
|
||||
{
|
||||
Document.ReadOnly = value;
|
||||
OptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
[DefaultValue(false)]
|
||||
[Category("Appearance")]
|
||||
[Description("Show default Icons in ContextMenu")]
|
||||
[Browsable(true)]
|
||||
public bool ContextMenuShowDefaultIcons
|
||||
{
|
||||
get
|
||||
{
|
||||
return _contextMenuShowDefaultIcons & _contextMenuEnabled;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_contextMenuShowDefaultIcons = _contextMenuEnabled & value;
|
||||
}
|
||||
}
|
||||
|
||||
[DefaultValue(false)]
|
||||
[Category("Appearance")]
|
||||
[Description("Show shortcut keys in ContextMenu")]
|
||||
[Browsable(true)]
|
||||
public bool ContextMenuShowShortCutKeys
|
||||
{
|
||||
get
|
||||
{
|
||||
return _contextMenuShowShortCutKeys & _contextMenuEnabled;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_contextMenuShowShortCutKeys = _contextMenuEnabled & value;
|
||||
}
|
||||
}
|
||||
|
||||
[DefaultValue(false)]
|
||||
[Category("Appearance")]
|
||||
[Description("Enable a ContextMenu")]
|
||||
[Browsable(true)]
|
||||
public bool ContextMenuEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return _contextMenuEnabled;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_contextMenuEnabled = value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Sets the text and refreshes the control.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="updateFoldings">if set to <c>true</c> [update foldings].</param>
|
||||
public void SetTextAndRefresh(string text, bool updateFoldings = false)
|
||||
{
|
||||
ResetText();
|
||||
Text = text;
|
||||
|
||||
if (updateFoldings && Document.TextEditorProperties.EnableFolding)
|
||||
{
|
||||
Document.FoldingManager.UpdateFoldings(null, null);
|
||||
}
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the folding strategy. Currently only XML is supported.
|
||||
/// </summary>
|
||||
/// <param name="foldingStrategy">The foldingStrategy.</param>
|
||||
public void SetFoldingStrategy(string foldingStrategy)
|
||||
{
|
||||
if (foldingStrategy == null)
|
||||
{
|
||||
throw new ArgumentNullException("foldingStrategy");
|
||||
}
|
||||
|
||||
if (!Document.TextEditorProperties.EnableFolding)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (foldingStrategy)
|
||||
{
|
||||
case "XML":
|
||||
_foldingStrategy = foldingStrategy;
|
||||
Document.FoldingManager.FoldingStrategy = new XmlFoldingStrategy();
|
||||
break;
|
||||
|
||||
case "C#":
|
||||
_foldingStrategy = foldingStrategy;
|
||||
Document.FoldingManager.FoldingStrategy = new CSharpFoldingStrategy();
|
||||
break;
|
||||
|
||||
case "JSON":
|
||||
_foldingStrategy = foldingStrategy;
|
||||
Document.FoldingManager.FoldingStrategy = new JSONFoldingStrategy();
|
||||
break;
|
||||
|
||||
default:
|
||||
Document.FoldingManager.FoldingStrategy = null;
|
||||
_foldingStrategy = null;
|
||||
break;
|
||||
}
|
||||
|
||||
Document.FoldingManager.UpdateFoldings(null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the folding errors. Currently only XML is supported.
|
||||
/// </summary>
|
||||
/// <returns>List of errors, else empty list</returns>
|
||||
public List<string> GetFoldingErrors()
|
||||
{
|
||||
if (_foldingStrategy == "XML")
|
||||
{
|
||||
var foldingStrategy = Document.FoldingManager.FoldingStrategy as IFoldingStrategyEx;
|
||||
if (foldingStrategy != null)
|
||||
{
|
||||
return foldingStrategy.GetFoldingErrors();
|
||||
}
|
||||
}
|
||||
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
#region ContextMenu Commands implementations
|
||||
private bool CanUndo()
|
||||
{
|
||||
return Document.UndoStack.CanUndo;
|
||||
}
|
||||
|
||||
private bool CanRedo()
|
||||
{
|
||||
return Document.UndoStack.CanRedo;
|
||||
}
|
||||
|
||||
private bool CanCopy()
|
||||
{
|
||||
return ActiveTextAreaControl.SelectionManager.HasSomethingSelected;
|
||||
}
|
||||
|
||||
private bool CanCut()
|
||||
{
|
||||
return ActiveTextAreaControl.SelectionManager.HasSomethingSelected;
|
||||
}
|
||||
|
||||
private bool CanDelete()
|
||||
{
|
||||
return ActiveTextAreaControl.SelectionManager.HasSomethingSelected;
|
||||
}
|
||||
|
||||
private bool CanPaste()
|
||||
{
|
||||
return ActiveTextAreaControl.TextArea.ClipboardHandler.EnablePaste;
|
||||
}
|
||||
|
||||
private bool CanSelectAll()
|
||||
{
|
||||
if (Document.TextContent == null)
|
||||
return false;
|
||||
|
||||
return !Document.TextContent.Trim().Equals(string.Empty);
|
||||
}
|
||||
|
||||
private bool CanFind()
|
||||
{
|
||||
if (Document.TextContent == null)
|
||||
return false;
|
||||
|
||||
return Document.TextContent.Trim().Any();
|
||||
}
|
||||
|
||||
private void DoCut()
|
||||
{
|
||||
new Cut().Execute(ActiveTextAreaControl.TextArea);
|
||||
ActiveTextAreaControl.Focus();
|
||||
}
|
||||
|
||||
private void DoDelete()
|
||||
{
|
||||
new Delete().Execute(ActiveTextAreaControl.TextArea);
|
||||
ActiveTextAreaControl.Focus();
|
||||
}
|
||||
|
||||
private void DoCopy()
|
||||
{
|
||||
new Copy().Execute(ActiveTextAreaControl.TextArea);
|
||||
ActiveTextAreaControl.Focus();
|
||||
}
|
||||
|
||||
private void DoPaste()
|
||||
{
|
||||
new Paste().Execute(ActiveTextAreaControl.TextArea);
|
||||
ActiveTextAreaControl.Focus();
|
||||
}
|
||||
|
||||
private void DoSelectAll()
|
||||
{
|
||||
new SelectWholeDocument().Execute(ActiveTextAreaControl.TextArea);
|
||||
ActiveTextAreaControl.Focus();
|
||||
}
|
||||
|
||||
public void DoToggleFoldings()
|
||||
{
|
||||
new ToggleAllFoldings().Execute(ActiveTextAreaControl.TextArea);
|
||||
}
|
||||
|
||||
private void DoFind()
|
||||
{
|
||||
new EditFindAction(_findForm, this).Execute(ActiveTextAreaControl.TextArea);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ContextMenu Initialization
|
||||
|
||||
|
||||
private ContextMenuStrip CreateNewContextMenu(bool showImages, bool showKeys)
|
||||
{
|
||||
var mnu = new ContextMenuStripEx();
|
||||
mnu.AddToolStripMenuItem("&Undo",
|
||||
showImages ? Resources.sc_undo : null,
|
||||
(sender, e) => Undo(),
|
||||
showKeys ? Keys.Control | Keys.Z : Keys.None,
|
||||
CanUndo
|
||||
);
|
||||
|
||||
mnu.AddToolStripMenuItem("&Redo",
|
||||
showImages ? Resources.sc_redo : null,
|
||||
(sender, e) => Redo(),
|
||||
showKeys ? Keys.Control | Keys.Y : Keys.None,
|
||||
CanRedo
|
||||
);
|
||||
|
||||
mnu.AddToolStripSeparator();
|
||||
|
||||
mnu.AddToolStripMenuItem("&Cut",
|
||||
showImages ? Resources.cut : null,
|
||||
(sender, e) => DoCut(),
|
||||
showKeys ? Keys.Control | Keys.X : Keys.None,
|
||||
CanCut
|
||||
);
|
||||
|
||||
mnu.AddToolStripMenuItem("Cop&y",
|
||||
showImages ? Resources.sc_copy : null,
|
||||
(sender, e) => DoCopy(),
|
||||
showKeys ? Keys.Control | Keys.C : Keys.None,
|
||||
CanCopy
|
||||
);
|
||||
|
||||
mnu.AddToolStripMenuItem("&Paste",
|
||||
showImages ? Resources.sc_paste : null,
|
||||
(sender, e) => DoPaste(),
|
||||
showKeys ? Keys.Control | Keys.V : Keys.None,
|
||||
CanPaste
|
||||
);
|
||||
|
||||
mnu.AddToolStripSeparator();
|
||||
|
||||
mnu.AddToolStripMenuItem("&Delete",
|
||||
showImages ? Resources.sc_cancel : null,
|
||||
(sender, e) => DoDelete(),
|
||||
showKeys ? Keys.Delete : Keys.None,
|
||||
CanDelete
|
||||
);
|
||||
|
||||
mnu.AddToolStripMenuItem("&Select All",
|
||||
showImages ? Resources.sc_selectall : null,
|
||||
(sender, e) => DoSelectAll(),
|
||||
showKeys ? Keys.Control | Keys.A : Keys.None,
|
||||
CanSelectAll
|
||||
);
|
||||
|
||||
mnu.AddToolStripMenuItem("&Find",
|
||||
showImages ? Resources.sc_searchdialog : null,
|
||||
(sender, e) => DoFind(),
|
||||
showKeys ? Keys.Control | Keys.F : Keys.None,
|
||||
CanFind
|
||||
);
|
||||
|
||||
return mnu;
|
||||
}
|
||||
|
||||
private void AssignContextMenu(ContextMenuStrip mnu)
|
||||
{
|
||||
if (ActiveTextAreaControl.ContextMenuStrip != null)
|
||||
{
|
||||
ActiveTextAreaControl.ContextMenuStrip.Dispose();
|
||||
ActiveTextAreaControl.ContextMenuStrip = null;
|
||||
}
|
||||
|
||||
ActiveTextAreaControl.ContextMenuStrip = mnu;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ContextMenu Methods
|
||||
public void SelectText(int start, int length)
|
||||
{
|
||||
var textLength = Document.TextLength;
|
||||
if (textLength < (start + length))
|
||||
{
|
||||
length = (textLength - 1) - start;
|
||||
}
|
||||
ActiveTextAreaControl.Caret.Position = Document.OffsetToPosition(start + length);
|
||||
ActiveTextAreaControl.SelectionManager.ClearSelection();
|
||||
ActiveTextAreaControl.SelectionManager.SetSelection(new DefaultSelection(Document, Document.OffsetToPosition(start), Document.OffsetToPosition(start + length)));
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void OnDocumentChangedDoUpdateContextMenu(object sender, DocumentEventArgs e)
|
||||
{
|
||||
bool isVisible = (Document.TotalNumberOfLines > ActiveTextAreaControl.TextArea.TextView.VisibleLineCount);
|
||||
ActiveTextAreaControl.VScrollBar.Visible = isVisible;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static class TextAreaControlExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension method to show a scrollbar.
|
||||
/// </summary>
|
||||
/// <param name="textAreaControl">The text area control.</param>
|
||||
/// <param name="orientation">The orientation.</param>
|
||||
/// <param name="isVisible">if set to <c>true</c> [is visible].</param>
|
||||
public static void ShowScrollBars(this TextAreaControl textAreaControl, Orientation orientation, bool isVisible)
|
||||
{
|
||||
if (orientation == Orientation.Vertical)
|
||||
{
|
||||
textAreaControl.VScrollBar.Visible = isVisible;
|
||||
}
|
||||
else
|
||||
{
|
||||
textAreaControl.HScrollBar.Visible = isVisible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1096
ICSharpCode.TextEditor/Project/Src/Gui/TextView.cs
Normal file
1096
ICSharpCode.TextEditor/Project/Src/Gui/TextView.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,62 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace ICSharpCode.TextEditor
|
||||
{
|
||||
public delegate void ToolTipRequestEventHandler(object sender, ToolTipRequestEventArgs e);
|
||||
|
||||
public class ToolTipRequestEventArgs
|
||||
{
|
||||
Point mousePosition;
|
||||
TextLocation logicalPosition;
|
||||
bool inDocument;
|
||||
|
||||
public Point MousePosition {
|
||||
get {
|
||||
return mousePosition;
|
||||
}
|
||||
}
|
||||
|
||||
public TextLocation LogicalPosition {
|
||||
get {
|
||||
return logicalPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public bool InDocument {
|
||||
get {
|
||||
return inDocument;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if some client handling the event has already shown a tool tip.
|
||||
/// </summary>
|
||||
public bool ToolTipShown {
|
||||
get {
|
||||
return toolTipText != null;
|
||||
}
|
||||
}
|
||||
|
||||
internal string toolTipText;
|
||||
|
||||
public void ShowToolTip(string text)
|
||||
{
|
||||
toolTipText = text;
|
||||
}
|
||||
|
||||
public ToolTipRequestEventArgs(Point mousePosition, TextLocation logicalPosition, bool inDocument)
|
||||
{
|
||||
this.mousePosition = mousePosition;
|
||||
this.logicalPosition = logicalPosition;
|
||||
this.inDocument = inDocument;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user