KD Reports API Documentation  2.2
KDReportsAutoTableElement.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** This file is part of the KD Reports library.
4 **
5 ** SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
6 **
7 ** SPDX-License-Identifier: MIT
8 **
9 ****************************************************************************/
10 
14 #include "KDReportsReport_p.h" // modelForKey
15 #include <QAbstractItemModel>
16 #include <QBitArray>
17 #include <QDateTime>
18 #include <QDebug>
19 #include <QIcon>
20 #include <QTextCursor>
21 #include <QTextTableCell>
22 #include <QUrl>
23 #include <QVector>
24 
25 class KDReports::AutoTableElementPrivate
26 {
27 public:
28  void fillCellFromHeaderData(int section, Qt::Orientation orientation, QTextTableCell &cell, QTextDocument &textDoc, QTextTable *textTable, ReportBuilder &builder) const;
29  QSize fillTableCell(int row, int column, QTextTableCell &cell, QTextDocument &textDoc, QTextTable *textTable, ReportBuilder &builder) const;
30  void createHorizontalHeader(ReportBuilder &builder, QTextDocument &textDoc, QTextTable *textTable, int columns, int headerColumnCount) const;
31  void createVerticalHeader(ReportBuilder &builder, QTextDocument &textDoc, QTextTable *textTable, int rows, int headerRowCount) const;
32 
33  QAbstractItemModel *m_tableModel = nullptr;
34  QString m_modelKey;
35  bool m_verticalHeaderVisible = true;
36  bool m_horizontalHeaderVisible = true;
37  QBrush m_headerBackground = QColor(218, 218, 218);
38  QSize m_iconSize = QSize(32, 32);
39  AutoTableElement::CellFormatFunc m_horizontalHeaderFormatFunc;
40  AutoTableElement::CellFormatFunc m_verticalHeaderFormatFunc;
41 };
42 
43 // Helper for fillCellFromHeaderData and fillTableCell
44 class FillCellHelper
45 {
46 public:
47  FillCellHelper(QAbstractItemModel *tableModel, int section, Qt::Orientation orientation, QSize iconSz)
48  : iconSize(iconSz)
49  , cellDecoration(tableModel->headerData(section, orientation, Qt::DecorationRole))
50  , cellFont(tableModel->headerData(section, orientation, Qt::FontRole))
51  , cellText(tableModel->headerData(section, orientation, Qt::DisplayRole).toString())
52  , foreground(tableModel->headerData(section, orientation, Qt::ForegroundRole))
53  , background(tableModel->headerData(section, orientation, Qt::BackgroundRole))
54  , alignment(Qt::Alignment(tableModel->headerData(section, orientation, Qt::TextAlignmentRole).toInt()))
55  , decorationAlignment(tableModel->headerData(section, orientation, KDReports::AutoTableElement::DecorationAlignmentRole))
56  , nonBreakableLines(tableModel->headerData(section, orientation, KDReports::AutoTableElement::NonBreakableLinesRole).toBool())
57  , span(1, 1)
58  {
59  }
60  FillCellHelper(QAbstractItemModel *tableModel, const QModelIndex &index, QSize _span, QSize iconSz)
61  : iconSize(iconSz)
62  , cellDecoration(tableModel->data(index, Qt::DecorationRole))
63  , cellFont(tableModel->data(index, Qt::FontRole))
64  , cellText(displayText(tableModel->data(index, Qt::DisplayRole)))
65  , foreground(tableModel->data(index, Qt::ForegroundRole))
66  , background(tableModel->data(index, Qt::BackgroundRole))
67  , alignment(Qt::Alignment(tableModel->data(index, Qt::TextAlignmentRole).toInt()))
68  , decorationAlignment(tableModel->data(index, KDReports::AutoTableElement::DecorationAlignmentRole))
69  , nonBreakableLines(tableModel->data(index, KDReports::AutoTableElement::NonBreakableLinesRole).toBool())
70  , span(_span)
71  {
72  }
73  void fill(QTextTable *textTable, KDReports::ReportBuilder &builder, QTextDocument &textDoc, QTextTableCell &cell);
74 
75 private:
76  void insertDecoration(KDReports::ReportBuilder &builder, QTextDocument &textDoc);
77  static QString displayText(const QVariant &value);
78 
79  QSize iconSize;
80  QVariant cellDecoration;
81  QVariant cellFont;
82  QString cellText;
83  QVariant foreground;
84  QVariant background;
85  Qt::Alignment alignment;
86  QVariant decorationAlignment;
87  bool nonBreakableLines;
88  QSize span;
89 
90  QTextCursor cellCursor;
91 };
92 
93 void FillCellHelper::fill(QTextTable *textTable, KDReports::ReportBuilder &builder, QTextDocument &textDoc, QTextTableCell &cell)
94 {
95  cellCursor = cell.firstCursorPosition();
96  QTextCharFormat cellFormat = cell.format();
97  if (background.canConvert<QBrush>()) {
98  cellFormat.setBackground(qvariant_cast<QBrush>(background));
99  }
100  cellFormat.setVerticalAlignment(KDReports::ReportBuilder::toVerticalAlignment(alignment));
101  cell.setFormat(cellFormat);
102 
103  QTextBlockFormat blockFormat = cellCursor.blockFormat();
104  blockFormat.setAlignment(alignment);
105  blockFormat.setNonBreakableLines(nonBreakableLines);
106  builder.setupBlockFormat(blockFormat);
107 
108  cellCursor.setBlockFormat(blockFormat);
109 
110  const bool hasIcon = !cellDecoration.isNull();
111  const bool iconAfterText = decorationAlignment.isValid() && (decorationAlignment.toInt() & Qt::AlignRight);
112  if (hasIcon && !iconAfterText) {
113  insertDecoration(builder, textDoc);
114  }
115 
116  QTextCharFormat charFormat = cellCursor.charFormat();
117  if (cellFont.isValid()) {
118  QFont cellQFont = qvariant_cast<QFont>(cellFont);
119 #if QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)
120  charFormat.setFont(cellQFont, QTextCharFormat::FontPropertiesSpecifiedOnly);
121 #else
122  charFormat.setFont(cellQFont);
123 #endif
124  } else {
125  charFormat.setFont(builder.defaultFont());
126  }
127  if (foreground.canConvert<QBrush>()) {
128  charFormat.setForeground(qvariant_cast<QBrush>(foreground));
129  }
130  cellCursor.setCharFormat(charFormat);
131 
132  if (hasIcon && !iconAfterText) {
133  cellCursor.insertText(QChar::fromLatin1(' ')); // spacing between icon and text
134  }
135 
136  // qDebug() << cellText;
137  if (cellText.startsWith(QLatin1String("<qt>")) || cellText.startsWith(QLatin1String("<html>")))
138  cellCursor.insertHtml(cellText);
139  else
140  cellCursor.insertText(cellText);
141 
142  if (hasIcon && iconAfterText) {
143  cellCursor.insertText(QChar::fromLatin1(' ')); // spacing between icon and text
144  insertDecoration(builder, textDoc);
145  }
146 
147  if (span.width() > 1 || span.height() > 1)
148  textTable->mergeCells(cell.row(), cell.column(), span.height(), span.width());
149 }
150 
151 void FillCellHelper::insertDecoration(KDReports::ReportBuilder &builder, QTextDocument &textDoc)
152 {
153  QImage img = qvariant_cast<QImage>(cellDecoration);
154  if (img.isNull()) {
155  img = qvariant_cast<QIcon>(cellDecoration).pixmap(iconSize).toImage();
156  }
157  if (!img.isNull()) {
158  static int imageNumber = 0;
159  const QString name = QStringLiteral("cell-image%1.png").arg(++imageNumber);
160  textDoc.addResource(QTextDocument::ImageResource, QUrl(name), img);
161  builder.currentDocumentData().addResourceName(name);
162  cellCursor.insertImage(name);
163  }
164 }
165 
166 QString FillCellHelper::displayText(const QVariant &value)
167 {
168  QLocale locale; // in QStyledItemDelegate this is configurable, it comes from QWidget::locale()...
169  QString text;
170  switch (value.userType()) {
171  case QMetaType::Float:
172  case QVariant::Double:
173  text = locale.toString(value.toReal());
174  break;
175  case QVariant::Int:
176  case QVariant::LongLong:
177  text = locale.toString(value.toLongLong());
178  break;
179  case QVariant::UInt:
180  case QVariant::ULongLong:
181  text = locale.toString(value.toULongLong());
182  break;
183  case QVariant::Date:
184  text = locale.toString(value.toDate(), QLocale::ShortFormat);
185  break;
186  case QVariant::Time:
187  text = locale.toString(value.toTime(), QLocale::ShortFormat);
188  break;
189  case QVariant::DateTime:
190  text = locale.toString(value.toDateTime().date(), QLocale::ShortFormat);
191  text += QLatin1Char(' ');
192  text += locale.toString(value.toDateTime().time(), QLocale::ShortFormat);
193  break;
194  default:
195  text = value.toString();
196  break;
197  }
198  return text;
199 }
200 
202 
203 KDReports::AutoTableElement::AutoTableElement(QAbstractItemModel *tableModel)
204  : d(new AutoTableElementPrivate)
205 {
206  d->m_tableModel = tableModel;
207 }
208 
210  : d(new AutoTableElementPrivate)
211 {
212  d->m_tableModel = KDReports::modelForKey(modelKey);
213 }
214 
216  : AbstractTableElement(other)
217  , d(new AutoTableElementPrivate(*other.d))
218 {
219 }
220 
222 {
223  if (&other == this)
224  return *this;
226  *d = *other.d;
227  return *this;
228 }
229 
231 {
232 }
233 
234 void KDReports::AutoTableElementPrivate::fillCellFromHeaderData(int section, Qt::Orientation orientation, QTextTableCell &cell, QTextDocument &textDoc, QTextTable *textTable,
235  ReportBuilder &builder) const
236 {
237  FillCellHelper helper(m_tableModel, section, orientation, m_iconSize);
238  helper.fill(textTable, builder, textDoc, cell);
239 }
240 
241 QSize KDReports::AutoTableElementPrivate::fillTableCell(int row, int column, QTextTableCell &cell, QTextDocument &textDoc, QTextTable *textTable, ReportBuilder &builder) const
242 {
243  const QModelIndex index = m_tableModel->index(row, column);
244  const QSize span = m_tableModel->span(index);
245  FillCellHelper helper(m_tableModel, index, span, m_iconSize);
246  helper.fill(textTable, builder, textDoc, cell);
247  return span;
248 }
249 
250 void KDReports::AutoTableElementPrivate::createHorizontalHeader(ReportBuilder &builder, QTextDocument &textDoc, QTextTable *textTable, int columns, int headerColumnCount) const
251 {
252  if (m_horizontalHeaderVisible) {
253  for (int column = 0; column < columns; column++) {
254  QTextTableCell cell = textTable->cellAt(0, column + headerColumnCount);
255  Q_ASSERT(cell.isValid());
256  QTextTableCellFormat tableHeaderFormat;
257  tableHeaderFormat.setBackground(m_headerBackground);
258  if (m_horizontalHeaderFormatFunc)
259  m_horizontalHeaderFormatFunc(column, tableHeaderFormat);
260  cell.setFormat(tableHeaderFormat);
261  fillCellFromHeaderData(column, Qt::Horizontal, cell, textDoc, textTable, builder);
262  }
263  }
264 }
265 
266 void KDReports::AutoTableElementPrivate::createVerticalHeader(ReportBuilder &builder, QTextDocument &textDoc, QTextTable *textTable, int rows, int headerRowCount) const
267 {
268  if (m_verticalHeaderVisible) {
269  for (int row = 0; row < rows; row++) {
270  QTextTableCell cell = textTable->cellAt(row + headerRowCount, 0);
271  Q_ASSERT(cell.isValid());
272  QTextTableCellFormat tableHeaderFormat;
273  tableHeaderFormat.setBackground(m_headerBackground);
274  if (m_verticalHeaderFormatFunc)
275  m_verticalHeaderFormatFunc(row, tableHeaderFormat);
276  cell.setFormat(tableHeaderFormat);
277  fillCellFromHeaderData(row, Qt::Vertical, cell, textDoc, textTable, builder);
278  }
279  }
280 }
281 
283 {
284  if (!d->m_tableModel) {
285  return;
286  }
287  QTextDocument &textDoc = builder.currentDocument();
288  QTextCursor &textDocCursor = builder.cursor();
289  textDocCursor.beginEditBlock();
290 
291  QTextTableFormat tableFormat;
292  const int headerRowCount = d->m_horizontalHeaderVisible ? 1 : 0;
293  const int headerColumnCount = d->m_verticalHeaderVisible ? 1 : 0;
294  tableFormat.setHeaderRowCount(headerRowCount);
295  tableFormat.setProperty(KDReports::HeaderColumnsProperty, headerColumnCount);
296 
297  tableFormat.setAlignment(textDocCursor.blockFormat().alignment());
298  fillTableFormat(tableFormat, textDocCursor);
299 
300  while (d->m_tableModel->canFetchMore(QModelIndex()))
301  d->m_tableModel->fetchMore(QModelIndex());
302 
303  const int rows = d->m_tableModel->rowCount();
304  const int columns = d->m_tableModel->columnCount();
305 
306  QTextTable *textTable = textDocCursor.insertTable(rows + headerRowCount, columns + headerColumnCount, tableFormat);
307 
308  // qDebug( "rows = %d, columns = %d", textTable->rows(), textTable->columns() );
309 
310  d->createHorizontalHeader(builder, textDoc, textTable, columns, headerColumnCount);
311  d->createVerticalHeader(builder, textDoc, textTable, rows, headerRowCount);
312 
313  QVector<QBitArray> coveredCells;
314  coveredCells.resize(rows);
315  for (int row = 0; row < rows; row++)
316  coveredCells[row].resize(columns);
317 
318  // The normal data
319  for (int row = 0; row < rows; row++) {
320  for (int column = 0; column < columns; column++) {
321  if (coveredCells[row].testBit(column))
322  continue;
323  QTextTableCell cell = textTable->cellAt(row + headerRowCount, column + headerColumnCount);
324  Q_ASSERT(cell.isValid());
325  const QSize span = d->fillTableCell(row, column, cell, textDoc, textTable, builder);
326  if (span.isValid()) {
327  for (int r = row; r < row + span.height() && r < rows; ++r) {
328  for (int c = column; c < column + span.width() && c < columns; ++c) {
329  coveredCells[r].setBit(c);
330  }
331  }
332  }
333  }
334  }
335 
336  textDocCursor.movePosition(QTextCursor::End);
337  textDocCursor.endEditBlock();
338 
339  builder.currentDocumentData().registerAutoTable(textTable, this);
340 }
341 
343 {
344  // never used at the moment
345  return new AutoTableElement(*this);
346 }
347 
349 {
350  d->m_verticalHeaderVisible = visible;
351 }
352 
354 {
355  d->m_horizontalHeaderVisible = visible;
356 }
357 
359 {
360  d->m_headerBackground = brush;
361 }
362 
364 {
365  return d->m_verticalHeaderVisible;
366 }
367 
369 {
370  return d->m_horizontalHeaderVisible;
371 }
372 
374 {
375  d->m_iconSize = iconSize;
376 }
377 
379 {
380  return d->m_iconSize;
381 }
382 
383 QAbstractItemModel *KDReports::AutoTableElement::tableModel() const
384 {
385  return d->m_tableModel;
386 }
387 
388 void KDReports::AutoTableElement::setTableModel(QAbstractItemModel *tableModel)
389 {
390  d->m_tableModel = tableModel;
391 }
392 
393 void KDReports::AutoTableElement::setModelKey(const QString &modelKey)
394 {
395  d->m_tableModel = KDReports::modelForKey(modelKey);
396 }
397 
399 {
400  return d->m_headerBackground;
401 }
402 
404 {
405  d->m_horizontalHeaderFormatFunc = func;
406 }
407 
409 {
410  d->m_verticalHeaderFormatFunc = func;
411 }
AbstractTableElement & operator=(const AbstractTableElement &other)
QAbstractItemModel * tableModel() const
AutoTableElement(QAbstractItemModel *tableModel)
AutoTableElement & operator=(const AutoTableElement &other)
void setModelKey(const QString &modelKey)
void setVerticalHeaderFormatFunction(const CellFormatFunc &func)
Sets the function to call in order to customize the format of the cells created for the horizontal he...
void setTableModel(QAbstractItemModel *tableModel)
void setHorizontalHeaderFormatFunction(const CellFormatFunc &func)
Sets the function to call in order to customize the format of the cells created for the horizontal he...
std::function< void(int, QTextTableCellFormat &)> CellFormatFunc
void build(ReportBuilder &) const override
void setHeaderBackground(const QBrush &brush)
TextDocumentData & currentDocumentData()
static QTextCharFormat::VerticalAlignment toVerticalAlignment(Qt::Alignment alignment)
void setupBlockFormat(QTextBlockFormat &blockFormat) const
void addResourceName(const QString &resourceName)
void registerAutoTable(QTextTable *table, const KDReports::AutoTableElement *element)
QAbstractItemModel * modelForKey(const QString &key)

© Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
https://www.kdab.com/development-resources/qt-tools/kd-reports/
Generated by doxygen 1.9.1