using FastReport.Table;
using FastReport.Utils;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace FastReport.AdvMatrix
{
///
/// Represents a base class for matrix buttons such as expand or sort button.
///
public class MatrixButton : ReportComponentBase
{
///
/// Determines the symbol size, in pixels. 0 indicates the auto size.
///
[Category("Appearance")]
public int SymbolSize { get; set; }
///
/// Determines whether this buttons belongs to column header. For internal use only.
///
[Browsable(false)]
public bool IsColumn { get; set; }
///
/// Gets or set the index of this button. For internal use only.
///
[Browsable(false)]
public int Index { get; set; }
internal AdvMatrixObject Matrix
{
get
{
Base obj = this;
while (obj != null && !(obj is AdvMatrixObject))
obj = obj.Parent;
if (obj is AdvMatrixObject)
return Report.FindObject(obj.Name) as AdvMatrixObject;
return null;
}
}
internal TableCell Cell
{
get
{
Base obj = this;
while (obj != null && !(obj is TableCell))
obj = obj.Parent;
if (obj is TableCell)
return obj as TableCell;
return null;
}
}
///
public override void Assign(Base source)
{
base.Assign(source);
MatrixButton src = source as MatrixButton;
if (src != null)
{
SymbolSize = src.SymbolSize;
IsColumn = src.IsColumn;
Index = src.Index;
}
}
///
public override void Serialize(FRWriter writer)
{
base.Serialize(writer);
MatrixButton c = writer.DiffObject as MatrixButton;
if (SymbolSize != c.SymbolSize)
writer.WriteInt("SymbolSize", SymbolSize);
if (IsColumn != c.IsColumn)
writer.WriteBool("IsColumn", IsColumn);
if (Index != c.Index)
writer.WriteInt("Index", Index);
}
///
/// Initializes a new instance of the class.
///
public MatrixButton()
{
Cursor = System.Windows.Forms.Cursors.Hand;
Printable = false;
Exportable = false;
SymbolSize = 0;
}
}
///
/// Represents the symbol used to display the matrix expand/collapse state.
///
public enum CollapseSymbol
{
///
/// Plus/minus.
///
PlusMinus,
///
/// The pointer.
///
Pointer,
///
/// The arrow.
///
Arrow
}
///
/// Represents the matrix button used to toggle expand/collapse state of matrix headers.
///
public partial class MatrixCollapseButton : MatrixButton
{
///
/// Determines whether this button has a collapsed state. For internal use only.
///
[Browsable(false)]
public bool Collapsed { get; set; }
///
/// Determines whether to show collapse/expand menu on right click.
///
public bool ShowCollapseExpandMenu { get; set; }
///
/// Determines the symbol used to display the button's state.
///
[Category("Appearance")]
public CollapseSymbol Symbol { get; set; }
///
/// Determines if only one button in the group can be expanded.
///
[Category("Behavior")]
public bool Exclusive { get; set; }
///
public override void Assign(Base source)
{
base.Assign(source);
MatrixCollapseButton src = source as MatrixCollapseButton;
if (src != null)
{
Collapsed = src.Collapsed;
Symbol = src.Symbol;
Exclusive = src.Exclusive;
ShowCollapseExpandMenu = src.ShowCollapseExpandMenu;
}
}
///
public override void Draw(FRPaintEventArgs e)
{
if (Parent == null || (Parent as ReportComponentBase).Width == 0 || (Parent as ReportComponentBase).Height == 0)
return;
IGraphics g = e.Graphics;
IGraphicsState state = g.Save();
#if SKIA
g.SmoothingMode = SmoothingMode.None;
#else
g.SmoothingMode = SmoothingMode.AntiAlias;
#endif
PointF[] points = null;
Pen pen = e.Cache.GetPen(Border.Color, Border.Width, DashStyle.Solid);
Brush brush = e.Cache.GetBrush(Border.Color);
// trying to be pixel perfect
float dx = SymbolSize > 0 ? SymbolSize : (int)Math.Min(Width, Height) - 6;
if ((int)dx % 2 == 1)
dx -= 1;
float dy = dx;
g.ScaleTransform(e.ScaleX, e.ScaleY);
g.TranslateTransform((int)Math.Round(AbsLeft + Width / 2 - dx / 2), (int)Math.Round(AbsTop + Height / 2 - dy / 2));
if (Fill is SolidFill)
g.FillRectangle(e.Cache.GetBrush((Fill as SolidFill).Color), new RectangleF(-2, -2, dx + 4, dy + 4));
if (Border.Lines != BorderLines.None)
g.DrawRectangle(pen, -2, -2, dx + 4, dy + 4);
if (Collapsed)
{
switch (Symbol)
{
case CollapseSymbol.PlusMinus:
g.DrawLine(pen, 0, dy / 2, dx, dy / 2);
g.DrawLine(pen, dx / 2, 0, dx / 2, dy);
break;
case CollapseSymbol.Arrow:
points = new PointF[] { new PointF(dx / 2, 0), new PointF(dx, dy / 2), new PointF(dx / 2, dy) };
g.FillAndDrawPolygon(pen, brush, points);
break;
case CollapseSymbol.Pointer:
g.DrawLines(pen, new PointF[] { new PointF(dx / 2, 0), new PointF(dx, dy / 2), new PointF(dx / 2, dy) });
break;
}
}
else
{
switch (Symbol)
{
case CollapseSymbol.PlusMinus:
g.DrawLine(pen, 0, dy / 2, dx, dy / 2);
break;
case CollapseSymbol.Arrow:
points = new PointF[] { new PointF(0, dy / 2), new PointF(dx / 2, dy), new PointF(dx, dy / 2) };
g.FillAndDrawPolygon(pen, brush, points);
break;
case CollapseSymbol.Pointer:
var d = dy / 4 - 1; // a bit more perfect placement
g.DrawLines(pen, new PointF[] { new PointF(0 + d, dy / 2 - d), new PointF(dx / 2 + d, dy - d), new PointF(dx + d, dy / 2 - d) });
break;
}
}
g.Restore(state);
}
///
public override void Serialize(FRWriter writer)
{
base.Serialize(writer);
MatrixCollapseButton c = writer.DiffObject as MatrixCollapseButton;
if (Collapsed != c.Collapsed)
writer.WriteBool("Collapsed", Collapsed);
if (Exclusive != c.Exclusive)
writer.WriteBool("Exclusive", Exclusive);
if (Symbol != c.Symbol)
writer.WriteValue("Symbol", Symbol);
if (ShowCollapseExpandMenu != c.ShowCollapseExpandMenu)
writer.WriteBool("ShowCollapseExpandMenu", ShowCollapseExpandMenu);
}
///
/// For internal use only,return action click for Advanced Matrix collapse button
///
public void MatrixCollapseButtonClick()
{
if (IsColumn)
Matrix.ToggleColumnVisible(Index);
else
Matrix.ToggleRowVisible(Index);
Report.Refresh();
}
internal List GetLinkedItems(HeaderData item)
{
List list = new List();
Action func = (root) =>
{
if (root == null)
return;
foreach (HeaderDataList dl in root.AllItems)
{
if (dl.Descriptor.VisibleToggledBy == Name)
list.Add(dl);
}
};
func(item);
if (list.Count == 0 && item.Value == null)
func(item.Parent);
return list;
}
internal int GetLinkedItemsCount(HeaderData item)
{
int result = 0;
foreach (HeaderDataList dl in GetLinkedItems(item))
result += dl.Count;
return result;
}
///
/// Initializes a new instance of the class.
///
public MatrixCollapseButton()
{
BaseName = "CollapseButton";
Symbol = CollapseSymbol.PlusMinus;
Border.Lines = BorderLines.All;
}
}
///
/// Represents the symbol used to display the matrix sort order.
///
public enum SortSymbol
{
///
/// The arrow.
///
Arrow,
///
/// The pointer.
///
Pointer
}
///
/// Represents the matrix button used to toggle sort order of matrix headers.
///
public partial class MatrixSortButton : MatrixButton
{
///
/// Determines the sort state of this button.
///
[Category("Behavior")]
public SortOrder Sort { get; set; }
///
/// Determines whether "None" sort is allowed when you switch sort states.
///
[Category("Behavior")]
public bool AllowInactiveSort { get; set; }
///
/// Determines the symbol used to display the state of this button.
///
[Category("Appearance")]
public SortSymbol Symbol { get; set; }
///
/// Determines the color used to display button with inactive sort state (Sort = None).
///
[Category("Appearance")]
public Color InactiveSortColor { get; set; }
///
public override void Assign(Base source)
{
base.Assign(source);
MatrixSortButton src = source as MatrixSortButton;
if (src != null)
{
Sort = src.Sort;
AllowInactiveSort = src.AllowInactiveSort;
Symbol = src.Symbol;
InactiveSortColor = src.InactiveSortColor;
}
}
///
/// For internal use only,return action click for Advanced Matrix sort button
///
public void MatrixSortButtonClick()
{
SortOrder sort = (Sort == SortOrder.None ? SortOrder.Ascending : (Sort == SortOrder.Ascending ? SortOrder.Descending : SortOrder.None));
if (Index == 0)
{
// this button toggles sort order of descriptors with SortToggledBy = this
if (!AllowInactiveSort && sort == SortOrder.None)
sort = SortOrder.Ascending;
(Report.FindObject(Name) as MatrixSortButton).Sort = sort;
}
else if (Matrix != null)
{
if (IsColumn)
Matrix.SortRowsByColumn(Index, sort);
else
Matrix.SortColumnsByRow(Index, sort);
}
Report.Refresh();
}
///
public override void Draw(FRPaintEventArgs e)
{
IGraphics g = e.Graphics;
IGraphicsState state = g.Save();
PointF[] points = null;
Pen pen = e.Cache.GetPen(Border.Color, Border.Width, DashStyle.Solid);
Brush brush = e.Cache.GetBrush(pen.Color);
// trying to be pixel perfect
float dx = SymbolSize > 0 ? SymbolSize : (int)Math.Min(Width, Height) - 6;
if ((int)dx % 2 == 1)
dx -= 1;
float dy = dx;
float add = 0;
#if SKIA
g.SmoothingMode = SmoothingMode.None;
add = e.ScaleX == 1 ? 0.5f : 0;
#else
g.SmoothingMode = SmoothingMode.AntiAlias;
#endif
g.ScaleTransform(e.ScaleX, e.ScaleY);
g.TranslateTransform((int)Math.Round(AbsLeft + Width / 2 - dx / 2), (int)Math.Round(AbsTop + Height / 2 - dy / 2));
if (Fill is SolidFill)
g.FillRectangle(e.Cache.GetBrush((Fill as SolidFill).Color), new RectangleF(-2, -2, dx + 4, dy + 4));
if (Border.Lines != BorderLines.None)
g.DrawRectangle(pen, -2, -2, dx + 4, dy + 4);
if (Sort == SortOrder.Ascending)
{
switch (Symbol)
{
case SortSymbol.Arrow:
points = new PointF[] { new PointF(0, dy / 2), new PointF(dx / 2, 0), new PointF(dx, dy / 2) };
g.FillAndDrawPolygon(pen, brush, points);
break;
case SortSymbol.Pointer:
g.DrawLines(pen, new PointF[] { new PointF(0, dy / 2), new PointF(dx / 2, 0), new PointF(dx, dy / 2) });
break;
}
}
else if (Sort == SortOrder.Descending)
{
switch (Symbol)
{
case SortSymbol.Arrow:
points = new PointF[] { new PointF(0 - add, dy / 2), new PointF(dx / 2, dy + add), new PointF(dx + add, dy / 2) };
g.FillAndDrawPolygon(pen, brush, points);
break;
case SortSymbol.Pointer:
g.DrawLines(pen, new PointF[] { new PointF(0, dy / 2), new PointF(dx / 2, dy), new PointF(dx, dy / 2) });
break;
}
}
else if (Sort == SortOrder.None)
{
pen = e.Cache.GetPen(InactiveSortColor, pen.Width, DashStyle.Solid);
brush = e.Cache.GetBrush(pen.Color);
switch (Symbol)
{
case SortSymbol.Arrow:
points = new PointF[] { new PointF(1, dy / 2 - 1), new PointF(dx / 2, 0), new PointF(dx - 1, dy / 2 - 1) };
g.FillAndDrawPolygon(pen, brush, points);
points = new PointF[] { new PointF(1 - add, dy / 2 + 1), new PointF(dx / 2, dy + add), new PointF(dx - 1 + add, dy / 2 + 1) };
g.FillAndDrawPolygon(pen, brush, points);
break;
case SortSymbol.Pointer:
g.DrawLines(pen, new PointF[] { new PointF(1, dy / 2 - 1), new PointF(dx / 2, 0), new PointF(dx - 1, dy / 2 - 1) });
g.DrawLines(pen, new PointF[] { new PointF(1, dy / 2 + 1), new PointF(dx / 2, dy), new PointF(dx - 1, dy / 2 + 1) });
break;
}
}
g.Restore(state);
}
///
public override void Serialize(FRWriter writer)
{
base.Serialize(writer);
MatrixSortButton c = writer.DiffObject as MatrixSortButton;
if (Sort != c.Sort)
writer.WriteValue("Sort", Sort);
if (AllowInactiveSort != c.AllowInactiveSort)
writer.WriteBool("AllowInactiveSort", AllowInactiveSort);
if (Symbol != c.Symbol)
writer.WriteValue("Symbol", Symbol);
if (InactiveSortColor != c.InactiveSortColor)
writer.WriteValue("InactiveSortColor", InactiveSortColor);
}
///
/// Initializes a new instance of the class.
///
public MatrixSortButton()
{
BaseName = "SortButton";
Sort = SortOrder.None;
AllowInactiveSort = true;
Symbol = SortSymbol.Arrow;
InactiveSortColor = Color.Gray;
}
}
}