TableHelper.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Drawing;
  5. namespace FastReport.Table
  6. {
  7. internal class TableHelper
  8. {
  9. private TableObject sourceTable;
  10. private TableResult resultTable;
  11. private enum NowPrinting { None, Row, Column }
  12. private NowPrinting nowPrinting;
  13. private bool rowsPriority;
  14. private int originalRowIndex;
  15. private int originalColumnIndex;
  16. private int printingRowIndex;
  17. private int printingColumnIndex;
  18. private List<SpanData> columnSpans;
  19. private List<SpanData> rowSpans;
  20. private bool pageBreak;
  21. private bool AutoSpans
  22. {
  23. get { return sourceTable.ManualBuildAutoSpans; }
  24. }
  25. #region Build the Table
  26. public void PrintRow(int rowIndex)
  27. {
  28. originalRowIndex = rowIndex;
  29. if (nowPrinting == NowPrinting.None)
  30. {
  31. // we are at the start. Rows will now have priority over columns.
  32. rowsPriority = true;
  33. }
  34. if (rowsPriority)
  35. {
  36. switch (nowPrinting)
  37. {
  38. case NowPrinting.None:
  39. printingRowIndex = 0;
  40. break;
  41. case NowPrinting.Column:
  42. printingRowIndex++;
  43. break;
  44. case NowPrinting.Row:
  45. // we have two sequential calls of the PrintRow. But we must print
  46. // some columns...
  47. break;
  48. }
  49. // add new row, do not copy cells: it will be done in the PrintColumn.
  50. TableRow row = new TableRow();
  51. row.Assign(sourceTable.Rows[rowIndex]);
  52. row.PageBreak = pageBreak;
  53. resultTable.Rows.Add(row);
  54. columnSpans.Clear();
  55. }
  56. else
  57. {
  58. if (nowPrinting == NowPrinting.Column)
  59. {
  60. // this is the first row inside a column, reset the index
  61. printingRowIndex = 0;
  62. }
  63. else
  64. {
  65. // not the first row, increment the index
  66. printingRowIndex++;
  67. }
  68. TableRow row = null;
  69. if (resultTable.Rows.Count <= printingRowIndex)
  70. {
  71. // index is outside existing rows. Probably not all rows created yet,
  72. // we're at the start. Add new row.
  73. row = new TableRow();
  74. row.Assign(sourceTable.Rows[rowIndex]);
  75. resultTable.Rows.Add(row);
  76. }
  77. else
  78. {
  79. // do not create row, use existing one
  80. row = resultTable.Rows[printingRowIndex];
  81. }
  82. // apply page break
  83. row.PageBreak = pageBreak;
  84. // copy cells from the template to the result
  85. CopyCells(originalColumnIndex, originalRowIndex,
  86. printingColumnIndex, printingRowIndex);
  87. }
  88. nowPrinting = NowPrinting.Row;
  89. pageBreak = false;
  90. }
  91. public void PrintColumn(int columnIndex)
  92. {
  93. originalColumnIndex = columnIndex;
  94. if (nowPrinting == NowPrinting.None)
  95. {
  96. // we are at the start. Columns will now have priority over rows.
  97. rowsPriority = false;
  98. }
  99. if (!rowsPriority)
  100. {
  101. switch (nowPrinting)
  102. {
  103. case NowPrinting.None:
  104. printingColumnIndex = 0;
  105. break;
  106. case NowPrinting.Column:
  107. // we have two sequential calls of the PrintColumn. But we must print
  108. // some rows...
  109. break;
  110. case NowPrinting.Row:
  111. printingColumnIndex++;
  112. break;
  113. }
  114. // add new column, do not copy cells: it will be done in the PrintRow.
  115. TableColumn column = new TableColumn();
  116. column.Assign(sourceTable.Columns[columnIndex]);
  117. column.PageBreak = pageBreak;
  118. resultTable.Columns.Add(column);
  119. rowSpans.Clear();
  120. }
  121. else
  122. {
  123. if (nowPrinting == NowPrinting.Row)
  124. {
  125. // this is the first column inside a row, reset the index
  126. printingColumnIndex = 0;
  127. }
  128. else
  129. {
  130. // not the first column, increment the index
  131. printingColumnIndex++;
  132. }
  133. TableColumn column = null;
  134. if (resultTable.Columns.Count <= printingColumnIndex)
  135. {
  136. // index is outside existing columns. Probably not all columns
  137. // created yet, we're at the start. Add new column.
  138. column = new TableColumn();
  139. column.Assign(sourceTable.Columns[columnIndex]);
  140. resultTable.Columns.Add(column);
  141. }
  142. else
  143. {
  144. // do not create column, use existing one
  145. column = resultTable.Columns[printingColumnIndex];
  146. }
  147. // apply page break
  148. column.PageBreak = pageBreak;
  149. // copy cells from the template to the result
  150. CopyCells(originalColumnIndex, originalRowIndex,
  151. printingColumnIndex, printingRowIndex);
  152. }
  153. nowPrinting = NowPrinting.Column;
  154. pageBreak = false;
  155. }
  156. public void PageBreak()
  157. {
  158. pageBreak = true;
  159. }
  160. private void CopyCells(int originalColumnIndex, int originalRowIndex,
  161. int resultColumnIndex, int resultRowIndex)
  162. {
  163. TableCell cell = sourceTable[originalColumnIndex, originalRowIndex];
  164. TableCellData cellTo = resultTable.GetCellData(resultColumnIndex, resultRowIndex);
  165. sourceTable.PrintingCell = cellTo;
  166. bool needData = true;
  167. if (AutoSpans)
  168. {
  169. if (rowsPriority)
  170. {
  171. // We are printing columns inside a row. Check if we need to finish the column cell.
  172. if (columnSpans.Count > 0)
  173. {
  174. SpanData spanData = columnSpans[0];
  175. // check if we are printing the last column of the cell's span. From now, we will not accept
  176. // the first column.
  177. if (originalColumnIndex == spanData.originalCellOrigin.X + spanData.originalCell.ColSpan - 1)
  178. spanData.finishFlag = true;
  179. if ((spanData.finishFlag && originalColumnIndex == spanData.originalCellOrigin.X) ||
  180. (originalColumnIndex < spanData.originalCellOrigin.X ||
  181. originalColumnIndex > spanData.originalCellOrigin.X + spanData.originalCell.ColSpan - 1))
  182. columnSpans.Clear();
  183. else
  184. {
  185. spanData.resultCell.ColSpan++;
  186. needData = false;
  187. }
  188. }
  189. // add the column cell if it has ColSpan > 1
  190. if (cell.ColSpan > 1 && columnSpans.Count == 0)
  191. {
  192. SpanData spanData = new SpanData();
  193. columnSpans.Add(spanData);
  194. spanData.originalCell = cell;
  195. spanData.resultCell = cellTo;
  196. spanData.originalCellOrigin = new Point(originalColumnIndex, originalRowIndex);
  197. spanData.resultCellOrigin = new Point(resultColumnIndex, resultRowIndex);
  198. }
  199. // now check the row cells. Do this once for each row.
  200. if (printingColumnIndex == 0)
  201. {
  202. for (int i = 0; i < rowSpans.Count; i++)
  203. {
  204. SpanData spanData = rowSpans[i];
  205. // check if we are printing the last row of the cell's span. From now, we will not accept
  206. // the first row.
  207. if (originalRowIndex == spanData.originalCellOrigin.Y + spanData.originalCell.RowSpan - 1)
  208. spanData.finishFlag = true;
  209. if ((spanData.finishFlag && originalRowIndex == spanData.originalCellOrigin.Y) ||
  210. (originalRowIndex < spanData.originalCellOrigin.Y ||
  211. originalRowIndex > spanData.originalCellOrigin.Y + spanData.originalCell.RowSpan - 1))
  212. {
  213. rowSpans.RemoveAt(i);
  214. i--;
  215. }
  216. else
  217. spanData.resultCell.RowSpan++;
  218. }
  219. }
  220. // check if we should skip current cell because it is inside a span
  221. for (int i = 0; i < rowSpans.Count; i++)
  222. {
  223. SpanData spanData = rowSpans[i];
  224. if (resultColumnIndex >= spanData.resultCellOrigin.X &&
  225. resultColumnIndex <= spanData.resultCellOrigin.X + spanData.resultCell.ColSpan - 1 &&
  226. resultRowIndex >= spanData.resultCellOrigin.Y &&
  227. resultRowIndex <= spanData.resultCellOrigin.Y + spanData.resultCell.RowSpan)
  228. {
  229. needData = false;
  230. break;
  231. }
  232. }
  233. // add the row cell if it has RowSpan > 1 and not added yet
  234. if (cell.RowSpan > 1 && needData)
  235. {
  236. SpanData spanData = new SpanData();
  237. rowSpans.Add(spanData);
  238. spanData.originalCell = cell;
  239. spanData.resultCell = cellTo;
  240. spanData.originalCellOrigin = new Point(originalColumnIndex, originalRowIndex);
  241. spanData.resultCellOrigin = new Point(resultColumnIndex, resultRowIndex);
  242. }
  243. }
  244. else
  245. {
  246. // We are printing rows inside a column. Check if we need to finish the row cell.
  247. if (rowSpans.Count > 0)
  248. {
  249. SpanData spanData = rowSpans[0];
  250. // check if we are printing the last row of the cell's span. From now, we will not accept
  251. // the first row.
  252. if (originalRowIndex == spanData.originalCellOrigin.Y + spanData.originalCell.RowSpan - 1)
  253. spanData.finishFlag = true;
  254. if ((spanData.finishFlag && originalRowIndex == spanData.originalCellOrigin.Y) ||
  255. (originalRowIndex < spanData.originalCellOrigin.Y ||
  256. originalRowIndex > spanData.originalCellOrigin.Y + spanData.originalCell.RowSpan - 1))
  257. rowSpans.Clear();
  258. else
  259. {
  260. spanData.resultCell.RowSpan++;
  261. needData = false;
  262. }
  263. }
  264. // add the row cell if it has RowSpan > 1
  265. if (cell.RowSpan > 1 && rowSpans.Count == 0)
  266. {
  267. SpanData spanData = new SpanData();
  268. rowSpans.Add(spanData);
  269. spanData.originalCell = cell;
  270. spanData.resultCell = cellTo;
  271. spanData.originalCellOrigin = new Point(originalColumnIndex, originalRowIndex);
  272. spanData.resultCellOrigin = new Point(resultColumnIndex, resultRowIndex);
  273. }
  274. // now check the column cells. Do this once for each column.
  275. if (printingRowIndex == 0)
  276. {
  277. for (int i = 0; i < columnSpans.Count; i++)
  278. {
  279. SpanData spanData = columnSpans[i];
  280. // check if we are printing the last column of the cell's span. From now, we will not accept
  281. // the first column.
  282. if (originalColumnIndex == spanData.originalCellOrigin.X + spanData.originalCell.ColSpan - 1)
  283. spanData.finishFlag = true;
  284. if ((spanData.finishFlag && originalColumnIndex == spanData.originalCellOrigin.X) ||
  285. (originalColumnIndex < spanData.originalCellOrigin.X ||
  286. originalColumnIndex > spanData.originalCellOrigin.X + spanData.originalCell.ColSpan - 1))
  287. {
  288. columnSpans.RemoveAt(i);
  289. i--;
  290. }
  291. else
  292. spanData.resultCell.ColSpan++;
  293. }
  294. }
  295. // check if we should skip current cell because it is inside a span
  296. for (int i = 0; i < columnSpans.Count; i++)
  297. {
  298. SpanData spanData = columnSpans[i];
  299. if (resultColumnIndex >= spanData.resultCellOrigin.X &&
  300. resultColumnIndex <= spanData.resultCellOrigin.X + spanData.resultCell.ColSpan - 1 &&
  301. resultRowIndex >= spanData.resultCellOrigin.Y &&
  302. resultRowIndex <= spanData.resultCellOrigin.Y + spanData.resultCell.RowSpan - 1)
  303. {
  304. needData = false;
  305. break;
  306. }
  307. }
  308. // add the column cell if it has ColSpan > 1 and not added yet
  309. if (cell.ColSpan > 1 && needData)
  310. {
  311. SpanData spanData = new SpanData();
  312. columnSpans.Add(spanData);
  313. spanData.originalCell = cell;
  314. spanData.resultCell = cellTo;
  315. spanData.originalCellOrigin = new Point(originalColumnIndex, originalRowIndex);
  316. spanData.resultCellOrigin = new Point(resultColumnIndex, resultRowIndex);
  317. }
  318. }
  319. }
  320. else
  321. {
  322. cellTo.ColSpan = cell.ColSpan;
  323. cellTo.RowSpan = cell.RowSpan;
  324. }
  325. if (needData)
  326. {
  327. cell.SaveState();
  328. cell.GetData();
  329. cellTo.RunTimeAssign(cell, true);
  330. cell.RestoreState();
  331. }
  332. }
  333. #endregion
  334. #region Aggregate Functions
  335. #endregion
  336. public TableHelper(TableObject source, TableResult result)
  337. {
  338. sourceTable = source;
  339. resultTable = result;
  340. columnSpans = new List<SpanData>();
  341. rowSpans = new List<SpanData>();
  342. }
  343. private class SpanData
  344. {
  345. public TableCell originalCell;
  346. public TableCellData resultCell;
  347. public Point originalCellOrigin;
  348. public Point resultCellOrigin;
  349. public bool finishFlag;
  350. }
  351. }
  352. }