//
//
//
//
// $Revision$
//
using ICSharpCode.AvalonEdit.Editing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;
using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering;
namespace XmlDOM
{
///
/// Handles the text markers for a code editor.
///
sealed class TextMarkerService : DocumentColorizingTransformer, IBackgroundRenderer, ITextMarkerService
{
internal TextSegmentCollection markers;
TextArea area;
public TextMarkerService(TextArea area)
{
this.area = area;
markers = new TextSegmentCollection(area.Document);
this.area.TextView.BackgroundRenderers.Add(this);
this.area.TextView.LineTransformers.Add(this);
}
#region ITextMarkerService
public ITextMarker Create(int startOffset, int length)
{
TextMarker m = new TextMarker(this, startOffset, length);
markers.Add(m);
// no need to mark segment for redraw: the text marker is invisible until a property is set
return m;
}
public IEnumerable TextMarkers {
get { return markers.Cast(); }
}
public void RemoveAll(Predicate predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
foreach (TextMarker m in markers.ToArray()) {
if (predicate(m))
m.Delete();
}
}
internal void Remove(TextMarker marker)
{
markers.Remove(marker);
Redraw(marker);
}
///
/// Redraws the specified text segment.
///
internal void Redraw(ISegment segment)
{
area.TextView.Redraw(segment, DispatcherPriority.Normal);
}
#endregion
#region DocumentColorizingTransformer
protected override void ColorizeLine(DocumentLine line)
{
if (markers == null)
return;
int lineStart = line.Offset;
int lineEnd = lineStart + line.Length;
foreach (TextMarker marker in markers.FindOverlappingSegments(lineStart, line.Length)) {
Brush foregroundBrush = null;
if (marker.ForegroundColor != null) {
foregroundBrush = new SolidColorBrush(marker.ForegroundColor.Value);
foregroundBrush.Freeze();
}
ChangeLinePart(
Math.Max(marker.StartOffset, lineStart),
Math.Min(marker.EndOffset, lineEnd),
element => {
if (foregroundBrush != null) {
element.TextRunProperties.SetForegroundBrush(foregroundBrush);
}
}
);
}
}
#endregion
#region IBackgroundRenderer
public KnownLayer Layer {
get {
// draw behind selection
return KnownLayer.Selection;
}
}
public void Draw(TextView textView, DrawingContext drawingContext)
{
if (textView == null)
throw new ArgumentNullException("textView");
if (drawingContext == null)
throw new ArgumentNullException("drawingContext");
if (markers == null || !textView.VisualLinesValid)
return;
var visualLines = textView.VisualLines;
if (visualLines.Count == 0)
return;
int viewStart = visualLines.First().FirstDocumentLine.Offset;
int viewEnd = visualLines.Last().LastDocumentLine.Offset + visualLines.Last().LastDocumentLine.Length;
foreach (TextMarker marker in markers.FindOverlappingSegments(viewStart, viewEnd - viewStart)) {
if (marker.BackgroundColor != null) {
BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
geoBuilder.AddSegment(textView, marker);
Geometry geometry = geoBuilder.CreateGeometry();
if (geometry != null) {
Color color = marker.BackgroundColor.Value;
SolidColorBrush brush = new SolidColorBrush(color);
brush.Freeze();
drawingContext.DrawGeometry(brush, null, geometry);
}
}
}
}
#endregion
}
sealed class TextMarker : TextSegment, ITextMarker
{
readonly TextMarkerService service;
public TextMarker(TextMarkerService service, int startOffset, int length)
{
if (service == null)
throw new ArgumentNullException("service");
this.service = service;
this.StartOffset = startOffset;
this.Length = length;
}
public event EventHandler Deleted;
public bool IsDeleted {
get { return !this.IsConnectedToCollection; }
}
public void Delete()
{
if (this.IsConnectedToCollection) {
service.Remove(this);
if (Deleted != null)
Deleted(this, EventArgs.Empty);
}
}
void Redraw()
{
service.Redraw(this);
}
Color? backgroundColor;
public Color? BackgroundColor {
get { return backgroundColor; }
set {
if (backgroundColor != value) {
backgroundColor = value;
Redraw();
}
}
}
Color? foregroundColor;
public Color? ForegroundColor {
get { return foregroundColor; }
set {
if (foregroundColor != value) {
foregroundColor = value;
Redraw();
}
}
}
public object Tag { get; set; }
}
///
/// Represents a text marker.
///
public interface ITextMarker
{
///
/// Gets the start offset of the marked text region.
///
int StartOffset { get; }
///
/// Gets the end offset of the marked text region.
///
int EndOffset { get; }
///
/// Gets the length of the marked region.
///
int Length { get; }
///
/// Deletes the text marker.
///
void Delete();
///
/// Gets whether the text marker was deleted.
///
bool IsDeleted { get; }
///
/// Event that occurs when the text marker is deleted.
///
event EventHandler Deleted;
///
/// Gets/Sets the background color.
///
Color? BackgroundColor { get; set; }
///
/// Gets/Sets the foreground color.
///
Color? ForegroundColor { get; set; }
///
/// Gets/Sets an object with additional data for this text marker.
///
object Tag { get; set; }
}
public interface ITextMarkerService
{
///
/// Creates a new text marker. The text marker will be invisible at first,
/// you need to set one of the Color properties to make it visible.
///
ITextMarker Create(int startOffset, int length);
///
/// Gets the list of text markers.
///
IEnumerable TextMarkers { get; }
///
/// Removes all text markers that match the condition.
///
void RemoveAll(Predicate predicate);
}
}