#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

501 lines
13 KiB

// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.CodeQualityAnalysis.Utility;
using System.Drawing.Drawing2D;
using System.Windows;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Interop;
using PointF = System.Drawing.PointF;
namespace ICSharpCode.CodeQualityAnalysis.Controls
{
public class MatrixControl<TMatrix, TItem, TValue> : FrameworkElement, IScrollInfo
where TValue : IValue
where TMatrix : Matrix<TItem, TValue>
{
public event EventHandler<HoveredCellEventArgs<TValue>> HoveredCellChanged;
private Dictionary<string, ImageSource> imgs = new Dictionary<string, ImageSource>();
private Coords currentCell = new Coords(0, 0);
private string font;
private bool canHorizontalScroll = true;
private bool canVerticalScroll = true;
private Size extent = new Size(0, 0);
private Size viewport = new Size(0, 0);
private Point offset;
// will be loaded from Matrix
private int matrixWidth = 0;
private int matrixHeight = 0;
private int fontSize = 0;
private int penSize = 0;
private TMatrix matrix;
public TMatrix Matrix
{
get { return matrix; }
set
{
matrix = value;
// matrixHeight = matrix.HeaderRows.Count - 1;
// matrixWidth = matrix.HeaderColumns.Count - 1;
}
}
public int CellHeight { get; set; }
public int CellWidth { get; set; }
public HoveredCell<TValue> HoveredCell { get; set; }
public bool RenderZeroes { get; set; }
public IColorizer<TValue> Colorizer { get; set; }
public MatrixControl()
{
CellHeight = CellWidth = 18;
matrixWidth = 0;
matrixHeight = 0;
fontSize = CellHeight / 3;
penSize = 1;
font = "Verdana";
HoveredCell = new HoveredCell<TValue>();
}
public void SetVisibleItems(HeaderType type, ICollection<TItem> visibleItems)
{
matrix.SetVisibleItems(type, visibleItems);
matrixHeight = matrix.HeaderRows.Count;
matrixWidth = matrix.HeaderColumns.Count;
bool changedCoords = false;
if (currentCell.X > matrixHeight) {
currentCell = new Coords(matrixHeight - 1, currentCell.Y);
changedCoords = true;
}
if (currentCell.Y > matrixWidth) {
currentCell = new Coords(currentCell.X, matrixWidth - 1);
changedCoords = true;
}
if (changedCoords) {
SetHoveredCell();
}
if (matrixHeight >= 0 && matrixWidth >= 0)
InvalidateVisual();
}
public void HighlightLine(HeaderType type, INode node)
{
var items = type == HeaderType.Columns ? matrix.HeaderColumns : matrix.HeaderRows;
for (int i = 0; i < items.Count; i++) {
if (node.Equals(items[i])) {
currentCell = type == HeaderType.Columns ?
new Coords(i, currentCell.Y) :
new Coords(currentCell.X, i);
SetHoveredCell();
}
}
}
protected override void OnMouseMove(System.Windows.Input.MouseEventArgs e)
{
base.OnMouseMove(e);
var point = e.GetPosition(this);
if (point.X < matrixWidth * CellWidth
&& point.Y < matrixHeight * CellHeight)
currentCell = new Coords(
(int)((point.X + offset.X) / CellWidth),
(int)((point.Y + offset.Y) / CellHeight));
// else // if we are out of matrix just use last cell
// currentCell = new Coords(-1, -1);
if (currentCell.X != HoveredCell.RowIndex ||
currentCell.Y != HoveredCell.ColumnIndex)
{
InvalidateVisual();
SetHoveredCell();
}
}
protected void SetHoveredCell()
{
HoveredCell.RowIndex = currentCell.Y;
HoveredCell.ColumnIndex = currentCell.X;
HoveredCell.Value = matrix[HoveredCell.RowIndex, HoveredCell.ColumnIndex];
if (HoveredCellChanged != null)
HoveredCellChanged(this, new HoveredCellEventArgs<TValue>(HoveredCell));
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
// how much space we got for cells
var maxWidth = ((int) viewport.Width / CellWidth) + 1;
var maxHeight = ((int) viewport.Height / CellHeight) + 1;
// how many cells we will draw
// sometimes happens when half of cell is hidden in scroll so text isnt drawn
// so lets drawn one more cell
var cellsHorizontally = maxWidth > matrixWidth ? matrixWidth : maxWidth + 1;
var cellsVertically = maxHeight > matrixHeight ? matrixHeight : maxHeight + 1;
// number of cell which will be drawn
var scaledOffsetX = (int)offset.X / CellWidth;
var scaledOffsetY = (int)offset.Y / CellHeight;
// sets how much of cell on border should be drawn
var offsetDiffX = offset.X - scaledOffsetX * CellWidth;
var offsetDiffY = offset.Y - scaledOffsetY * CellHeight;
// background
var background = new Rect(0, 0, cellsHorizontally * CellWidth, cellsVertically * CellHeight);
var backgroundColor = new SolidColorBrush(Colors.Yellow);
backgroundColor.Freeze();
drawingContext.DrawRectangle(backgroundColor, null, background);
var currentXLine = (currentCell.X - scaledOffsetX) * CellWidth - offsetDiffX;
var currentYLine = (currentCell.Y - scaledOffsetY) * CellHeight - offsetDiffY;
// hovering
if (currentCell.X >= 0 || currentCell.Y >= 0) {
// hover y line
var rect = new Rect(0,
currentYLine,
CellWidth * cellsHorizontally,
CellHeight);
var brush = new SolidColorBrush(Colors.GreenYellow);
brush.Freeze();
drawingContext.DrawRectangle(brush, null, rect);
// hover x line
rect = new Rect(currentXLine,
0,
CellWidth,
CellHeight * cellsVertically);
brush = new SolidColorBrush(Colors.GreenYellow);
brush.Freeze();
drawingContext.DrawRectangle(brush, null, rect);
// hover cell
rect = new Rect(
(currentCell.X - scaledOffsetX) * CellWidth - offsetDiffX,
(currentCell.Y - scaledOffsetY) * CellHeight - offsetDiffY,
CellWidth,
CellHeight);
brush = new SolidColorBrush(Colors.Red);
brush.Freeze();
drawingContext.DrawRectangle(brush, null, rect);
}
// text
for (int i = 0; i < cellsHorizontally; i++) {
for (int j = 0; j < cellsVertically; j++) { // dont draw text in unavailables places
int rowIndex = j;
int columnIndex = i;
var value = matrix[rowIndex, columnIndex];
if (Colorizer != null) {
var rect = new Rect(
i * CellWidth - offsetDiffX,
j * CellHeight - offsetDiffY,
CellWidth,
CellHeight);
SolidColorBrush brush = null;
// if ((i * CellWidth - offsetDiffX) == currentXLine ||
// ((i * CellWidth - offsetDiffX) == currentXLine))
// brush = Brushes.Pink; //Colorizer.GetColorBrushMixedWith(Colors.GreenYellow, value);
// else
brush = Colorizer.GetColorBrush(value);
drawingContext.DrawRectangle(brush, null, rect);
}
if (!RenderZeroes && value != null && value.Text != "0") // rendering zeroes would be distracting
drawingContext.DrawImage(
CreateText(value.Text),
new Rect(i * CellWidth - offsetDiffX, j * CellHeight - offsetDiffY, CellWidth, CellHeight));
}
}
// grid
var pen = new Pen(Brushes.Black, penSize);
pen.Freeze();
// grid x
for (int i = 0; i <= cellsHorizontally; i++)
drawingContext.DrawLine(pen,
new Point(i * CellWidth - offsetDiffX, 0),
new Point(i * CellWidth - offsetDiffX,
cellsVertically * CellWidth));
// grid y
for (int i = 0; i <= cellsVertically; i++)
drawingContext.DrawLine(pen,
new Point(0, i * CellHeight - offsetDiffY),
new Point(cellsHorizontally * CellHeight,
i * CellHeight - offsetDiffY));
}
public ImageSource CreateText(string text)
{
if (imgs.ContainsKey(text))
return imgs[text];
var bmp = new System.Drawing.Bitmap(CellWidth, CellHeight);
var g = System.Drawing.Graphics.FromImage(bmp);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
var fontOjb = new System.Drawing.Font(font, fontSize);
var size = g.MeasureString(text, fontOjb);
var spanWidth = (CellWidth - size.Width) / 2;
var spanHeight = (CellHeight - size.Height) / 2;
g.DrawString(text, fontOjb, System.Drawing.Brushes.Black, new PointF(spanWidth, spanHeight));
g.Dispose();
var bitmap = bmp.GetHbitmap();
var img = Imaging.CreateBitmapSourceFromHBitmap(bitmap,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromWidthAndHeight(bmp.Width, bmp.Height));
img.Freeze();
imgs.Add(text, img);
Helper.DeleteObject(bitmap);
return img;
}
public bool CanVerticallyScroll
{
get { return canVerticalScroll; }
set { canVerticalScroll = value; }
}
public bool CanHorizontallyScroll
{
get { return canHorizontalScroll; }
set { canHorizontalScroll = value; }
}
/// <summary>
/// Width of entire component
/// </summary>
public double ExtentWidth
{
get { return extent.Width; }
}
/// <summary>
/// Height of entire component
/// </summary>
public double ExtentHeight
{
get { return extent.Height; }
}
/// <summary>
/// Width of available area
/// </summary>
public double ViewportWidth
{
get { return viewport.Width; }
}
/// <summary>
/// Height of available area
/// </summary>
public double ViewportHeight
{
get { return viewport.Height; }
}
/// <summary>
/// Where is scrollbar located on X
/// </summary>
public double HorizontalOffset
{
get { return offset.X; }
}
/// <summary>
/// Where is scrollbar located on Y
/// </summary>
public double VerticalOffset
{
get { return offset.Y; }
}
public ScrollViewer ScrollOwner { get; set; }
public void LineUp()
{
SetVerticalOffset(VerticalOffset - 1);
}
public void LineDown()
{
SetVerticalOffset(VerticalOffset + 1);
}
public void LineLeft()
{
SetHorizontalOffset(HorizontalOffset - 1);
}
public void LineRight()
{
SetHorizontalOffset(HorizontalOffset + 1);
}
public void PageUp()
{
SetVerticalOffset(VerticalOffset - CellHeight);
}
public void PageDown()
{
SetVerticalOffset(VerticalOffset + CellHeight);
}
public void PageLeft()
{
SetHorizontalOffset(HorizontalOffset - CellWidth);
}
public void PageRight()
{
SetHorizontalOffset(HorizontalOffset + CellWidth);
}
public void MouseWheelUp()
{
SetVerticalOffset(VerticalOffset - 4);
}
public void MouseWheelDown()
{
SetVerticalOffset(VerticalOffset + 4);
}
public void MouseWheelLeft()
{
SetVerticalOffset(HorizontalOffset - 4);
}
public void MouseWheelRight()
{
SetVerticalOffset(HorizontalOffset + 4);
}
public void SetHorizontalOffset(double offset)
{
if (offset == this.offset.X) return;
this.offset.X = offset;
InvalidateVisual();
}
public void SetVerticalOffset(double offset)
{
if (offset == this.offset.Y) return;
this.offset.Y = offset;
InvalidateVisual();
}
public Rect MakeVisible(Visual visual, Rect rectangle)
{
throw new NotImplementedException();
}
protected override Size MeasureOverride(Size availableSize)
{
VerifyScrollData(availableSize, new Size(matrixWidth * CellWidth, matrixHeight * CellHeight));
return viewport;
}
protected override Size ArrangeOverride(Size finalSize)
{
VerifyScrollData(finalSize, new Size(matrixWidth * CellWidth, matrixHeight * CellHeight));
return finalSize;
}
protected void VerifyScrollData(Size viewport, Size extent)
{
if (double.IsInfinity(viewport.Width))
viewport.Width = extent.Width;
if (double.IsInfinity(viewport.Height))
viewport.Height = extent.Height;
// if viewport changes so recalculates offsets
offset.X = Math.Max(0, Math.Min(offset.X, ExtentWidth - ViewportWidth));
offset.Y = Math.Max(0, Math.Min(offset.Y, ExtentHeight - ViewportHeight));
ScrollOwner.InvalidateScrollInfo();
this.extent = extent;
this.viewport = viewport;
if (ScrollOwner != null)
ScrollOwner.InvalidateScrollInfo();
}
private struct Coords
{
public int Y { get; private set; }
public int X { get; private set; }
public Coords(int x, int y) : this()
{
X = x;
Y = y;
}
}
}
public class HoveredCell<TValue>
{
public int RowIndex { get; set; }
public int ColumnIndex { get; set; }
public TValue Value { get; set; }
}
public class HoveredCellEventArgs<TValue> : EventArgs
{
public HoveredCell<TValue> HoveredCell { get; set; }
public HoveredCellEventArgs(HoveredCell<TValue> cell)
{
HoveredCell = cell;
}
}
}