using System; using System.Collections; using System.Collections.Generic; using FastReport.Gauge; using FastReport.Gauge.Simple.Progress; #if MSCHART using FastReport.MSChart; #endif using FastReport.Table; namespace FastReport.AdvMatrix { internal static class ResultBuilder { private static int EnumColumnData(HeaderData root, int columnIndex, int rowIndex, Action func) { foreach (HeaderData d in root) { func(d, columnIndex, rowIndex, true); if (d.Items.Count > 0) { if (d.Descriptor.Stepped) columnIndex = EnumColumnData(d, columnIndex + 1, rowIndex, func); else columnIndex = EnumColumnData(d, columnIndex, rowIndex + 1, func); } columnIndex++; } return columnIndex - 1; } private static int EnumRowData(HeaderData root, int columnIndex, int rowIndex, Action func) { foreach (HeaderData d in root) { func(d, columnIndex, rowIndex, false); if (d.Items.Count > 0) { if (d.Descriptor.Stepped) rowIndex = EnumRowData(d, columnIndex, rowIndex + 1, func); else rowIndex = EnumRowData(d, columnIndex + 1, rowIndex, func); } rowIndex++; } return rowIndex - 1; } private static void PrintHeaderCell(AdvMatrixObject matrix, HeaderData item, TableCellData resultCell, bool isColumn, bool isEven) { HeaderDescriptor descr = item.Descriptor; TableCell templateCell = descr.TemplateCell; if (matrix.DataSource != null) matrix.DataSource.CurrentRowNo = item.DataRowNo; matrix.RowNo = item.RowNo; matrix.ItemCount = item.ItemCount; MatrixCollapseButton btn = templateCell.GetCollapseButton(); if (btn != null) matrix.ItemCount = btn.GetLinkedItemsCount(item); templateCell.SaveState(); if (isEven) templateCell.ApplyEvenStyle(); object value = item.Value; if (value != null) { templateCell.SetValue(value); templateCell.Text = templateCell.Format.FormatValue(value); } if (!String.IsNullOrEmpty(descr.DisplayText)) { templateCell.Text = descr.DisplayText; templateCell.AllowExpressions = true; } templateCell.GetData(); if (String.IsNullOrEmpty(templateCell.Hyperlink.Expression) && (templateCell.Hyperlink.Kind == HyperlinkKind.DetailReport || templateCell.Hyperlink.Kind == HyperlinkKind.DetailPage || templateCell.Hyperlink.Kind == HyperlinkKind.Custom)) templateCell.Hyperlink.Value = templateCell.Text; if (templateCell.Objects != null) { foreach (Base c in templateCell.Objects) { if (c is MatrixButton) { MatrixButton b = c as MatrixButton; b.IsColumn = isColumn; b.Index = item.Index; } if (c is MatrixCollapseButton) { MatrixCollapseButton b = c as MatrixCollapseButton; foreach (HeaderDataList dl in b.GetLinkedItems(item)) { b.Collapsed = !dl.Visible; break; } } if (c is MatrixSortButton) { MatrixSortButton b = c as MatrixSortButton; b.Sort = SortOrder.None; InteractiveSortInfo info = matrix.Data.InteractiveSort; if (isColumn && item.Index == info.Column.Index) b.Sort = info.Column.Sort; if (!isColumn && item.Index == info.Row.Index) b.Sort = info.Row.Sort; } } } resultCell.RunTimeAssign(templateCell, true); templateCell.RestoreState(); } private static void PrintCornerCell(TableCell templateCell, TableCellData resultCell) { templateCell.SaveState(); templateCell.GetData(); resultCell.RunTimeAssign(templateCell, true); templateCell.RestoreState(); } private static void PrintDataCell(AdvMatrixObject matrix, TableCell templateCell, TableCellData resultCell, bool isEven) { templateCell.SaveState(); if (isEven) templateCell.ApplyEvenStyle(); CellDescriptor descriptor = templateCell.GetDescriptor(); switch (descriptor.ContentType) { case CellContentType.MixedText: templateCell.AllowExpressions = true; templateCell.Text = descriptor.Text; break; case CellContentType.Aggregate: templateCell.SetValue(descriptor.Aggregates[0].GetValue(matrix.Data.Context.ColumnItem.Index, matrix.Data.Context.RowItem.Index)); templateCell.Text = templateCell.FormatValue(templateCell.Value); break; case CellContentType.SingleExpression: templateCell.SetValue(matrix.Report.Calc(descriptor.Text)); templateCell.Text = templateCell.FormatValue(templateCell.Value); break; } templateCell.GetData(); ProcessCellObjects(templateCell); if (String.IsNullOrEmpty(templateCell.Hyperlink.Expression) && (templateCell.Hyperlink.Kind == HyperlinkKind.DetailReport || templateCell.Hyperlink.Kind == HyperlinkKind.DetailPage || templateCell.Hyperlink.Kind == HyperlinkKind.Custom)) { string hyperlinkValue = ""; string separator = templateCell.Hyperlink.ValuesSeparator; foreach (object obj in matrix.ColumnValues) { hyperlinkValue += obj.ToString() + separator; } foreach (object obj in matrix.RowValues) { hyperlinkValue += obj.ToString() + separator; } templateCell.Hyperlink.Value = hyperlinkValue.Substring(0, hyperlinkValue.Length - separator.Length); } resultCell.RunTimeAssign(templateCell, true); templateCell.RestoreState(); resultCell.ColSpan = templateCell.ColSpan; resultCell.RowSpan = templateCell.RowSpan; } private static IList ReduceValues(this IList list, int valuesCount) { int ratio = list.Count / valuesCount; if (ratio < 2) return list; ArrayList reducedList = new ArrayList(); for (int i = 0; i < list.Count; i += ratio) { dynamic v = list[i]; for (int j = 0; j < ratio && i + j < list.Count; j++) { if ((dynamic)list[i + j] > v) v = list[i + j]; } reducedList.Add(v); } return reducedList; } private static void ProcessCellObjects(TableCell templateCell) { if (templateCell.Objects != null) { foreach (ReportComponentBase c in templateCell.Objects) { #if MSCHART MSChartObject chart = c as MSChartObject; if (chart != null && chart.Series.Count == 1) { chart.Series[0].ClearValues(); if (templateCell.Value is IList) { IList list = (templateCell.Value as IList).ReduceValues((int)chart.Width / 3); for (int i = 0; i < list.Count; i++) { chart.Series[0].AddValue(i, list[i]); } } templateCell.Text = ""; } #endif GaugeObject gauge = c as GaugeObject; if (gauge != null) { if (templateCell.Value == null || templateCell.Value is DBNull) gauge.Visible = false; else gauge.Value = (double)(dynamic)templateCell.Value; templateCell.Text = ""; } } } } public static void BuildResult(AdvMatrixObject matrix) { TableResult resultTable = matrix.ResultTable; int cornerWidth = matrix.Data.Rows.Size; int cornerHeight = matrix.Data.Columns.Size; resultTable.FixedColumns = cornerWidth; resultTable.FixedRows = cornerHeight; // rows/columns in the corner area. Enumerate on descriptors to do it fast. // We need this code because some rows and columns may be invisible due to its collapsed state, // so its properties will not be assigned to resultTable resultTable.ColumnCount = cornerWidth; resultTable.RowCount = cornerHeight; Action func = (d, columnIndex, rowIndex, isColumn) => { if (!isColumn && d.TemplateColumn != null) resultTable.Columns[columnIndex].Assign(d.TemplateColumn); if (isColumn && d.TemplateRow != null) resultTable.Rows[rowIndex].Assign(d.TemplateRow); }; TemplateBuilder.EnumColumnDescriptors(matrix.Data.Columns.Descriptor, cornerWidth, 0, func); TemplateBuilder.EnumRowDescriptors(matrix.Data.Rows.Descriptor, 0, cornerHeight, func); // rows/columns Action func1 = (data, columnIndex, rowIndex, isColumn) => { // create resultTable columns and rows while (columnIndex >= resultTable.Columns.Count) resultTable.Columns.Add(new TableColumn()); while (rowIndex >= resultTable.Rows.Count) resultTable.Rows.Add(new TableRow()); // assign properties from existing template columns/rows. // The same assignment may occur several times, but it's a cheap operation so it's ok HeaderDescriptor d = data.Descriptor; if (isColumn && d.TemplateColumn != null) resultTable.Columns[columnIndex].Assign(d.TemplateColumn); if (!isColumn && d.TemplateRow != null) resultTable.Rows[rowIndex].Assign(d.TemplateRow); // page breaks if (d.PageBreak && data.RowNo > 1) { if (isColumn) resultTable.Columns[columnIndex].PageBreak = true; else resultTable.Rows[rowIndex].PageBreak = true; } }; EnumColumnData(matrix.Data.Columns.Data, cornerWidth, 0, func1); EnumRowData(matrix.Data.Rows.Data, 0, cornerHeight, func1); // corner cells for (int left = 0; left < cornerWidth; left++) { for (int top = 0; top < cornerHeight; top++) { TableCellData resultCell = resultTable.GetCellData(left, top); TableCell templateCell = matrix[left, top]; PrintCornerCell(templateCell, resultCell); // do not allow spans to go outside corner area. Fix template cells as well (else we will get empty cells in the header area) templateCell.ColSpan = Math.Min(templateCell.ColSpan, cornerWidth - left); templateCell.RowSpan = Math.Min(templateCell.RowSpan, cornerHeight - top); resultCell.ColSpan = templateCell.ColSpan; resultCell.RowSpan = templateCell.RowSpan; } } // header cells Action func2 = (data, columnIndex, rowIndex, isColumn) => { TableCellData resultCell = resultTable.GetCellData(columnIndex, rowIndex); // required check for MergeSingleItem if (resultTable.IsInsideSpan(resultCell.Cell)) return; PrintHeaderCell(matrix, data, resultCell, isColumn, (isColumn ? columnIndex - cornerWidth : rowIndex - cornerHeight) % 2 == 0); HeaderDescriptor descr = data.Descriptor; if (isColumn) { resultCell.ColSpan = data.Span; if (data.Items.Count == 0 || (descr.MergeSingleItem && data.TerminalItems.Count == 1) || descr.Stepped) resultCell.RowSpan = cornerHeight - rowIndex; else if (descr.MergeSingleItem && data.ItemCount == 1) resultCell.RowSpan = 2; } else { resultCell.RowSpan = data.Span; if (data.Items.Count == 0 || (descr.MergeSingleItem && data.TerminalItems.Count == 1) || descr.Stepped) resultCell.ColSpan = cornerWidth - columnIndex; else if (descr.MergeSingleItem && data.ItemCount == 1) resultCell.ColSpan = 2; } if (descr.ColSpan > 0) resultCell.ColSpan = descr.ColSpan; if (descr.RowSpan > 0) resultCell.RowSpan = descr.RowSpan; }; EnumColumnData(matrix.Data.Columns.Data, cornerWidth, 0, func2); EnumRowData(matrix.Data.Rows.Data, 0, cornerHeight, func2); // data cells List columnTerminalItems = matrix.Data.Columns.Data.TerminalItems; List rowTerminalItems = matrix.Data.Rows.Data.TerminalItems; matrix.ColumnIndex = 0; foreach (HeaderData columnItem in columnTerminalItems) { matrix.RowIndex = 0; matrix.ColumnValues = columnItem.Values; foreach (HeaderData rowItem in rowTerminalItems) { matrix.RowValues = rowItem.Values; TableCellData resultCell = resultTable.GetCellData(matrix.ColumnIndex + cornerWidth, matrix.RowIndex + cornerHeight); TableCell templateCell = matrix[columnItem.Descriptor.TemplateColumn.Index, rowItem.Descriptor.TemplateRow.Index]; // set context required by aggregate calculation matrix.Data.Context.Descriptor = templateCell.GetDescriptor(); matrix.Data.Context.ColumnItem = columnItem; matrix.Data.Context.RowItem = rowItem; if (matrix.DataSource != null) matrix.DataSource.CurrentRowNo = matrix.DataRowPriority == HeaderPriority.Columns ? columnItem.DataRowNo : rowItem.DataRowNo; int evenStyleIndex = matrix.EvenStylePriority == HeaderPriority.Columns ? matrix.ColumnIndex : matrix.RowIndex; PrintDataCell(matrix, templateCell, resultCell, evenStyleIndex % 2 == 0); matrix.RowIndex++; } matrix.ColumnIndex++; } } } }