#define INTERNAL_MARKS
using System;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Diagnostics;
using System.Globalization;
using System.Windows.Controls;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using agree;
namespace agree.Wpf.Util
{
using Math = System.Math;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public abstract class TfsDrawingVisual : DrawingVisual
{
protected TfsDrawingVisual(TfsVisualHost host)
{
this.host = host;
}
readonly public TfsVisualHost host;
public Size m_size;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Visual element of a coreference box
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class TdeCoreference : TfsDrawingVisual
{
static Brush tag_brush = new SolidColorBrush(Colors.ForestGreen);
static Pen tag_pen = new Pen(Brushes.ForestGreen, 1);
int i_tag;
public TdeCoreference(TfsVisualHost host, int i_tag)
: base(host)
{
this.i_tag = i_tag;
using (DrawingContext dc = RenderOpen())
Draw(dc);
}
void Draw(DrawingContext dc)
{
Double x = 0, y = 0;
Typeface font = host.owner.TypeFace;
Double font_size = host.owner.FontSize;
Double tag_height = font_size * .85;
Rect r = new Rect(x, y, tag_height, tag_height);
FormattedText ft = i_tag.ToString().FormattedText(font, tag_height * .9, tag_brush);
r.Width = Math.Max(r.Width, ft.Width + 2);
r.Width++;
r.Height++;
if (dc != null)
{
dc.DrawText(ft, x + ((r.Width / 2) - (ft.Width / 2)), y + ((r.Height / 2) - (ft.Height / 2)));
dc.DrawRectangle(null, tag_pen, r);
}
m_size = new Size(r.Width, r.Height);
}
public int TagNumber { get { return i_tag; } }
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Visual element of a Typed Feature Structure
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public class TdeConstraint : TfsDrawingVisual
{
const Double pen_width = 1.5;
const Double half_pen = pen_width / 2;
static Pen black_pen = new Pen(Brushes.Black, pen_width);
static Brush type_brush = new SolidColorBrush(Color.FromRgb(0x4e, 0x4e, 0xea));
static Brush mark_brush = new SolidColorBrush(Colors.Crimson);
static Brush info_brush = new SolidColorBrush(Colors.Gray);
static Brush hilt_brush = new SolidColorBrush(Color.FromArgb(0x80, 0xFF, 0xFE, 0x00));
static TdeConstraint()
{
black_pen.StartLineCap = PenLineCap.Square;
black_pen.EndLineCap = PenLineCap.Square;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public TdeConstraint(TfsVisualHost host, Edge e)
: base(host)
{
this.m_edge = e;
if (host.tfs_highlight != null && host.tfs_highlight.Edge.Equals(e))
m_flags |= DrawState.Highlight;
using (DrawingContext dc = RenderOpen())
Draw(dc);
}
Edge m_edge;
public Edge Edge { get { return m_edge; } }
[Flags]
enum DrawState
{
Highlight = 0x01,
OwnerBackground = 0x02,
};
DrawState m_flags = 0;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Draw(DrawingContext dc)
{
Tfs tfs = host.owner._TfsEdge;
Typeface font = host.owner.TypeFace;
Double font_size = host.owner.FontSize;
Double line_height = font_size * 1.15;
Double x = 0;
Double y = 0;
Double x_max = 0;
if (host.owner.f_brackets)
{
x += 6;
y += 2;
}
{
Double x0 = x;
FormattedText ft = tfs.tm.GetEdgeType(m_edge.FlagsId).Name.FormattedText(font, font_size, type_brush);
if (dc != null)
dc.DrawText(ft, x0, y);
x0 += ft.Width + 4;
if (host.viz == this)
{
String info = null;
agree.Type ht = host.owner._TfsEdge.Type;
if (ht.Definition.Edge.Equals(m_edge))
info = "definition";
else if (ht.Expanded.Edge.Equals(m_edge))
info = "expanded";
else
info = "copy";
Typeface f2 = new Typeface(font.FontFamily, FontStyles.Italic, FontWeights.Normal, FontStretches.Normal);
info = "(" + info + ")";
ft = info.FormattedText(f2, font_size, info_brush);
if (dc != null)
dc.DrawText(ft, x0, y);
x0 += ft.Width + 2;
}
miew.Math.Extensions.Maximize(ref x_max, x0);
y += line_height;
}
// get maximum width among feature strings
Double feature_end_x = FeatureStringMax() + 3;
// feature/type tuples
foreach (ConstraintRef cref in tfs.AllConstraintRefs(m_edge).OrderBy(e => e.FeatureInfo.maximal_type.m_level))
{
Size sz = DrawFeature(dc, cref, feature_end_x, x, y);
if (x + sz.Width > x_max)
x_max = x + sz.Width;
y += sz.Height;
}
x = x_max;
// feature structure outer brackets
if (host.owner.f_brackets)
{
x += 4;
y += 2;
if (dc != null)
{
Double xhp = Math.Round(x) + half_pen;
Double yhp = Math.Round(y) + half_pen;
dc.DrawLine(black_pen, new Point(half_pen, half_pen), new Point(5 + half_pen, half_pen));
dc.DrawLine(black_pen, new Point(half_pen, half_pen), new Point(half_pen, yhp));
dc.DrawLine(black_pen, new Point(half_pen, yhp), new Point(5 + half_pen, yhp));
dc.DrawLine(black_pen, new Point(xhp - 5, half_pen), new Point(xhp, half_pen));
dc.DrawLine(black_pen, new Point(xhp, half_pen), new Point(xhp, y));
dc.DrawLine(black_pen, new Point(xhp - 5, yhp), new Point(xhp, yhp));
}
x += pen_width + half_pen;
y += pen_width + half_pen;
}
Size tsize = new Size(x, y);
if (dc != null)
{
if (m_flags.HasFlag(DrawState.Highlight))
{
//Rect rr = new Rect(VisualOffset.X, VisualOffset.Y, tsize.Width, tsize.Height);
Rect rr = new Rect(0, 0, tsize.Width, tsize.Height);
dc.DrawRectangle(hilt_brush, null, rr);
}
//else
// if (m_flags.HasFlag(DrawState.Highlight))
// dc.DrawRectangle(host.owner.Background, null, new Rect(VisualOffset.X, VisualOffset.Y, m_size.Width, m_size.Height));
}
m_size = tsize;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Double FeatureStringMax()
{
Tfs tfs = host.owner._TfsEdge;
if (!tfs.tm.GetEdgeType(m_edge.FlagsId).HasAnyFeatures)
return 0;
Typeface font = host.owner.TypeFace;
Double font_size = host.owner.FontSize;
return tfs.AllConstraintRefs(m_edge).Max(cref =>
{
Double w = cref.Feature.ToUpper().FormattedText(font, font_size).Width;
if (host.owner.f_marks)
w += (" " + MarkText(cref.Constraint)).FormattedText(font, font_size * .85).Width;
return w;
});
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Size DrawFeature(DrawingContext dc, ConstraintRef cref, Double feature_end_x, Double x_base, Double y_base)
{
TypeMgr tm = host.owner._TfsEdge.tm;
Typeface font = host.owner.TypeFace;
Double font_size = host.owner.FontSize;
Double line_height = font_size * 1.15;
FormattedText ft;
Edge constraint = cref.Constraint;
Double x = x_base;
Double y = y_base;
Brush b = tm.GetMaximalTypeForFeature(cref.i_feat) == tm.GetEdgeType(m_edge.FlagsId) ? Brushes.Black : Brushes.Gray;
ft = cref.Feature.ToUpper().FormattedText(font, font_size, b);
if (dc != null)
dc.DrawText(ft, x, y);
x += ft.Width;
if (host.owner.f_marks
#if INTERNAL_MARKS
&& !constraint.IsCoreferenced
#endif
)
{
ft = (" " + MarkText(constraint)).FormattedText(font, font_size * .85, mark_brush);
if (dc != null)
dc.DrawText(ft, x, y + (font_size * .15));
x += ft.Width;
}
x = x_base + feature_end_x;
bool f_coref_nodisplay = false;
if (constraint.IsCoreferenced)
{
ReentrancyFinder.Entry ce;
if (host.dict_corefs.TryGetValue(constraint,out ce) && (!cref.Equals(ce.cref_display) || cref.ConstraintTypeIsTop))
f_coref_nodisplay = true;
}
//if (cref.Pool.i_feat == tm.f_ix_dlist_list && !f_coref_nodisplay)
//{
// y += 3;
// TdeDiffList tv = new TdeDiffList(host, host.owner._TfsEdge.WrapEdgeInSameTfs(cref.Host));
// this.Children.Add(tv);
// tv.Offset = new Vector(x, y);
// x += tv.m_size.Width;
// y += tv.m_size.Height;
//}
//else
{
ReentrancyFinder.Entry coref = null;
if (constraint.IsCoreferenced)
{
Debug.Assert(host.dict_corefs != null);
if (host.dict_corefs.TryGetValue(constraint, out coref))
{
TdeCoreference tcr =
#if INTERNAL_MARKS
new TdeCoreference(host, constraint.Mark);
#else
new TdeCoreference(host, coref.i_tag);
#endif
this.Children.Add(tcr);
Double yc = y + font_size * .20;
tcr.Offset = new Vector(x, yc);
x += tcr.m_size.Width + 5;
}
}
#if true
if (f_coref_nodisplay)
{
// don't render the type for coreference nodes when either:
// a. it's been rendered elsewhere, or
// b. it's *top*
y += line_height;
}
else
#endif
{
String val = tm.GetStringValue(cref.Constraint.FlagsId);
if (val != null)
{
val = "“" + val + "”";
ft = val.FormattedText(font, font_size, type_brush);
if (dc != null)
dc.DrawText(ft, x, y);
x += ft.Width + 2;
y += line_height;
}
else if ((constraint.FlagsId & agree.Edge.Flag.PrunedDuringParsing) > 0)
{
ft = "(pruned during parsing)".FormattedText(font, font_size, mark_brush);
if (dc != null)
dc.DrawText(ft, x, y);
x += ft.Width + 2;
y += line_height;
}
else
{
Type t = tm.GetEdgeType(constraint.FlagsId);
if (t.IsBare)
{
val = cref.ConstraintType.Name;
ft = val.FormattedText(font, font_size, type_brush);
if (dc != null)
dc.DrawText(ft, x, y);
x += ft.Width + 2;
y += line_height;
}
else
{
y += 3;
TdeConstraint tv = new TdeConstraint(host, constraint);
this.Children.Add(tv);
tv.Offset = new Vector(x, y);
x += tv.m_size.Width;
y += tv.m_size.Height;
}
}
}
}
return new Size(x - x_base, y - y_base);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
String MarkText(Edge e)
{
if (e.Mark == 0)
return String.Empty;
String s = e.IsCoreferenced ? "⇌" : String.Empty;
return s + String.Format("{0}", e.Mark);
}
#if false
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
Debug.WriteLine("viz.htc {0} {1}", this.Edge, hitTestParameters.HitPoint);
return base.HitTestCore(hitTestParameters);
}
#endif
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public class TdeDiffList : TfsDrawingVisual
{
static Brush type_brush = new SolidColorBrush(Color.FromRgb(0x4e, 0x4e, 0xea));
static Pen thin_black_pen = new Pen(Brushes.Black, 1);
Tfs tfs;
public TdeDiffList(TfsVisualHost host, Tfs te)
: base(host)
{
this.tfs = te;
using (DrawingContext dcx = RenderOpen())
Draw(dcx);
}
const double OPlusLRPad = 4;
const double OPlusRadius = 6;
void Draw(DrawingContext dc)
{
TypeMgr tm = host.owner._TfsEdge.tm;
Typeface font = host.owner.TypeFace;
Double font_size = host.owner.FontSize;
Double line_height = font_size * 1.15;
Double x = 0;
Double y_max = line_height;
var rgrgcr = PartitionDiffList(tfs).ToArray();
int j;
for (j = 0; j < rgrgcr.Length; j++)
{
ConstraintRef[] rgcr = rgrgcr[j];
Edge he = rgcr[0].Host;
if (he.IsCoreferenced)
{
#if INTERNAL_MARKS
TdeCoreference tcr = new TdeCoreference(host, he.Mark);
#else
ReentrancyFinder.Entry coref = host.dict_corefs[he];
TdeCoreference tcr = new TdeCoreference(host, coref.i_tag);
#endif
this.Children.Add(tcr);
x += tcr.m_size.Width + 3;
}
TdeList tv = new TdeList(host, rgcr);
this.Children.Add(tv);
x += tv.m_size.Width + 2;
/// OPlus goes here
x += OPlusLRPad + (OPlusRadius * 2) + OPlusLRPad;
y_max = Math.Max(y_max, tv.m_size.Height);
}
double y_center = y_max / 2;
//////////////////////////////////////////////////////
/// second pass
//////////////////////////////////////////////////////
x = 0;
j = 0;
foreach (TfsDrawingVisual viz in Children)
{
ConstraintRef[] rgcr = rgrgcr[j];
viz.Offset = new Vector(x, y_center - viz.m_size.Height / 2);
x += viz.m_size.Width;
if (viz is TdeCoreference)
x += 3;
if (viz is TdeList)
{
x += 2;
if (j < rgrgcr.Length)
{
x += OPlusLRPad + OPlusRadius;
dc.DrawEllipse(null, thin_black_pen, new Point(x, y_center), OPlusRadius, OPlusRadius);
dc.DrawLine(thin_black_pen, new Point(x, y_center - OPlusRadius), new Point(x, y_center + OPlusRadius));
dc.DrawLine(thin_black_pen, new Point(x - OPlusRadius, y_center), new Point(x + OPlusRadius, y_center));
x += OPlusRadius + OPlusLRPad;
j++;
}
}
}
Edge e = tfs.GetEdge(tfs.tm.f_ix_dlist_last, tfs.Edge.Mark);
if (e.IsCoreferenced)
{
#if INTERNAL_MARKS
TdeCoreference tcr = new TdeCoreference(host, e.Mark);
#else
ReentrancyFinder.Entry coref = host.dict_corefs[e];
TdeCoreference tcr = new TdeCoreference(host, coref.i_tag);
#endif
this.Children.Add(tcr);
tcr.Offset = new Vector(x, y_center - tcr.m_size.Height / 2);
x += tcr.m_size.Width + 3;
}
String val = tfs.tm.GetEdgeType(e.FlagsId).Name;
FormattedText ft = val.FormattedText(font, font_size, type_brush);
dc.DrawText(ft, x, y_center - ft.Height /2);
x += ft.Width + 2;
m_size = new Size(x, y_max);
}
static IEnumerable<ConstraintRef[]> PartitionDiffList(Tfs te)
{
var rgcr = te.GetDiffListLists(te.Edge).ToArray();
ConstraintRef[] sect;
for (int i = 0; (sect = rgcr.Skip(i).TakeWhile((cr, ix) => ix == 0 || !cr.Host.IsCoreferenced).ToArray()).Length > 0; i += sect.Length)
yield return sect;
}
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Retained graphics instructions for an AVM "list"
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public class TdeList : TfsDrawingVisual
{
const Double pen_width = 1.5;
const Double half_pen = pen_width / 2;
static Pen black_pen = new Pen(Brushes.Black, pen_width);
static Brush type_brush = new SolidColorBrush(Color.FromRgb(0x4e, 0x4e, 0xea));
IEnumerable<ConstraintRef> rgcr;
public TdeList(TfsVisualHost host, IEnumerable<ConstraintRef> rgcr)
: base(host)
{
this.rgcr = rgcr;
using (DrawingContext dcx = RenderOpen())
Draw(dcx);
}
const Double AngleWidth = 7.0;
const Double AngleYMargin = 10;
void Draw(DrawingContext dc)
{
TypeMgr tm = host.owner._TfsEdge.tm;
Typeface font = host.owner.TypeFace;
Double font_size = host.owner.FontSize;
Double line_height = font_size * 1.15;
FormattedText comma = ",".FormattedText(font, 20.0);
//////////////////////////////////////////////////////
/// first pass: create items and find item with the
/// largest height
//////////////////////////////////////////////////////
Double x = AngleWidth + 5;
Double y_max = 0;
bool f_first = true;
foreach (ConstraintRef cr in rgcr)
{
if (f_first)
f_first = false;
else
x += comma.Width + 5;
TdeConstraint tv = new TdeConstraint(host, cr.Constraint);
this.Children.Add(tv);
y_max = Math.Max(y_max, tv.m_size.Height);
x += tv.m_size.Width + 5;
}
//////////////////////////////////////////////////////
/// calculations based on tallest item
//////////////////////////////////////////////////////
double y_center = y_max / 2;
double angle_height = Math.Max(Math.Min(y_max - (AngleYMargin * 2), 180), 35);
double y_margin = (y_max - angle_height) / 2;
//////////////////////////////////////////////////////
/// second pass
//////////////////////////////////////////////////////
/// Front angle '<'
Point pt_apex = new Point(0, y_max / 2);
dc.DrawLine(black_pen, pt_apex, new Point(AngleWidth, y_margin));
dc.DrawLine(black_pen, pt_apex, new Point(AngleWidth, y_max - y_margin));
/// center each item vertically
x = AngleWidth + 5;
f_first = true;
foreach (TdeConstraint tv in Children.OfType<TdeConstraint>())
{
if (f_first)
f_first = false;
else
{
dc.DrawText(comma, new Point(x, y_center - comma.Height / 2));
x += comma.Width + 5;
}
tv.Offset = new Vector(x, y_center - tv.m_size.Height / 2);
x += tv.m_size.Width + 5;
}
/// Back angle '>'
x += AngleWidth;
pt_apex = new Point(x, y_max / 2);
dc.DrawLine(black_pen, pt_apex, new Point(x - AngleWidth, y_margin));
dc.DrawLine(black_pen, pt_apex, new Point(x - AngleWidth, y_max - y_margin));
m_size = new Size(x, y_max);
}
};
}