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; } } }