first commit
This commit is contained in:
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user