AvalonEdit/ICSharpCode.AvalonEdit/TextViewPosition.cs

200 строки
5.9 KiB
C#

// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Globalization;
using ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.AvalonEdit
{
/// <summary>
/// Represents a text location with a visual column.
/// </summary>
public struct TextViewPosition : IEquatable<TextViewPosition>, IComparable<TextViewPosition>
{
int line, column, visualColumn;
bool isAtEndOfLine;
/// <summary>
/// Gets/Sets Location.
/// </summary>
public TextLocation Location {
get {
return new TextLocation(line, column);
}
set {
line = value.Line;
column = value.Column;
}
}
/// <summary>
/// Gets/Sets the line number.
/// </summary>
public int Line {
get { return line; }
set { line = value; }
}
/// <summary>
/// Gets/Sets the (text) column number.
/// </summary>
public int Column {
get { return column; }
set { column = value; }
}
/// <summary>
/// Gets/Sets the visual column number.
/// Can be -1 (meaning unknown visual column).
/// </summary>
public int VisualColumn {
get { return visualColumn; }
set { visualColumn = value; }
}
/// <summary>
/// When word-wrap is enabled and a line is wrapped at a position where there is no space character;
/// then both the end of the first TextLine and the beginning of the second TextLine
/// refer to the same position in the document, and also have the same visual column.
/// In this case, the IsAtEndOfLine property is used to distinguish between the two cases:
/// the value <c>true</c> indicates that the position refers to the end of the previous TextLine;
/// the value <c>false</c> indicates that the position refers to the beginning of the next TextLine.
///
/// If this position is not at such a wrapping position, the value of this property has no effect.
/// </summary>
public bool IsAtEndOfLine {
get { return isAtEndOfLine; }
set { isAtEndOfLine = value; }
}
/// <summary>
/// Creates a new TextViewPosition instance.
/// </summary>
public TextViewPosition(int line, int column, int visualColumn)
{
this.line = line;
this.column = column;
this.visualColumn = visualColumn;
this.isAtEndOfLine = false;
}
/// <summary>
/// Creates a new TextViewPosition instance.
/// </summary>
public TextViewPosition(int line, int column)
: this(line, column, -1)
{
}
/// <summary>
/// Creates a new TextViewPosition instance.
/// </summary>
public TextViewPosition(TextLocation location, int visualColumn)
{
this.line = location.Line;
this.column = location.Column;
this.visualColumn = visualColumn;
this.isAtEndOfLine = false;
}
/// <summary>
/// Creates a new TextViewPosition instance.
/// </summary>
public TextViewPosition(TextLocation location)
: this(location, -1)
{
}
/// <inheritdoc/>
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture,
"[TextViewPosition Line={0} Column={1} VisualColumn={2} IsAtEndOfLine={3}]",
this.line, this.column, this.visualColumn, this.isAtEndOfLine);
}
#region Equals and GetHashCode implementation
// The code in this region is useful if you want to use this structure in collections.
// If you don't need it, you can just remove the region and the ": IEquatable<Struct1>" declaration.
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is TextViewPosition)
return Equals((TextViewPosition)obj); // use Equals method below
else
return false;
}
/// <inheritdoc/>
public override int GetHashCode()
{
int hashCode = isAtEndOfLine ? 115817 : 0;
unchecked {
hashCode += 1000000007 * Line.GetHashCode();
hashCode += 1000000009 * Column.GetHashCode();
hashCode += 1000000021 * VisualColumn.GetHashCode();
}
return hashCode;
}
/// <summary>
/// Equality test.
/// </summary>
public bool Equals(TextViewPosition other)
{
return this.Line == other.Line && this.Column == other.Column && this.VisualColumn == other.VisualColumn && this.IsAtEndOfLine == other.IsAtEndOfLine;
}
/// <summary>
/// Equality test.
/// </summary>
public static bool operator ==(TextViewPosition left, TextViewPosition right)
{
return left.Equals(right);
}
/// <summary>
/// Inequality test.
/// </summary>
public static bool operator !=(TextViewPosition left, TextViewPosition right)
{
return !(left.Equals(right)); // use operator == and negate result
}
#endregion
/// <inheritdoc/>
public int CompareTo(TextViewPosition other)
{
int r = this.Location.CompareTo(other.Location);
if (r != 0)
return r;
r = this.visualColumn.CompareTo(other.visualColumn);
if (r != 0)
return r;
if (isAtEndOfLine && !other.isAtEndOfLine)
return -1;
else if (!isAtEndOfLine && other.isAtEndOfLine)
return 1;
return 0;
}
}
}