|
//
// ___ _____ ___ ___ __ __
// / __|_ _/ _ \| _ \ \/ |
// \__ \ | || (_) | / |\/| |
// |___/ |_| \___/|_|_\_| |_|
//
// Storm.TextEditor.dll
// ��������������������
// Storm.TabControl.dll was created under the LGPL
// license. Some of the code was created from scratch,
// some was not. Code not created from scratch was
// based on the DotNetFireball framework and evolved
// from that.
//
// What I mostly did in this library was that I
// cleaned up the code, structured it, documentated
// it and added new features.
//
// Although it might not sound like it, it was very
// hard and took a long (pretty much a shitload)
// time. Why? Well, this was* some of the crappiest,
// most unstructured, undocumentated, ugly code I've
// ever seen. It would actually have taken me less
// time to create it from scratch, but I figured that
// out too late. Figuring out what the code actually
// did and then documentating it was (mostly) a
// day-long process. Well, I hope you enjoy some of
// my hard work. /rant
//
// Of course some/most of it is made from scratch by me.
// *Yes, was. It isn't now. :) Some of the naming
// conventions might still seem a little bit off though.
//
// What is Storm?
// ��������������
// Storm is a set of dynamic link libraries used in
// Theodor "Vestras" Storm Kristensen's application
// "Moonlite".
//
//
// Thanks:
// �������
// - The DotNetFireball team for creating and publishing
// DotNetFireball which I based some of my code on.
//
//
// Copyright (c) Theodor "Vestras" Storm Kristensen 2009
// �����������������������������������������������������
//
namespace Storm.TextEditor.Interacting
{
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Design;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Design;
using System.Diagnostics.SymbolStore;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Text;
using System.Windows.Forms;
using Storm.TextEditor;
using Storm.TextEditor.Controls;
using Storm.TextEditor.Controls.Core;
using Storm.TextEditor.Controls.Core.Globalization;
using Storm.TextEditor.Controls.Core.Timers;
using Storm.TextEditor.Controls.IntelliMouse;
using Storm.TextEditor.Document;
using Storm.TextEditor.Document.Exporters;
using Storm.TextEditor.Forms;
using Storm.TextEditor.Interacting;
using Storm.TextEditor.Painting;
using Storm.TextEditor.Parsing;
using Storm.TextEditor.Parsing.Base;
using Storm.TextEditor.Parsing.Classes;
using Storm.TextEditor.Parsing.Language;
using Storm.TextEditor.Preset;
using Storm.TextEditor.Preset.Painting;
using Storm.TextEditor.Preset.TextDraw;
using Storm.TextEditor.Printing;
using Storm.TextEditor.Utilities;
using Storm.TextEditor.Win32;
/// <summary>
/// A class that simulates the Windows/RichTextBox caret.
/// </summary>
public sealed class Caret
{
#region Members
// Basic members.
private bool _blink;
private int _oldLogicalXPos = 0;
private TextPoint _position;
private TextEditorBase _control;
/// <summary>
/// Event fired when the caret's position has changed.
/// </summary>
public event EventHandler Change = null;
#endregion
#region EventHandlers
private void PositionChange(object s, EventArgs e)
{ OnChange(); }
private void OnChange()
{
if (Change != null)
Change(this, null);
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the position of the caret.
/// </summary>
public TextPoint Position
{
get { return _position; }
set
{
_position = value;
_position.Change += new EventHandler(PositionChange);
OnChange();
}
}
/// <summary>
/// Gets or sets whether the caret should blink.
/// </summary>
public bool Blink
{
get { return _blink; }
set { _blink = value; }
}
/// <summary>
/// Gets the word that the caret is placed on.
/// This only applies if the active row is fully parsed.
/// </summary>
public Word CurrentWord
{ get { return _control.Document.GetWordFromPos(Position); } }
/// <summary>
/// Gets the row that the caret is placed on.
/// </summary>
public Row CurrentRow
{ get { return _control.Document[Position.Y]; } }
/// <summary>
/// Gets the current offset of the caret.
/// </summary>
public int Offset
{
get
{
if (Position.Y >= _control.Document.Lines.Length)
return 0;
TextRange tr = new TextRange(0, 0, Position.X, Position.Y);
int l = _control.Document.GetRange(tr).Length;
int tl = _control.Document.Text.Length;
return Math.Min(tl, l);
}
}
/// <summary>
/// Gets or sets the logical position of the caret.
/// </summary>
public TextPoint LogicalPosition
{
get
{
if (Position.X < 0)
return new TextPoint(0, Position.Y);
Row xtr = CurrentRow;
int x = 0;
if (xtr == null)
return new TextPoint(0, 0);
int max = xtr.Text.Length;
int Padd = Math.Max(Position.X - xtr.Text.Length, 0);
string PaddStr = new String(' ', Padd);
string TotStr = xtr.Text + PaddStr;
foreach (char c in TotStr.ToCharArray(0, Position.X))
{
if (c == '\t')
x += _control.TabSpaces - (x % _control.TabSpaces);
else
x++;
}
return new TextPoint(x, Position.Y);
}
set
{
Row xtr = CurrentRow;
int x = 0;
int xx = 0;
if (value.X > 0)
{
char[] chars = xtr.Text.ToCharArray();
int i = 0;
while (x < value.X)
{
char c;
if (i < chars.Length)
c = chars[i];
else
c = ' ';
xx++;
if (c == '\t')
x += _control.TabSpaces - (x % _control.TabSpaces);
else
x++;
i++;
}
}
Position.Y = value.Y;
Position.X = xx;
}
}
#endregion
#region Helpers
private void RememberXPos()
{ _oldLogicalXPos = LogicalPosition.X; }
/// <summary>
/// Confines the caret to a valid position within the active document.
/// </summary>
public void CropPosition()
{
if (Position.X < 0)
Position.X = 0;
if (Position.Y >= _control.Document.Count)
Position.Y = _control.Document.Count - 1;
if (Position.Y < 0)
Position.Y = 0;
Row xtr = CurrentRow;
if (Position.X > xtr.Text.Length && !_control.VirtualWhitespace)
Position.X = xtr.Text.Length;
}
#endregion
#region Methods
/// <summary>
/// Moves the caret right one step.
/// </summary>
public void MoveRight(bool select)
{
CropPosition();
Position.X ++;
if (CurrentRow.IsCollapsed)
{
if (Position.X > CurrentRow.Expansion_EndChar)
{
Position.Y = CurrentRow.Expansion_EndRow.Index;
Position.X = CurrentRow.Expansion_EndRow.Expansion_StartChar;
CropPosition();
}
RememberXPos();
CaretMoved(select);
}
else
{
Row xtr = CurrentRow;
if (Position.X > xtr.Text.Length && !_control.VirtualWhitespace)
{
if (Position.Y < _control.Document.Count - 1)
{
MoveDown(select);
Position.X = 0;
CropPosition();
}
else
CropPosition();
}
RememberXPos();
CaretMoved(select);
}
}
/// <summary>
/// Moves the caret up one row.
/// </summary>
/// <param name="Select">True if a selection should be created from the current caret pos to the new pos</param>
public void MoveUp(bool select)
{
CropPosition();
int x = _oldLogicalXPos;
if (CurrentRow != null && CurrentRow.PrevVisibleRow != null)
{
Position.Y = CurrentRow.PrevVisibleRow.Index;
if (CurrentRow.IsCollapsed)
x = 0;
}
CropPosition();
LogicalPosition = new TextPoint(x, Position.Y);
CropPosition();
CaretMoved(select);
}
/// <summary>
/// Moves the caret up x rows.
/// </summary>
/// <param name="rows">Number of rows the caret should be moved up.</param>
public void MoveUp(int rows, bool select)
{
CropPosition();
int x = _oldLogicalXPos;
int pos = CurrentRow.VisibleIndex;
pos -= rows;
if (pos < 0)
pos = 0;
Row r = _control.Document.VisibleRows[pos];
pos = r.Index;
Position.Y = pos;
if (CurrentRow.IsCollapsed)
x = 0;
CropPosition();
LogicalPosition = new TextPoint(x, Position.Y);
CropPosition();
CaretMoved(select);
}
/// <summary>
/// Moves the caret down x rows.
/// </summary>
/// <param name="rows">The number of rows the caret should be moved down.</param>
public void MoveDown(int rows, bool select)
{
int x = _oldLogicalXPos;
CropPosition();
try
{
int pos = CurrentRow.VisibleIndex;
pos += rows;
if (pos > _control.Document.VisibleRows.Count - 1)
pos = _control.Document.VisibleRows.Count - 1;
Row r = _control.Document.VisibleRows[pos];
pos = r.Index;
Position.Y = pos;
if (CurrentRow.IsCollapsed)
x = 0;
}
finally
{
CropPosition();
LogicalPosition = new TextPoint(x, Position.Y);
CropPosition();
CaretMoved(select);
}
}
/// <summary>
/// Moves the caret down one row.
/// </summary>
public void MoveDown(bool select)
{
CropPosition();
int x = _oldLogicalXPos;
Row r = CurrentRow;
Row r2 = r.NextVisibleRow;
if (r2 != null)
{
Position.Y = r2.Index;
if (CurrentRow.IsCollapsed)
x = 0;
}
CropPosition();
LogicalPosition = new TextPoint(x, Position.Y);
CropPosition();
CaretMoved(select);
}
/// <summary>
/// Moves the caret left one step.
/// </summary>
public void MoveLeft(bool select)
{
CropPosition();
Position.X--;
if (CurrentRow.IsCollapsedEndPart)
{
if (Position.X < CurrentRow.Expansion_StartChar)
{
if (CurrentRow.Expansion_StartRow.Index == -1)
Debugger.Break();
Position.Y = CurrentRow.Expansion_StartRow.Index;
Position.X = CurrentRow.Expansion_StartRow.Expansion_EndChar;
CropPosition();
}
RememberXPos();
CaretMoved(select);
}
else
{
if (Position.X < 0)
{
if (Position.Y > 0)
{
MoveUp(select);
CropPosition();
Row xtr = CurrentRow;
Position.X = xtr.Text.Length;
if (CurrentRow.IsCollapsed)
{
Position.Y = CurrentRow.Expansion_EndRow.Index;
Position.X = CurrentRow.Text.Length;
}
}
else
CropPosition();
}
RememberXPos();
CaretMoved(select);
}
}
/// <summary>
/// Moves the caret to the first non whitespace column at the active row.
/// </summary>
public void MoveHome(bool select)
{
CropPosition();
if (CurrentRow.IsCollapsedEndPart)
{
Position.Y = CurrentRow.Expansion_StartRow.Index;
MoveHome(select);
}
else
{
int i = CurrentRow.GetLeadingWhitespace().Length;
if (Position.X == i)
Position.X = 0;
else
Position.X = i;
RememberXPos();
CaretMoved(select);
}
}
/// <summary>
/// Moves the caret to the end of a row ignoring any whitespace characters at the end of the row.
/// </summary>
public void MoveEnd(bool select)
{
if (CurrentRow.IsCollapsed)
{
Position.Y = CurrentRow.Expansion_EndRow.Index;
MoveEnd(select);
}
else
{
CropPosition();
Row xtr = CurrentRow;
Position.X = xtr.Text.Length;
RememberXPos();
CaretMoved(select);
}
}
public void CaretMoved(bool select)
{
_control.ScrollIntoView();
if (!select)
_control.Selection.ClearSelection();
else
_control.Selection.MakeSelection();
}
/// <summary>
/// Moves the caret to the first column of the active row.
/// </summary>
public void MoveAbsoluteHome(bool select)
{
Position.X = 0;
Position.Y = 0;
RememberXPos();
CaretMoved(select);
}
/// <summary>
/// Moves the caret to the absolute end of the active row.
/// </summary>
public void MoveAbsoluteEnd(bool select)
{
Position.X = _control.Document[_control.Document.Count - 1].Text.Length;
Position.Y = _control.Document.Count - 1;
RememberXPos();
CaretMoved(select);
}
public Segment CurrentSegment()
{ return _control.Document.GetSegmentFromPos(Position); }
/// <summary>
/// Sets the position of the caret.
/// </summary>
public void SetPos(TextPoint pos)
{
Position = pos;
RememberXPos();
}
#endregion
/// <summary>
/// Initializes the Caret.
/// </summary>
public Caret(TextEditorBase control)
{
Position = new TextPoint(0, 0);
_control = control;
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.