using System; using FastReport.Data; using FastReport.Table; namespace FastReport.AdvMatrix { internal static class TemplateBuilder { public static int EnumColumnDescriptors(HeaderDescriptor root, int columnIndex, int rowIndex, Action func) { foreach (HeaderDescriptor d in root.Items) { func(d, columnIndex, rowIndex, true); if (d.Items.Count > 0) { if (d.Stepped) columnIndex = EnumColumnDescriptors(d, columnIndex + 1, rowIndex, func); else columnIndex = EnumColumnDescriptors(d, columnIndex, rowIndex + 1, func); } columnIndex++; } return columnIndex - 1; } public static int EnumRowDescriptors(HeaderDescriptor root, int columnIndex, int rowIndex, Action func) { foreach (HeaderDescriptor d in root.Items) { func(d, columnIndex, rowIndex, false); if (d.Items.Count > 0) { if (d.Stepped) rowIndex = EnumRowDescriptors(d, columnIndex, rowIndex + 1, func); else rowIndex = EnumRowDescriptors(d, columnIndex + 1, rowIndex, func); } rowIndex++; } return rowIndex - 1; } private static void CopyFromResultTable(AdvMatrixObject matrix, TableResult resultTable) { matrix.ColumnCount = resultTable.ColumnCount; matrix.RowCount = resultTable.RowCount; matrix.CreateUniqueNames(); for (int x = 0; x < matrix.Columns.Count; x++) { matrix.Columns[x].Assign(resultTable.Columns[x]); } for (int y = 0; y < matrix.Rows.Count; y++) { matrix.Rows[y].Assign(resultTable.Rows[y]); } for (int x = 0; x < matrix.Columns.Count; x++) { for (int y = 0; y < matrix.Rows.Count; y++) { TableCell cell = matrix[x, y]; // call AssignAll with assignName parameter to preserve names of objects contained in a cell resultTable[x, y].Name = cell.Name; cell.AssignAll(resultTable[x, y], true); cell.SetFlags(Flags.CanEdit, true); } } } private static TableCell CreateHeaderCell(AdvMatrixObject matrix, HeaderDescriptor descr) { TableCell cell = new TableCell(); cell.HorzAlign = HorzAlign.Center; cell.VertAlign = VertAlign.Center; ApplyStyle(matrix, cell, "Header"); if (descr != null) { if (descr.IsGroup) cell.Text = descr.Expression; else cell.Text = descr.DisplayText; } return cell; } private static TableCell CreateCell(AdvMatrixObject matrix) { TableCell cell = new TableCell(); cell.HorzAlign = HorzAlign.Right; cell.VertAlign = VertAlign.Center; ApplyStyle(matrix, cell, "Body"); return cell; } private static void ApplyStyle(AdvMatrixObject matrix, TableCell cell, string styleName) { Style style = null; int styleIndex = matrix.StyleSheet.IndexOf(matrix.Style); if (styleIndex != -1) { StyleCollection styles = matrix.StyleSheet[styleIndex]; style = styles[styles.IndexOf(styleName)]; } if (style != null) cell.ApplyStyle(style); } public static void UpdateStyle(AdvMatrixObject matrix) { int cornerWidth = matrix.Data.Rows.Size; int cornerHeight = matrix.Data.Columns.Size; for (int y = 0; y < matrix.RowCount; y++) { for (int x = 0; x < matrix.ColumnCount; x++) { string style = x < cornerWidth || y < cornerHeight ? "Header" : "Body"; ApplyStyle(matrix, matrix[x, y], style); } } } public static void UpdateDescriptors(AdvMatrixObject matrix) { int cornerWidth = matrix.Data.Rows.Size; int cornerHeight = matrix.Data.Columns.Size; // update header descriptors Action func = (d, columnIndex, rowIndex, isColumn) => { d.Matrix = matrix; d.IsColumn = isColumn; d.TemplateColumn = matrix.Columns[columnIndex]; d.TemplateRow = matrix.Rows[rowIndex]; d.TemplateCell = matrix[columnIndex, rowIndex]; if (d.IsGroup) d.TemplateCell.AllowExpressions = false; if (!d.IsGroup && String.IsNullOrEmpty(d.DisplayText)) d.DisplayText = d.TemplateCell.Text; }; EnumColumnDescriptors(matrix.Data.Columns.Descriptor, cornerWidth, 0, func); EnumRowDescriptors(matrix.Data.Rows.Descriptor, 0, cornerHeight, func); // create cell descriptors matrix.Data.CellData.InitDescriptors(); } public static void BuildTemplate(AdvMatrixObject matrix) { TableResult resultTable = new TableResult(); int cornerWidth = matrix.Data.Rows.Size; int cornerHeight = matrix.Data.Columns.Size; TableColumn[] cornerColumns = new TableColumn[cornerWidth]; TableRow[] cornerRows = new TableRow[cornerHeight]; Action func = (d, columnIndex, rowIndex, isColumn) => { // create resultTable columns and rows. Assign properties from existing template columns/rows while (columnIndex >= resultTable.Columns.Count) resultTable.Columns.Add(new TableColumn()); while (rowIndex >= resultTable.Rows.Count) resultTable.Rows.Add(new TableRow()); // the same assignment may occur several times, but it's a cheap operation so it's ok if (d.TemplateColumn != null) { resultTable.Columns[columnIndex].Assign(d.TemplateColumn); if (columnIndex < cornerWidth) cornerColumns[columnIndex] = d.TemplateColumn; } if (d.TemplateRow != null) { resultTable.Rows[rowIndex].Assign(d.TemplateRow); if (rowIndex < cornerHeight) cornerRows[rowIndex] = d.TemplateRow; } }; EnumColumnDescriptors(matrix.Data.Columns.Descriptor, cornerWidth, 0, func); EnumRowDescriptors(matrix.Data.Rows.Descriptor, 0, cornerHeight, func); // header cells Action func1 = (d, columnIndex, rowIndex, isColumn) => { TableCellData resultCell = resultTable.GetCellData(columnIndex, rowIndex); if (d.TemplateCell != null) resultCell.RunTimeAssign(d.TemplateCell, true); else resultCell.RunTimeAssign(CreateHeaderCell(matrix, d), true); if (d.IsGroup) resultCell.Text = d.Expression; if (!d.IsGroup && !String.IsNullOrEmpty(d.DisplayText)) resultCell.Text = d.DisplayText; if (isColumn) { resultCell.ColSpan = d.Span; if (d.Items.Count == 0 || d.Stepped) resultCell.RowSpan = cornerHeight - rowIndex; } else { resultCell.RowSpan = d.Span; if (d.Items.Count == 0 || d.Stepped) resultCell.ColSpan = cornerWidth - columnIndex; } }; EnumColumnDescriptors(matrix.Data.Columns.Descriptor, cornerWidth, 0, func1); EnumRowDescriptors(matrix.Data.Rows.Descriptor, 0, cornerHeight, func1); // corner int left, top; for (left = 0; left < cornerWidth; left++) { for (top = 0; top < cornerHeight; top++) { TableCellData resultCell = resultTable.GetCellData(left, top); if (cornerColumns[left] != null && cornerRows[top] != null) { TableCell templateCell = matrix[cornerColumns[left].Index, cornerRows[top].Index]; resultCell.RunTimeAssign(templateCell, true); // do not allow spans to go outside corner area resultCell.ColSpan = Math.Min(templateCell.ColSpan, cornerWidth - left); resultCell.RowSpan = Math.Min(templateCell.RowSpan, cornerHeight - top); } else { resultCell.RunTimeAssign(CreateHeaderCell(matrix, null), true); } } } // cells left = cornerWidth; foreach (HeaderDescriptor columnItem in matrix.Data.Columns.Descriptor.TerminalItems) { top = cornerHeight; foreach (HeaderDescriptor rowItem in matrix.Data.Rows.Descriptor.TerminalItems) { TableCellData resultCell = resultTable.GetCellData(left, top); if (columnItem.TemplateColumn != null && rowItem.TemplateRow != null) { TableCell templateCell = matrix[columnItem.TemplateColumn.Index, rowItem.TemplateRow.Index]; resultCell.RunTimeAssign(templateCell, true); resultCell.ColSpan = templateCell.ColSpan; resultCell.RowSpan = templateCell.RowSpan; } else { resultCell.RunTimeAssign(CreateCell(matrix), true); } top++; } left++; } // copy resultTable to matrix CopyFromResultTable(matrix, resultTable); UpdateDescriptors(matrix); resultTable.Dispose(); } } }