using System; using System.Collections.Generic; using System.Text; using System.Drawing; namespace FastReport.Table { internal class TableHelper { private TableObject sourceTable; private TableResult resultTable; private enum NowPrinting { None, Row, Column } private NowPrinting nowPrinting; private bool rowsPriority; private int originalRowIndex; private int originalColumnIndex; private int printingRowIndex; private int printingColumnIndex; private List columnSpans; private List rowSpans; private bool pageBreak; private bool AutoSpans { get { return sourceTable.ManualBuildAutoSpans; } } #region Build the Table public void PrintRow(int rowIndex) { originalRowIndex = rowIndex; if (nowPrinting == NowPrinting.None) { // we are at the start. Rows will now have priority over columns. rowsPriority = true; } if (rowsPriority) { switch (nowPrinting) { case NowPrinting.None: printingRowIndex = 0; break; case NowPrinting.Column: printingRowIndex++; break; case NowPrinting.Row: // we have two sequential calls of the PrintRow. But we must print // some columns... break; } // add new row, do not copy cells: it will be done in the PrintColumn. TableRow row = new TableRow(); row.Assign(sourceTable.Rows[rowIndex]); row.PageBreak = pageBreak; resultTable.Rows.Add(row); columnSpans.Clear(); } else { if (nowPrinting == NowPrinting.Column) { // this is the first row inside a column, reset the index printingRowIndex = 0; } else { // not the first row, increment the index printingRowIndex++; } TableRow row = null; if (resultTable.Rows.Count <= printingRowIndex) { // index is outside existing rows. Probably not all rows created yet, // we're at the start. Add new row. row = new TableRow(); row.Assign(sourceTable.Rows[rowIndex]); resultTable.Rows.Add(row); } else { // do not create row, use existing one row = resultTable.Rows[printingRowIndex]; } // apply page break row.PageBreak = pageBreak; // copy cells from the template to the result CopyCells(originalColumnIndex, originalRowIndex, printingColumnIndex, printingRowIndex); } nowPrinting = NowPrinting.Row; pageBreak = false; } public void PrintColumn(int columnIndex) { originalColumnIndex = columnIndex; if (nowPrinting == NowPrinting.None) { // we are at the start. Columns will now have priority over rows. rowsPriority = false; } if (!rowsPriority) { switch (nowPrinting) { case NowPrinting.None: printingColumnIndex = 0; break; case NowPrinting.Column: // we have two sequential calls of the PrintColumn. But we must print // some rows... break; case NowPrinting.Row: printingColumnIndex++; break; } // add new column, do not copy cells: it will be done in the PrintRow. TableColumn column = new TableColumn(); column.Assign(sourceTable.Columns[columnIndex]); column.PageBreak = pageBreak; resultTable.Columns.Add(column); rowSpans.Clear(); } else { if (nowPrinting == NowPrinting.Row) { // this is the first column inside a row, reset the index printingColumnIndex = 0; } else { // not the first column, increment the index printingColumnIndex++; } TableColumn column = null; if (resultTable.Columns.Count <= printingColumnIndex) { // index is outside existing columns. Probably not all columns // created yet, we're at the start. Add new column. column = new TableColumn(); column.Assign(sourceTable.Columns[columnIndex]); resultTable.Columns.Add(column); } else { // do not create column, use existing one column = resultTable.Columns[printingColumnIndex]; } // apply page break column.PageBreak = pageBreak; // copy cells from the template to the result CopyCells(originalColumnIndex, originalRowIndex, printingColumnIndex, printingRowIndex); } nowPrinting = NowPrinting.Column; pageBreak = false; } public void PageBreak() { pageBreak = true; } private void CopyCells(int originalColumnIndex, int originalRowIndex, int resultColumnIndex, int resultRowIndex) { TableCell cell = sourceTable[originalColumnIndex, originalRowIndex]; TableCellData cellTo = resultTable.GetCellData(resultColumnIndex, resultRowIndex); sourceTable.PrintingCell = cellTo; bool needData = true; if (AutoSpans) { if (rowsPriority) { // We are printing columns inside a row. Check if we need to finish the column cell. if (columnSpans.Count > 0) { SpanData spanData = columnSpans[0]; // check if we are printing the last column of the cell's span. From now, we will not accept // the first column. if (originalColumnIndex == spanData.originalCellOrigin.X + spanData.originalCell.ColSpan - 1) spanData.finishFlag = true; if ((spanData.finishFlag && originalColumnIndex == spanData.originalCellOrigin.X) || (originalColumnIndex < spanData.originalCellOrigin.X || originalColumnIndex > spanData.originalCellOrigin.X + spanData.originalCell.ColSpan - 1)) columnSpans.Clear(); else { spanData.resultCell.ColSpan++; needData = false; } } // add the column cell if it has ColSpan > 1 if (cell.ColSpan > 1 && columnSpans.Count == 0) { SpanData spanData = new SpanData(); columnSpans.Add(spanData); spanData.originalCell = cell; spanData.resultCell = cellTo; spanData.originalCellOrigin = new Point(originalColumnIndex, originalRowIndex); spanData.resultCellOrigin = new Point(resultColumnIndex, resultRowIndex); } // now check the row cells. Do this once for each row. if (printingColumnIndex == 0) { for (int i = 0; i < rowSpans.Count; i++) { SpanData spanData = rowSpans[i]; // check if we are printing the last row of the cell's span. From now, we will not accept // the first row. if (originalRowIndex == spanData.originalCellOrigin.Y + spanData.originalCell.RowSpan - 1) spanData.finishFlag = true; if ((spanData.finishFlag && originalRowIndex == spanData.originalCellOrigin.Y) || (originalRowIndex < spanData.originalCellOrigin.Y || originalRowIndex > spanData.originalCellOrigin.Y + spanData.originalCell.RowSpan - 1)) { rowSpans.RemoveAt(i); i--; } else spanData.resultCell.RowSpan++; } } // check if we should skip current cell because it is inside a span for (int i = 0; i < rowSpans.Count; i++) { SpanData spanData = rowSpans[i]; if (resultColumnIndex >= spanData.resultCellOrigin.X && resultColumnIndex <= spanData.resultCellOrigin.X + spanData.resultCell.ColSpan - 1 && resultRowIndex >= spanData.resultCellOrigin.Y && resultRowIndex <= spanData.resultCellOrigin.Y + spanData.resultCell.RowSpan) { needData = false; break; } } // add the row cell if it has RowSpan > 1 and not added yet if (cell.RowSpan > 1 && needData) { SpanData spanData = new SpanData(); rowSpans.Add(spanData); spanData.originalCell = cell; spanData.resultCell = cellTo; spanData.originalCellOrigin = new Point(originalColumnIndex, originalRowIndex); spanData.resultCellOrigin = new Point(resultColumnIndex, resultRowIndex); } } else { // We are printing rows inside a column. Check if we need to finish the row cell. if (rowSpans.Count > 0) { SpanData spanData = rowSpans[0]; // check if we are printing the last row of the cell's span. From now, we will not accept // the first row. if (originalRowIndex == spanData.originalCellOrigin.Y + spanData.originalCell.RowSpan - 1) spanData.finishFlag = true; if ((spanData.finishFlag && originalRowIndex == spanData.originalCellOrigin.Y) || (originalRowIndex < spanData.originalCellOrigin.Y || originalRowIndex > spanData.originalCellOrigin.Y + spanData.originalCell.RowSpan - 1)) rowSpans.Clear(); else { spanData.resultCell.RowSpan++; needData = false; } } // add the row cell if it has RowSpan > 1 if (cell.RowSpan > 1 && rowSpans.Count == 0) { SpanData spanData = new SpanData(); rowSpans.Add(spanData); spanData.originalCell = cell; spanData.resultCell = cellTo; spanData.originalCellOrigin = new Point(originalColumnIndex, originalRowIndex); spanData.resultCellOrigin = new Point(resultColumnIndex, resultRowIndex); } // now check the column cells. Do this once for each column. if (printingRowIndex == 0) { for (int i = 0; i < columnSpans.Count; i++) { SpanData spanData = columnSpans[i]; // check if we are printing the last column of the cell's span. From now, we will not accept // the first column. if (originalColumnIndex == spanData.originalCellOrigin.X + spanData.originalCell.ColSpan - 1) spanData.finishFlag = true; if ((spanData.finishFlag && originalColumnIndex == spanData.originalCellOrigin.X) || (originalColumnIndex < spanData.originalCellOrigin.X || originalColumnIndex > spanData.originalCellOrigin.X + spanData.originalCell.ColSpan - 1)) { columnSpans.RemoveAt(i); i--; } else spanData.resultCell.ColSpan++; } } // check if we should skip current cell because it is inside a span for (int i = 0; i < columnSpans.Count; i++) { SpanData spanData = columnSpans[i]; if (resultColumnIndex >= spanData.resultCellOrigin.X && resultColumnIndex <= spanData.resultCellOrigin.X + spanData.resultCell.ColSpan - 1 && resultRowIndex >= spanData.resultCellOrigin.Y && resultRowIndex <= spanData.resultCellOrigin.Y + spanData.resultCell.RowSpan - 1) { needData = false; break; } } // add the column cell if it has ColSpan > 1 and not added yet if (cell.ColSpan > 1 && needData) { SpanData spanData = new SpanData(); columnSpans.Add(spanData); spanData.originalCell = cell; spanData.resultCell = cellTo; spanData.originalCellOrigin = new Point(originalColumnIndex, originalRowIndex); spanData.resultCellOrigin = new Point(resultColumnIndex, resultRowIndex); } } } else { cellTo.ColSpan = cell.ColSpan; cellTo.RowSpan = cell.RowSpan; } if (needData) { cell.SaveState(); cell.GetData(); cellTo.RunTimeAssign(cell, true); cell.RestoreState(); } } #endregion #region Aggregate Functions #endregion public TableHelper(TableObject source, TableResult result) { sourceTable = source; resultTable = result; columnSpans = new List(); rowSpans = new List(); } private class SpanData { public TableCell originalCell; public TableCellData resultCell; public Point originalCellOrigin; public Point resultCellOrigin; public bool finishFlag; } } }